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

Commit 25f0275d authored by Evan Laird's avatar Evan Laird
Browse files

Add a third notification section

A bunch of things, but they are (mostly) hidden behind a flag:

- Made the NotificationEntry#bucket concept more generic
- Added logic to NotificationData to set a bucket int on each entry
- Flag config_usePeopleFiltering in systemui.config turns on new behavior
- Reduced the number of hacks in NotificationData to 1. Now it sets the
  buckets n the entries only once post-sort
- NotificationEntry has a basic check for "people"
- NSSL delegates to NotificationData/NotificationSectionsManager for
  creating sections
- NotificationSectionsManager can now manage any number of sections

The basic gist of this change is to enable and partially implement a
"people" notification section. In order to do that, we have to do a
little bit of cleanup to make NotificationSections more generic, then
find a way to differentiate "people" notifications.

To generify the sections logic, this change furthers the concept of
notification "buckets". A bucket is entirely a concept shared between
NotificationData and NotificationEntry, but with the intention that each
bucket will get its own section. Once a set of buckets is decided upon,
NSSL tells NotificationSectionsManager to create the necessary sections.
NSSL also will need to ask the sections manager to check the entire list
of view in the panel for section boundaries, since they can be anywhere
and there can be any number of them.

The people filtering is currently straightforward. NotifiationEntry
checks for EXTRA_PEOPLE_LIST or EXTRA_MESSAGES and checks for people
information on a notification. If it exists, then that entry gets sorted
to the top and will get its own bucket set (if the setting is on).

Test: visual
Bug: 140232781
Change-Id: I7fa5c4d7509f2ca5f485216f2de0160c91802235
parent 82a9cd16
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -480,4 +480,7 @@
    <!-- Preferred refresh rate at keyguard, if supported by the display -->
    <integer name="config_keyguardRefreshRate">-1</integer>

    <!-- Whether or not to add a "people" notifications section -->
    <bool name="config_usePeopleFiltering">false</bool>

</resources>
+2 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;

import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;

