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

Commit 7f19a248 authored by Flavio Fiszman's avatar Flavio Fiszman
Browse files

Calculate People Tiles messages count properly

Change-Id: If072e43e1c827ebbf7250f832b969118df81755a
Test: manual
Bug: 183382000
parent 5a96ceaf
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