Merge "Inline notif pipeline flag into BubblesManager" into tm-qpr-dev am: 53044b7a89

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19110369

Change-Id: I845a48e37c7827bc7176dc0daa2fe97293224f1f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Steve Elliott
2022-06-30 23:40:28 +00:00
committed by Automerger Merge Worker
4 changed files with 138 additions and 1843 deletions

View File

@@ -55,8 +55,6 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.QsFrameTranslateModule; import com.android.systemui.statusbar.QsFrameTranslateModule;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
@@ -220,11 +218,9 @@ public abstract class SystemUIModule {
ZenModeController zenModeController, ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager, NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager, NotificationGroupManagerLegacy groupManager,
NotificationEntryManager entryManager,
CommonNotifCollection notifCollection, CommonNotifCollection notifCollection,
NotifPipeline notifPipeline, NotifPipeline notifPipeline,
SysUiState sysUiState, SysUiState sysUiState,
NotifPipelineFlags notifPipelineFlags,
DumpManager dumpManager, DumpManager dumpManager,
@Main Executor sysuiMainExecutor) { @Main Executor sysuiMainExecutor) {
return Optional.ofNullable(BubblesManager.create(context, return Optional.ofNullable(BubblesManager.create(context,
@@ -240,11 +236,9 @@ public abstract class SystemUIModule {
zenModeController, zenModeController,
notifUserManager, notifUserManager,
groupManager, groupManager,
entryManager,
notifCollection, notifCollection,
notifPipeline, notifPipeline,
sysUiState, sysUiState,
notifPipelineFlags,
dumpManager, dumpManager,
sysuiMainExecutor)); sysuiMainExecutor));
} }

View File

