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

Commit 7a35b959 authored by Ned Burns's avatar Ned Burns Committed by Android (Google) Code Review
Browse files

Merge "Don't auto-dismiss children with certain flags"

parents fa65b021 2e639666
Loading
Loading
Loading
Loading
+24 −2
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import static java.util.Objects.requireNonNull;
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.app.Notification;
import android.os.RemoteException;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -239,8 +240,7 @@ public class NotifCollection implements Dumpable {
            // Also mark any children as dismissed as system server will auto-dismiss them as well
            if (entry.getSbn().getNotification().isGroupSummary()) {
                for (NotificationEntry otherEntry : mNotificationSet.values()) {
                    if (otherEntry.getSbn().getGroupKey().equals(entry.getSbn().getGroupKey())
                            && otherEntry.getDismissState() != DISMISSED) {
                    if (shouldAutoDismiss(otherEntry, entry.getSbn().getGroupKey())) {
                        otherEntry.setDismissState(PARENT_DISMISSED);
                        if (isCanceled(otherEntry)) {
                            canceledEntries.add(otherEntry);
@@ -544,6 +544,28 @@ public class NotifCollection implements Dumpable {
        return entry.getDismissState() != NOT_DISMISSED;
    }

    /**
     * When a group summary is dismissed, NotificationManager will also try to dismiss its children.
     * Returns true if we think dismissing the group summary with group key
     * <code>dismissedGroupKey</code> will cause NotificationManager to also dismiss
     * <code>entry</code>.
     *
     * See NotificationManager.cancelGroupChildrenByListLocked() for corresponding code.
     */
    private static boolean shouldAutoDismiss(
            NotificationEntry entry,
            String dismissedGroupKey) {
        return entry.getSbn().getGroupKey().equals(dismissedGroupKey)
                && !entry.getSbn().getNotification().isGroupSummary()
                && !hasFlag(entry, Notification.FLAG_FOREGROUND_SERVICE)
                && !hasFlag(entry, Notification.FLAG_BUBBLE)
                && entry.getDismissState() != DISMISSED;
    }

    private static boolean hasFlag(NotificationEntry entry, int flag) {
        return (entry.getSbn().getNotification().flags & flag) != 0;
    }

    private void dispatchOnEntryInit(NotificationEntry entry) {
        mAmDispatchingToOtherCode = true;
        for (NotifCollectionListener listener : mNotifCollectionListeners) {
+5 −0
Original line number Diff line number Diff line
@@ -140,6 +140,11 @@ public class SbnBuilder {
        return this;
    }

    public SbnBuilder setFlag(Context context, int mask, boolean value) {
        modifyNotification(context).setFlag(mask, value);
        return this;
    }

    public Notification.Builder modifyNotification(Context context) {
        if (mNotification != null) {
            mNotificationBuilder = new Notification.Builder(context, mNotification);
+103 −0
Original line number Diff line number Diff line
@@ -46,9 +46,12 @@ import static org.mockito.Mockito.when;
import static java.util.Objects.requireNonNull;

import android.annotation.Nullable;
import android.app.Notification;
import android.os.RemoteException;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
@@ -733,6 +736,78 @@ public class NotifCollectionTest extends SysuiTestCase {
        assertEquals(NOT_DISMISSED, entry1.getDismissState());
    }

    @Test
    public void testDismissingSummaryDoesNotDismissForegroundServiceChildren() {
        // GIVEN a collection with three grouped notifs in it
        CollectionEvent notif0 = postNotif(
                buildNotif(TEST_PACKAGE, 0)
                        .setGroup(mContext, GROUP_1)
                        .setGroupSummary(mContext, true));
        CollectionEvent notif1 = postNotif(
                buildNotif(TEST_PACKAGE, 1)
                        .setGroup(mContext, GROUP_1)
                        .setFlag(mContext, Notification.FLAG_FOREGROUND_SERVICE, true));
        CollectionEvent notif2 = postNotif(
                buildNotif(TEST_PACKAGE, 2)
                        .setGroup(mContext, GROUP_1));

        // WHEN the summary is dismissed
        mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry));

        // THEN the foreground service child is not dismissed
        assertEquals(DISMISSED, notif0.entry.getDismissState());
        assertEquals(NOT_DISMISSED, notif1.entry.getDismissState());
        assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState());
    }

    @Test
    public void testDismissingSummaryDoesNotDismissBubbledChildren() {
        // GIVEN a collection with three grouped notifs in it
        CollectionEvent notif0 = postNotif(
                buildNotif(TEST_PACKAGE, 0)
                        .setGroup(mContext, GROUP_1)
                        .setGroupSummary(mContext, true));
        CollectionEvent notif1 = postNotif(
                buildNotif(TEST_PACKAGE, 1)
                        .setGroup(mContext, GROUP_1)
                        .setFlag(mContext, Notification.FLAG_BUBBLE, true));
        CollectionEvent notif2 = postNotif(
                buildNotif(TEST_PACKAGE, 2)
                        .setGroup(mContext, GROUP_1));

        // WHEN the summary is dismissed
        mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry));

        // THEN the bubbled child is not dismissed
        assertEquals(DISMISSED, notif0.entry.getDismissState());
        assertEquals(NOT_DISMISSED, notif1.entry.getDismissState());
        assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState());
    }

    @Test
    public void testDismissingSummaryDoesNotDismissDuplicateSummaries() {
        // GIVEN a group with a two summaries
        CollectionEvent notif0 = postNotif(
                buildNotif(TEST_PACKAGE, 0)
                        .setGroup(mContext, GROUP_1)
                        .setGroupSummary(mContext, true));
        CollectionEvent notif1 = postNotif(
                buildNotif(TEST_PACKAGE, 1)
                        .setGroup(mContext, GROUP_1)
                        .setGroupSummary(mContext, true));
        CollectionEvent notif2 = postNotif(
                buildNotif(TEST_PACKAGE, 2)
                        .setGroup(mContext, GROUP_1));

        // WHEN the first summary is dismissed
        mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry));

        // THEN the second summary is not auto-dismissed (but the child is)
        assertEquals(DISMISSED, notif0.entry.getDismissState());
        assertEquals(NOT_DISMISSED, notif1.entry.getDismissState());
        assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState());
    }

    @Test
    public void testLifetimeExtendersAreQueriedWhenNotifRemoved() {
        // GIVEN a couple notifications and a few lifetime extenders
@@ -1000,6 +1075,13 @@ public class NotifCollectionTest extends SysuiTestCase {
                NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
    }

    public CollectionEvent postNotif(NotificationEntryBuilder builder) {
        clearInvocations(mCollectionListener);
        NotifEvent rawEvent = mNoMan.postNotif(builder);
        verify(mCollectionListener).onEntryAdded(mEntryCaptor.capture());
        return new CollectionEvent(rawEvent, requireNonNull(mEntryCaptor.getValue()));
    }

    private static class RecordingCollectionListener implements NotifCollectionListener {
        private final Map<String, NotificationEntry> mLastSeenEntries = new ArrayMap<>();

@@ -1014,6 +1096,7 @@ public class NotifCollectionTest extends SysuiTestCase {

        @Override
        public void onEntryUpdated(NotificationEntry entry) {
            mLastSeenEntries.put(entry.getKey(), entry);
        }

        @Override
@@ -1098,6 +1181,26 @@ public class NotifCollectionTest extends SysuiTestCase {
        }
    }

    /**
     * Wrapper around {@link NotifEvent} that adds the NotificationEntry that the collection under
     * test creates.
     */
    private static class CollectionEvent {
        public final String key;
        public final StatusBarNotification sbn;
        public final Ranking ranking;
        public final RankingMap rankingMap;
        public final NotificationEntry entry;

        private CollectionEvent(NotifEvent rawEvent, NotificationEntry entry) {
            this.key = rawEvent.key;
            this.sbn = rawEvent.sbn;
            this.ranking = rawEvent.ranking;
            this.rankingMap = rawEvent.rankingMap;
            this.entry = entry;
        }
    }

    private static final String TEST_PACKAGE = "com.android.test.collection";
    private static final String TEST_PACKAGE2 = "com.android.test.collection2";

+5 −0
Original line number Diff line number Diff line
@@ -162,6 +162,11 @@ public class NotificationEntryBuilder {
        return this;
    }

    public NotificationEntryBuilder setFlag(Context context, int mask, boolean value) {
        mSbnBuilder.setFlag(context, mask, value);
        return this;
    }

    /* Delegated to RankingBuilder */

    public NotificationEntryBuilder setRank(int rank) {