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

Commit d0ac45a7 authored by Mady Mellor's avatar Mady Mellor
Browse files

If something is suppressed from the notification list don't bubble it

Also set it to not auto-expand since it's unexpected to have bubbles
expand when DND turns off.

Test: BubblesTest NewNotifPipelineBubblesTest
Bug: 226316876
Change-Id: Ieb77b211f2b11fea83a2ff6e34e6f31b0f96919e
parent e4d18345
Loading
Loading
Loading
Loading
+23 −10
Original line number Diff line number Diff line
@@ -1037,9 +1037,17 @@ public class BubbleController {
            }
        } else {
            Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
            if (notif.shouldSuppressNotificationList()) {
                // If we're suppressing notifs for DND, we don't want the bubbles to randomly
                // expand when DND turns off so flip the flag.
                if (bubble.shouldAutoExpand()) {
                    bubble.setShouldAutoExpand(false);
                }
            } else {
                inflateAndAdd(bubble, suppressFlyout, showInShade);
            }
        }
    }

    void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
        // Lazy init stack view when a bubble is created
@@ -1069,7 +1077,8 @@ public class BubbleController {
        }
    }

    private void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
    @VisibleForTesting
    public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
        // shouldBubbleUp checks canBubble & for bubble metadata
        boolean shouldBubble = shouldBubbleUp && canLaunchInTaskView(mContext, entry);
        if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
