Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit baa72ebf authored by Flavio Fiszman's avatar Flavio Fiszman Committed by Android (Google) Code Review
Browse files

Merge "Calculate People Tiles messages count properly" into sc-dev

parents ae88ce7e 7f19a248
Loading
Loading
Loading
Loading
+213 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.systemui.people;

import static android.Manifest.permission.READ_CONTACTS;
import static android.app.Notification.CATEGORY_MISSED_CALL;
import static android.app.Notification.EXTRA_MESSAGES;
import static android.app.Notification.EXTRA_PEOPLE_LIST;

import android.annotation.Nullable;
import android.app.Notification;
import android.app.Person;
import android.content.pm.PackageManager;
import android.os.Parcelable;
import android.service.notification.StatusBarNotification;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/** Helper functions to handle notifications in People Tiles. */
public class NotificationHelper {
    private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
    private static final String TAG = "PeopleNotificationHelper";

    /** Returns the notification with highest priority to be shown in People Tiles. */
    public static NotificationEntry getHighestPriorityNotification(
            Set<NotificationEntry> notificationEntries) {
        if (notificationEntries == null || notificationEntries.isEmpty()) {
            return null;
        }

        return notificationEntries
                .stream()
                .filter(NotificationHelper::isMissedCallOrHasContent)
                .sorted(notificationEntryComparator)
                .findFirst().orElse(null);
    }


    /** Notification comparator, checking category and timestamps, in reverse order of priority. */
    public static Comparator<NotificationEntry> notificationEntryComparator =
            new Comparator<NotificationEntry>() {
                @Override
                public int compare(NotificationEntry e1, NotificationEntry e2) {
                    Notification n1 = e1.getSbn().getNotification();
                    Notification n2 = e2.getSbn().getNotification();

                    boolean missedCall1 = isMissedCall(n1);
                    boolean missedCall2 = isMissedCall(n2);
                    if (missedCall1 && !missedCall2) {
                        return -1;
                    }
                    if (!missedCall1 && missedCall2) {
                        return 1;
                    }

                    // Get messages in reverse chronological order.
                    List<Notification.MessagingStyle.Message> messages1 =
                            getMessagingStyleMessages(n1);
                    List<Notification.MessagingStyle.Message> messages2 =
                            getMessagingStyleMessages(n2);

                    if (messages1 != null && messages2 != null) {
                        Notification.MessagingStyle.Message message1 = messages1.get(0);
                        Notification.MessagingStyle.Message message2 = messages2.get(0);
                        return (int) (message2.getTimestamp() - message1.getTimestamp());
                    }

                    if (messages1 == null) {
                        return 1;
                    }
                    if (messages2 == null) {
                        return -1;
                    }
                    return (int) (n2.when - n1.when);
                }
            };

    /** Returns whether {@code e} is a missed call notification. */
    public static boolean isMissedCall(NotificationEntry e) {
        return e != null && e.getSbn().getNotification() != null
                && isMissedCall(e.getSbn().getNotification());
    }

    /** Returns whether {@code notification} is a missed call notification. */
    public static boolean isMissedCall(Notification notification) {
        return notification != null && Objects.equals(notification.category, CATEGORY_MISSED_CALL);
    }

    private static boolean hasContent(NotificationEntry e) {
        if (e == null) {
            return false;
        }
        List<Notification.MessagingStyle.Message> messages =
                getMessagingStyleMessages(e.getSbn().getNotification());
        return messages != null && !messages.isEmpty();
    }

    /** Returns whether {@code e} is a valid conversation notification. */
    public static boolean isValid(NotificationEntry e) {
        return e != null && e.getRanking() != null
                && e.getRanking().getConversationShortcutInfo() != null
                && e.getSbn().getNotification() != null;
    }

    /** Returns whether conversation notification should be shown in People Tile. */
    public static boolean isMissedCallOrHasContent(NotificationEntry e) {
        return isMissedCall(e) || hasContent(e);
    }

    /** Returns whether {@code sbn}'s package has permission to read contacts. */
    public static boolean hasReadContactsPermission(
            PackageManager packageManager, StatusBarNotification sbn) {
        return packageManager.checkPermission(READ_CONTACTS,
                sbn.getPackageName()) == PackageManager.PERMISSION_GRANTED;
    }