import android.app.ActivityManager;
import android.app.KeyguardManager;
@@ -316,7 +317,7 @@ public class NotificationLockscreenUserManagerImpl implements
        boolean exceedsPriorityThreshold;
        if (NotificationUtils.useNewInterruptionModel(mContext)
                && hideSilentNotificationsOnLockscreen()) {
            exceedsPriorityThreshold = entry.isTopBucket();
            exceedsPriorityThreshold = entry.getBucket() != BUCKET_SILENT;
        } else {
            exceedsPriorityThreshold =
                    !getEntryManager().getNotificationData().isAmbient(entry.key);
+1 −1
Original line number Diff line number Diff line
@@ -124,7 +124,7 @@ public class NotificationEntryManager implements

    @Inject
    public NotificationEntryManager(Context context) {
        mNotificationData = new NotificationData();
        mNotificationData = new NotificationData(context);
    }

    /** Adds a {@link NotificationEntryListener}. */
+14 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ public class NotificationUtils {
    private static final int[] sLocationOffset = new int[2];

    @Nullable private static Boolean sUseNewInterruptionModel = null;
    @Nullable private static Boolean sUsePeopleFiltering = null;

    public static boolean isGrayscale(ImageView v, ContrastColorUtil colorUtil) {
        Object isGrayscale = v.getTag(R.id.icon_is_grayscale);
@@ -87,4 +88,17 @@ public class NotificationUtils {
        }
        return sUseNewInterruptionModel;
    }

    /**
     * Caches and returns the value of the people filtering setting. Cannot change except through
     * process restarts.
     */
    public static boolean usePeopleFiltering(Context context) {
        if (sUsePeopleFiltering == null) {
            sUsePeopleFiltering = context.getResources().getBoolean(
                    R.bool.config_usePeopleFiltering);
        }

        return sUsePeopleFiltering;
    }
}
+82 −52
Original line number Diff line number Diff line
@@ -16,10 +16,15 @@

package com.android.systemui.statusbar.notification.collection;

import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
import android.content.Context;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.SnoozeCriterion;
@@ -30,6 +35,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;

@@ -44,6 +50,7 @@ import java.util.Objects;
 * The list of currently displaying notifications.
 */
public class NotificationData {
    private static final String TAG = "NotificationData";

    private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);

@@ -64,6 +71,11 @@ public class NotificationData {

    private RankingMap mRankingMap;
    private final Ranking mTmpRanking = new Ranking();
    private final boolean mUsePeopleFiltering;

    public NotificationData(Context context) {
        mUsePeopleFiltering = NotificationUtils.usePeopleFiltering(context);
    }

    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
        mHeadsUpManager = headsUpManager;
@@ -72,52 +84,25 @@ public class NotificationData {
    @VisibleForTesting
    protected final Comparator<NotificationEntry> mRankingComparator =
            new Comparator<NotificationEntry>() {
        private final Ranking mRankingA = new Ranking();
        private final Ranking mRankingB = new Ranking();

        @Override
        public int compare(NotificationEntry a, NotificationEntry b) {
            final StatusBarNotification na = a.notification;
            final StatusBarNotification nb = b.notification;
            int aImportance = NotificationManager.IMPORTANCE_DEFAULT;
            int bImportance = NotificationManager.IMPORTANCE_DEFAULT;
            int aRank = 0;
            int bRank = 0;

            if (mRankingMap != null) {
                // RankingMap as received from NoMan
                getRanking(a.key, mRankingA);
                getRanking(b.key, mRankingB);
                aImportance = mRankingA.getImportance();
                bImportance = mRankingB.getImportance();
                aRank = mRankingA.getRank();
                bRank = mRankingB.getRank();
            }

            String mediaNotification = getMediaManager().getMediaNotificationKey();

            // IMPORTANCE_MIN media streams are allowed to drift to the bottom
            final boolean aMedia = a.key.equals(mediaNotification)
                    && aImportance > NotificationManager.IMPORTANCE_MIN;
            final boolean bMedia = b.key.equals(mediaNotification)
                    && bImportance > NotificationManager.IMPORTANCE_MIN;

            boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH
                    && isSystemNotification(na);
            boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH
                    && isSystemNotification(nb);
            int aRank = getRank(a.key);
            int bRank = getRank(b.key);

            boolean aMedia = isImportantMedia(a);
            boolean bMedia = isImportantMedia(b);

            boolean aHeadsUp = a.getRow().isHeadsUp();
            boolean bHeadsUp = b.getRow().isHeadsUp();
            boolean aSystemMax = isSystemMax(a);
            boolean bSystemMax = isSystemMax(b);

            // HACK: This should really go elsewhere, but it's currently not straightforward to
            // extract the comparison code and we're guaranteed to touch every element, so this is
            // the best place to set the buckets for the moment.
            a.setIsTopBucket(aHeadsUp || aMedia || aSystemMax || a.isHighPriority());
            b.setIsTopBucket(bHeadsUp || bMedia || bSystemMax || b.isHighPriority());
            boolean aHeadsUp = a.isRowHeadsUp();
            boolean bHeadsUp = b.isRowHeadsUp();

            if (aHeadsUp != bHeadsUp) {
            if (mUsePeopleFiltering && a.hasAssociatedPeople() != b.hasAssociatedPeople()) {
                return a.hasAssociatedPeople() ? -1 : 1;
            } else if (aHeadsUp != bHeadsUp) {
                return aHeadsUp ? -1 : 1;
            } else if (aHeadsUp) {
                // Provide consistent ranking with headsUpManager
@@ -317,14 +302,6 @@ public class NotificationData {
        return Ranking.VISIBILITY_NO_OVERRIDE;
    }

    public int getImportance(String key) {
        if (mRankingMap != null) {
            getRanking(key, mTmpRanking);
            return mTmpRanking.getImportance();
        }
        return NotificationManager.IMPORTANCE_UNSPECIFIED;
    }

    public List<SnoozeCriterion> getSnoozeCriteria(String key) {
        if (mRankingMap != null) {
            getRanking(key, mTmpRanking);
@@ -349,6 +326,22 @@ public class NotificationData {
        return 0;
    }

    private boolean isImportantMedia(NotificationEntry e) {
        int importance = e.ranking().getImportance();
        boolean media = e.key.equals(getMediaManager().getMediaNotificationKey())
                && importance > NotificationManager.IMPORTANCE_MIN;

        return media;
    }

    private boolean isSystemMax(NotificationEntry e) {
        int importance = e.ranking().getImportance();
        boolean sys = importance  >= NotificationManager.IMPORTANCE_HIGH
                && isSystemNotification(e.notification);

        return sys;
    }

    public boolean shouldHide(String key) {
        if (mRankingMap != null) {
            getRanking(key, mTmpRanking);
@@ -414,13 +407,37 @@ public class NotificationData {
            }
        }

        if (mSortedAndFiltered.size() == 1) {
            // HACK: We need the comparator to run on all children in order to set the
            // isHighPriority field. If there is only one child, then the comparison won't be run,
            // so we have to trigger it manually. Get rid of this code as soon as possible.
            mRankingComparator.compare(mSortedAndFiltered.get(0), mSortedAndFiltered.get(0));
        } else {
        Collections.sort(mSortedAndFiltered, mRankingComparator);

        int bucket = BUCKET_PEOPLE;
        for (NotificationEntry e : mSortedAndFiltered) {
            assignBucketForEntry(e);
            if (e.getBucket() < bucket) {
                android.util.Log.wtf(TAG, "Detected non-contiguous bucket!");
            }
            bucket = e.getBucket();
        }
    }

    private void assignBucketForEntry(NotificationEntry e) {
        boolean isHeadsUp = e.isRowHeadsUp();
        boolean isMedia = isImportantMedia(e);
        boolean isSystemMax = isSystemMax(e);

        setBucket(e, isHeadsUp, isMedia, isSystemMax);
    }

    private void setBucket(
            NotificationEntry e,
            boolean isHeadsUp,
            boolean isMedia,
            boolean isSystemMax) {
        if (mUsePeopleFiltering && e.hasAssociatedPeople()) {
            e.setBucket(BUCKET_PEOPLE);
        } else if (isHeadsUp || isMedia || isSystemMax || e.isHighPriority()) {
            e.setBucket(BUCKET_ALERTING);
        } else {
            e.setBucket(BUCKET_SILENT);
        }
    }

@@ -465,6 +482,19 @@ public class NotificationData {
        return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage);
    }

    /**
     * Get the current set of buckets for notification entries, as defined here
     */
    public static int[] getNotificationBuckets(Context context) {
        if (NotificationUtils.usePeopleFiltering(context)) {
            return new int[]{BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT};
        } else if (NotificationUtils.useNewInterruptionModel(context)) {
            return new int[]{BUCKET_ALERTING, BUCKET_SILENT};
        } else {
            return new int[]{BUCKET_ALERTING};
        }
    }

    /**
     * Provides access to keyguard state and user settings dependent data.
     */
Loading