@@ -1095,7 +1104,8 @@ public class BubbleController {
        }
    }

    private void onRankingUpdated(RankingMap rankingMap,
    @VisibleForTesting
    public void onRankingUpdated(RankingMap rankingMap,
            HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey) {
        if (mTmpRanking == null) {
            mTmpRanking = new NotificationListenerService.Ranking();
@@ -1106,19 +1116,22 @@ public class BubbleController {
            Pair<BubbleEntry, Boolean> entryData = entryDataByKey.get(key);
            BubbleEntry entry = entryData.first;
            boolean shouldBubbleUp = entryData.second;

            if (entry != null && !isCurrentProfile(
                    entry.getStatusBarNotification().getUser().getIdentifier())) {
                return;
            }

            if (entry != null && (entry.shouldSuppressNotificationList()
                    || entry.getRanking().isSuspended())) {
                shouldBubbleUp = false;
            }
            rankingMap.getRanking(key, mTmpRanking);
            boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key);
            if (isActiveBubble && !mTmpRanking.canBubble()) {
            boolean isActiveOrInOverflow = mBubbleData.hasAnyBubbleWithKey(key);
            boolean isActive = mBubbleData.hasBubbleInStackWithKey(key);
            if (isActiveOrInOverflow && !mTmpRanking.canBubble()) {
                // If this entry is no longer allowed to bubble, dismiss with the BLOCKED reason.
                // This means that the app or channel's ability to bubble has been revoked.
                mBubbleData.dismissBubbleWithKey(key, DISMISS_BLOCKED);
            } else if (isActiveBubble && (!shouldBubbleUp || entry.getRanking().isSuspended())) {
            } else if (isActiveOrInOverflow && !shouldBubbleUp) {
                // If this entry is allowed to bubble, but cannot currently bubble up or is
                // suspended, dismiss it. This happens when DND is enabled and configured to hide
                // bubbles, or focus mode is enabled and the app is designated as distracting.
@@ -1126,9 +1139,9 @@ public class BubbleController {
                // notification, so that the bubble will be re-created if shouldBubbleUp returns
                // true.
                mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
            } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
            } else if (entry != null && mTmpRanking.isBubble() && !isActive) {
                entry.setFlagBubble(true);
                onEntryUpdated(entry, shouldBubbleUp && !entry.getRanking().isSuspended());
                onEntryUpdated(entry, shouldBubbleUp);
            }
        }
    }
+16 −0
Original line number Diff line number Diff line
@@ -1057,6 +1057,22 @@ public class BubbleData {
        return null;
    }

    /**
     * Get a pending bubble with given notification <code>key</code>
     *
     * @param key notification key
     * @return bubble that matches or null
     */
    @VisibleForTesting(visibility = PRIVATE)
    public Bubble getPendingBubbleWithKey(String key) {
        for (Bubble b : mPendingBubbles.values()) {
            if (b.getKey().equals(key)) {
                return b;
            }
        }
        return null;
    }

    @VisibleForTesting(visibility = PRIVATE)
    void setTimeSource(TimeSource timeSource) {
        mTimeSource = timeSource;
+48 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -70,6 +71,8 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Pair;
import android.util.SparseArray;
import android.view.View;
import android.view.WindowManager;

@@ -147,6 +150,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.HashMap;
import java.util.List;
import java.util.Optional;

@@ -1466,6 +1470,50 @@ public class BubblesTest extends SysuiTestCase {
        verify(mBubbleController).onBubbleMetadataFlagChanged(b);
    }

    @Test
    public void testUpdateBubble_skipsDndSuppressListNotifs() {
        mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(),
                mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */,
                mRow.shouldSuppressPeek());
        mBubbleEntry.getBubbleMetadata().setFlags(
                Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);

        mBubbleController.updateBubble(mBubbleEntry);

        Bubble b = mBubbleData.getPendingBubbleWithKey(mBubbleEntry.getKey());
        assertThat(b.shouldAutoExpand()).isFalse();
        assertThat(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey())).isNull();
    }

    @Test
    public void testOnRankingUpdate_DndSuppressListNotif() {
        // It's in the stack
        mBubbleController.updateBubble(mBubbleEntry);
        assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isTrue();

        // Set current user profile
        SparseArray<UserInfo> userInfos = new SparseArray<>();
        userInfos.put(mBubbleEntry.getStatusBarNotification().getUser().getIdentifier(),
                mock(UserInfo.class));
        mBubbleController.onCurrentProfilesChanged(userInfos);

        // Send ranking update that the notif is suppressed from the list.
        HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey = new HashMap<>();
        mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(),
                mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */,
                mRow.shouldSuppressPeek());
        Pair<BubbleEntry, Boolean> pair = new Pair(mBubbleEntry, true);
        entryDataByKey.put(mBubbleEntry.getKey(), pair);

        NotificationListenerService.RankingMap rankingMap =
                mock(NotificationListenerService.RankingMap.class);
        when(rankingMap.getOrderedKeys()).thenReturn(new String[] { mBubbleEntry.getKey() });
        mBubbleController.onRankingUpdated(rankingMap, entryDataByKey);

        // Should no longer be in the stack
        assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse();
    }

    /** Creates a bubble using the userId and package. */
    private Bubble createBubble(int userId, String pkg) {
        final UserHandle userHandle = new UserHandle(userId);
+48 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.LauncherApps;
import android.content.pm.UserInfo;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.PowerManager;
@@ -59,6 +60,8 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Pair;
import android.util.SparseArray;
import android.view.View;
import android.view.WindowManager;

@@ -128,6 +131,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.HashMap;
import java.util.List;
import java.util.Optional;

@@ -1286,6 +1290,50 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
        verify(mBubbleController).onBubbleMetadataFlagChanged(b);
    }

    @Test
    public void testUpdateBubble_skipsDndSuppressListNotifs() {
        mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(),
                mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */,
                mRow.shouldSuppressPeek());
        mBubbleEntry.getBubbleMetadata().setFlags(
                Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);

        mBubbleController.updateBubble(mBubbleEntry);

        Bubble b = mBubbleData.getPendingBubbleWithKey(mBubbleEntry.getKey());
        assertThat(b.shouldAutoExpand()).isFalse();
        assertThat(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey())).isNull();
    }

    @Test
    public void testOnRankingUpdate_DndSuppressListNotif() {
        // It's in the stack
        mBubbleController.updateBubble(mBubbleEntry);
        assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isTrue();

        // Set current user profile
        SparseArray<UserInfo> userInfos = new SparseArray<>();
        userInfos.put(mBubbleEntry.getStatusBarNotification().getUser().getIdentifier(),
                mock(UserInfo.class));
        mBubbleController.onCurrentProfilesChanged(userInfos);

        // Send ranking update that the notif is suppressed from the list.
        HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey = new HashMap<>();
        mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(),
                mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */,
                mRow.shouldSuppressPeek());
        Pair<BubbleEntry, Boolean> pair = new Pair(mBubbleEntry, true);
        entryDataByKey.put(mBubbleEntry.getKey(), pair);

        NotificationListenerService.RankingMap rankingMap =
                mock(NotificationListenerService.RankingMap.class);
        when(rankingMap.getOrderedKeys()).thenReturn(new String[] { mBubbleEntry.getKey() });
        mBubbleController.onRankingUpdated(rankingMap, entryDataByKey);

        // Should no longer be in the stack
        assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse();
    }

    /**
     * Sets the bubble metadata flags for this entry. These flags are normally set by
     * NotificationManagerService when the notification is sent, however, these tests do not