    /**
     * Returns whether a notification should be matched to other Tiles by Uri.
     *
     * <p>Currently only matches missed calls.
     */
    public static boolean shouldMatchNotificationByUri(StatusBarNotification sbn) {
        Notification notification = sbn.getNotification();
        if (notification == null) {
            if (DEBUG) Log.d(TAG, "Notification is null");
            return false;
        }
        boolean isMissedCall = isMissedCall(notification);
        if (!isMissedCall) {
            if (DEBUG) Log.d(TAG, "Not missed call");
        }
        return isMissedCall;
    }

    /**
     * Try to retrieve a valid Uri via {@code sbn}, falling back to the {@code
     * contactUriFromShortcut} if valid.
     */
    @Nullable
    public static String getContactUri(StatusBarNotification sbn) {
        // First, try to get a Uri from the Person directly set on the Notification.
        ArrayList<Person> people = sbn.getNotification().extras.getParcelableArrayList(
                EXTRA_PEOPLE_LIST);
        if (people != null && people.get(0) != null) {
            String contactUri = people.get(0).getUri();
            if (contactUri != null && !contactUri.isEmpty()) {
                return contactUri;
            }
        }

        // Then, try to get a Uri from the Person set on the Notification message.
        List<Notification.MessagingStyle.Message> messages =
                getMessagingStyleMessages(sbn.getNotification());
        if (messages != null && !messages.isEmpty()) {
            Notification.MessagingStyle.Message message = messages.get(0);
            Person sender = message.getSenderPerson();
            if (sender != null && sender.getUri() != null && !sender.getUri().isEmpty()) {
                return sender.getUri();
            }
        }

        return null;
    }

    /**
     * Returns {@link Notification.MessagingStyle.Message}s from the Notification in chronological
     * order from most recent to least.
     */
    @VisibleForTesting
    @Nullable
    public static List<Notification.MessagingStyle.Message> getMessagingStyleMessages(
            Notification notification) {
        if (notification == null) {
            return null;
        }
        if (Notification.MessagingStyle.class.equals(notification.getNotificationStyle())
                && notification.extras != null) {
            final Parcelable[] messages = notification.extras.getParcelableArray(EXTRA_MESSAGES);
            if (!ArrayUtils.isEmpty(messages)) {
                List<Notification.MessagingStyle.Message> sortedMessages =
                        Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
                sortedMessages.sort(Collections.reverseOrder(
                        Comparator.comparing(Notification.MessagingStyle.Message::getTimestamp)));
                return sortedMessages;
            }
        }
        return null;
    }
}
+8 −22
Original line number Diff line number Diff line
@@ -16,36 +16,28 @@

package com.android.systemui.people;

import android.app.people.IPeopleManager;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Log;
import android.widget.RemoteViews;

import com.android.systemui.Dependency;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.shared.system.PeopleProviderUtils;
import com.android.systemui.statusbar.notification.NotificationEntryManager;

/** API that returns a People Tile preview. */
public class PeopleProvider extends ContentProvider {

    LauncherApps mLauncherApps;
    IPeopleManager mPeopleManager;
    NotificationEntryManager mNotificationEntryManager;

    private static final String TAG = "PeopleProvider";
    private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
    private static final String EMPTY_STRING = "";

    PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;