@@ -21,14 +21,10 @@ import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES; import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL; import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE; import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -55,7 +51,6 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable; import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
@@ -63,9 +58,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationChannelHelper; import com.android.systemui.statusbar.notification.NotificationChannelHelper;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -114,7 +107,6 @@ public class BubblesManager implements Dumpable {
private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final NotificationLockscreenUserManager mNotifUserManager; private final NotificationLockscreenUserManager mNotifUserManager;
private final NotificationGroupManagerLegacy mNotificationGroupManager; private final NotificationGroupManagerLegacy mNotificationGroupManager;
private final NotificationEntryManager mNotificationEntryManager;
private final CommonNotifCollection mCommonNotifCollection; private final CommonNotifCollection mCommonNotifCollection;
private final NotifPipeline mNotifPipeline; private final NotifPipeline mNotifPipeline;
private final Executor mSysuiMainExecutor; private final Executor mSysuiMainExecutor;
@@ -142,11 +134,9 @@ public class BubblesManager implements Dumpable {
ZenModeController zenModeController, ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager, NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager, NotificationGroupManagerLegacy groupManager,
NotificationEntryManager entryManager,
CommonNotifCollection notifCollection, CommonNotifCollection notifCollection,
NotifPipeline notifPipeline, NotifPipeline notifPipeline,
SysUiState sysUiState, SysUiState sysUiState,
NotifPipelineFlags notifPipelineFlags,
DumpManager dumpManager, DumpManager dumpManager,
Executor sysuiMainExecutor) { Executor sysuiMainExecutor) {
if (bubblesOptional.isPresent()) { if (bubblesOptional.isPresent()) {
@@ -163,11 +153,9 @@ public class BubblesManager implements Dumpable {
zenModeController, zenModeController,
notifUserManager, notifUserManager,
groupManager, groupManager,
entryManager,
notifCollection, notifCollection,
notifPipeline, notifPipeline,
sysUiState, sysUiState,
notifPipelineFlags,
dumpManager, dumpManager,
sysuiMainExecutor); sysuiMainExecutor);
} else { } else {
@@ -189,11 +177,9 @@ public class BubblesManager implements Dumpable {
ZenModeController zenModeController, ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager, NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager, NotificationGroupManagerLegacy groupManager,
NotificationEntryManager entryManager,
CommonNotifCollection notifCollection, CommonNotifCollection notifCollection,
NotifPipeline notifPipeline, NotifPipeline notifPipeline,
SysUiState sysUiState, SysUiState sysUiState,
NotifPipelineFlags notifPipelineFlags,
DumpManager dumpManager, DumpManager dumpManager,
Executor sysuiMainExecutor) { Executor sysuiMainExecutor) {
mContext = context; mContext = context;
@@ -205,7 +191,6 @@ public class BubblesManager implements Dumpable {
mNotificationInterruptStateProvider = interruptionStateProvider; mNotificationInterruptStateProvider = interruptionStateProvider;
mNotifUserManager = notifUserManager; mNotifUserManager = notifUserManager;
mNotificationGroupManager = groupManager; mNotificationGroupManager = groupManager;
mNotificationEntryManager = entryManager;
mCommonNotifCollection = notifCollection; mCommonNotifCollection = notifCollection;
mNotifPipeline = notifPipeline; mNotifPipeline = notifPipeline;
mSysuiMainExecutor = sysuiMainExecutor; mSysuiMainExecutor = sysuiMainExecutor;
@@ -215,11 +200,7 @@ public class BubblesManager implements Dumpable {
ServiceManager.getService(Context.STATUS_BAR_SERVICE)) ServiceManager.getService(Context.STATUS_BAR_SERVICE))
: statusBarService; : statusBarService;
if (notifPipelineFlags.isNewPipelineEnabled()) { setupNotifPipeline();
setupNotifPipeline();
} else {
setupNEM();
}
dumpManager.registerDumpable(TAG, this); dumpManager.registerDumpable(TAG, this);
@@ -438,141 +419,6 @@ public class BubblesManager implements Dumpable {
mBubbles.setSysuiProxy(mSysuiProxy); mBubbles.setSysuiProxy(mSysuiProxy);
} }
private void setupNEM() {
mNotificationEntryManager.addNotificationEntryListener(
new NotificationEntryListener() {
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
BubblesManager.this.onEntryAdded(entry);
}
@Override
public void onPreEntryUpdated(NotificationEntry entry) {
BubblesManager.this.onEntryUpdated(entry);
}
@Override
public void onEntryRemoved(
NotificationEntry entry,
@Nullable NotificationVisibility visibility,
boolean removedByUser,
int reason) {
BubblesManager.this.onEntryRemoved(entry);
}
@Override
public void onNotificationRankingUpdated(RankingMap rankingMap) {
BubblesManager.this.onRankingUpdate(rankingMap);
}
@Override
public void onNotificationChannelModified(
String pkgName,
UserHandle user,
NotificationChannel channel,
int modificationType) {
BubblesManager.this.onNotificationChannelModified(pkgName,
user,
channel,
modificationType);
}
});
// The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator
mNotificationEntryManager.addNotificationRemoveInterceptor(
(key, entry, dismissReason) -> {
final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
final boolean isUserDismiss = dismissReason == REASON_CANCEL
|| dismissReason == REASON_CLICK;
final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
|| dismissReason == REASON_APP_CANCEL_ALL;
final boolean isSummaryCancel =
dismissReason == REASON_GROUP_SUMMARY_CANCELED;
// Need to check for !appCancel here because the notification may have
// previously been dismissed & entry.isRowDismissed would still be true
boolean userRemovedNotif =
(entry != null && entry.isRowDismissed() && !isAppCancel)
|| isClearAll || isUserDismiss || isSummaryCancel;
if (userRemovedNotif) {
return handleDismissalInterception(entry);
}
return false;
});
mNotificationGroupManager.registerGroupChangeListener(
new NotificationGroupManagerLegacy.OnGroupChangeListener() {
@Override
public void onGroupSuppressionChanged(
NotificationGroupManagerLegacy.NotificationGroup group,
boolean suppressed) {
// More notifications could be added causing summary to no longer
// be suppressed -- in this case need to remove the key.
final String groupKey = group.summary != null
? group.summary.getSbn().getGroupKey()
: null;
if (!suppressed && groupKey != null) {
mBubbles.removeSuppressedSummaryIfNecessary(groupKey, null, null);
}
}
});
addNotifCallback(new NotifCallback() {
@Override
public void removeNotification(NotificationEntry entry,
DismissedByUserStats dismissedByUserStats, int reason) {
mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
dismissedByUserStats, reason);
}
@Override
public void invalidateNotifications(String reason) {
mNotificationEntryManager.updateNotifications(reason);
}
@Override
public void maybeCancelSummary(NotificationEntry entry) {
// Check if removed bubble has an associated suppressed group summary that needs
// to be removed now.
final String groupKey = entry.getSbn().getGroupKey();
mBubbles.removeSuppressedSummaryIfNecessary(groupKey, (summaryKey) -> {
final NotificationEntry summary =
mNotificationEntryManager.getActiveNotificationUnfiltered(summaryKey);
if (summary != null) {
mNotificationEntryManager.performRemoveNotification(
summary.getSbn(),
getDismissedByUserStats(summary, false),
UNDEFINED_DISMISS_REASON);
}
}, mSysuiMainExecutor);
// Check if we still need to remove the summary from NoManGroup because the summary
// may not be in the mBubbleData.mSuppressedGroupKeys list and removed above.
// For example:
// 1. Bubbled notifications (group) is posted to shade and are visible bubbles
// 2. User expands bubbles so now their respective notifications in the shade are
// hidden, including the group summary
// 3. User removes all bubbles
// 4. We expect all the removed bubbles AND the summary (note: the summary was
// never added to the suppressedSummary list in BubbleData, so we add this check)
NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(entry);
if (summary != null) {
ArrayList<NotificationEntry> summaryChildren =
mNotificationGroupManager.getLogicalChildren(summary.getSbn());
boolean isSummaryThisNotif = summary.getKey().equals(entry.getKey());
if (!isSummaryThisNotif && (summaryChildren == null
|| summaryChildren.isEmpty())) {
mNotificationEntryManager.performRemoveNotification(
summary.getSbn(),
getDismissedByUserStats(summary, false),
UNDEFINED_DISMISS_REASON);
}
}
}
});
}
private void setupNotifPipeline() { private void setupNotifPipeline() {
mNotifPipeline.addCollectionListener(new NotifCollectionListener() { mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
@Override @Override

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 The Android Open Source Project * Copyright (C) 2020 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -21,12 +21,9 @@ import static android.app.PendingIntent.FLAG_MUTABLE;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NOTIF_CANCEL;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
@@ -62,7 +59,6 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.face.FaceManager;
import android.os.Handler; import android.os.Handler;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.UserHandle; import android.os.UserHandle;
@@ -90,18 +86,16 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
@@ -118,7 +112,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.TaskViewTransitions; import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.WindowManagerShellWrapper;
@@ -140,8 +133,6 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedController;
import com.google.common.collect.ImmutableList;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@@ -155,22 +146,17 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
/**
* Tests the NotificationEntryManager setup with BubbleController.
* The {@link NotifPipeline} setup with BubbleController is tested in
* {@link NewNotifPipelineBubblesTest}.
*/
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true) @TestableLooper.RunWithLooper(setAsMainLooper = true)
public class BubblesTest extends SysuiTestCase { public class BubblesTest extends SysuiTestCase {
@Mock
private NotificationEntryManager mNotificationEntryManager;
@Mock @Mock
private CommonNotifCollection mCommonNotifCollection; private CommonNotifCollection mCommonNotifCollection;
@Mock @Mock
private NotificationGroupManagerLegacy mNotificationGroupManager; private NotificationGroupManagerLegacy mNotificationGroupManager;
@Mock @Mock
private BubblesManager.NotifCallback mNotifCallback;
@Mock
private WindowManager mWindowManager; private WindowManager mWindowManager;
@Mock @Mock
private IActivityManager mActivityManager; private IActivityManager mActivityManager;
@@ -183,8 +169,6 @@ public class BubblesTest extends SysuiTestCase {
@Mock @Mock
private ZenModeConfig mZenModeConfig; private ZenModeConfig mZenModeConfig;
@Mock @Mock
private FaceManager mFaceManager;
@Mock
private NotificationLockscreenUserManager mLockscreenUserManager; private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock @Mock
private SysuiStatusBarStateController mStatusBarStateController; private SysuiStatusBarStateController mStatusBarStateController;
@@ -196,15 +180,17 @@ public class BubblesTest extends SysuiTestCase {
private FloatingContentCoordinator mFloatingContentCoordinator; private FloatingContentCoordinator mFloatingContentCoordinator;
@Mock @Mock
private BubbleDataRepository mDataRepository; private BubbleDataRepository mDataRepository;
@Mock
private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock
private AuthController mAuthController;
private SysUiState mSysUiState; private SysUiState mSysUiState;
private boolean mSysUiStateBubblesExpanded; private boolean mSysUiStateBubblesExpanded;
private boolean mSysUiStateBubblesManageMenuExpanded; private boolean mSysUiStateBubblesManageMenuExpanded;
@Captor @Captor
private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor; private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
@Captor
private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor;
@Captor @Captor
private ArgumentCaptor<List<Bubble>> mBubbleListCaptor; private ArgumentCaptor<List<Bubble>> mBubbleListCaptor;
@Captor @Captor
@@ -212,22 +198,16 @@ public class BubblesTest extends SysuiTestCase {
@Captor @Captor
private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor; private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
private BubblesManager mBubblesManager; private BubblesManager mBubblesManager;
// TODO(178618782): Move tests on the controller directly to the shell
private TestableBubbleController mBubbleController; private TestableBubbleController mBubbleController;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private NotificationEntryListener mEntryListener; private NotifCollectionListener mEntryListener;
private NotificationRemoveInterceptor mRemoveInterceptor;
private NotificationTestHelper mNotificationTestHelper; private NotificationTestHelper mNotificationTestHelper;
private NotificationEntry mRow; private NotificationEntry mRow;
private NotificationEntry mRow2; private NotificationEntry mRow2;
private NotificationEntry mRow3;
private ExpandableNotificationRow mNonBubbleNotifRow; private ExpandableNotificationRow mNonBubbleNotifRow;
private BubbleEntry mBubbleEntry; private BubbleEntry mBubbleEntry;
private BubbleEntry mBubbleEntry2; private BubbleEntry mBubbleEntry2;
private BubbleEntry mBubbleEntry3;
private BubbleEntry mBubbleEntryUser11; private BubbleEntry mBubbleEntryUser11;
private BubbleEntry mBubbleEntry2User11; private BubbleEntry mBubbleEntry2User11;
@@ -245,12 +225,8 @@ public class BubblesTest extends SysuiTestCase {
@Mock @Mock
private NotifPipeline mNotifPipeline; private NotifPipeline mNotifPipeline;
@Mock @Mock
private NotifPipelineFlags mNotifPipelineFlags;
@Mock
private DumpManager mDumpManager; private DumpManager mDumpManager;
@Mock @Mock
private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock
private IStatusBarService mStatusBarService; private IStatusBarService mStatusBarService;
@Mock @Mock
private NotificationVisibilityProvider mVisibilityProvider; private NotificationVisibilityProvider mVisibilityProvider;
@@ -269,8 +245,6 @@ public class BubblesTest extends SysuiTestCase {
@Mock @Mock
private ScreenOffAnimationController mScreenOffAnimationController; private ScreenOffAnimationController mScreenOffAnimationController;
@Mock @Mock
private AuthController mAuthController;
@Mock
private TaskViewTransitions mTaskViewTransitions; private TaskViewTransitions mTaskViewTransitions;
@Mock @Mock
private Optional<OneHandedController> mOneHandedOptional; private Optional<OneHandedController> mOneHandedOptional;
@@ -290,7 +264,6 @@ public class BubblesTest extends SysuiTestCase {
// For the purposes of this test, just run everything synchronously // For the purposes of this test, just run everything synchronously
ShellExecutor syncExecutor = new SyncExecutor(); ShellExecutor syncExecutor = new SyncExecutor();
mContext.addMockSystemService(FaceManager.class, mFaceManager);
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
@@ -308,11 +281,9 @@ public class BubblesTest extends SysuiTestCase {
TestableLooper.get(this)); TestableLooper.get(this));
mRow = mNotificationTestHelper.createBubble(mDeleteIntent); mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent); mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow3 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow(); mNonBubbleNotifRow = mNotificationTestHelper.createRow();
mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow); mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow);
mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2); mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2);
mBubbleEntry3 = BubblesManager.notifToBubbleEntry(mRow3);
UserHandle handle = mock(UserHandle.class); UserHandle handle = mock(UserHandle.class);
when(handle.getIdentifier()).thenReturn(11); when(handle.getIdentifier()).thenReturn(11);
@@ -321,9 +292,6 @@ public class BubblesTest extends SysuiTestCase {
mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry( mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry(
mNotificationTestHelper.createBubble(handle)); mNotificationTestHelper.createBubble(handle));
// Return non-null notification data from the CommonNotifCollection
when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
mZenModeConfig.suppressedVisualEffects = 0; mZenModeConfig.suppressedVisualEffects = 0;
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
@@ -336,7 +304,6 @@ public class BubblesTest extends SysuiTestCase {
(sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0; (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0;
}); });
// TODO: Fix
mPositioner = new TestableBubblePositioner(mContext, mWindowManager); mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
mPositioner.setMaxBubbles(5); mPositioner.setMaxBubbles(5);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor); mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
@@ -355,8 +322,6 @@ public class BubblesTest extends SysuiTestCase {
mock(NotifPipelineFlags.class), mock(NotifPipelineFlags.class),
mock(KeyguardNotificationVisibilityProvider.class) mock(KeyguardNotificationVisibilityProvider.class)
); );
when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false);
when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor); when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor);
mBubbleController = new TestableBubbleController( mBubbleController = new TestableBubbleController(
mContext, mContext,
@@ -396,23 +361,17 @@ public class BubblesTest extends SysuiTestCase {
mZenModeController, mZenModeController,
mLockscreenUserManager, mLockscreenUserManager,
mNotificationGroupManager, mNotificationGroupManager,
mNotificationEntryManager,
mCommonNotifCollection, mCommonNotifCollection,
mNotifPipeline, mNotifPipeline,
mSysUiState, mSysUiState,
mNotifPipelineFlags,
mDumpManager, mDumpManager,
syncExecutor); syncExecutor);
mBubblesManager.addNotifCallback(mNotifCallback);
// XXX: Does *this* need to be changed?
// Get a reference to the BubbleController's entry listener // Get a reference to the BubbleController's entry listener
verify(mNotificationEntryManager, atLeastOnce()) verify(mNotifPipeline, atLeastOnce())
.addNotificationEntryListener(mEntryListenerCaptor.capture()); .addCollectionListener(mNotifListenerCaptor.capture());
mEntryListener = mEntryListenerCaptor.getValue(); mEntryListener = mNotifListenerCaptor.getValue();
// And the remove interceptor
verify(mNotificationEntryManager, atLeastOnce())
.addNotificationRemoveInterceptor(mRemoveInterceptorCaptor.capture());
mRemoveInterceptor = mRemoveInterceptorCaptor.getValue();
} }
@Test @Test
@@ -433,90 +392,75 @@ public class BubblesTest extends SysuiTestCase {
@Test @Test
public void testRemoveBubble() { public void testRemoveBubble() {
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
assertNotNull(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey())); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
assertTrue(mBubbleController.hasBubbles()); assertTrue(mBubbleController.hasBubbles());
verify(mNotificationEntryManager).updateNotifications(any()); verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
mBubbleController.removeBubble( mBubbleController.removeBubble(
mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
verify(mNotificationEntryManager, times(2)).updateNotifications(anyString()); verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
} }
@Test @Test
public void testPromoteBubble_autoExpand() throws Exception { public void testRemoveBubble_withDismissedNotif_inOverflow() {
mBubbleController.updateBubble(mBubbleEntry2); mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
mBubbleController.removeBubble(
mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getKey());
assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b));
verify(mNotificationEntryManager, never()).performRemoveNotification(
eq(mRow.getSbn()), any(), anyInt());
assertThat(mRow.isBubble()).isFalse();
Bubble b2 = mBubbleData.getBubbleInStackWithKey(mRow2.getKey());
assertThat(mBubbleData.getSelectedBubble()).isEqualTo(b2);
mBubbleController.promoteBubbleFromOverflow(b);
assertThat(b.isBubble()).isTrue();
assertThat(b.shouldAutoExpand()).isTrue();
int flags = Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE
| Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
verify(mStatusBarService, times(1)).onNotificationBubbleChanged(
eq(b.getKey()), eq(true), eq(flags));
}
@Test
public void testCancelOverflowBubble() {
mBubbleController.updateBubble(mBubbleEntry2);
mBubbleController.updateBubble(mBubbleEntry, /* suppressFlyout */
false, /* showInShade */ true);
when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
mBubbleController.removeBubble(
mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
mBubbleController.removeBubble(
mRow.getKey(), DISMISS_NOTIF_CANCEL);
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
eq(mRow.getSbn()), any(), anyInt());
assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
assertFalse(mRow.isBubble());
}
@Test
public void testUserChange_doesNotRemoveNotif() {
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles()); assertTrue(mBubbleController.hasBubbles());
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Make it look like dismissed notif
mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true);
// Now remove the bubble
mBubbleController.removeBubble( mBubbleController.removeBubble(
mRow.getKey(), Bubbles.DISMISS_USER_CHANGED); mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
verify(mNotificationEntryManager, never()).performRemoveNotification( assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey()));
eq(mRow.getSbn()), any(), anyInt());
// We don't remove the notification since the bubble is still in overflow.
verify(mNotifCallback, never()).removeNotification(eq(mRow), any(), anyInt());
assertFalse(mBubbleController.hasBubbles());
}
@Test
public void testRemoveBubble_withDismissedNotif_notInOverflow() {
mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
assertTrue(mBubbleController.hasBubbles());
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Make it look like dismissed notif
mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true);
// Now remove the bubble
mBubbleController.removeBubble(
mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey()));
// Since the notif is dismissed and not in overflow, once the bubble is removed,
// removeNotification gets called to really remove the notif
verify(mNotifCallback, times(1)).removeNotification(eq(mRow),
any(), anyInt());
assertFalse(mBubbleController.hasBubbles()); assertFalse(mBubbleController.hasBubbles());
assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
assertTrue(mRow.isBubble());
} }
@Test @Test
public void testDismissStack() { public void testDismissStack() {
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
verify(mNotificationEntryManager, times(1)).updateNotifications(any()); verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
mBubbleController.updateBubble(mBubbleEntry2); mBubbleController.updateBubble(mBubbleEntry2);
verify(mNotificationEntryManager, times(2)).updateNotifications(any()); verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey())); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey()));
assertTrue(mBubbleController.hasBubbles()); assertTrue(mBubbleController.hasBubbles());
mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
verify(mNotificationEntryManager, times(3)).updateNotifications(any()); verify(mNotifCallback, times(3)).invalidateNotifications(anyString());
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey())); assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey()));
@@ -528,7 +472,7 @@ public class BubblesTest extends SysuiTestCase {
assertStackCollapsed(); assertStackCollapsed();
// Mark it as a bubble and add it explicitly // Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed // We should have bubbles & their notifs should not be suppressed
@@ -536,7 +480,6 @@ public class BubblesTest extends SysuiTestCase {
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Expand the stack // Expand the stack
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true); mBubbleData.setExpanded(true);
assertStackExpanded(); assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
@@ -556,8 +499,8 @@ public class BubblesTest extends SysuiTestCase {
@Ignore("Currently broken.") @Ignore("Currently broken.")
public void testCollapseAfterChangingExpandedBubble() { public void testCollapseAfterChangingExpandedBubble() {
// Mark it as a bubble and add it explicitly // Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
mEntryListener.onPendingEntryAdded(mRow2); mEntryListener.onEntryAdded(mRow2);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.updateBubble(mBubbleEntry2); mBubbleController.updateBubble(mBubbleEntry2);
@@ -593,6 +536,7 @@ public class BubblesTest extends SysuiTestCase {
verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
true, mRow.getKey()); true, mRow.getKey());
// Collapse // Collapse
mBubbleController.collapseStack(); mBubbleController.collapseStack();
assertStackCollapsed(); assertStackCollapsed();
@@ -602,7 +546,7 @@ public class BubblesTest extends SysuiTestCase {
@Test @Test
public void testExpansionRemovesShowInShadeAndDot() { public void testExpansionRemovesShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly // Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed // We should have bubbles & their notifs should not be suppressed
@@ -627,7 +571,7 @@ public class BubblesTest extends SysuiTestCase {
@Test @Test
public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() { public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly // Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed // We should have bubbles & their notifs should not be suppressed
@@ -649,7 +593,7 @@ public class BubblesTest extends SysuiTestCase {
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
// Send update // Send update
mEntryListener.onPreEntryUpdated(mRow); mEntryListener.onEntryUpdated(mRow);
// Nothing should have changed // Nothing should have changed
// Notif is suppressed after expansion // Notif is suppressed after expansion
@@ -661,8 +605,8 @@ public class BubblesTest extends SysuiTestCase {
@Test @Test
public void testRemoveLastExpanded_collapses() { public void testRemoveLastExpanded_collapses() {
// Mark it as a bubble and add it explicitly // Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
mEntryListener.onPendingEntryAdded(mRow2); mEntryListener.onEntryAdded(mRow2);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.updateBubble(mBubbleEntry2); mBubbleController.updateBubble(mBubbleEntry2);
@@ -707,7 +651,7 @@ public class BubblesTest extends SysuiTestCase {
@Test @Test
public void testRemoveLastExpandedEmptyOverflow_collapses() { public void testRemoveLastExpandedEmptyOverflow_collapses() {
// Mark it as a bubble and add it explicitly // Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
// Expand // Expand
@@ -732,6 +676,7 @@ public class BubblesTest extends SysuiTestCase {
assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
} }
@Test @Test
public void testAutoExpand_fails_noFlag() { public void testAutoExpand_fails_noFlag() {
assertStackCollapsed(); assertStackCollapsed();
@@ -739,7 +684,7 @@ public class BubblesTest extends SysuiTestCase {
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */); Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */);
// Add the auto expand bubble // Add the auto expand bubble
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
// Expansion shouldn't change // Expansion shouldn't change
@@ -755,7 +700,7 @@ public class BubblesTest extends SysuiTestCase {
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */); Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */);
// Add the auto expand bubble // Add the auto expand bubble
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
// Expansion should change // Expansion should change
@@ -771,7 +716,7 @@ public class BubblesTest extends SysuiTestCase {
Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */); Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
// Add the suppress notif bubble // Add the suppress notif bubble
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed because we were foreground // Notif should be suppressed because we were foreground
@@ -804,23 +749,9 @@ public class BubblesTest extends SysuiTestCase {
assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
} }
@Test
public void testExpandStackAndSelectBubble_removedFirst() {
mEntryListener.onPendingEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
// Simulate notification cancellation.
mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getKey(), mRow, REASON_APP_CANCEL);
mBubbleController.expandStackAndSelectBubble(mBubbleEntry);
assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
}
@Test @Test
public void testMarkNewNotificationAsShowInShade() { public void testMarkNewNotificationAsShowInShade() {
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mTestableLooper.processAllMessages(); mTestableLooper.processAllMessages();
@@ -829,8 +760,8 @@ public class BubblesTest extends SysuiTestCase {
@Test @Test
public void testAddNotif_notBubble() { public void testAddNotif_notBubble() {
mEntryListener.onPendingEntryAdded(mNonBubbleNotifRow.getEntry()); mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry());
mEntryListener.onPreEntryUpdated(mNonBubbleNotifRow.getEntry()); mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry());
assertThat(mBubbleController.hasBubbles()).isFalse(); assertThat(mBubbleController.hasBubbles()).isFalse();
} }
@@ -868,48 +799,33 @@ public class BubblesTest extends SysuiTestCase {
NotificationListenerService.Ranking ranking = new RankingBuilder( NotificationListenerService.Ranking ranking = new RankingBuilder(
mRow.getRanking()).setCanBubble(false).build(); mRow.getRanking()).setCanBubble(false).build();
mRow.setRanking(ranking); mRow.setRanking(ranking);
mEntryListener.onPreEntryUpdated(mRow); mEntryListener.onEntryUpdated(mRow);
assertFalse(mBubbleController.hasBubbles()); assertFalse(mBubbleController.hasBubbles());
verify(mDeleteIntent, never()).send(); verify(mDeleteIntent, never()).send();
} }
@Test
public void testRemoveBubble_succeeds_appCancel() {
mEntryListener.onPendingEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getKey(), mRow, REASON_APP_CANCEL);
// Cancels always remove so no need to intercept
assertFalse(intercepted);
}
@Test @Test
public void testRemoveBubble_entryListenerRemove() { public void testRemoveBubble_entryListenerRemove() {
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles()); assertTrue(mBubbleController.hasBubbles());
// Removes the notification // Removes the notification
mEntryListener.onEntryRemoved(mRow, null, false, REASON_APP_CANCEL); mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL);
assertFalse(mBubbleController.hasBubbles()); assertFalse(mBubbleController.hasBubbles());
} }
@Test @Test
public void removeBubble_clearAllIntercepted() { public void removeBubble_intercepted() {
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles()); assertTrue(mBubbleController.hasBubbles());
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
mRow.getKey(), mRow, REASON_CANCEL_ALL);
// Intercept! // Intercept!
assertTrue(intercepted); assertTrue(intercepted);
@@ -918,99 +834,51 @@ public class BubblesTest extends SysuiTestCase {
} }
@Test @Test
public void removeBubble_userDismissNotifIntercepted() { public void removeBubble_dismissIntoOverflow_intercepted() {
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles()); assertTrue(mBubbleController.hasBubbles());
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( // Dismiss the bubble
mRow.getKey(), mRow, REASON_CANCEL); mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
// Intercept!
assertTrue(intercepted);
// Should update show in shade state
assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
}
@Test
public void removeNotif_inOverflow_intercepted() {
// Get bubble with notif in shade.
mEntryListener.onPendingEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Dismiss the bubble into overflow.
mBubbleController.removeBubble(
mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
assertFalse(mBubbleController.hasBubbles()); assertFalse(mBubbleController.hasBubbles());
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( // Dismiss the notification
mRow.getKey(), mRow, REASON_CANCEL); boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
// Notif is no longer a bubble, but still in overflow, so we intercept removal. // Intercept dismissal since bubble is going into overflow
assertTrue(intercepted); assertTrue(intercepted);
} }
@Test @Test
public void removeNotif_notInOverflow_notIntercepted() { public void removeBubble_notIntercepted() {
// Get bubble with notif in shade. mEntryListener.onEntryAdded(mRow);
mEntryListener.onPendingEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry); mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles()); assertTrue(mBubbleController.hasBubbles());
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mBubbleController.removeBubble( // Dismiss the bubble
mRow.getKey(), Bubbles.DISMISS_NO_LONGER_BUBBLE); mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
assertFalse(mBubbleController.hasBubbles()); assertFalse(mBubbleController.hasBubbles());
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( // Dismiss the notification
mRow.getKey(), mRow, REASON_CANCEL); boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
// Notif is no longer a bubble, so we should not intercept removal. // Not a bubble anymore so we don't intercept dismissal.
assertFalse(intercepted); assertFalse(intercepted);
} }
@Test
public void testOverflowBubble_maxReached_notInShade_bubbleRemoved() {
mBubbleController.updateBubble(
mBubbleEntry, /* suppressFlyout */ false, /* showInShade */ false);
mBubbleController.updateBubble(
mBubbleEntry2, /* suppressFlyout */ false, /* showInShade */ false);
mBubbleController.updateBubble(
mBubbleEntry3, /* suppressFlyout */ false, /* showInShade */ false);
when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
when(mCommonNotifCollection.getEntry(mRow3.getKey())).thenReturn(mRow3);
assertEquals(mBubbleData.getBubbles().size(), 3);
mBubbleData.setMaxOverflowBubbles(1);
mBubbleController.removeBubble(
mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
assertEquals(mBubbleData.getBubbles().size(), 2);
assertEquals(mBubbleData.getOverflowBubbles().size(), 1);
mBubbleController.removeBubble(
mRow2.getKey(), Bubbles.DISMISS_USER_GESTURE);
// Overflow max of 1 is reached; mRow is oldest, so it gets removed
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
eq(mRow.getSbn()), any(), eq(REASON_CANCEL));
assertEquals(mBubbleData.getBubbles().size(), 1);
assertEquals(mBubbleData.getOverflowBubbles().size(), 1);
}
@Test @Test
public void testNotifyShadeSuppressionChange_notificationDismiss() { public void testNotifyShadeSuppressionChange_notificationDismiss() {
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
assertTrue(mBubbleController.hasBubbles()); assertTrue(mBubbleController.hasBubbles());
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mRemoveInterceptor.onNotificationRemoveRequested( mBubblesManager.handleDismissalInterception(mRow);
mRow.getKey(), mRow, REASON_CANCEL);
// Should update show in shade state // Should update show in shade state
assertBubbleNotificationSuppressedFromShade(mBubbleEntry); assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
@@ -1022,7 +890,7 @@ public class BubblesTest extends SysuiTestCase {
@Test @Test
public void testNotifyShadeSuppressionChange_bubbleExpanded() { public void testNotifyShadeSuppressionChange_bubbleExpanded() {
mEntryListener.onPendingEntryAdded(mRow); mEntryListener.onEntryAdded(mRow);
assertTrue(mBubbleController.hasBubbles()); assertTrue(mBubbleController.hasBubbles());
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
@@ -1042,9 +910,9 @@ public class BubblesTest extends SysuiTestCase {
// GIVEN a group summary with a bubble child // GIVEN a group summary with a bubble child
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onEntryAdded(groupedBubble.getEntry());
when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
.thenReturn(groupedBubble.getEntry()); .thenReturn(groupedBubble.getEntry());
mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble); groupSummary.addChildNotification(groupedBubble);
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -1054,10 +922,10 @@ public class BubblesTest extends SysuiTestCase {
// THEN the summary and bubbled child are suppressed from the shade // THEN the summary and bubbled child are suppressed from the shade
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry().getKey(), groupedBubble.getEntry().getKey(),
groupSummary.getEntry().getSbn().getGroupKey())); groupedBubble.getEntry().getSbn().getGroupKey()));
assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry().getKey(), groupedBubble.getEntry().getKey(),
groupSummary.getEntry().getSbn().getGroupKey())); groupedBubble.getEntry().getSbn().getGroupKey()));
assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey())); assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
} }
@@ -1066,7 +934,7 @@ public class BubblesTest extends SysuiTestCase {
// GIVEN a group summary with a bubble child // GIVEN a group summary with a bubble child
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onPendingEntryAdded(groupedBubble.getEntry()); mEntryListener.onEntryAdded(groupedBubble.getEntry());
when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
.thenReturn(groupedBubble.getEntry()); .thenReturn(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble); groupSummary.addChildNotification(groupedBubble);
@@ -1076,7 +944,7 @@ public class BubblesTest extends SysuiTestCase {
mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// WHEN the summary is cancelled by the app // WHEN the summary is cancelled by the app
mEntryListener.onEntryRemoved(groupSummary.getEntry(), null, false, REASON_APP_CANCEL); mEntryListener.onEntryRemoved(groupSummary.getEntry(), REASON_APP_CANCEL);
// THEN the summary and its children are removed from bubble data // THEN the summary and its children are removed from bubble data
assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -1085,14 +953,14 @@ public class BubblesTest extends SysuiTestCase {
} }
@Test @Test
public void testSummaryDismissal_marksBubblesHiddenFromShadeAndDismissesNonBubbledChildren() public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren()
throws Exception { throws Exception {
// GIVEN a group summary with two (non-bubble) children and one bubble child // GIVEN a group summary with two (non-bubble) children and one bubble child
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2); ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onEntryAdded(groupedBubble.getEntry());
when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
.thenReturn(groupedBubble.getEntry()); .thenReturn(groupedBubble.getEntry());
mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble); groupSummary.addChildNotification(groupedBubble);
// WHEN the summary is dismissed // WHEN the summary is dismissed
@@ -1100,16 +968,15 @@ public class BubblesTest extends SysuiTestCase {
// THEN only the NON-bubble children are dismissed // THEN only the NON-bubble children are dismissed
List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren(); List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren();
verify(mNotificationEntryManager, times(1)).performRemoveNotification( verify(mNotifCallback, times(1)).removeNotification(
eq(childrenRows.get(0).getEntry().getSbn()), any(), eq(childrenRows.get(0).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
eq(REASON_GROUP_SUMMARY_CANCELED)); verify(mNotifCallback, times(1)).removeNotification(
verify(mNotificationEntryManager, times(1)).performRemoveNotification( eq(childrenRows.get(1).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
eq(childrenRows.get(1).getEntry().getSbn()), any(), verify(mNotifCallback, never()).removeNotification(eq(groupedBubble.getEntry()),
eq(REASON_GROUP_SUMMARY_CANCELED)); any(), anyInt());
verify(mNotificationEntryManager, never()).performRemoveNotification(
eq(groupedBubble.getEntry().getSbn()), any(), anyInt());
// THEN the bubble child is suppressed from the shade // THEN the bubble child still exists as a bubble and is suppressed from the shade
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry().getKey(), groupedBubble.getEntry().getKey(),
groupedBubble.getEntry().getSbn().getGroupKey())); groupedBubble.getEntry().getSbn().getGroupKey()));
@@ -1117,33 +984,16 @@ public class BubblesTest extends SysuiTestCase {
groupedBubble.getEntry().getKey(), groupedBubble.getEntry().getKey(),
groupedBubble.getEntry().getSbn().getGroupKey())); groupedBubble.getEntry().getSbn().getGroupKey()));
// THEN the summary is removed from GroupManager // THEN the summary is also suppressed from the shade
verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupSummary.getEntry().getKey(),
groupSummary.getEntry().getSbn().getGroupKey()));
assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
groupSummary.getEntry().getKey(),
groupSummary.getEntry().getSbn().getGroupKey()));
} }
/**
* Verifies that when a non visually interruptive update occurs for a bubble in the overflow,
* the that bubble does not get promoted from the overflow.
*/
@Test
public void test_notVisuallyInterruptive_updateOverflowBubble_notAdded() {
// Setup
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.updateBubble(mBubbleEntry2);
assertTrue(mBubbleController.hasBubbles());
// Overflow it
mBubbleData.dismissBubbleWithKey(mRow.getKey(),
Bubbles.DISMISS_USER_GESTURE);
assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getKey())).isFalse();
assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
// Test
mBubbleController.updateBubble(mBubbleEntry);
assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getKey())).isFalse();
}
/** /**
* Verifies that when the user changes, the bubbles in the overflow list is cleared. Doesn't * Verifies that when the user changes, the bubbles in the overflow list is cleared. Doesn't
* test the loading from the repository which would be a nice thing to add. * test the loading from the repository which would be a nice thing to add.
@@ -1185,15 +1035,17 @@ public class BubblesTest extends SysuiTestCase {
*/ */
@Test @Test
public void testOverflowLoadedOnce() { public void testOverflowLoadedOnce() {
mBubbleController.updateBubble(mBubbleEntry); // XXX
mBubbleController.updateBubble(mBubbleEntry2); when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
assertThat(mBubbleData.getOverflowBubbles().isEmpty()).isFalse();
mBubbleController.updateBubble(mBubbleEntry); mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry2); mEntryListener.onEntryAdded(mRow2);
mBubbleController.removeBubble(mBubbleEntry.getKey(), DISMISS_NOTIF_CANCEL); mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
mBubbleController.removeBubble(mBubbleEntry2.getKey(), DISMISS_NOTIF_CANCEL); assertThat(mBubbleData.getOverflowBubbles()).isNotEmpty();
mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL);
mEntryListener.onEntryRemoved(mRow2, REASON_APP_CANCEL);
assertThat(mBubbleData.getOverflowBubbles()).isEmpty(); assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
verify(mDataRepository, times(1)).loadBubbles(anyInt(), any()); verify(mDataRepository, times(1)).loadBubbles(anyInt(), any());
@@ -1376,6 +1228,7 @@ public class BubblesTest extends SysuiTestCase {
assertStackCollapsed(); assertStackCollapsed();
} }
@Test @Test
public void testRegisterUnregisterBroadcastListener() { public void testRegisterUnregisterBroadcastListener() {
spyOn(mContext); spyOn(mContext);
@@ -1455,7 +1308,7 @@ public class BubblesTest extends SysuiTestCase {
@Test @Test
public void testSetShouldAutoExpand_notifiesFlagChanged() { public void testSetShouldAutoExpand_notifiesFlagChanged() {
mEntryListener.onPendingEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles()); assertTrue(mBubbleController.hasBubbles());
Bubble b = mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey()); Bubble b = mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey());
@@ -1551,7 +1404,7 @@ public class BubblesTest extends SysuiTestCase {
} }
/** /**
* Sets the bubble metadata flags for this entry. These ]flags are normally set by * 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 * NotificationManagerService when the notification is sent, however, these tests do not
* go through that path so we set them explicitly when testing. * go through that path so we set them explicitly when testing.
*/ */
@@ -1570,12 +1423,15 @@ public class BubblesTest extends SysuiTestCase {
private Notification.BubbleMetadata getMetadata() { private Notification.BubbleMetadata getMetadata() {
Intent target = new Intent(mContext, BubblesTestActivity.class); Intent target = new Intent(mContext, BubblesTestActivity.class);
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, FLAG_MUTABLE); PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, FLAG_MUTABLE);
return new Notification.BubbleMetadata.Builder(
return new Notification.BubbleMetadata.Builder(bubbleIntent, bubbleIntent,
Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble)) Icon.createWithResource(
mContext,
com.android.wm.shell.R.drawable.bubble_ic_create_bubble))
.build(); .build();
} }
/** /**
* Asserts that the bubble stack is expanded and also validates the cached state is updated. * Asserts that the bubble stack is expanded and also validates the cached state is updated.
*/ */