    @Override
    public Bundle call(String method, String arg, Bundle extras) {
        if (!doesCallerHavePermission()) {
@@ -83,17 +75,11 @@ public class PeopleProvider extends ContentProvider {
            throw new IllegalArgumentException("Null user handle");
        }

        // If services are not set as mocks in tests, fetch them now.
        mPeopleManager = mPeopleManager != null ? mPeopleManager
                : IPeopleManager.Stub.asInterface(
                        ServiceManager.getService(Context.PEOPLE_SERVICE));
        mLauncherApps = mLauncherApps != null ? mLauncherApps
                : getContext().getSystemService(LauncherApps.class);
        mNotificationEntryManager = mNotificationEntryManager != null ? mNotificationEntryManager
                : Dependency.get(NotificationEntryManager.class);

        RemoteViews view = PeopleSpaceUtils.getPreview(getContext(), mPeopleManager, mLauncherApps,
                mNotificationEntryManager, shortcutId, userHandle, packageName, extras);
        if (mPeopleSpaceWidgetManager == null) {
            mPeopleSpaceWidgetManager = new PeopleSpaceWidgetManager(getContext());
        }
        RemoteViews view =
                mPeopleSpaceWidgetManager.getPreview(shortcutId, userHandle, packageName, extras);
        if (view == null) {
            if (DEBUG) Log.d(TAG, "No preview available for shortcutId: " + shortcutId);
            return null;
+5 −3
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.widget.LinearLayout;

import com.android.systemui.R;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.people.widget.PeopleTileKey;

import java.util.ArrayList;
import java.util.List;
@@ -136,14 +137,15 @@ public class PeopleSpaceActivity extends Activity {
                    getSizeInDp(mContext, R.dimen.avatar_size_for_medium,
                            mContext.getResources().getDisplayMetrics().density)));

            tileView.setOnClickListener(v -> storeWidgetConfiguration(tile));
            PeopleTileKey key = new PeopleTileKey(tile);
            tileView.setOnClickListener(v -> storeWidgetConfiguration(tile, key));
        } catch (Exception e) {
            Log.e(TAG, "Couldn't retrieve shortcut information", e);
        }
    }

    /** Stores the user selected configuration for {@code mAppWidgetId}. */
    private void storeWidgetConfiguration(PeopleSpaceTile tile) {
    private void storeWidgetConfiguration(PeopleSpaceTile tile, PeopleTileKey key) {
        if (PeopleSpaceUtils.DEBUG) {
            if (DEBUG) {
                Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: "
@@ -151,7 +153,7 @@ public class PeopleSpaceActivity extends Activity {
                        + mAppWidgetId);
            }
        }
        mPeopleSpaceWidgetManager.addNewWidget(mAppWidgetId, tile);
        mPeopleSpaceWidgetManager.addNewWidget(mAppWidgetId, key);
        finishActivity();
    }

+88 −117

File changed.

Preview size limit exceeded, changes collapsed.

+5 −3
Original line number Diff line number Diff line
@@ -71,7 +71,8 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

class PeopleTileViewHelper {
/** Functions that help creating the People tile layouts. */
public class PeopleTileViewHelper {
    /** Turns on debugging information about People Space. */
    public static final boolean DEBUG = true;
    private static final String TAG = "PeopleTileView";
@@ -115,7 +116,7 @@ class PeopleTileViewHelper {
    private Locale mLocale;
    private NumberFormat mIntegerFormat;

    PeopleTileViewHelper(Context context, PeopleSpaceTile tile,
    public PeopleTileViewHelper(Context context, PeopleSpaceTile tile,
            int appWidgetId, Bundle options) {
        mContext = context;
        mTile = tile;
@@ -346,6 +347,7 @@ class PeopleTileViewHelper {
    private RemoteViews createMissedCallRemoteViews() {
        RemoteViews views = getViewForContentLayout();
        views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
        views.setViewVisibility(R.id.messages_count, View.GONE);
        setMaxLines(views);
        views.setTextViewText(R.id.text_content, mTile.getNotificationContent());
        views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_phone_missed);
@@ -546,7 +548,6 @@ class PeopleTileViewHelper {
        if (mLayoutSize == LAYOUT_SMALL) {
            views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
            views.setViewVisibility(R.id.name, View.GONE);
            views.setViewVisibility(R.id.messages_count, View.GONE);
        } else {
            views.setViewVisibility(R.id.predefined_icon, View.GONE);
            views.setViewVisibility(R.id.name, View.VISIBLE);
@@ -561,6 +562,7 @@ class PeopleTileViewHelper {
            views.setViewPadding(R.id.item, horizontalPadding, verticalPadding, horizontalPadding,
                    verticalPadding);
        }
        views.setViewVisibility(R.id.messages_count, View.GONE);
        return views;
    }

Loading