diff options
44 files changed, 1727 insertions, 1433 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 8ac6edb9dfa2..cf576dd6b964 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -37,7 +37,6 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.appops.AppOpsController; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; @@ -308,7 +307,6 @@ public class Dependency { @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil; @Inject Lazy<SmartReplyController> mSmartReplyController; @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler; - @Inject Lazy<Bubbles> mBubbles; @Inject Lazy<NotificationEntryManager> mNotificationEntryManager; @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager; @Inject Lazy<AutoHideController> mAutoHideController; @@ -506,7 +504,6 @@ public class Dependency { mProviders.put(SmartReplyController.class, mSmartReplyController::get); mProviders.put(RemoteInputQuickSettingsDisabler.class, mRemoteInputQuickSettingsDisabler::get); - mProviders.put(Bubbles.class, mBubbles::get); mProviders.put(NotificationEntryManager.class, mNotificationEntryManager::get); mProviders.put(ForegroundServiceNotificationListener.class, mForegroundServiceNotificationListener::get); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 1891daf59007..6b8af3ebe1ed 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -53,7 +53,8 @@ import java.util.Objects; /** * Encapsulates the data and UI elements of a bubble. */ -class Bubble implements BubbleViewProvider { +@VisibleForTesting +public class Bubble implements BubbleViewProvider { private static final String TAG = "Bubble"; private final String mKey; @@ -62,7 +63,7 @@ class Bubble implements BubbleViewProvider { private long mLastAccessed; @Nullable - private BubbleController.NotificationSuppressionChangedListener mSuppressionListener; + private Bubbles.NotificationSuppressionChangedListener mSuppressionListener; /** Whether the bubble should show a dot for the notification indicating updated content. */ private boolean mShowBubbleUpdateDot = true; @@ -173,8 +174,8 @@ class Bubble implements BubbleViewProvider { @VisibleForTesting(visibility = PRIVATE) Bubble(@NonNull final BubbleEntry entry, - @Nullable final BubbleController.NotificationSuppressionChangedListener listener, - final BubbleController.PendingIntentCanceledListener intentCancelListener) { + @Nullable final Bubbles.NotificationSuppressionChangedListener listener, + final Bubbles.PendingIntentCanceledListener intentCancelListener) { mKey = entry.getKey(); mSuppressionListener = listener; mIntentCancelListener = intent -> { @@ -309,11 +310,13 @@ class Bubble implements BubbleViewProvider { * * @param callback the callback to notify one the bubble is ready to be displayed. * @param context the context for the bubble. + * @param controller * @param stackView the stackView the bubble is eventually added to. * @param iconFactory the iconfactory use to create badged images for the bubble. */ void inflate(BubbleViewInfoTask.Callback callback, Context context, + BubbleController controller, BubbleStackView stackView, BubbleIconFactory iconFactory, boolean skipInflation) { @@ -322,6 +325,7 @@ class Bubble implements BubbleViewProvider { } mInflationTask = new BubbleViewInfoTask(this, context, + controller, stackView, iconFactory, skipInflation, @@ -522,7 +526,8 @@ class Bubble implements BubbleViewProvider { /** * Sets whether this notification should be suppressed in the shade. */ - void setSuppressNotification(boolean suppressNotification) { + @VisibleForTesting + public void setSuppressNotification(boolean suppressNotification) { boolean prevShowInShade = showInShade(); if (suppressNotification) { mFlags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; @@ -559,7 +564,8 @@ class Bubble implements BubbleViewProvider { /** * Whether the flyout for the bubble should be shown. */ - boolean showFlyout() { + @VisibleForTesting + public boolean showFlyout() { return !mSuppressFlyout && !mShouldSuppressPeek && !shouldSuppressNotification() && !mShouldSuppressNotificationList; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 0c3dc8222a34..598a604099ac 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -17,39 +17,19 @@ package com.android.systemui.bubbles; import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.app.Notification.FLAG_BUBBLE; -import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; -import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; -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_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.NotificationStats.DISMISSAL_BUBBLE; -import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.systemui.statusbar.StatusBarState.SHADE; -import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.LOCAL_VARIABLE; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.NonNull; import android.annotation.UserIdInt; -import android.app.ActivityManager.RunningTaskInfo; +import android.app.ActivityManager; import android.app.ActivityTaskManager; -import android.app.INotificationManager; import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.pm.ActivityInfo; @@ -65,7 +45,6 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.RankingMap; -import android.service.notification.ZenModeConfig; import android.util.ArraySet; import android.util.Log; import android.util.Pair; @@ -74,45 +53,14 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; -import androidx.annotation.IntDef; import androidx.annotation.MainThread; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.statusbar.NotificationVisibility; -import com.android.systemui.Dumpable; import com.android.systemui.bubbles.dagger.BubbleModule; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.model.SysUiState; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.shared.system.TaskStackChangeListener; -import com.android.systemui.shared.system.TaskStackChangeListeners; -import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationRemoveInterceptor; -import com.android.systemui.statusbar.NotificationShadeWindowController; -import com.android.systemui.statusbar.ScrimView; -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.collection.NotifCollection; -import com.android.systemui.statusbar.notification.collection.NotifPipeline; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator; -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; -import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; -import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; -import com.android.systemui.statusbar.notification.logging.NotificationLogger; -import com.android.systemui.statusbar.phone.ScrimController; -import com.android.systemui.statusbar.phone.ShadeController; -import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.ZenModeController; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -120,11 +68,10 @@ import com.android.wm.shell.pip.PinnedStackListenerForwarder; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.function.IntConsumer; /** * Bubbles are a special type of content that can "float" on top of other apps or System UI. @@ -132,52 +79,23 @@ import java.util.Objects; * * The controller manages addition, removal, and visible state of bubbles on screen. */ -public class BubbleController implements Bubbles, ConfigurationController.ConfigurationListener, - Dumpable { +public class BubbleController implements Bubbles { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES; - @Retention(SOURCE) - @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED, - DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE, - DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT, - DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED, - DISMISS_NO_BUBBLE_UP}) - @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) - @interface DismissReason {} - - static final int DISMISS_USER_GESTURE = 1; - static final int DISMISS_AGED = 2; - static final int DISMISS_TASK_FINISHED = 3; - static final int DISMISS_BLOCKED = 4; - static final int DISMISS_NOTIF_CANCEL = 5; - static final int DISMISS_ACCESSIBILITY_ACTION = 6; - static final int DISMISS_NO_LONGER_BUBBLE = 7; - static final int DISMISS_USER_CHANGED = 8; - static final int DISMISS_GROUP_CANCELLED = 9; - static final int DISMISS_INVALID_INTENT = 10; - static final int DISMISS_OVERFLOW_MAX_REACHED = 11; - static final int DISMISS_SHORTCUT_REMOVED = 12; - static final int DISMISS_PACKAGE_REMOVED = 13; - static final int DISMISS_NO_BUBBLE_UP = 14; - private final Context mContext; - private final NotificationEntryManager mNotificationEntryManager; - private final NotifPipeline mNotifPipeline; - private final BubbleTaskStackListener mTaskStackListener; private BubbleExpandListener mExpandListener; @Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer; - private final NotificationGroupManagerLegacy mNotificationGroupManager; - private final ShadeController mShadeController; private final FloatingContentCoordinator mFloatingContentCoordinator; private final BubbleDataRepository mDataRepository; private BubbleLogger mLogger; private final Handler mMainHandler; private BubbleData mBubbleData; - private ScrimView mBubbleScrim; + private View mBubbleScrim; @Nullable private BubbleStackView mStackView; private BubbleIconFactory mBubbleIconFactory; private BubblePositioner mBubblePositioner; + private SysuiProxy mSysuiProxy; /** * The relative position of the stack when we removed it and nulled it out. If the stack is @@ -193,12 +111,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config // Used when ranking updates occur and we check if things should bubble / unbubble private NotificationListenerService.Ranking mTmpRanking; - // Bubbles get added to the status bar view - private final NotificationShadeWindowController mNotificationShadeWindowController; - private final ZenModeController mZenModeController; - private StatusBarStateListener mStatusBarStateListener; - private INotificationManager mINotificationManager; - // Callback that updates BubbleOverflowActivity on data change. @Nullable private BubbleData.Listener mOverflowListener = null; @@ -209,12 +121,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config * When the shade status changes to SHADE (from anything but SHADE, like LOCKED) we'll select * this bubble and expand the stack. */ - @Nullable private NotificationEntry mNotifEntryToExpandOnShadeUnlock; + @Nullable private BubbleEntry mNotifEntryToExpandOnShadeUnlock; - private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private IStatusBarService mBarService; private WindowManager mWindowManager; - private SysUiState mSysUiState; // Used to post to main UI thread private Handler mHandler = new Handler(); @@ -224,9 +134,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config /** Whether or not the BubbleStackView has been added to the WindowManager. */ private boolean mAddedToWindowManager = false; - // Listens to user switch so bubbles can be saved and restored. - private final NotificationLockscreenUserManager mNotifUserManager; - /** Last known orientation, used to detect orientation changes in {@link #onConfigChanged}. */ private int mOrientation = Configuration.ORIENTATION_UNDEFINED; @@ -247,9 +154,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config private ShellTaskOrganizer mTaskOrganizer; - // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline - private final List<NotifCallback> mCallbacks = new ArrayList<>(); - /** * Whether the IME is visible, as reported by the BubbleStackView. If it is, we'll make the * Bubbles window NOT_FOCUSABLE so that touches on the Bubbles UI doesn't steal focus from the @@ -257,122 +161,15 @@ public class BubbleController implements Bubbles, ConfigurationController.Config */ private boolean mImeVisible = false; - /** - * Listener to find out about stack expansion / collapse events. - */ - public interface BubbleExpandListener { - /** - * Called when the expansion state of the bubble stack changes. - * - * @param isExpanding whether it's expanding or collapsing - * @param key the notification key associated with bubble being expanded - */ - void onBubbleExpandChanged(boolean isExpanding, String key); - } - - /** - * Listener to be notified when a bubbles' notification suppression state changes. - */ - public interface NotificationSuppressionChangedListener { - /** - * Called when the notification suppression state of a bubble changes. - */ - void onBubbleNotificationSuppressionChange(Bubble bubble); - } - - /** - * Listener to be notified when a pending intent has been canceled for a bubble. - */ - public interface PendingIntentCanceledListener { - /** - * Called when the pending intent for a bubble has been canceled. - */ - void onPendingIntentCanceled(Bubble bubble); - } - - /** - * Callback for when the BubbleController wants to interact with the notification pipeline to: - * - Remove a previously bubbled notification - * - Update the notification shade since bubbled notification should/shouldn't be showing - */ - public interface NotifCallback { - /** - * Called when a bubbled notification that was hidden from the shade is now being removed - * This can happen when an app cancels a bubbled notification or when the user dismisses a - * bubble. - */ - void removeNotification( - @NonNull NotificationEntry entry, - @NonNull DismissedByUserStats stats, - int reason); - - /** - * Called when a bubbled notification has changed whether it should be - * filtered from the shade. - */ - void invalidateNotifications(@NonNull String reason); - - /** - * Called on a bubbled entry that has been removed when there are no longer - * bubbled entries in its group. - * - * Checks whether its group has any other (non-bubbled) children. If it doesn't, - * removes all remnants of the group's summary from the notification pipeline. - * TODO: (b/145659174) Only old pipeline needs this - delete post-migration. - */ - void maybeCancelSummary(@NonNull NotificationEntry entry); - } - - /** - * Listens for the current state of the status bar and updates the visibility state - * of bubbles as needed. - */ - private class StatusBarStateListener implements StatusBarStateController.StateListener { - private int mState; - /** - * Returns the current status bar state. - */ - public int getCurrentState() { - return mState; - } - - @Override - public void onStateChanged(int newState) { - mState = newState; - boolean shouldCollapse = (mState != SHADE); - if (shouldCollapse) { - collapseStack(); - } - - if (mNotifEntryToExpandOnShadeUnlock != null) { - expandStackAndSelectBubble(mNotifEntryToExpandOnShadeUnlock); - mNotifEntryToExpandOnShadeUnlock = null; - } - - updateStack(); - } - } + /** true when user is in status bar unlock shade. */ + private boolean mIsStatusBarShade = true; /** * Injected constructor. See {@link BubbleModule}. */ public static BubbleController create(Context context, - NotificationShadeWindowController notificationShadeWindowController, - StatusBarStateController statusBarStateController, - ShadeController shadeController, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer, - ConfigurationController configurationController, - NotificationInterruptStateProvider interruptionStateProvider, - ZenModeController zenModeController, - NotificationLockscreenUserManager notifUserManager, - NotificationGroupManagerLegacy groupManager, - NotificationEntryManager entryManager, - NotifPipeline notifPipeline, - FeatureFlags featureFlags, - DumpManager dumpManager, FloatingContentCoordinator floatingContentCoordinator, - SysUiState sysUiState, - INotificationManager notificationManager, @Nullable IStatusBarService statusBarService, WindowManager windowManager, WindowManagerShellWrapper windowManagerShellWrapper, @@ -381,39 +178,23 @@ public class BubbleController implements Bubbles, ConfigurationController.Config @Main Handler mainHandler, ShellTaskOrganizer organizer) { BubbleLogger logger = new BubbleLogger(uiEventLogger); - return new BubbleController(context, notificationShadeWindowController, - statusBarStateController, shadeController, new BubbleData(context, logger), - synchronizer, configurationController, interruptionStateProvider, zenModeController, - notifUserManager, groupManager, entryManager, notifPipeline, featureFlags, - dumpManager, floatingContentCoordinator, - new BubbleDataRepository(context, launcherApps), sysUiState, notificationManager, - statusBarService, windowManager, windowManagerShellWrapper, launcherApps, logger, - mainHandler, organizer, new BubblePositioner(context, windowManager)); + return new BubbleController(context, + new BubbleData(context, logger), synchronizer, + floatingContentCoordinator, new BubbleDataRepository(context, launcherApps), + statusBarService, windowManager, + windowManagerShellWrapper, launcherApps, logger, mainHandler, organizer, + new BubblePositioner(context, windowManager)); } /** * Testing constructor. */ @VisibleForTesting - BubbleController(Context context, - NotificationShadeWindowController notificationShadeWindowController, - StatusBarStateController statusBarStateController, - ShadeController shadeController, + public BubbleController(Context context, BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer, - ConfigurationController configurationController, - NotificationInterruptStateProvider interruptionStateProvider, - ZenModeController zenModeController, - NotificationLockscreenUserManager notifUserManager, - NotificationGroupManagerLegacy groupManager, - NotificationEntryManager entryManager, - NotifPipeline notifPipeline, - FeatureFlags featureFlags, - DumpManager dumpManager, FloatingContentCoordinator floatingContentCoordinator, BubbleDataRepository dataRepository, - SysUiState sysUiState, - INotificationManager notificationManager, @Nullable IStatusBarService statusBarService, WindowManager windowManager, WindowManagerShellWrapper windowManagerShellWrapper, @@ -422,82 +203,35 @@ public class BubbleController implements Bubbles, ConfigurationController.Config Handler mainHandler, ShellTaskOrganizer organizer, BubblePositioner positioner) { - dumpManager.registerDumpable(TAG, this); mContext = context; - mShadeController = shadeController; - mNotificationInterruptStateProvider = interruptionStateProvider; - mNotifUserManager = notifUserManager; - mZenModeController = zenModeController; mFloatingContentCoordinator = floatingContentCoordinator; mDataRepository = dataRepository; - mINotificationManager = notificationManager; mLogger = bubbleLogger; mMainHandler = mainHandler; - mZenModeController.addCallback(new ZenModeController.Callback() { - @Override - public void onZenChanged(int zen) { - for (Bubble b : mBubbleData.getBubbles()) { - b.setShowDot(b.showInShade()); - } - } - - @Override - public void onConfigChanged(ZenModeConfig config) { - for (Bubble b : mBubbleData.getBubbles()) { - b.setShowDot(b.showInShade()); - } - } - }); - - configurationController.addCallback(this /* configurationListener */); - mSysUiState = sysUiState; mBubbleData = data; mBubbleData.setListener(mBubbleDataListener); - mBubbleData.setSuppressionChangedListener(new NotificationSuppressionChangedListener() { - @Override - public void onBubbleNotificationSuppressionChange(Bubble bubble) { - // Make sure NoMan knows it's not showing in the shade anymore so anyone querying it - // can tell. - try { - mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(), - !bubble.showInShade()); - } catch (RemoteException e) { - // Bad things have happened - } + mBubbleData.setSuppressionChangedListener(bubble -> { + // Make sure NoMan knows it's not showing in the shade anymore so anyone querying it + // can tell. + try { + mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(), + !bubble.showInShade()); + } catch (RemoteException e) { + // Bad things have happened } }); mBubbleData.setPendingIntentCancelledListener(bubble -> { if (bubble.getBubbleIntent() == null) { return; } - if (bubble.isIntentActive() - || mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { + if (bubble.isIntentActive() || mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { bubble.setPendingIntentCanceled(); return; } - mHandler.post( - () -> removeBubble(bubble.getKey(), - BubbleController.DISMISS_INVALID_INTENT)); + mHandler.post(() -> removeBubble(bubble.getKey(), DISMISS_INVALID_INTENT)); }); - mNotificationEntryManager = entryManager; - mNotificationGroupManager = groupManager; - mNotifPipeline = notifPipeline; - - if (!featureFlags.isNewNotifPipelineRenderingEnabled()) { - setupNEM(); - } else { - setupNotifPipeline(); - } - - mNotificationShadeWindowController = notificationShadeWindowController; - mStatusBarStateListener = new StatusBarStateListener(); - statusBarStateController.addCallback(mStatusBarStateListener); - - mTaskStackListener = new BubbleTaskStackListener(); - TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener); - try { windowManagerShellWrapper.addPinnedStackListener(new BubblesImeListener()); } catch (RemoteException e) { @@ -511,25 +245,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config ServiceManager.getService(Context.STATUS_BAR_SERVICE)) : statusBarService; - mBubbleScrim = new ScrimView(mContext); - mBubbleScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); - mSavedBubbleKeysPerUser = new SparseSetArray<>(); - mCurrentUserId = mNotifUserManager.getCurrentUserId(); + mCurrentUserId = ActivityManager.getCurrentUser(); mBubbleData.setCurrentUserId(mCurrentUserId); - mNotifUserManager.addUserChangedListener( - new NotificationLockscreenUserManager.UserChangedListener() { - @Override - public void onUserChanged(int newUserId) { - BubbleController.this.saveBubbles(mCurrentUserId); - mBubbleData.dismissAll(DISMISS_USER_CHANGED); - BubbleController.this.restoreBubbles(newUserId); - mCurrentUserId = newUserId; - mBubbleData.setCurrentUserId(newUserId); - } - }); - mBubbleIconFactory = new BubbleIconFactory(context); mTaskOrganizer = organizer; mBubblePositioner = positioner; @@ -549,10 +268,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } @Override - public void onPackagesAvailable(String[] strings, UserHandle userHandle, - boolean b) { - - } + public void onPackagesAvailable(String[] strings, UserHandle userHandle, boolean b) {} @Override public void onPackagesUnavailable(String[] packages, UserHandle userHandle, @@ -577,17 +293,9 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } /** - * See {@link NotifCallback}. - */ - @Override - public void addNotifCallback(NotifCallback callback) { - mCallbacks.add(callback); - } - - /** * Hides the current input method, wherever it may be focused, via InputMethodManagerInternal. */ - public void hideCurrentInputMethod() { + void hideCurrentInputMethod() { try { mBarService.hideCurrentInputMethodForBubbles(); } catch (RemoteException e) { @@ -595,183 +303,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } - private void onBubbleExpandChanged(boolean shouldExpand) { - mSysUiState - .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand) - .commitUpdate(mContext.getDisplayId()); - } - - private void setupNEM() { - mNotificationEntryManager.addNotificationEntryListener( - new NotificationEntryListener() { - @Override - public void onPendingEntryAdded(NotificationEntry entry) { - onEntryAdded(entry); - } - - @Override - public void onPreEntryUpdated(NotificationEntry entry) { - onEntryUpdated(entry); - } - - @Override - public void onEntryRemoved( - NotificationEntry entry, - @android.annotation.Nullable NotificationVisibility visibility, - boolean removedByUser, - int reason) { - BubbleController.this.onEntryRemoved(entry); - } - - @Override - public void onNotificationRankingUpdated(RankingMap rankingMap) { - onRankingUpdated(rankingMap); - } - }); - - // The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator - mNotificationEntryManager.addNotificationRemoveInterceptor( - new NotificationRemoveInterceptor() { - @Override - public boolean onNotificationRemoveRequested( - String key, - NotificationEntry entry, - int 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 - && mBubbleData.isSummarySuppressed(groupKey)) { - mBubbleData.removeSuppressedSummary(groupKey); - } - } - }); - - 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(); - if (mBubbleData.isSummarySuppressed(groupKey)) { - mBubbleData.removeSuppressedSummary(groupKey); - - final NotificationEntry summary = - mNotificationEntryManager.getActiveNotificationUnfiltered( - mBubbleData.getSummaryKey(groupKey)); - if (summary != null) { - mNotificationEntryManager.performRemoveNotification( - summary.getSbn(), - getDismissedByUserStats(summary, false), - UNDEFINED_DISMISS_REASON); - } - } - - // 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() { - mNotifPipeline.addCollectionListener(new NotifCollectionListener() { - @Override - public void onEntryAdded(NotificationEntry entry) { - BubbleController.this.onEntryAdded(entry); - } - - @Override - public void onEntryUpdated(NotificationEntry entry) { - BubbleController.this.onEntryUpdated(entry); - } - - @Override - public void onRankingUpdate(RankingMap rankingMap) { - onRankingUpdated(rankingMap); - } - - @Override - public void onEntryRemoved(NotificationEntry entry, - @NotifCollection.CancellationReason int reason) { - BubbleController.this.onEntryRemoved(entry); - } - }); - } - - /** - * Returns the scrim drawn behind the bubble stack. This is managed by {@link ScrimController} - * since we want the scrim's appearance and behavior to be identical to that of the notification - * shade scrim. - */ - @Override - public ScrimView getScrimForBubble() { - return mBubbleScrim; - } - /** * Called when the status bar has become visible or invisible (either permanently or * temporarily). @@ -785,16 +316,47 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } + @Override + public void onZenStateChanged() { + for (Bubble b : mBubbleData.getBubbles()) { + b.setShowDot(b.showInShade()); + } + } + + @Override + public void onStatusBarStateChanged(boolean isShade) { + mIsStatusBarShade = isShade; + if (!mIsStatusBarShade) { + collapseStack(); + } + + if (mNotifEntryToExpandOnShadeUnlock != null) { + expandStackAndSelectBubble(mNotifEntryToExpandOnShadeUnlock); + mNotifEntryToExpandOnShadeUnlock = null; + } + + updateStack(); + } + + @Override + public void onUserChanged(int newUserId) { + saveBubbles(mCurrentUserId); + mBubbleData.dismissAll(DISMISS_USER_CHANGED); + restoreBubbles(newUserId); + mCurrentUserId = newUserId; + mBubbleData.setCurrentUserId(newUserId); + } + /** * Sets whether to perform inflation on the same thread as the caller. This method should only * be used in tests, not in production. */ @VisibleForTesting - void setInflateSynchronously(boolean inflateSynchronously) { + public void setInflateSynchronously(boolean inflateSynchronously) { mInflateSynchronously = inflateSynchronously; } - @Override + /** Set a listener to be notified of when overflow view update. */ public void setOverflowListener(BubbleData.Listener listener) { mOverflowListener = listener; } @@ -802,21 +364,24 @@ public class BubbleController implements Bubbles, ConfigurationController.Config /** * @return Bubbles for updating overflow. */ - @Override - public List<Bubble> getOverflowBubbles() { + List<Bubble> getOverflowBubbles() { return mBubbleData.getOverflowBubbles(); } - @Override + /** The task listener for events in bubble tasks. */ public ShellTaskOrganizer getTaskOrganizer() { return mTaskOrganizer; } - @Override - public BubblePositioner getPositioner() { + /** Contains information to help position things on the screen. */ + BubblePositioner getPositioner() { return mBubblePositioner; } + SysuiProxy getSysuiProxy() { + return mSysuiProxy; + } + /** * BubbleStackView is lazily created by this method the first time a Bubble is added. This * method initializes the stack view and adds it to the StatusBar just above the scrim. @@ -824,23 +389,14 @@ public class BubbleController implements Bubbles, ConfigurationController.Config private void ensureStackViewCreated() { if (mStackView == null) { mStackView = new BubbleStackView( - mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator, - this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged, - this::hideCurrentInputMethod, this::onBubbleExpandChanged, mBubblePositioner); + mContext, this, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator); mStackView.setStackStartPosition(mPositionFromRemovedStack); mStackView.addView(mBubbleScrim); mStackView.onOrientationChanged(); if (mExpandListener != null) { mStackView.setExpandListener(mExpandListener); } - - mStackView.setUnbubbleConversationCallback(key -> { - final NotificationEntry entry = - mNotificationEntryManager.getPendingOrActiveNotif(key); - if (entry != null) { - onUserChangedBubble(entry, false /* shouldBubble */); - } - }); + mStackView.setUnbubbleConversationCallback(mSysuiProxy::onUnbubbleConversation); } addToWindowManagerMaybe(); @@ -882,7 +438,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } - private void onImeVisibilityChanged(boolean imeVisible) { + void onImeVisibilityChanged(boolean imeVisible) { mImeVisible = imeVisible; } @@ -913,7 +469,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config * Called by the BubbleStackView and whenever all bubbles have animated out, and none have been * added in the meantime. */ - private void onAllBubblesAnimatedOut() { + void onAllBubblesAnimatedOut() { if (mStackView != null) { mStackView.setVisibility(INVISIBLE); removeFromWindowManagerMaybe(); @@ -946,12 +502,8 @@ public class BubbleController implements Bubbles, ConfigurationController.Config // There were no bubbles saved for this used. return; } - for (NotificationEntry e : - mNotificationEntryManager.getActiveNotificationsForCurrentUser()) { - if (savedBubbleKeys.contains(e.getKey()) - && mNotificationInterruptStateProvider.shouldBubbleUp(e) - && e.isBubble() - && canLaunchInActivityView(mContext, e)) { + for (BubbleEntry e : mSysuiProxy.getShouldRestoredEntries(savedBubbleKeys)) { + if (canLaunchInActivityView(mContext, e)) { updateBubble(e, true /* suppressFlyout */, false /* showInShade */); } } @@ -960,27 +512,18 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } @Override - public void onUiModeChanged() { - updateForThemeChanges(); - } - - @Override - public void onOverlayChanged() { - updateForThemeChanges(); - } - - private void updateForThemeChanges() { + public void updateForThemeChanges() { if (mStackView != null) { mStackView.onThemeChanged(); } mBubbleIconFactory = new BubbleIconFactory(mContext); // Reload each bubble for (Bubble b: mBubbleData.getBubbles()) { - b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory, + b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory, false /* skipInflation */); } for (Bubble b: mBubbleData.getOverflowBubbles()) { - b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory, + b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory, false /* skipInflation */); } } @@ -1012,9 +555,16 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } - /** - * Set a listener to be notified of bubble expand events. - */ + @Override + public void setBubbleScrim(View view) { + mBubbleScrim = view; + } + + @Override + public void setSysuiProxy(SysuiProxy proxy) { + mSysuiProxy = proxy; + } + @Override public void setExpandListener(BubbleExpandListener listener) { mExpandListener = ((isExpanding, key) -> { @@ -1032,7 +582,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config * screen (e.g. if on AOD). */ @VisibleForTesting - boolean hasBubbles() { + public boolean hasBubbles() { if (mStackView == null) { return false; } @@ -1050,24 +600,37 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } @Override - public boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry) { - String key = entry.getKey(); + public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) { boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key) && !mBubbleData.getAnyBubbleWithkey(key).showInShade()); - String groupKey = entry.getSbn().getGroupKey(); boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey); boolean isSummary = key.equals(mBubbleData.getSummaryKey(groupKey)); return (isSummary && isSuppressedSummary) || isSuppressedBubble; } @Override - public boolean isBubbleExpanded(NotificationEntry entry) { - return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null - && mBubbleData.getSelectedBubble().getKey().equals(entry.getKey()); + public boolean isSummarySuppressed(String groupKey) { + return mBubbleData.isSummarySuppressed(groupKey); } @Override + public void removeSuppressedSummary(String groupKey) { + mBubbleData.removeSuppressedSummary(groupKey); + } + + @Override + public String getSummaryKey(String groupKey) { + return mBubbleData.getSummaryKey(groupKey); + } + + @Override + public boolean isBubbleExpanded(String key) { + return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null + && mBubbleData.getSelectedBubble().getKey().equals(key); + } + + /** Promote the provided bubble from the overflow view. */ public void promoteBubbleFromOverflow(Bubble bubble) { mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK); bubble.setInflateSynchronously(mInflateSynchronously); @@ -1077,8 +640,8 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } @Override - public void expandStackAndSelectBubble(NotificationEntry entry) { - if (mStatusBarStateListener.getCurrentState() == SHADE) { + public void expandStackAndSelectBubble(BubbleEntry entry) { + if (mIsStatusBarShade) { mNotifEntryToExpandOnShadeUnlock = null; String key = entry.getKey(); @@ -1104,27 +667,13 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } - @Override - public void onUserChangedImportance(NotificationEntry entry) { - try { - int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; - flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE; - mBarService.onNotificationBubbleChanged(entry.getKey(), true, flags); - } catch (RemoteException e) { - Log.e(TAG, e.getMessage()); - } - mShadeController.collapsePanel(true); - if (entry.getRow() != null) { - entry.getRow().updateBubbleButton(); - } - } - /** * Adds or updates a bubble associated with the provided notification entry. * * @param notif the notification associated with this bubble. */ - void updateBubble(NotificationEntry notif) { + @VisibleForTesting + public void updateBubble(BubbleEntry notif) { updateBubble(notif, false /* suppressFlyout */, true /* showInShade */); } @@ -1144,27 +693,32 @@ public class BubbleController implements Bubbles, ConfigurationController.Config return; } bubble.inflate((b) -> mBubbleData.overflowBubble(DISMISS_AGED, bubble), - mContext, mStackView, mBubbleIconFactory, true /* skipInflation */); + mContext, this, mStackView, mBubbleIconFactory, true /* skipInflation */); }); return null; }); } - void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) { + /** + * Adds or updates a bubble associated with the provided notification entry. + * + * @param notif the notification associated with this bubble. + * @param suppressFlyout this bubble suppress flyout or not. + * @param showInShade this bubble show in shade or not. + */ + @VisibleForTesting + public void updateBubble(BubbleEntry notif, boolean suppressFlyout, boolean showInShade) { // If this is an interruptive notif, mark that it's interrupted - if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) { - notif.setInterruption(); - } + mSysuiProxy.setNotificationInterruption(notif.getKey()); if (!notif.getRanking().visuallyInterruptive() && (notif.getBubbleMetadata() != null && !notif.getBubbleMetadata().getAutoExpandBubble()) && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) { // Update the bubble but don't promote it out of overflow Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey()); - b.setEntry(notifToBubbleEntry(notif)); + b.setEntry(notif); } else { - Bubble bubble = mBubbleData.getOrCreateBubble( - notifToBubbleEntry(notif), null /* persistedBubble */); + Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */); inflateAndAdd(bubble, suppressFlyout, showInShade); } } @@ -1174,68 +728,33 @@ public class BubbleController implements Bubbles, ConfigurationController.Config ensureStackViewCreated(); bubble.setInflateSynchronously(mInflateSynchronously); bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade), - mContext, mStackView, mBubbleIconFactory, false /* skipInflation */); - } - - @Override - public void onUserChangedBubble(@NonNull final NotificationEntry entry, boolean shouldBubble) { - NotificationChannel channel = entry.getChannel(); - final String appPkg = entry.getSbn().getPackageName(); - final int appUid = entry.getSbn().getUid(); - if (channel == null || appPkg == null) { - return; - } - - // Update the state in NotificationManagerService - try { - int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; - flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE; - mBarService.onNotificationBubbleChanged(entry.getKey(), shouldBubble, flags); - } catch (RemoteException e) { - } - - // Change the settings - channel = NotificationChannelHelper.createConversationChannelIfNeeded(mContext, - mINotificationManager, entry, channel); - channel.setAllowBubbles(shouldBubble); - try { - int currentPref = mINotificationManager.getBubblePreferenceForPackage(appPkg, appUid); - if (shouldBubble && currentPref == BUBBLE_PREFERENCE_NONE) { - mINotificationManager.setBubblesAllowed(appPkg, appUid, BUBBLE_PREFERENCE_SELECTED); - } - mINotificationManager.updateNotificationChannelForPackage(appPkg, appUid, channel); - } catch (RemoteException e) { - Log.e(TAG, e.getMessage()); - } - - if (shouldBubble) { - mShadeController.collapsePanel(true); - if (entry.getRow() != null) { - entry.getRow().updateBubbleButton(); - } - } + mContext, this, mStackView, mBubbleIconFactory, false /* skipInflation */); } + /** + * Removes the bubble with the given key. + * <p> + * Must be called from the main thread. + */ + @VisibleForTesting @MainThread - @Override public void removeBubble(String key, int reason) { if (mBubbleData.hasAnyBubbleWithKey(key)) { mBubbleData.dismissBubbleWithKey(key, reason); } } - private void onEntryAdded(NotificationEntry entry) { - if (mNotificationInterruptStateProvider.shouldBubbleUp(entry) - && entry.isBubble() - && canLaunchInActivityView(mContext, entry)) { + @Override + public void onEntryAdded(BubbleEntry entry) { + if (canLaunchInActivityView(mContext, entry)) { updateBubble(entry); } } - private void onEntryUpdated(NotificationEntry entry) { + @Override + public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) { // shouldBubbleUp checks canBubble & for bubble metadata - boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry) - && canLaunchInActivityView(mContext, entry); + boolean shouldBubble = shouldBubbleUp && canLaunchInActivityView(mContext, entry); if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { // It was previously a bubble but no longer a bubble -- lets remove it removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE); @@ -1244,9 +763,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } - private void onEntryRemoved(NotificationEntry entry) { + @Override + public void onEntryRemoved(BubbleEntry entry) { if (isSummaryOfBubbles(entry)) { - final String groupKey = entry.getSbn().getGroupKey(); + final String groupKey = entry.getStatusBarNotification().getGroupKey(); mBubbleData.removeSuppressedSummary(groupKey); // Remove any associated bubble children with the summary @@ -1259,39 +779,30 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } - /** - * Called when NotificationListener has received adjusted notification rank and reapplied - * filtering and sorting. This is used to dismiss or create bubbles based on changes in - * permissions on the notification channel or the global setting. - * - * @param rankingMap the updated ranking map from NotificationListenerService - */ - private void onRankingUpdated(RankingMap rankingMap) { + @Override + public void onRankingUpdated(RankingMap rankingMap) { if (mTmpRanking == null) { mTmpRanking = new NotificationListenerService.Ranking(); } String[] orderedKeys = rankingMap.getOrderedKeys(); for (int i = 0; i < orderedKeys.length; i++) { String key = orderedKeys[i]; - NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key); + BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(key); rankingMap.getRanking(key, mTmpRanking); boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key); if (isActiveBubble && !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, BubbleController.DISMISS_BLOCKED); - } else if (isActiveBubble - && !mNotificationInterruptStateProvider.shouldBubbleUp(entry)) { + mBubbleData.dismissBubbleWithKey(key, DISMISS_BLOCKED); + } else if (isActiveBubble && !mSysuiProxy.shouldBubbleUp(key)) { // If this entry is allowed to bubble, but cannot currently bubble up, dismiss it. // This happens when DND is enabled and configured to hide bubbles. Dismissing with // the reason DISMISS_NO_BUBBLE_UP will retain the underlying notification, so that // the bubble will be re-created if shouldBubbleUp returns true. - mBubbleData.dismissBubbleWithKey( - key, BubbleController.DISMISS_NO_BUBBLE_UP); + mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP); } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) { entry.setFlagBubble(true); - onEntryUpdated(entry); + onEntryUpdated(entry, true /* shouldBubbleUp */); } } } @@ -1306,23 +817,18 @@ public class BubbleController implements Bubbles, ConfigurationController.Config return bubbleChildren; } for (Bubble bubble : mBubbleData.getActiveBubbles()) { - final NotificationEntry entry = - mNotificationEntryManager.getPendingOrActiveNotif(bubble.getKey()); - if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) { + final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(bubble.getKey()); + if (entry != null && groupKey.equals(entry.getStatusBarNotification().getGroupKey())) { bubbleChildren.add(bubble); } } return bubbleChildren; } - private void setIsBubble(@NonNull final NotificationEntry entry, final boolean isBubble, + private void setIsBubble(@NonNull final BubbleEntry entry, final boolean isBubble, final boolean autoExpand) { Objects.requireNonNull(entry); - if (isBubble) { - entry.getSbn().getNotification().flags |= FLAG_BUBBLE; - } else { - entry.getSbn().getNotification().flags &= ~FLAG_BUBBLE; - } + entry.setFlagBubble(isBubble); try { int flags = 0; if (autoExpand) { @@ -1338,8 +844,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config private void setIsBubble(@NonNull final Bubble b, final boolean isBubble) { Objects.requireNonNull(b); b.setIsBubble(isBubble); - final NotificationEntry entry = mNotificationEntryManager - .getPendingOrActiveNotif(b.getKey()); + final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(b.getKey()); if (entry != null) { // Updating the entry to be a bubble will trigger our normal update flow setIsBubble(entry, isBubble, b.shouldAutoExpand()); @@ -1372,7 +877,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config // Collapsing? Do this first before remaining steps. if (update.expandedChanged && !update.expanded) { mStackView.setExpanded(false); - mNotificationShadeWindowController.setRequestTopUi(false, TAG); + mSysuiProxy.requestNotificationShadeTopUi(false, TAG); } // Do removals, if any. @@ -1396,8 +901,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config if (reason == DISMISS_NOTIF_CANCEL) { bubblesToBeRemovedFromRepository.add(bubble); } - final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( - bubble.getKey()); if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { if (!mBubbleData.hasOverflowBubbleWithKey(bubble.getKey()) && (!bubble.showInShade() @@ -1405,31 +908,21 @@ public class BubbleController implements Bubbles, ConfigurationController.Config || reason == DISMISS_GROUP_CANCELLED)) { // The bubble is now gone & the notification is hidden from the shade, so // time to actually remove it - for (NotifCallback cb : mCallbacks) { - if (entry != null) { - cb.removeNotification( - entry, - getDismissedByUserStats(entry, true), - REASON_CANCEL); - } - } + mSysuiProxy.notifyRemoveNotification(bubble.getKey(), REASON_CANCEL); } else { if (bubble.isBubble()) { setIsBubble(bubble, false /* isBubble */); } - if (entry != null && entry.getRow() != null) { - entry.getRow().updateBubbleButton(); - } + mSysuiProxy.updateNotificationBubbleButton(bubble.getKey()); } } + final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(bubble.getKey()); if (entry != null) { - final String groupKey = entry.getSbn().getGroupKey(); + final String groupKey = entry.getStatusBarNotification().getGroupKey(); if (getBubblesInGroup(groupKey).isEmpty()) { // Time to potentially remove the summary - for (NotifCallback cb : mCallbacks) { - cb.maybeCancelSummary(entry); - } + mSysuiProxy.notifyMaybeCancelSummary(bubble.getKey()); } } } @@ -1454,11 +947,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config if (update.selectionChanged && mStackView != null) { mStackView.setSelectedBubble(update.selectedBubble); if (update.selectedBubble != null) { - final NotificationEntry entry = mNotificationEntryManager - .getPendingOrActiveNotif(update.selectedBubble.getKey()); - if (entry != null) { - mNotificationGroupManager.updateSuppression(entry); - } + mSysuiProxy.updateNotificationSuppression(update.selectedBubble.getKey()); } } @@ -1466,24 +955,20 @@ public class BubbleController implements Bubbles, ConfigurationController.Config if (update.expandedChanged && update.expanded) { if (mStackView != null) { mStackView.setExpanded(true); - mNotificationShadeWindowController.setRequestTopUi(true, TAG); + mSysuiProxy.requestNotificationShadeTopUi(true, TAG); } } - for (NotifCallback cb : mCallbacks) { - cb.invalidateNotifications("BubbleData.Listener.applyUpdate"); - } + mSysuiProxy.notifyInvalidateNotifications("BubbleData.Listener.applyUpdate"); updateStack(); } }; @Override - public boolean handleDismissalInterception(NotificationEntry entry) { - if (entry == null) { - return false; - } + public boolean handleDismissalInterception(BubbleEntry entry, + @Nullable List<BubbleEntry> children, IntConsumer removeCallback) { if (isSummaryOfBubbles(entry)) { - handleSummaryDismissalInterception(entry); + handleSummaryDismissalInterception(entry, children, removeCallback); } else { Bubble bubble = mBubbleData.getBubbleInStackWithKey(entry.getKey()); if (bubble == null || !entry.isBubble()) { @@ -1496,87 +981,50 @@ public class BubbleController implements Bubbles, ConfigurationController.Config bubble.setShowDot(false /* show */); } // Update the shade - for (NotifCallback cb : mCallbacks) { - cb.invalidateNotifications("BubbleController.handleDismissalInterception"); - } + mSysuiProxy.notifyInvalidateNotifications("BubbleController.handleDismissalInterception"); return true; } - private boolean isSummaryOfBubbles(NotificationEntry entry) { - if (entry == null) { - return false; - } - - String groupKey = entry.getSbn().getGroupKey(); + private boolean isSummaryOfBubbles(BubbleEntry entry) { + String groupKey = entry.getStatusBarNotification().getGroupKey(); ArrayList<Bubble> bubbleChildren = getBubblesInGroup(groupKey); boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey) && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey())); - boolean isSummary = entry.getSbn().getNotification().isGroupSummary(); - return (isSuppressedSummary || isSummary) - && bubbleChildren != null - && !bubbleChildren.isEmpty(); + boolean isSummary = entry.getStatusBarNotification().getNotification().isGroupSummary(); + return (isSuppressedSummary || isSummary) && !bubbleChildren.isEmpty(); } - private void handleSummaryDismissalInterception(NotificationEntry summary) { - // current children in the row: - final List<NotificationEntry> children = summary.getAttachedNotifChildren(); + private void handleSummaryDismissalInterception( + BubbleEntry summary, @Nullable List<BubbleEntry> children, IntConsumer removeCallback) { if (children != null) { for (int i = 0; i < children.size(); i++) { - NotificationEntry child = children.get(i); + BubbleEntry child = children.get(i); if (mBubbleData.hasAnyBubbleWithKey(child.getKey())) { // Suppress the bubbled child // As far as group manager is concerned, once a child is no longer shown // in the shade, it is essentially removed. Bubble bubbleChild = mBubbleData.getAnyBubbleWithkey(child.getKey()); if (bubbleChild != null) { - final NotificationEntry entry = mNotificationEntryManager - .getPendingOrActiveNotif(bubbleChild.getKey()); - if (entry != null) { - mNotificationGroupManager.onEntryRemoved(entry); - } + mSysuiProxy.removeNotificationEntry(bubbleChild.getKey()); bubbleChild.setSuppressNotification(true); bubbleChild.setShowDot(false /* show */); } } else { // non-bubbled children can be removed - for (NotifCallback cb : mCallbacks) { - cb.removeNotification( - child, - getDismissedByUserStats(child, true), - REASON_GROUP_SUMMARY_CANCELED); - } + removeCallback.accept(i); } } } // And since all children are removed, remove the summary. - mNotificationGroupManager.onEntryRemoved(summary); + removeCallback.accept(-1); // TODO: (b/145659174) remove references to mSuppressedGroupKeys once fully migrated - mBubbleData.addSummaryToSuppress(summary.getSbn().getGroupKey(), + mBubbleData.addSummaryToSuppress(summary.getStatusBarNotification().getGroupKey(), summary.getKey()); } /** - * Gets the DismissedByUserStats used by {@link NotificationEntryManager}. - * Will not be necessary when using the new notification pipeline's {@link NotifCollection}. - * Instead, this is taken care of by {@link BubbleCoordinator}. - */ - private DismissedByUserStats getDismissedByUserStats( - NotificationEntry entry, - boolean isVisible) { - return new DismissedByUserStats( - DISMISSAL_BUBBLE, - DISMISS_SENTIMENT_NEUTRAL, - NotificationVisibility.obtain( - entry.getKey(), - entry.getRanking().getRank(), - mNotificationEntryManager.getActiveNotificationsCount(), - isVisible, - NotificationLogger.getNotificationLocation(entry))); - } - - /** * Updates the visibility of the bubbles based on current state. * Does not un-bubble, just hides or un-hides. * Updates stack description for TalkBack focus. @@ -1586,7 +1034,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config return; } - if (mStatusBarStateListener.getCurrentState() != SHADE) { + if (!mIsStatusBarShade) { // Bubbles don't appear over the locked shade. mStackView.setVisibility(INVISIBLE); } else if (hasBubbles()) { @@ -1610,20 +1058,21 @@ public class BubbleController implements Bubbles, ConfigurationController.Config final BubbleViewProvider expandedViewProvider = mStackView.getExpandedBubble(); if (expandedViewProvider != null && isStackExpanded() && !mStackView.isExpansionAnimating() - && !mNotificationShadeWindowController.getPanelExpanded()) { + && !mSysuiProxy.isNotificationShadeExpand()) { return expandedViewProvider.getTaskId(); } return INVALID_TASK_ID; } @VisibleForTesting - BubbleStackView getStackView() { + public BubbleStackView getStackView() { return mStackView; } /** * Description of current bubble state. */ + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("BubbleController state:"); mBubbleData.dump(fd, pw, args); @@ -1635,39 +1084,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } /** - * This task stack listener is responsible for responding to tasks moved to the front - * which are on the default (main) display. When this happens, expanded bubbles must be - * collapsed so the user may interact with the app which was just moved to the front. - * <p> - * This listener is registered with SystemUI's ActivityManagerWrapper which dispatches - * these calls via a main thread Handler. - */ - @MainThread - private class BubbleTaskStackListener extends TaskStackChangeListener { - - @Override - public void onTaskMovedToFront(RunningTaskInfo taskInfo) { - int expandedId = getExpandedTaskId(); - if (expandedId != INVALID_TASK_ID && expandedId != taskInfo.taskId) { - mBubbleData.setExpanded(false); - } - } - - @Override - public void onActivityRestartAttempt(RunningTaskInfo taskInfo, boolean homeTaskVisible, - boolean clearedTask, boolean wasVisible) { - for (Bubble b : mBubbleData.getBubbles()) { - if (taskInfo.taskId == b.getTaskId()) { - mBubbleData.setSelectedBubble(b); - mBubbleData.setExpanded(true); - return; - } - } - } - - } - - /** * Whether an intent is properly configured to display in an {@link android.app.ActivityView}. * * Keep checks in sync with NotificationManagerService#canLaunchInActivityView. Typically @@ -1676,7 +1092,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config * @param context the context to use. * @param entry the entry to bubble. */ - static boolean canLaunchInActivityView(Context context, NotificationEntry entry) { + static boolean canLaunchInActivityView(Context context, BubbleEntry entry) { PendingIntent intent = entry.getBubbleMetadata() != null ? entry.getBubbleMetadata().getIntent() : null; @@ -1688,8 +1104,8 @@ public class BubbleController implements Bubbles, ConfigurationController.Config Log.w(TAG, "Unable to create bubble -- no intent: " + entry.getKey()); return false; } - PackageManager packageManager = StatusBar.getPackageManagerForUser( - context, entry.getSbn().getUser().getIdentifier()); + PackageManager packageManager = getPackageManagerForUser( + context, entry.getStatusBarNotification().getUser().getIdentifier()); ActivityInfo info = intent.getIntent().resolveActivityInfo(packageManager, 0); if (info == null) { @@ -1707,6 +1123,24 @@ public class BubbleController implements Bubbles, ConfigurationController.Config return true; } + static PackageManager getPackageManagerForUser(Context context, int userId) { + Context contextForUser = context; + // UserHandle defines special userId as negative values, e.g. USER_ALL + if (userId >= 0) { + try { + // Create a context for the correct user so if a package isn't installed + // for user 0 we can still load information about the package. + contextForUser = + context.createPackageContextAsUser(context.getPackageName(), + Context.CONTEXT_RESTRICTED, + new UserHandle(userId)); + } catch (PackageManager.NameNotFoundException e) { + // Shouldn't fail to find the package name for system ui. + } + } + return contextForUser.getPackageManager(); + } + /** PinnedStackListener that dispatches IME visibility updates to the stack. */ //TODO(b/170442945): Better way to do this / insets listener? private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener { @@ -1717,10 +1151,4 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } } - - static BubbleEntry notifToBubbleEntry(NotificationEntry e) { - return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(), - e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(), - e.shouldSuppressPeek()); - } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index b4626f27d370..8cacc8f2ef01 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -34,7 +34,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; import com.android.systemui.R; -import com.android.systemui.bubbles.BubbleController.DismissReason; +import com.android.systemui.bubbles.Bubbles.DismissReason; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -137,8 +137,8 @@ public class BubbleData { private Listener mListener; @Nullable - private BubbleController.NotificationSuppressionChangedListener mSuppressionListener; - private BubbleController.PendingIntentCanceledListener mCancelledListener; + private Bubbles.NotificationSuppressionChangedListener mSuppressionListener; + private Bubbles.PendingIntentCanceledListener mCancelledListener; /** * We track groups with summaries that aren't visibly displayed but still kept around because @@ -165,12 +165,12 @@ public class BubbleData { } public void setSuppressionChangedListener( - BubbleController.NotificationSuppressionChangedListener listener) { + Bubbles.NotificationSuppressionChangedListener listener) { mSuppressionListener = listener; } public void setPendingIntentCancelledListener( - BubbleController.PendingIntentCanceledListener listener) { + Bubbles.PendingIntentCanceledListener listener) { mCancelledListener = listener; } @@ -344,7 +344,8 @@ public class BubbleData { /** * Whether the summary for the provided group key is suppressed. */ - boolean isSummarySuppressed(String groupKey) { + @VisibleForTesting + public boolean isSummarySuppressed(String groupKey) { return mSuppressedGroupKeys.containsKey(groupKey); } @@ -415,7 +416,7 @@ public class BubbleData { // skip the selected bubble .filter((b) -> !b.equals(mSelectedBubble)) .findFirst() - .ifPresent((b) -> doRemove(b.getKey(), BubbleController.DISMISS_AGED)); + .ifPresent((b) -> doRemove(b.getKey(), Bubbles.DISMISS_AGED)); } } @@ -459,12 +460,12 @@ public class BubbleData { int indexToRemove = indexForKey(key); if (indexToRemove == -1) { if (hasOverflowBubbleWithKey(key) - && (reason == BubbleController.DISMISS_NOTIF_CANCEL - || reason == BubbleController.DISMISS_GROUP_CANCELLED - || reason == BubbleController.DISMISS_NO_LONGER_BUBBLE - || reason == BubbleController.DISMISS_BLOCKED - || reason == BubbleController.DISMISS_SHORTCUT_REMOVED - || reason == BubbleController.DISMISS_PACKAGE_REMOVED)) { + && (reason == Bubbles.DISMISS_NOTIF_CANCEL + || reason == Bubbles.DISMISS_GROUP_CANCELLED + || reason == Bubbles.DISMISS_NO_LONGER_BUBBLE + || reason == Bubbles.DISMISS_BLOCKED + || reason == Bubbles.DISMISS_SHORTCUT_REMOVED + || reason == Bubbles.DISMISS_PACKAGE_REMOVED)) { Bubble b = getOverflowBubbleWithKey(key); if (DEBUG_BUBBLE_DATA) { @@ -512,8 +513,8 @@ public class BubbleData { void overflowBubble(@DismissReason int reason, Bubble bubble) { if (bubble.getPendingIntentCanceled() - || !(reason == BubbleController.DISMISS_AGED - || reason == BubbleController.DISMISS_USER_GESTURE)) { + || !(reason == Bubbles.DISMISS_AGED + || reason == Bubbles.DISMISS_USER_GESTURE)) { return; } if (DEBUG_BUBBLE_DATA) { @@ -529,7 +530,7 @@ public class BubbleData { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "Overflow full. Remove: " + oldest); } - mStateChange.bubbleRemoved(oldest, BubbleController.DISMISS_OVERFLOW_MAX_REACHED); + mStateChange.bubbleRemoved(oldest, Bubbles.DISMISS_OVERFLOW_MAX_REACHED); mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_MAX_REACHED); mOverflowBubbles.remove(oldest); mStateChange.removedOverflowBubble = oldest; @@ -694,7 +695,7 @@ public class BubbleData { } private void maybeSendDeleteIntent(@DismissReason int reason, @NonNull final Bubble bubble) { - if (reason != BubbleController.DISMISS_USER_GESTURE) return; + if (reason != Bubbles.DISMISS_USER_GESTURE) return; PendingIntent deleteIntent = bubble.getDeleteIntent(); if (deleteIntent == null) return; try { @@ -726,7 +727,7 @@ public class BubbleData { * The set of bubbles in overflow. */ @VisibleForTesting(visibility = PRIVATE) - List<Bubble> getOverflowBubbles() { + public List<Bubble> getOverflowBubbles() { return Collections.unmodifiableList(mOverflowBubbles); } @@ -742,7 +743,7 @@ public class BubbleData { @VisibleForTesting(visibility = PRIVATE) @Nullable - Bubble getBubbleInStackWithKey(String key) { + public Bubble getBubbleInStackWithKey(String key) { for (int i = 0; i < mBubbles.size(); i++) { Bubble bubble = mBubbles.get(i); if (bubble.getKey().equals(key)) { @@ -764,7 +765,7 @@ public class BubbleData { } @VisibleForTesting(visibility = PRIVATE) - Bubble getOverflowBubbleWithKey(String key) { + public Bubble getOverflowBubbleWithKey(String key) { for (int i = 0; i < mOverflowBubbles.size(); i++) { Bubble bubble = mOverflowBubbles.get(i); if (bubble.getKey().equals(key)) { @@ -788,7 +789,7 @@ public class BubbleData { * This method should only be used in tests, not in production. */ @VisibleForTesting - void setMaxOverflowBubbles(int maxOverflowBubbles) { + public void setMaxOverflowBubbles(int maxOverflowBubbles) { mMaxOverflowBubbles = maxOverflowBubbles; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java index d98fee399470..3937422750cc 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java @@ -33,10 +33,10 @@ public class BubbleDebugConfig { // to figure-out the origin of a log message while debugging the Bubbles a little painful. By // setting this constant to true, log messages from the Bubbles package will be tagged with // their class names instead fot the generic tag. - static final boolean TAG_WITH_CLASS_NAME = false; + public static final boolean TAG_WITH_CLASS_NAME = false; // Default log tag for the Bubbles package. - static final String TAG_BUBBLES = "Bubbles"; + public static final String TAG_BUBBLES = "Bubbles"; static final boolean DEBUG_BUBBLE_CONTROLLER = false; static final boolean DEBUG_BUBBLE_DATA = false; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java index 6a1302518699..a0d3391f8347 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java @@ -16,6 +16,9 @@ package com.android.systemui.bubbles; +import static android.app.Notification.FLAG_BUBBLE; + +import android.app.Notification; import android.app.Notification.BubbleMetadata; import android.app.NotificationManager.Policy; import android.service.notification.NotificationListenerService.Ranking; @@ -67,12 +70,45 @@ public class BubbleEntry { return mSbn.getKey(); } + /** @return the group key in the {@link StatusBarNotification}. */ + public String getGroupKey() { + return mSbn.getGroupKey(); + } + /** @return the {@link BubbleMetadata} in the {@link StatusBarNotification}. */ @Nullable public BubbleMetadata getBubbleMetadata() { return getStatusBarNotification().getNotification().getBubbleMetadata(); } + /** + * Updates the {@link Notification#FLAG_BUBBLE} flag on this notification to indicate + * whether it is a bubble or not. If this entry is set to not bubble, or does not have + * the required info to bubble, the flag cannot be set to true. + * + * @param shouldBubble whether this notification should be flagged as a bubble. + * @return true if the value changed. + */ + public boolean setFlagBubble(boolean shouldBubble) { + boolean wasBubble = isBubble(); + if (!shouldBubble) { + mSbn.getNotification().flags &= ~FLAG_BUBBLE; + } else if (getBubbleMetadata() != null && canBubble()) { + // wants to be bubble & can bubble, set flag + mSbn.getNotification().flags |= FLAG_BUBBLE; + } + return wasBubble != isBubble(); + } + + public boolean isBubble() { + return (mSbn.getNotification().flags & FLAG_BUBBLE) != 0; + } + + /** @see Ranking#canBubble() */ + public boolean canBubble() { + return mRanking.canBubble(); + } + /** @return true if this notification is clearable. */ public boolean isClearable() { return mIsClearable; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index cf2e13356cca..ae3c683cb165 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -54,7 +54,6 @@ import android.widget.LinearLayout; import androidx.annotation.Nullable; import com.android.internal.policy.ScreenDecorationsUtils; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.recents.TriangleShape; import com.android.systemui.statusbar.AlphaOptimizedButton; @@ -99,7 +98,7 @@ public class BubbleExpandedView extends LinearLayout { // TODO(b/170891664): Don't use a flag, set the BubbleOverflow object instead private boolean mIsOverflow; - private Bubbles mBubbles = Dependency.get(Bubbles.class); + private BubbleController mController; private BubbleStackView mStackView; private BubblePositioner mPositioner; @@ -156,8 +155,7 @@ public class BubbleExpandedView extends LinearLayout { // the bubble again so we'll just remove it. Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey() + ", " + e.getMessage() + "; removing bubble"); - mBubbles.removeBubble(getBubbleKey(), - BubbleController.DISMISS_INVALID_INTENT); + mController.removeBubble(getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT); } }); mInitialized = true; @@ -195,15 +193,15 @@ public class BubbleExpandedView extends LinearLayout { } if (mBubble != null) { // Must post because this is called from a binder thread. - post(() -> mBubbles.removeBubble(mBubble.getKey(), - BubbleController.DISMISS_TASK_FINISHED)); + post(() -> mController.removeBubble( + mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED)); } } @Override public void onBackPressedOnTaskRoot(int taskId) { if (mTaskId == taskId && mStackView.isExpanded()) { - mBubbles.collapseStack(); + mController.collapseStack(); } } }; @@ -250,9 +248,6 @@ public class BubbleExpandedView extends LinearLayout { R.dimen.bubble_manage_button_height); mSettingsIcon = findViewById(R.id.settings_button); - mPositioner = mBubbles.getPositioner(); - - mTaskView = new TaskView(mContext, mBubbles.getTaskOrganizer()); // Set ActivityView's alpha value as zero, since there is no view content to be shown. setContentVisibility(false); @@ -263,7 +258,6 @@ public class BubbleExpandedView extends LinearLayout { } }); mExpandedViewContainer.setClipToOutline(true); - mExpandedViewContainer.addView(mTaskView); mExpandedViewContainer.setLayoutParams( new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); addView(mExpandedViewContainer); @@ -274,9 +268,7 @@ public class BubbleExpandedView extends LinearLayout { // ==> expanded view // ==> activity view // ==> manage button - bringChildToFront(mTaskView); bringChildToFront(mSettingsIcon); - mTaskView.setListener(mTaskViewListener); applyThemeAttrs(); @@ -314,6 +306,20 @@ public class BubbleExpandedView extends LinearLayout { super.onAttachedToWindow(); mTaskView.setExecutor(new HandlerExecutor(getHandler())); } + /** + * Initialize {@link BubbleController} and {@link BubbleStackView} here, this method must need + * to be called after view inflate. + */ + void initialize(BubbleController controller, BubbleStackView stackView) { + mController = controller; + mStackView = stackView; + + mTaskView = new TaskView(mContext, mController.getTaskOrganizer()); + mExpandedViewContainer.addView(mTaskView); + bringChildToFront(mTaskView); + mTaskView.setListener(mTaskViewListener); + mPositioner = mController.getPositioner(); + } void updateDimensions() { Resources res = getResources(); @@ -464,16 +470,12 @@ public class BubbleExpandedView extends LinearLayout { return mTaskId; } - void setStackView(BubbleStackView stackView) { - mStackView = stackView; - } - public void setOverflow(boolean overflow) { mIsOverflow = overflow; Intent target = new Intent(mContext, BubbleOverflowActivity.class); Bundle extras = new Bundle(); - extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mBubbles)); + extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mController)); target.putExtras(extras); mPendingIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */, target, PendingIntent.FLAG_UPDATE_CURRENT); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java index 48c809d1b0a7..a24f5c2b54c5 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java @@ -91,14 +91,14 @@ public class BubbleLogger { * @param b Bubble removed from overflow * @param r Reason that bubble was removed */ - public void logOverflowRemove(Bubble b, @BubbleController.DismissReason int r) { - if (r == BubbleController.DISMISS_NOTIF_CANCEL) { + public void logOverflowRemove(Bubble b, @Bubbles.DismissReason int r) { + if (r == Bubbles.DISMISS_NOTIF_CANCEL) { log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_CANCEL); - } else if (r == BubbleController.DISMISS_GROUP_CANCELLED) { + } else if (r == Bubbles.DISMISS_GROUP_CANCELLED) { log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_GROUP_CANCEL); - } else if (r == BubbleController.DISMISS_NO_LONGER_BUBBLE) { + } else if (r == Bubbles.DISMISS_NO_LONGER_BUBBLE) { log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_NO_LONGER_BUBBLE); - } else if (r == BubbleController.DISMISS_BLOCKED) { + } else if (r == Bubbles.DISMISS_BLOCKED) { log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BLOCKED); } } @@ -107,10 +107,10 @@ public class BubbleLogger { * @param b Bubble added to overflow * @param r Reason that bubble was added to overflow */ - public void logOverflowAdd(Bubble b, @BubbleController.DismissReason int r) { - if (r == BubbleController.DISMISS_AGED) { + public void logOverflowAdd(Bubble b, @Bubbles.DismissReason int r) { + if (r == Bubbles.DISMISS_AGED) { log(b, Event.BUBBLE_OVERFLOW_ADD_AGED); - } else if (r == BubbleController.DISMISS_USER_GESTURE) { + } else if (r == Bubbles.DISMISS_USER_GESTURE) { log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt index 102055de2bea..297144a86143 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt @@ -35,6 +35,7 @@ import com.android.systemui.R class BubbleOverflow( private val context: Context, + private val controller: BubbleController, private val stack: BubbleStackView ) : BubbleViewProvider { @@ -56,8 +57,8 @@ class BubbleOverflow( init { updateResources() with(expandedView) { + initialize(controller, stack) setOverflow(true) - setStackView(stack) applyThemeAttrs() } with(overflowBtn) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java index fc3f5b6cbf5e..bc841730833c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java @@ -63,7 +63,7 @@ public class BubbleOverflowActivity extends Activity { private TextView mEmptyStateTitle; private TextView mEmptyStateSubtitle; private ImageView mEmptyStateImage; - private Bubbles mBubbles; + private BubbleController mController; private BubbleOverflowAdapter mAdapter; private RecyclerView mRecyclerView; private List<Bubble> mOverflowBubbles = new ArrayList<>(); @@ -111,7 +111,7 @@ public class BubbleOverflowActivity extends Activity { if (intent != null && intent.getExtras() != null) { IBinder binder = intent.getExtras().getBinder(EXTRA_BUBBLE_CONTROLLER); if (binder instanceof ObjectWrapper) { - mBubbles = ((ObjectWrapper<Bubbles>) binder).get(); + mController = ((ObjectWrapper<BubbleController>) binder).get(); } } else { Log.w(TAG, "Bubble overflow activity created without bubble controller!"); @@ -139,15 +139,15 @@ public class BubbleOverflowActivity extends Activity { final int viewHeight = recyclerViewHeight / rows; mAdapter = new BubbleOverflowAdapter(getApplicationContext(), mOverflowBubbles, - mBubbles::promoteBubbleFromOverflow, viewWidth, viewHeight); + mController::promoteBubbleFromOverflow, viewWidth, viewHeight); mRecyclerView.setAdapter(mAdapter); mOverflowBubbles.clear(); - mOverflowBubbles.addAll(mBubbles.getOverflowBubbles()); + mOverflowBubbles.addAll(mController.getOverflowBubbles()); mAdapter.notifyDataSetChanged(); updateEmptyStateVisibility(); - mBubbles.setOverflowListener(mDataListener); + mController.setOverflowListener(mDataListener); updateTheme(); } @@ -217,7 +217,7 @@ public class BubbleOverflowActivity extends Activity { if (DEBUG_OVERFLOW) { Log.d(TAG, BubbleDebugConfig.formatBubblesString( - mBubbles.getOverflowBubbles(), null)); + mController.getOverflowBubbles(), null)); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 1201d42b1fc5..e89b2526456a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -155,7 +155,7 @@ public class BubbleStackView extends FrameLayout * * {@hide} */ - interface SurfaceSynchronizer { + public interface SurfaceSynchronizer { /** * Wait until requested change on a {@link View} is reflected on the screen. * @@ -186,7 +186,7 @@ public class BubbleStackView extends FrameLayout }); } }; - + private final BubbleController mBubbleController; private final BubbleData mBubbleData; private final ValueAnimator mDesaturateAndDarkenAnimator; @@ -384,22 +384,6 @@ public class BubbleStackView extends FrameLayout private final SurfaceSynchronizer mSurfaceSynchronizer; /** - * Callback to run when the IME visibility changes - BubbleController uses this to update the - * Bubbles window focusability flags with the WindowManager. - */ - public final Consumer<Boolean> mOnImeVisibilityChanged; - - /** - * Callback to run when the bubble expand status changes. - */ - private final Consumer<Boolean> mOnBubbleExpandChanged; - - /** - * Callback to run to ask BubbleController to hide the current IME. - */ - private final Runnable mHideCurrentInputMethodCallback; - - /** * The currently magnetized object, which is being dragged and will be attracted to the magnetic * dismiss target. * @@ -733,16 +717,12 @@ public class BubbleStackView extends FrameLayout private BubblePositioner mPositioner; @SuppressLint("ClickableViewAccessibility") - public BubbleStackView(Context context, BubbleData data, - @Nullable SurfaceSynchronizer synchronizer, - FloatingContentCoordinator floatingContentCoordinator, - Runnable allBubblesAnimatedOutAction, - Consumer<Boolean> onImeVisibilityChanged, - Runnable hideCurrentInputMethodCallback, - Consumer<Boolean> onBubbleExpandChanged, - BubblePositioner positioner) { + public BubbleStackView(Context context, BubbleController bubbleController, + BubbleData data, @Nullable SurfaceSynchronizer synchronizer, + FloatingContentCoordinator floatingContentCoordinator) { super(context); + mBubbleController = bubbleController; mBubbleData = data; Resources res = getResources(); @@ -755,7 +735,7 @@ public class BubbleStackView extends FrameLayout mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); - mPositioner = positioner; + mPositioner = mBubbleController.getPositioner(); mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); @@ -767,7 +747,7 @@ public class BubbleStackView extends FrameLayout final Runnable onBubbleAnimatedOut = () -> { if (getBubbleCount() == 0) { - allBubblesAnimatedOutAction.run(); + mBubbleController.onAllBubblesAnimatedOut(); } }; @@ -839,7 +819,7 @@ public class BubbleStackView extends FrameLayout setFocusable(true); mBubbleContainer.bringToFront(); - mBubbleOverflow = new BubbleOverflow(getContext(), this); + mBubbleOverflow = new BubbleOverflow(getContext(), bubbleController, this); mBubbleContainer.addView(mBubbleOverflow.getIconView(), mBubbleContainer.getChildCount() /* index */, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, @@ -850,12 +830,9 @@ public class BubbleStackView extends FrameLayout showManageMenu(false); }); - mOnImeVisibilityChanged = onImeVisibilityChanged; - mHideCurrentInputMethodCallback = hideCurrentInputMethodCallback; - mOnBubbleExpandChanged = onBubbleExpandChanged; - setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> { - onImeVisibilityChanged.accept(insets.getInsets(WindowInsets.Type.ime()).bottom > 0); + mBubbleController.onImeVisibilityChanged( + insets.getInsets(WindowInsets.Type.ime()).bottom > 0); if (!mIsExpanded || mIsExpansionAnimating) { return view.onApplyWindowInsets(insets); } @@ -1299,7 +1276,7 @@ public class BubbleStackView extends FrameLayout // R constants are not final so we cannot use switch-case here. if (action == AccessibilityNodeInfo.ACTION_DISMISS) { - mBubbleData.dismissAll(BubbleController.DISMISS_ACCESSIBILITY_ACTION); + mBubbleData.dismissAll(Bubbles.DISMISS_ACCESSIBILITY_ACTION); announceForAccessibility( getResources().getString(R.string.accessibility_bubble_dismissed)); return true; @@ -1402,8 +1379,9 @@ public class BubbleStackView extends FrameLayout /** * The {@link Bubble} that is expanded, null if one does not exist. */ + @VisibleForTesting @Nullable - BubbleViewProvider getExpandedBubble() { + public BubbleViewProvider getExpandedBubble() { return mExpandedBubble; } @@ -1617,7 +1595,7 @@ public class BubbleStackView extends FrameLayout hideCurrentInputMethod(); - mOnBubbleExpandChanged.accept(shouldExpand); + mBubbleController.getSysuiProxy().onStackExpandChanged(shouldExpand); if (mIsExpanded) { animateCollapse(); @@ -1637,7 +1615,7 @@ public class BubbleStackView extends FrameLayout * not. */ void hideCurrentInputMethod() { - mHideCurrentInputMethodCallback.run(); + mBubbleController.hideCurrentInputMethod(); } private void beforeExpandedViewAnimation() { @@ -2135,14 +2113,13 @@ public class BubbleStackView extends FrameLayout final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject(); dismissBubbleIfExists(mBubbleData.getBubbleWithView(draggedOutBubbleView)); } else { - mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); } } private void dismissBubbleIfExists(@Nullable Bubble bubble) { if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { - mBubbleData.dismissBubbleWithKey( - bubble.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(bubble.getKey(), Bubbles.DISMISS_USER_GESTURE); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java index 010a29e3560a..a3e6a1ecc387 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java @@ -66,6 +66,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask private Bubble mBubble; private WeakReference<Context> mContext; + private WeakReference<BubbleController> mController; private WeakReference<BubbleStackView> mStackView; private BubbleIconFactory mIconFactory; private boolean mSkipInflation; @@ -77,12 +78,14 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask */ BubbleViewInfoTask(Bubble b, Context context, + BubbleController controller, BubbleStackView stackView, BubbleIconFactory factory, boolean skipInflation, Callback c) { mBubble = b; mContext = new WeakReference<>(context); + mController = new WeakReference<>(controller); mStackView = new WeakReference<>(stackView); mIconFactory = factory; mSkipInflation = skipInflation; @@ -91,8 +94,8 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask @Override protected BubbleViewInfo doInBackground(Void... voids) { - return BubbleViewInfo.populate(mContext.get(), mStackView.get(), mIconFactory, mBubble, - mSkipInflation); + return BubbleViewInfo.populate(mContext.get(), mController.get(), mStackView.get(), + mIconFactory, mBubble, mSkipInflation); } @Override @@ -121,8 +124,9 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask Bubble.FlyoutMessage flyoutMessage; @Nullable - static BubbleViewInfo populate(Context c, BubbleStackView stackView, - BubbleIconFactory iconFactory, Bubble b, boolean skipInflation) { + static BubbleViewInfo populate(Context c, BubbleController controller, + BubbleStackView stackView, BubbleIconFactory iconFactory, Bubble b, + boolean skipInflation) { BubbleViewInfo info = new BubbleViewInfo(); // View inflation: only should do this once per bubble @@ -133,7 +137,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask info.expandedView = (BubbleExpandedView) inflater.inflate( R.layout.bubble_expanded_view, stackView, false /* attachToRoot */); - info.expandedView.setStackView(stackView); + info.expandedView.initialize(controller, stackView); } if (b.getShortcutInfo() != null) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java index 5cc24ce5a775..589017242311 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java @@ -26,7 +26,7 @@ import androidx.annotation.Nullable; /** * Interface to represent actual Bubbles and UI elements that act like bubbles, like BubbleOverflow. */ -interface BubbleViewProvider { +public interface BubbleViewProvider { @Nullable BubbleExpandedView getExpandedView(); void setContentVisibility(boolean visible); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java index 4882abc51ed6..415edb1b647c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java @@ -16,62 +16,93 @@ package com.android.systemui.bubbles; -import android.annotation.NonNull; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; -import androidx.annotation.MainThread; +import android.content.res.Configuration; +import android.service.notification.NotificationListenerService.RankingMap; +import android.util.ArraySet; +import android.view.View; -import com.android.systemui.statusbar.ScrimView; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.phone.ScrimController; -import com.android.wm.shell.ShellTaskOrganizer; +import androidx.annotation.IntDef; +import androidx.annotation.Nullable; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; import java.util.List; +import java.util.function.IntConsumer; /** * Interface to engage bubbles feature. */ public interface Bubbles { + @Retention(SOURCE) + @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED, + DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE, + DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT, + DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED, + DISMISS_NO_BUBBLE_UP}) + @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) + @interface DismissReason {} + + int DISMISS_USER_GESTURE = 1; + int DISMISS_AGED = 2; + int DISMISS_TASK_FINISHED = 3; + int DISMISS_BLOCKED = 4; + int DISMISS_NOTIF_CANCEL = 5; + int DISMISS_ACCESSIBILITY_ACTION = 6; + int DISMISS_NO_LONGER_BUBBLE = 7; + int DISMISS_USER_CHANGED = 8; + int DISMISS_GROUP_CANCELLED = 9; + int DISMISS_INVALID_INTENT = 10; + int DISMISS_OVERFLOW_MAX_REACHED = 11; + int DISMISS_SHORTCUT_REMOVED = 12; + int DISMISS_PACKAGE_REMOVED = 13; + int DISMISS_NO_BUBBLE_UP = 14; + /** * @return {@code true} if there is a bubble associated with the provided key and if its * notification is hidden from the shade or there is a group summary associated with the * provided key that is hidden from the shade because it has been dismissed but still has child * bubbles active. */ - boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry); + boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey); /** * @return {@code true} if the current notification entry same as selected bubble * notification entry and the stack is currently expanded. */ - boolean isBubbleExpanded(NotificationEntry entry); + boolean isBubbleExpanded(String key); /** @return {@code true} if stack of bubbles is expanded or not. */ boolean isStackExpanded(); + /** @return {@code true} if the summary for the provided group key is suppressed. */ + boolean isSummarySuppressed(String groupKey); + /** - * @return the {@link ScrimView} drawn behind the bubble stack. This is managed by - * {@link ScrimController} since we want the scrim's appearance and behavior to be identical to - * that of the notification shade scrim. + * Removes a group key indicating that summary for this group should no longer be suppressed. */ - ScrimView getScrimForBubble(); - - /** @return Bubbles for updating overflow. */ - List<Bubble> getOverflowBubbles(); + void removeSuppressedSummary(String groupKey); /** Tell the stack of bubbles to collapse. */ void collapseStack(); + /** Tell the controller need update its UI to fit theme. */ + void updateForThemeChanges(); + /** * Request the stack expand if needed, then select the specified Bubble as current. * If no bubble exists for this entry, one is created. * * @param entry the notification for the bubble to be selected */ - void expandStackAndSelectBubble(NotificationEntry entry); - - /** Promote the provided bubbles when overflow view. */ - void promoteBubbleFromOverflow(Bubble bubble); + void expandStackAndSelectBubble(BubbleEntry entry); /** * We intercept notification entries (including group summaries) dismissed by the user when @@ -81,26 +112,65 @@ public interface Bubbles { * {@link Bubble#setSuppressNotification}. For the case of suppressed summaries, we also add * {@link BubbleData#addSummaryToSuppress}. * + * @param entry the notification of the BubbleEntry should be removed. + * @param children the list of child notification of the BubbleEntry from 1st param entry, + * this will be null if entry does have no children. + * @param removeCallback the remove callback for SystemUI side to remove notification, the int + * number should be list position of children list and use -1 for + * removing the parent notification. + * * @return true if we want to intercept the dismissal of the entry, else false. */ - boolean handleDismissalInterception(NotificationEntry entry); + boolean handleDismissalInterception(BubbleEntry entry, @Nullable List<BubbleEntry> children, + IntConsumer removeCallback); /** - * Removes the bubble with the given key. - * <p> - * Must be called from the main thread. + * Retrieves the notif entry key of the summary associated with the provided group key. + * + * @param groupKey the group to look up + * @return the key for the notification that is the summary of this group. */ - @MainThread - void removeBubble(String key, int reason); + String getSummaryKey(String groupKey); + + /** Set the proxy to commnuicate with SysUi side components. */ + void setSysuiProxy(SysuiProxy proxy); + + /** Set the scrim view for bubbles. */ + void setBubbleScrim(View view); + + /** Set a listener to be notified of bubble expand events. */ + void setExpandListener(BubbleExpandListener listener); + /** + * Called when new notification entry added. + * + * @param entry the {@link BubbleEntry} by the notification. + */ + void onEntryAdded(BubbleEntry entry); + + /** + * Called when new notification entry updated. + * + * @param entry the {@link BubbleEntry} by the notification. + * @param shouldBubbleUp {@code true} if this notification should bubble up. + */ + void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp); + + /** + * Called when new notification entry removed. + * + * @param entry the {@link BubbleEntry} by the notification. + */ + void onEntryRemoved(BubbleEntry entry); /** - * When a notification is marked Priority, expand the stack if needed, - * then (maybe create and) select the given bubble. + * Called when NotificationListener has received adjusted notification rank and reapplied + * filtering and sorting. This is used to dismiss or create bubbles based on changes in + * permissions on the notification channel or the global setting. * - * @param entry the notification for the bubble to show + * @param rankingMap the updated ranking map from NotificationListenerService */ - void onUserChangedImportance(NotificationEntry entry); + void onRankingUpdated(RankingMap rankingMap); /** * Called when the status bar has become visible or invisible (either permanently or @@ -108,30 +178,85 @@ public interface Bubbles { */ void onStatusBarVisibilityChanged(boolean visible); + /** Called when system zen mode state changed. */ + void onZenStateChanged(); + /** - * Called when a user has indicated that an active notification should be shown as a bubble. - * <p> - * This method will collapse the shade, create the bubble without a flyout or dot, and suppress - * the notification from appearing in the shade. + * Called when statusBar state changed. * - * @param entry the notification to change bubble state for. - * @param shouldBubble whether the notification should show as a bubble or not. + * @param isShade {@code true} is state is SHADE. */ - void onUserChangedBubble(@NonNull NotificationEntry entry, boolean shouldBubble); + void onStatusBarStateChanged(boolean isShade); + /** + * Called when the current user changed. + * + * @param newUserId the new user's id. + */ + void onUserChanged(int newUserId); - /** See {@link BubbleController.NotifCallback}. */ - void addNotifCallback(BubbleController.NotifCallback callback); + /** + * Called when config changed. + * + * @param newConfig the new config. + */ + void onConfigChanged(Configuration newConfig); - /** Set a listener to be notified of bubble expand events. */ - void setExpandListener(BubbleController.BubbleExpandListener listener); + /** Description of current bubble state. */ + void dump(FileDescriptor fd, PrintWriter pw, String[] args); + + /** Listener to find out about stack expansion / collapse events. */ + interface BubbleExpandListener { + /** + * Called when the expansion state of the bubble stack changes. + * + * @param isExpanding whether it's expanding or collapsing + * @param key the notification key associated with bubble being expanded + */ + void onBubbleExpandChanged(boolean isExpanding, String key); + } + + /** Listener to be notified when a bubbles' notification suppression state changes.*/ + interface NotificationSuppressionChangedListener { + /** Called when the notification suppression state of a bubble changes. */ + void onBubbleNotificationSuppressionChange(Bubble bubble); + } + + /** Listener to be notified when a pending intent has been canceled for a bubble. */ + interface PendingIntentCanceledListener { + /** Called when the pending intent for a bubble has been canceled. */ + void onPendingIntentCanceled(Bubble bubble); + } + + /** Callback to tell SysUi components execute some methods. */ + interface SysuiProxy { + @Nullable + BubbleEntry getPendingOrActiveEntry(String key); + + List<BubbleEntry> getShouldRestoredEntries(ArraySet<String> savedBubbleKeys); + + boolean isNotificationShadeExpand(); + + boolean shouldBubbleUp(String key); + + void setNotificationInterruption(String key); + + void requestNotificationShadeTopUi(boolean requestTopUi, String componentTag); + + void notifyRemoveNotification(String key, int reason); + + void notifyInvalidateNotifications(String reason); + + void notifyMaybeCancelSummary(String key); + + void removeNotificationEntry(String key); + + void updateNotificationBubbleButton(String key); - /** Set a listener to be notified of when overflow view update. */ - void setOverflowListener(BubbleData.Listener listener); + void updateNotificationSuppression(String key); - /** The task listener for events in bubble tasks. **/ - ShellTaskOrganizer getTaskOrganizer(); + void onStackExpandChanged(boolean shouldExpand); - /** Contains information to help position things on the screen. */ - BubblePositioner getPositioner(); + void onUnbubbleConversation(String key); + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java index 6b5f237ac76f..5a7e033607f8 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java @@ -22,6 +22,8 @@ import android.content.pm.LauncherApps; import android.os.Handler; import android.view.WindowManager; +import androidx.annotation.Nullable; + import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.bubbles.BubbleController; @@ -41,10 +43,13 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.FloatingContentCoordinator; +import java.util.Optional; + import dagger.Module; import dagger.Provides; @@ -56,23 +61,8 @@ public interface BubbleModule { */ @SysUISingleton @Provides - static Bubbles newBubbleController( - Context context, - NotificationShadeWindowController notificationShadeWindowController, - StatusBarStateController statusBarStateController, - ShadeController shadeController, - ConfigurationController configurationController, - NotificationInterruptStateProvider interruptionStateProvider, - ZenModeController zenModeController, - NotificationLockscreenUserManager notifUserManager, - NotificationGroupManagerLegacy groupManager, - NotificationEntryManager entryManager, - NotifPipeline notifPipeline, - FeatureFlags featureFlags, - DumpManager dumpManager, + static Bubbles newBubbleController(Context context, FloatingContentCoordinator floatingContentCoordinator, - SysUiState sysUiState, - INotificationManager notifManager, IStatusBarService statusBarService, WindowManager windowManager, WindowManagerShellWrapper windowManagerShellWrapper, @@ -80,30 +70,29 @@ public interface BubbleModule { UiEventLogger uiEventLogger, @Main Handler mainHandler, ShellTaskOrganizer organizer) { - return BubbleController.create( - context, - notificationShadeWindowController, - statusBarStateController, - shadeController, - null /* synchronizer */, - configurationController, - interruptionStateProvider, - zenModeController, - notifUserManager, - groupManager, - entryManager, - notifPipeline, - featureFlags, - dumpManager, - floatingContentCoordinator, - sysUiState, - notifManager, - statusBarService, - windowManager, - windowManagerShellWrapper, - launcherApps, - uiEventLogger, - mainHandler, - organizer); + return BubbleController.create(context, null /* synchronizer */, floatingContentCoordinator, + statusBarService, windowManager, windowManagerShellWrapper, launcherApps, + uiEventLogger, mainHandler, organizer); + } + + /** Provides Optional of BubbleManager */ + @SysUISingleton + @Provides + static Optional<BubblesManager> provideBubblesManager(Context context, + Optional<Bubbles> bubblesOptional, + NotificationShadeWindowController notificationShadeWindowController, + StatusBarStateController statusBarStateController, ShadeController shadeController, + ConfigurationController configurationController, + @Nullable IStatusBarService statusBarService, INotificationManager notificationManager, + NotificationInterruptStateProvider interruptionStateProvider, + ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, + NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager, + NotifPipeline notifPipeline, SysUiState sysUiState, FeatureFlags featureFlags, + DumpManager dumpManager) { + return Optional.ofNullable(BubblesManager.create(context, bubblesOptional, + notificationShadeWindowController, statusBarStateController, shadeController, + configurationController, statusBarService, notificationManager, + interruptionStateProvider, zenModeController, notifUserManager, + groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager)); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 53179ba4be90..a92b9e4818b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -159,7 +159,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle for (int i = 0; i < N; i++) { NotificationEntry ent = activeNotifications.get(i); final boolean isBubbleNotificationSuppressedFromShade = mBubblesOptional.isPresent() - && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(ent); + && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade( + ent.getKey(), ent.getSbn().getGroupKey()); if (ent.isRowDismissed() || ent.isRowRemoved() || isBubbleNotificationSuppressedFromShade || mFgsSectionController.hasEntry(ent)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java index 0455b0f18afc..83a569bd6573 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java @@ -25,6 +25,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor; +import com.android.systemui.wmshell.BubblesManager; import java.util.HashSet; import java.util.Optional; @@ -56,6 +57,7 @@ import javax.inject.Inject; public class BubbleCoordinator implements Coordinator { private static final String TAG = "BubbleCoordinator"; + private final Optional<BubblesManager> mBubblesManagerOptional; private final Optional<Bubbles> mBubblesOptional; private final NotifCollection mNotifCollection; private final Set<String> mInterceptedDismissalEntries = new HashSet<>(); @@ -64,8 +66,10 @@ public class BubbleCoordinator implements Coordinator { @Inject public BubbleCoordinator( + Optional<BubblesManager> bubblesManagerOptional, Optional<Bubbles> bubblesOptional, NotifCollection notifCollection) { + mBubblesManagerOptional = bubblesManagerOptional; mBubblesOptional = bubblesOptional; mNotifCollection = notifCollection; } @@ -75,8 +79,8 @@ public class BubbleCoordinator implements Coordinator { mNotifPipeline = pipeline; mNotifPipeline.addNotificationDismissInterceptor(mDismissInterceptor); mNotifPipeline.addFinalizeFilter(mNotifFilter); - if (mBubblesOptional.isPresent()) { - mBubblesOptional.get().addNotifCallback(mNotifCallback); + if (mBubblesManagerOptional.isPresent()) { + mBubblesManagerOptional.get().addNotifCallback(mNotifCallback); } } @@ -85,7 +89,8 @@ public class BubbleCoordinator implements Coordinator { @Override public boolean shouldFilterOut(NotificationEntry entry, long now) { return mBubblesOptional.isPresent() - && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(entry); + && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade( + entry.getKey(), entry.getSbn().getGroupKey()); } }; @@ -102,9 +107,8 @@ public class BubbleCoordinator implements Coordinator { @Override public boolean shouldInterceptDismissal(NotificationEntry entry) { - // for experimental bubbles - if (mBubblesOptional.isPresent() - && mBubblesOptional.get().handleDismissalInterception(entry)) { + if (mBubblesManagerOptional.isPresent() + && mBubblesManagerOptional.get().handleDismissalInterception(entry)) { mInterceptedDismissalEntries.add(entry.getKey()); return true; } else { @@ -119,8 +123,7 @@ public class BubbleCoordinator implements Coordinator { } }; - private final BubbleController.NotifCallback mNotifCallback = - new BubbleController.NotifCallback() { + private final BubblesManager.NotifCallback mNotifCallback = new BubblesManager.NotifCallback() { @Override public void removeNotification( NotificationEntry entry, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java index 490989dbb39e..36adfac21bc3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java @@ -64,7 +64,7 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener, new ArraySet<>(); private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>(); private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier; - private final Optional<Lazy<Bubbles>> mBubblesOptional; + private final Optional<Bubbles> mBubblesOptional; private int mBarState = -1; private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>(); private HeadsUpManager mHeadsUpManager; @@ -74,7 +74,7 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener, public NotificationGroupManagerLegacy( StatusBarStateController statusBarStateController, Lazy<PeopleNotificationIdentifier> peopleNotificationIdentifier, - Optional<Lazy<Bubbles>> bubblesOptional) { + Optional<Bubbles> bubblesOptional) { statusBarStateController.addCallback(this); mPeopleNotificationIdentifier = peopleNotificationIdentifier; mBubblesOptional = bubblesOptional; @@ -242,8 +242,9 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener, int childCount = 0; boolean hasBubbles = false; for (NotificationEntry entry : group.children.values()) { - if (mBubblesOptional.isPresent() && !mBubblesOptional.get().get() - .isBubbleNotificationSuppressedFromShade(entry)) { + if (mBubblesOptional.isPresent() && !mBubblesOptional.get() + .isBubbleNotificationSuppressedFromShade( + entry.getKey(), entry.getSbn().getGroupKey())) { childCount++; } else { hasBubbles = true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 4fff99b482d8..ff55cd60ab3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -26,7 +26,6 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -73,6 +72,7 @@ import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogC import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.leak.LeakDetector; +import com.android.systemui.wmshell.BubblesManager; import java.util.Optional; import java.util.concurrent.Executor; @@ -133,7 +133,7 @@ public interface NotificationsModule { UserContextProvider contextTracker, Provider<PriorityOnboardingDialogController.Builder> builderProvider, AssistantFeedbackController assistantFeedbackController, - Optional<Bubbles> bubblesOptional, + Optional<BubblesManager> bubblesManagerOptional, UiEventLogger uiEventLogger, OnUserInteractionCallback onUserInteractionCallback) { return new NotificationGutsManager( @@ -150,7 +150,7 @@ public interface NotificationsModule { contextTracker, builderProvider, assistantFeedbackController, - bubblesOptional, + bubblesManagerOptional, uiEventLogger, onUserInteractionCallback); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 5ee66fabe55a..280c525f41cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -72,7 +72,6 @@ import com.android.internal.widget.CachingIconView; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -102,12 +101,14 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions; +import com.android.systemui.wmshell.BubblesManager; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; import java.util.function.Consumer; @@ -147,6 +148,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private LayoutListener mLayoutListener; private RowContentBindStage mRowContentBindStage; private PeopleNotificationIdentifier mPeopleNotificationIdentifier; + private Optional<BubblesManager> mBubblesManagerOptional; private int mIconTransformContentShift; private int mMaxHeadsUpHeightBeforeN; private int mMaxHeadsUpHeightBeforeP; @@ -1078,13 +1080,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView /** The click listener for the bubble button. */ public View.OnClickListener getBubbleClickListener() { - return new View.OnClickListener() { - @Override - public void onClick(View v) { - Dependency.get(Bubbles.class) - .onUserChangedBubble(mEntry, !mEntry.isBubble() /* createBubble */); - mHeadsUpManager.removeNotification(mEntry.getKey(), true /* releaseImmediately */); + return v -> { + if (mBubblesManagerOptional.isPresent()) { + mBubblesManagerOptional.get() + .onUserChangedBubble(mEntry, !mEntry.isBubble() /* createBubble */); } + mHeadsUpManager.removeNotification(mEntry.getKey(), true /* releaseImmediately */); }; } @@ -1553,7 +1554,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView FalsingManager falsingManager, StatusBarStateController statusBarStateController, PeopleNotificationIdentifier peopleNotificationIdentifier, - OnUserInteractionCallback onUserInteractionCallback) { + OnUserInteractionCallback onUserInteractionCallback, + Optional<BubblesManager> bubblesManagerOptional) { mEntry = entry; mAppName = appName; if (mMenuRow == null) { @@ -1581,6 +1583,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView l.setPeopleNotificationIdentifier(mPeopleNotificationIdentifier); } mOnUserInteractionCallback = onUserInteractionCallback; + mBubblesManagerOptional = bubblesManagerOptional; cacheIsSystemNotification(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index c995e324ecfe..05b1dba36a7b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -43,8 +43,10 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.time.SystemClock; +import com.android.systemui.wmshell.BubblesManager; import java.util.List; +import java.util.Optional; import javax.inject.Inject; import javax.inject.Named; @@ -79,6 +81,7 @@ public class ExpandableNotificationRowController implements NodeController { private final FalsingManager mFalsingManager; private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; + private final Optional<BubblesManager> mBubblesManagerOptional; @Inject public ExpandableNotificationRowController( @@ -102,7 +105,8 @@ public class ExpandableNotificationRowController implements NodeController { @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, OnUserInteractionCallback onUserInteractionCallback, FalsingManager falsingManager, - PeopleNotificationIdentifier peopleNotificationIdentifier) { + PeopleNotificationIdentifier peopleNotificationIdentifier, + Optional<BubblesManager> bubblesManagerOptional) { mView = view; mListContainer = listContainer; mActivatableNotificationViewController = activatableNotificationViewController; @@ -125,6 +129,7 @@ public class ExpandableNotificationRowController implements NodeController { mAllowLongPress = allowLongPress; mFalsingManager = falsingManager; mPeopleNotificationIdentifier = peopleNotificationIdentifier; + mBubblesManagerOptional = bubblesManagerOptional; } /** @@ -148,8 +153,8 @@ public class ExpandableNotificationRowController implements NodeController { mFalsingManager, mStatusBarStateController, mPeopleNotificationIdentifier, - mOnUserInteractionCallback - + mOnUserInteractionCallback, + mBubblesManagerOptional ); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); if (mAllowLongPress) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index 07a4a188bc48..7000ba82b3af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -67,12 +67,12 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.notification.ConversationIconFactory; import com.android.systemui.Prefs; import com.android.systemui.R; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.notification.NotificationChannelHelper; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import com.android.systemui.wmshell.BubblesManager; import java.lang.annotation.Retention; import java.util.Optional; @@ -94,7 +94,7 @@ public class NotificationConversationInfo extends LinearLayout implements private OnUserInteractionCallback mOnUserInteractionCallback; private Handler mMainHandler; private Handler mBgHandler; - private Optional<Bubbles> mBubblesOptional; + private Optional<BubblesManager> mBubblesManagerOptional; private String mPackageName; private String mAppName; private int mAppUid; @@ -223,7 +223,7 @@ public class NotificationConversationInfo extends LinearLayout implements @Main Handler mainHandler, @Background Handler bgHandler, OnConversationSettingsClickListener onConversationSettingsClickListener, - Optional<Bubbles> bubblesOptional) { + Optional<BubblesManager> bubblesManagerOptional) { mSelectedAction = -1; mINotificationManager = iNotificationManager; mOnUserInteractionCallback = onUserInteractionCallback; @@ -242,7 +242,7 @@ public class NotificationConversationInfo extends LinearLayout implements mIconFactory = conversationIconFactory; mUserContext = userContext; mBubbleMetadata = bubbleMetadata; - mBubblesOptional = bubblesOptional; + mBubblesManagerOptional = bubblesManagerOptional; mBuilderProvider = builderProvider; mMainHandler = mainHandler; mBgHandler = bgHandler; @@ -641,9 +641,9 @@ public class NotificationConversationInfo extends LinearLayout implements mINotificationManager.setBubblesAllowed(mAppPkg, mAppUid, BUBBLE_PREFERENCE_SELECTED); } - if (mBubblesOptional.isPresent()) { + if (mBubblesManagerOptional.isPresent()) { post(() -> { - mBubblesOptional.get().onUserChangedImportance(mEntry); + mBubblesManagerOptional.get().onUserChangedImportance(mEntry); }); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 373f20e6ba96..d2cfb2908e9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -47,7 +47,6 @@ import com.android.settingslib.notification.ConversationIconFactory; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.R; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -67,6 +66,7 @@ import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSav import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.wmshell.BubblesManager; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -117,7 +117,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx private final Lazy<StatusBar> mStatusBarLazy; private final Handler mMainHandler; private final Handler mBgHandler; - private final Optional<Bubbles> mBubblesOptional; + private final Optional<BubblesManager> mBubblesManagerOptional; private Runnable mOpenRunnable; private final INotificationManager mNotificationManager; private final LauncherApps mLauncherApps; @@ -142,7 +142,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx UserContextProvider contextTracker, Provider<PriorityOnboardingDialogController.Builder> builderProvider, AssistantFeedbackController assistantFeedbackController, - Optional<Bubbles> bubblesOptional, + Optional<BubblesManager> bubblesManagerOptional, UiEventLogger uiEventLogger, OnUserInteractionCallback onUserInteractionCallback) { mContext = context; @@ -158,7 +158,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mBuilderProvider = builderProvider; mChannelEditorDialogController = channelEditorDialogController; mAssistantFeedbackController = assistantFeedbackController; - mBubblesOptional = bubblesOptional; + mBubblesManagerOptional = bubblesManagerOptional; mUiEventLogger = uiEventLogger; mOnUserInteractionCallback = onUserInteractionCallback; } @@ -491,7 +491,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mMainHandler, mBgHandler, onConversationSettingsListener, - mBubblesOptional); + mBubblesManagerOptional); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index e065b47ef5b1..f2ae3da73f5e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -306,7 +306,8 @@ public class NotificationIconAreaController implements || !entry.isPulseSuppressed())) { return false; } - if (mBubblesOptional.isPresent() && mBubblesOptional.get().isBubbleExpanded(entry)) { + if (mBubblesOptional.isPresent() + && mBubblesOptional.get().isBubbleExpanded(entry.getKey())) { return false; } return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 9bd98f2e8c24..a8d41046a1eb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -144,7 +144,6 @@ import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.bubbles.BubbleController; import com.android.systemui.bubbles.Bubbles; import com.android.systemui.charging.WirelessChargingAnimation; import com.android.systemui.classifier.FalsingLog; @@ -229,6 +228,7 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.volume.VolumeComponent; +import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.splitscreen.SplitScreen; import java.io.FileDescriptor; @@ -648,8 +648,9 @@ public class StatusBar extends SystemUI implements DemoMode, protected StatusBarNotificationPresenter mPresenter; private NotificationActivityStarter mNotificationActivityStarter; private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy; + private final Optional<BubblesManager> mBubblesManagerOptional; private final Optional<Bubbles> mBubblesOptional; - private final BubbleController.BubbleExpandListener mBubbleExpandListener; + private final Bubbles.BubbleExpandListener mBubbleExpandListener; private ActivityIntentHelper mActivityIntentHelper; private NotificationStackScrollLayoutController mStackScrollerController; @@ -697,6 +698,7 @@ public class StatusBar extends SystemUI implements DemoMode, WakefulnessLifecycle wakefulnessLifecycle, SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper, + Optional<BubblesManager> bubblesManagerOptional, Optional<Bubbles> bubblesOptional, VisualStabilityManager visualStabilityManager, DeviceProvisionedController deviceProvisionedController, @@ -776,6 +778,7 @@ public class StatusBar extends SystemUI implements DemoMode, mWakefulnessLifecycle = wakefulnessLifecycle; mStatusBarStateController = statusBarStateController; mVibratorHelper = vibratorHelper; + mBubblesManagerOptional = bubblesManagerOptional; mBubblesOptional = bubblesOptional; mVisualStabilityManager = visualStabilityManager; mDeviceProvisionedController = deviceProvisionedController; @@ -1141,8 +1144,8 @@ public class StatusBar extends SystemUI implements DemoMode, ScrimView scrimBehind = mNotificationShadeWindowView.findViewById(R.id.scrim_behind); ScrimView scrimInFront = mNotificationShadeWindowView.findViewById(R.id.scrim_in_front); - ScrimView scrimForBubble = mBubblesOptional.isPresent() - ? mBubblesOptional.get().getScrimForBubble() : null; + ScrimView scrimForBubble = mBubblesManagerOptional.isPresent() + ? mBubblesManagerOptional.get().getScrimForBubble() : null; mScrimController.setScrimVisibleListener(scrimsVisible -> { mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 014d5c274c1f..acca953629c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -48,7 +48,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.systemui.ActivityIntentHelper; import com.android.systemui.EventLogTags; import com.android.systemui.assist.AssistManager; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -76,6 +75,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.wmshell.BubblesManager; import java.util.Optional; import java.util.concurrent.Executor; @@ -104,7 +104,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final KeyguardManager mKeyguardManager; private final IDreamManager mDreamManager; - private final Optional<Bubbles> mBubblesOptional; + private final Optional<BubblesManager> mBubblesManagerOptional; private final Lazy<AssistManager> mAssistManagerLazy; private final NotificationRemoteInputManager mRemoteInputManager; private final GroupMembershipManager mGroupMembershipManager; @@ -142,7 +142,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit StatusBarKeyguardViewManager statusBarKeyguardViewManager, KeyguardManager keyguardManager, IDreamManager dreamManager, - Optional<Bubbles> bubblesOptional, + Optional<BubblesManager> bubblesManagerOptional, Lazy<AssistManager> assistManagerLazy, NotificationRemoteInputManager remoteInputManager, GroupMembershipManager groupMembershipManager, @@ -176,7 +176,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mKeyguardManager = keyguardManager; mDreamManager = dreamManager; - mBubblesOptional = bubblesOptional; + mBubblesManagerOptional = bubblesManagerOptional; mAssistManagerLazy = assistManagerLazy; mRemoteInputManager = remoteInputManager; mGroupMembershipManager = groupMembershipManager; @@ -399,14 +399,15 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } private void expandBubbleStackOnMainThread(NotificationEntry entry) { - if (!mBubblesOptional.isPresent()) { + if (!mBubblesManagerOptional.isPresent()) { return; } if (Looper.getMainLooper().isCurrentThread()) { - mBubblesOptional.get().expandStackAndSelectBubble(entry); + mBubblesManagerOptional.get().expandStackAndSelectBubble(entry); } else { - mMainThreadHandler.post(() -> mBubblesOptional.get().expandStackAndSelectBubble(entry)); + mMainThreadHandler.post( + () -> mBubblesManagerOptional.get().expandStackAndSelectBubble(entry)); } } @@ -606,7 +607,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final KeyguardManager mKeyguardManager; private final IDreamManager mDreamManager; - private final Optional<Bubbles> mBubblesOptional; + private final Optional<BubblesManager> mBubblesManagerOptional; private final Lazy<AssistManager> mAssistManagerLazy; private final NotificationRemoteInputManager mRemoteInputManager; private final GroupMembershipManager mGroupMembershipManager; @@ -643,7 +644,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit StatusBarKeyguardViewManager statusBarKeyguardViewManager, KeyguardManager keyguardManager, IDreamManager dreamManager, - Optional<Bubbles> bubblesOptional, + Optional<BubblesManager> bubblesManager, Lazy<AssistManager> assistManagerLazy, NotificationRemoteInputManager remoteInputManager, GroupMembershipManager groupMembershipManager, @@ -673,7 +674,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mKeyguardManager = keyguardManager; mDreamManager = dreamManager; - mBubblesOptional = bubblesOptional; + mBubblesManagerOptional = bubblesManager; mAssistManagerLazy = assistManagerLazy; mRemoteInputManager = remoteInputManager; mGroupMembershipManager = groupMembershipManager; @@ -729,7 +730,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mStatusBarKeyguardViewManager, mKeyguardManager, mDreamManager, - mBubblesOptional, + mBubblesManagerOptional, mAssistManagerLazy, mRemoteInputManager, mGroupMembershipManager, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index 6d4099b656cb..b69da859b3c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -97,6 +97,7 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.volume.VolumeComponent; +import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.splitscreen.SplitScreen; import java.util.Optional; @@ -155,6 +156,7 @@ public interface StatusBarPhoneModule { WakefulnessLifecycle wakefulnessLifecycle, SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper, + Optional<BubblesManager> bubblesManagerOptional, Optional<Bubbles> bubblesOptional, VisualStabilityManager visualStabilityManager, DeviceProvisionedController deviceProvisionedController, @@ -233,6 +235,7 @@ public interface StatusBarPhoneModule { wakefulnessLifecycle, statusBarStateController, vibratorHelper, + bubblesManagerOptional, bubblesOptional, visualStabilityManager, deviceProvisionedController, diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java new file mode 100644 index 000000000000..ad596c27ba97 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -0,0 +1,725 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wmshell; + +import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; +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_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.NotificationStats.DISMISSAL_BUBBLE; +import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; + +import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; +import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.systemui.statusbar.StatusBarState.SHADE; +import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON; + +import android.app.INotificationManager; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.content.res.Configuration; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.service.notification.NotificationListenerService.RankingMap; +import android.service.notification.ZenModeConfig; +import android.util.ArraySet; +import android.util.Log; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.Dumpable; +import com.android.systemui.bubbles.BubbleEntry; +import com.android.systemui.bubbles.Bubbles; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.model.SysUiState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.ScrimView; +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.collection.NotifCollection; +import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; +import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; +import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ZenModeController; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.IntConsumer; + +/** + * The SysUi side bubbles manager which communicate with other SysUi components. + */ +@SysUISingleton +public class BubblesManager implements Dumpable { + + private static final String TAG = TAG_WITH_CLASS_NAME ? "BubblesManager" : TAG_BUBBLES; + + private final Context mContext; + private final Bubbles mBubbles; + private final NotificationShadeWindowController mNotificationShadeWindowController; + private final ShadeController mShadeController; + private final IStatusBarService mBarService; + private final INotificationManager mNotificationManager; + private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; + private final NotificationGroupManagerLegacy mNotificationGroupManager; + private final NotificationEntryManager mNotificationEntryManager; + private final NotifPipeline mNotifPipeline; + + private final ScrimView mBubbleScrim; + private final Bubbles.SysuiProxy mSysuiProxy; + // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline + private final List<NotifCallback> mCallbacks = new ArrayList<>(); + + /** + * Creates {@link BubblesManager}, returns {@code null} if Optional {@link Bubbles} not present + * which means bubbles feature not support. + */ + @Nullable + public static BubblesManager create(Context context, + Optional<Bubbles> bubblesOptional, + NotificationShadeWindowController notificationShadeWindowController, + StatusBarStateController statusBarStateController, + ShadeController shadeController, + ConfigurationController configurationController, + @Nullable IStatusBarService statusBarService, + INotificationManager notificationManager, + NotificationInterruptStateProvider interruptionStateProvider, + ZenModeController zenModeController, + NotificationLockscreenUserManager notifUserManager, + NotificationGroupManagerLegacy groupManager, + NotificationEntryManager entryManager, + NotifPipeline notifPipeline, + SysUiState sysUiState, + FeatureFlags featureFlags, + DumpManager dumpManager) { + if (bubblesOptional.isPresent()) { + return new BubblesManager(context, bubblesOptional.get(), + notificationShadeWindowController, statusBarStateController, shadeController, + configurationController, statusBarService, notificationManager, + interruptionStateProvider, zenModeController, notifUserManager, + groupManager, entryManager, notifPipeline, sysUiState, featureFlags, + dumpManager); + } else { + return null; + } + } + + @VisibleForTesting + BubblesManager(Context context, + Bubbles bubbles, + NotificationShadeWindowController notificationShadeWindowController, + StatusBarStateController statusBarStateController, + ShadeController shadeController, + ConfigurationController configurationController, + @Nullable IStatusBarService statusBarService, + INotificationManager notificationManager, + NotificationInterruptStateProvider interruptionStateProvider, + ZenModeController zenModeController, + NotificationLockscreenUserManager notifUserManager, + NotificationGroupManagerLegacy groupManager, + NotificationEntryManager entryManager, + NotifPipeline notifPipeline, + SysUiState sysUiState, + FeatureFlags featureFlags, + DumpManager dumpManager) { + mContext = context; + mBubbles = bubbles; + mNotificationShadeWindowController = notificationShadeWindowController; + mShadeController = shadeController; + mNotificationManager = notificationManager; + mNotificationInterruptStateProvider = interruptionStateProvider; + mNotificationGroupManager = groupManager; + mNotificationEntryManager = entryManager; + mNotifPipeline = notifPipeline; + + mBarService = statusBarService == null + ? IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)) + : statusBarService; + + mBubbleScrim = new ScrimView(mContext); + mBubbleScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + mBubbles.setBubbleScrim(mBubbleScrim); + + if (featureFlags.isNewNotifPipelineRenderingEnabled()) { + setupNotifPipeline(); + } else { + setupNEM(); + } + + dumpManager.registerDumpable(TAG, this); + + statusBarStateController.addCallback(new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + boolean isShade = newState == SHADE; + bubbles.onStatusBarStateChanged(isShade); + } + }); + + configurationController.addCallback(new ConfigurationController.ConfigurationListener() { + @Override + public void onConfigChanged(Configuration newConfig) { + mBubbles.onConfigChanged(newConfig); + } + + @Override + public void onUiModeChanged() { + mBubbles.updateForThemeChanges(); + } + + @Override + public void onThemeChanged() { + mBubbles.updateForThemeChanges(); + } + }); + + zenModeController.addCallback(new ZenModeController.Callback() { + @Override + public void onZenChanged(int zen) { + mBubbles.onZenStateChanged(); + } + + @Override + public void onConfigChanged(ZenModeConfig config) { + mBubbles.onZenStateChanged(); + } + }); + + notifUserManager.addUserChangedListener( + new NotificationLockscreenUserManager.UserChangedListener() { + @Override + public void onUserChanged(int userId) { + mBubbles.onUserChanged(userId); + } + }); + + mSysuiProxy = new Bubbles.SysuiProxy() { + @Override + @Nullable + public BubbleEntry getPendingOrActiveEntry(String key) { + NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key); + return entry == null ? null : notifToBubbleEntry(entry); + } + + @Override + public List<BubbleEntry> getShouldRestoredEntries(ArraySet<String> savedBubbleKeys) { + List<BubbleEntry> result = new ArrayList<>(); + List<NotificationEntry> activeEntries = + mNotificationEntryManager.getActiveNotificationsForCurrentUser(); + for (int i = 0; i < activeEntries.size(); i++) { + NotificationEntry entry = activeEntries.get(i); + if (savedBubbleKeys.contains(entry.getKey()) + && mNotificationInterruptStateProvider.shouldBubbleUp(entry) + && entry.isBubble()) { + result.add(notifToBubbleEntry(entry)); + } + } + return result; + } + + @Override + public boolean isNotificationShadeExpand() { + return mNotificationShadeWindowController.getPanelExpanded(); + } + + @Override + public boolean shouldBubbleUp(String key) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null) { + return mNotificationInterruptStateProvider.shouldBubbleUp(entry); + } + return false; + } + + @Override + public void setNotificationInterruption(String key) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null && entry.getImportance() >= NotificationManager.IMPORTANCE_HIGH) { + entry.setInterruption(); + } + } + + @Override + public void requestNotificationShadeTopUi(boolean requestTopUi, String componentTag) { + mNotificationShadeWindowController.setRequestTopUi(requestTopUi, componentTag); + } + + @Override + public void notifyRemoveNotification(String key, int reason) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null) { + for (NotifCallback cb : mCallbacks) { + cb.removeNotification(entry, getDismissedByUserStats(entry, true), reason); + } + } + } + + @Override + public void notifyInvalidateNotifications(String reason) { + for (NotifCallback cb : mCallbacks) { + cb.invalidateNotifications(reason); + } + } + + @Override + public void notifyMaybeCancelSummary(String key) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null) { + for (NotifCallback cb : mCallbacks) { + cb.maybeCancelSummary(entry); + } + } + } + + @Override + public void removeNotificationEntry(String key) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null) { + mNotificationGroupManager.onEntryRemoved(entry); + } + } + + @Override + public void updateNotificationBubbleButton(String key) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null && entry.getRow() != null) { + entry.getRow().updateBubbleButton(); + } + } + + @Override + public void updateNotificationSuppression(String key) { + final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + key); + if (entry != null) { + mNotificationGroupManager.updateSuppression(entry); + } + } + + @Override + public void onStackExpandChanged(boolean shouldExpand) { + sysUiState + .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand) + .commitUpdate(mContext.getDisplayId()); + } + + @Override + public void onUnbubbleConversation(String key) { + final NotificationEntry entry = + mNotificationEntryManager.getPendingOrActiveNotif(key); + if (entry != null) { + onUserChangedBubble(entry, false /* shouldBubble */); + } + } + }; + 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); + } + }); + + // 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.isSummarySuppressed(groupKey)) { + mBubbles.removeSuppressedSummary(groupKey); + } + } + }); + + 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(); + if (mBubbles.isSummarySuppressed(groupKey)) { + mBubbles.removeSuppressedSummary(groupKey); + + final NotificationEntry summary = + mNotificationEntryManager.getActiveNotificationUnfiltered( + mBubbles.getSummaryKey(groupKey)); + if (summary != null) { + mNotificationEntryManager.performRemoveNotification( + summary.getSbn(), + getDismissedByUserStats(summary, false), + UNDEFINED_DISMISS_REASON); + } + } + + // 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() { + mNotifPipeline.addCollectionListener(new NotifCollectionListener() { + @Override + public void onEntryAdded(NotificationEntry entry) { + BubblesManager.this.onEntryAdded(entry); + } + + @Override + public void onEntryUpdated(NotificationEntry entry) { + BubblesManager.this.onEntryUpdated(entry); + } + + @Override + public void onEntryRemoved(NotificationEntry entry, + @NotifCollection.CancellationReason int reason) { + BubblesManager.this.onEntryRemoved(entry); + } + + @Override + public void onRankingUpdate(RankingMap rankingMap) { + BubblesManager.this.onRankingUpdate(rankingMap); + } + }); + } + + void onEntryAdded(NotificationEntry entry) { + if (mNotificationInterruptStateProvider.shouldBubbleUp(entry) + && entry.isBubble()) { + mBubbles.onEntryAdded(notifToBubbleEntry(entry)); + } + } + + void onEntryUpdated(NotificationEntry entry) { + mBubbles.onEntryUpdated(notifToBubbleEntry(entry), + mNotificationInterruptStateProvider.shouldBubbleUp(entry)); + } + + void onEntryRemoved(NotificationEntry entry) { + mBubbles.onEntryRemoved(notifToBubbleEntry(entry)); + } + + void onRankingUpdate(RankingMap rankingMap) { + mBubbles.onRankingUpdated(rankingMap); + } + + /** + * Gets the DismissedByUserStats used by {@link NotificationEntryManager}. + * Will not be necessary when using the new notification pipeline's {@link NotifCollection}. + * Instead, this is taken care of by {@link BubbleCoordinator}. + */ + private DismissedByUserStats getDismissedByUserStats( + NotificationEntry entry, + boolean isVisible) { + return new DismissedByUserStats( + DISMISSAL_BUBBLE, + DISMISS_SENTIMENT_NEUTRAL, + NotificationVisibility.obtain( + entry.getKey(), + entry.getRanking().getRank(), + mNotificationEntryManager.getActiveNotificationsCount(), + isVisible, + NotificationLogger.getNotificationLocation(entry))); + } + + /** + * Returns the scrim drawn behind the bubble stack. This is managed by {@link ScrimController} + * since we want the scrim's appearance and behavior to be identical to that of the notification + * shade scrim. + */ + public ScrimView getScrimForBubble() { + return mBubbleScrim; + } + + /** + * We intercept notification entries (including group summaries) dismissed by the user when + * there is an active bubble associated with it. We do this so that developers can still + * cancel it (and hence the bubbles associated with it). + * + * @return true if we want to intercept the dismissal of the entry, else false. + * @see Bubbles#handleDismissalInterception(BubbleEntry, List, IntConsumer) + */ + public boolean handleDismissalInterception(NotificationEntry entry) { + if (entry == null) { + return false; + } + + List<NotificationEntry> children = entry.getAttachedNotifChildren(); + List<BubbleEntry> bubbleChildren = null; + if (children != null) { + bubbleChildren = new ArrayList<>(); + for (int i = 0; i < children.size(); i++) { + bubbleChildren.add(notifToBubbleEntry(children.get(i))); + } + } + + return mBubbles.handleDismissalInterception(notifToBubbleEntry(entry), bubbleChildren, + // TODO : b/171847985 should re-work on notification side to make this more clear. + (int i) -> { + if (i >= 0) { + for (NotifCallback cb : mCallbacks) { + cb.removeNotification(children.get(i), + getDismissedByUserStats(children.get(i), true), + REASON_GROUP_SUMMARY_CANCELED); + } + } else { + mNotificationGroupManager.onEntryRemoved(entry); + } + }); + } + + /** + * Request the stack expand if needed, then select the specified Bubble as current. + * If no bubble exists for this entry, one is created. + * + * @param entry the notification for the bubble to be selected + */ + public void expandStackAndSelectBubble(NotificationEntry entry) { + mBubbles.expandStackAndSelectBubble(notifToBubbleEntry(entry)); + } + + /** See {@link NotifCallback}. */ + public void addNotifCallback(NotifCallback callback) { + mCallbacks.add(callback); + } + + /** + * When a notification is marked Priority, expand the stack if needed, + * then (maybe create and) select the given bubble. + * + * @param entry the notification for the bubble to show + */ + public void onUserChangedImportance(NotificationEntry entry) { + try { + int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; + flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE; + mBarService.onNotificationBubbleChanged(entry.getKey(), true, flags); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage()); + } + mShadeController.collapsePanel(true); + if (entry.getRow() != null) { + entry.getRow().updateBubbleButton(); + } + } + + /** + * Called when a user has indicated that an active notification should be shown as a bubble. + * <p> + * This method will collapse the shade, create the bubble without a flyout or dot, and suppress + * the notification from appearing in the shade. + * + * @param entry the notification to change bubble state for. + * @param shouldBubble whether the notification should show as a bubble or not. + */ + public void onUserChangedBubble(@NonNull final NotificationEntry entry, boolean shouldBubble) { + NotificationChannel channel = entry.getChannel(); + final String appPkg = entry.getSbn().getPackageName(); + final int appUid = entry.getSbn().getUid(); + if (channel == null || appPkg == null) { + return; + } + + // Update the state in NotificationManagerService + try { + int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; + flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE; + mBarService.onNotificationBubbleChanged(entry.getKey(), shouldBubble, flags); + } catch (RemoteException e) { + } + + // Change the settings + channel = NotificationChannelHelper.createConversationChannelIfNeeded(mContext, + mNotificationManager, entry, channel); + channel.setAllowBubbles(shouldBubble); + try { + int currentPref = mNotificationManager.getBubblePreferenceForPackage(appPkg, appUid); + if (shouldBubble && currentPref == BUBBLE_PREFERENCE_NONE) { + mNotificationManager.setBubblesAllowed(appPkg, appUid, BUBBLE_PREFERENCE_SELECTED); + } + mNotificationManager.updateNotificationChannelForPackage(appPkg, appUid, channel); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage()); + } + + if (shouldBubble) { + mShadeController.collapsePanel(true); + if (entry.getRow() != null) { + entry.getRow().updateBubbleButton(); + } + } + } + + @Override + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + mBubbles.dump(fd, pw, args); + } + + static BubbleEntry notifToBubbleEntry(NotificationEntry e) { + return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(), + e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(), + e.shouldSuppressPeek()); + } + + /** + * Callback for when the BubbleController wants to interact with the notification pipeline to: + * - Remove a previously bubbled notification + * - Update the notification shade since bubbled notification should/shouldn't be showing + */ + public interface NotifCallback { + /** + * Called when a bubbled notification that was hidden from the shade is now being removed + * This can happen when an app cancels a bubbled notification or when the user dismisses a + * bubble. + */ + void removeNotification(@NonNull NotificationEntry entry, + @NonNull DismissedByUserStats stats, int reason); + + /** + * Called when a bubbled notification has changed whether it should be + * filtered from the shade. + */ + void invalidateNotifications(@NonNull String reason); + + /** + * Called on a bubbled entry that has been removed when there are no longer + * bubbled entries in its group. + * + * Checks whether its group has any other (non-bubbled) children. If it doesn't, + * removes all remnants of the group's summary from the notification pipeline. + * TODO: (b/145659174) Only old pipeline needs this - delete post-migration. + */ + void maybeCancelSummary(@NonNull NotificationEntry entry); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java index 0c872db45194..31c08ae471ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java @@ -102,10 +102,10 @@ public class BubbleDataTest extends SysuiTestCase { private ArgumentCaptor<BubbleData.Update> mUpdateCaptor; @Mock - private BubbleController.NotificationSuppressionChangedListener mSuppressionListener; + private Bubbles.NotificationSuppressionChangedListener mSuppressionListener; @Mock - private BubbleController.PendingIntentCanceledListener mPendingIntentCanceledListener; + private Bubbles.PendingIntentCanceledListener mPendingIntentCanceledListener; @Before public void setUp() throws Exception { @@ -171,12 +171,11 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE); // Verify verifyUpdateReceived(); - assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_USER_GESTURE); + assertBubbleRemoved(mBubbleA1, Bubbles.DISMISS_USER_GESTURE); } @Test @@ -269,7 +268,7 @@ public class BubbleDataTest extends SysuiTestCase { sendUpdatedEntryAtTime(mEntryC1, 6000); verifyUpdateReceived(); - assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_AGED); + assertBubbleRemoved(mBubbleA1, Bubbles.DISMISS_AGED); assertOverflowChangedTo(ImmutableList.of(mBubbleA1)); Bubble bubbleA1 = mBubbleData.getOrCreateBubble(mEntryA1, null /* persistedBubble */); @@ -277,7 +276,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.notificationEntryUpdated(bubbleA1, false /* suppressFlyout*/, true /* showInShade */); verifyUpdateReceived(); - assertBubbleRemoved(mBubbleA2, BubbleController.DISMISS_AGED); + assertBubbleRemoved(mBubbleA2, Bubbles.DISMISS_AGED); assertOverflowChangedTo(ImmutableList.of(mBubbleA2)); } @@ -294,14 +293,12 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); mBubbleData.setMaxOverflowBubbles(1); - mBubbleData.dismissBubbleWithKey( - mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertOverflowChangedTo(ImmutableList.of(mBubbleA1)); // Overflow max of 1 is reached; A1 is oldest, so it gets removed - mBubbleData.dismissBubbleWithKey( - mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertOverflowChangedTo(ImmutableList.of(mBubbleA2)); } @@ -322,14 +319,12 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), - BubbleController.DISMISS_NOTIF_CANCEL); + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_NOTIF_CANCEL); verifyUpdateReceived(); assertOverflowChangedTo(ImmutableList.of(mBubbleA2)); // Test - mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), - BubbleController.DISMISS_GROUP_CANCELLED); + mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_GROUP_CANCELLED); verifyUpdateReceived(); assertOverflowChangedTo(ImmutableList.of()); } @@ -409,8 +404,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); // TODO: this should fail if things work as I expect them to? assertOrderChangedTo(mBubbleB2, mBubbleB1, mBubbleA1); @@ -430,8 +424,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertOrderNotChanged(); } @@ -450,8 +443,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA2.getKey(), BubbleController.DISMISS_NOTIF_CANCEL); + mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_NOTIF_CANCEL); verifyUpdateReceived(); assertSelectionChangedTo(mBubbleB2); } @@ -545,8 +537,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE); // Verify the selection was cleared. verifyUpdateReceived(); @@ -646,8 +637,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryB2.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryB2.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertOrderChangedTo(mBubbleA2, mBubbleB1, mBubbleA1); } @@ -671,13 +661,11 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertSelectionChangedTo(mBubbleB1); - mBubbleData.dismissBubbleWithKey( - mEntryB1.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryB1.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertSelectionChangedTo(mBubbleA1); } @@ -791,8 +779,7 @@ public class BubbleDataTest extends SysuiTestCase { mBubbleData.setListener(mListener); // Test - mBubbleData.dismissBubbleWithKey( - mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE); verifyUpdateReceived(); assertExpandedChangedTo(false); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java index 29ead593c51a..690a1ad42861 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java @@ -60,7 +60,7 @@ public class BubbleTest extends SysuiTestCase { private Bubble mBubble; @Mock - private BubbleController.NotificationSuppressionChangedListener mSuppressionListener; + private Bubbles.NotificationSuppressionChangedListener mSuppressionListener; @Before public void setUp() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java deleted file mode 100644 index aaeee16dc1fd..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.bubbles; - -import android.app.INotificationManager; -import android.content.Context; -import android.content.pm.LauncherApps; -import android.os.Handler; -import android.view.WindowManager; - -import com.android.internal.statusbar.IStatusBarService; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.model.SysUiState; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationShadeWindowController; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.collection.NotifPipeline; -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; -import com.android.systemui.statusbar.phone.ShadeController; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.WindowManagerShellWrapper; -import com.android.wm.shell.common.FloatingContentCoordinator; - -/** - * Testable BubbleController subclass that immediately synchronizes surfaces. - */ -public class TestableBubbleController extends BubbleController { - - // Let's assume surfaces can be synchronized immediately. - TestableBubbleController(Context context, - NotificationShadeWindowController notificationShadeWindowController, - StatusBarStateController statusBarStateController, - ShadeController shadeController, - BubbleData data, - ConfigurationController configurationController, - NotificationInterruptStateProvider interruptionStateProvider, - ZenModeController zenModeController, - NotificationLockscreenUserManager lockscreenUserManager, - NotificationGroupManagerLegacy groupManager, - NotificationEntryManager entryManager, - NotifPipeline notifPipeline, - FeatureFlags featureFlags, - DumpManager dumpManager, - FloatingContentCoordinator floatingContentCoordinator, - BubbleDataRepository dataRepository, - SysUiState sysUiState, - INotificationManager notificationManager, - IStatusBarService statusBarService, - WindowManager windowManager, - WindowManagerShellWrapper windowManagerShellWrapper, - LauncherApps launcherApps, - BubbleLogger bubbleLogger, - Handler mainHandler, - ShellTaskOrganizer shellTaskOrganizer, - BubblePositioner positioner) { - super(context, - notificationShadeWindowController, statusBarStateController, shadeController, - data, Runnable::run, configurationController, interruptionStateProvider, - zenModeController, lockscreenUserManager, groupManager, entryManager, - notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, - dataRepository, sysUiState, notificationManager, statusBarService, - windowManager, windowManagerShellWrapper, launcherApps, bubbleLogger, - mainHandler, shellTaskOrganizer, positioner); - setInflateSynchronously(true); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java index d835123e4cad..cd46dda772e3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java @@ -116,7 +116,7 @@ public class NotificationFilterTest extends SysuiTestCase { new NotificationGroupManagerLegacy( mock(StatusBarStateController.class), () -> mock(PeopleNotificationIdentifier.class), - Optional.of(() -> mock(Bubbles.class)))); + Optional.of(mock(Bubbles.class)))); mDependency.injectMockDependency(ShadeController.class); mDependency.injectMockDependency(NotificationLockscreenUserManager.class); mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index 61edcf9c200c..4698b8e50efb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -76,12 +76,12 @@ import com.android.settingslib.notification.ConversationIconFactory; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.bubbles.BubblesTestActivity; import com.android.systemui.statusbar.SbnBuilder; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.wmshell.BubblesManager; import org.junit.Before; import org.junit.Rule; @@ -137,7 +137,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Mock private OnUserInteractionCallback mOnUserInteractionCallback; @Mock - private Bubbles mBubbles; + private BubblesManager mBubblesManager; @Mock private LauncherApps mLauncherApps; @Mock @@ -255,7 +255,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon); assertEquals(mIconDrawable, view.getDrawable()); } @@ -279,7 +279,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name); assertTrue(textView.getText().toString().contains("App Name")); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -330,7 +330,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final TextView textView = mNotificationInfo.findViewById(R.id.group_name); assertTrue(textView.getText().toString().contains(group.getName())); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -355,7 +355,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final TextView textView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); assertEquals(GONE, textView.getVisibility()); @@ -379,7 +379,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(GONE, nameView.getVisibility()); } @@ -414,7 +414,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(VISIBLE, nameView.getVisibility()); assertTrue(nameView.getText().toString().contains("Proxied")); @@ -442,7 +442,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); @@ -468,7 +468,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -495,7 +495,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, false, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -520,7 +520,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View view = mNotificationInfo.findViewById(R.id.silence); assertThat(view.isSelected()).isTrue(); } @@ -548,7 +548,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View view = mNotificationInfo.findViewById(R.id.default_behavior); assertThat(view.isSelected()).isTrue(); assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo( @@ -579,7 +579,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View view = mNotificationInfo.findViewById(R.id.default_behavior); assertThat(view.isSelected()).isTrue(); assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo( @@ -609,7 +609,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -653,7 +653,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mTestableLooper.processAllMessages(); @@ -696,7 +696,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View silence = mNotificationInfo.findViewById(R.id.silence); @@ -740,7 +740,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -777,7 +777,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -813,7 +813,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -851,7 +851,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -887,7 +887,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -923,7 +923,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -958,7 +958,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); View silence = mNotificationInfo.findViewById(R.id.silence); silence.performClick(); @@ -992,7 +992,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage( anyString(), anyInt(), any(), eq(CONVERSATION_ID)); @@ -1017,7 +1017,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage( anyString(), anyInt(), any(), eq(CONVERSATION_ID)); @@ -1052,7 +1052,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { () -> b, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); // WHEN user clicks "priority" mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE); @@ -1092,7 +1092,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { () -> b, true, mTestHandler, - mTestHandler, null, Optional.of(mBubbles)); + mTestHandler, null, Optional.of(mBubblesManager)); // WHEN user clicks "priority" mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 8a5afe6ce667..dbaf5c467c45 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -84,6 +84,7 @@ import com.android.systemui.statusbar.policy.InflatedSmartReplies; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.wmshell.BubblesManager; import org.junit.After; import org.junit.Before; @@ -96,6 +97,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; +import java.util.Optional; import java.util.concurrent.CountDownLatch; /** @@ -243,7 +245,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { true, null, mFalsingManager, - mPeopleNotificationIdentifier + mPeopleNotificationIdentifier, + Optional.of(mock(BubblesManager.class)) )); when(mNotificationRowComponentBuilder.activatableNotificationView(any())) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index bbc1df21237f..3000b8b449c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -67,7 +67,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.SysuiTestCase; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -81,6 +80,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager.O import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.wmshell.BubblesManager; import org.junit.Before; import org.junit.Ignore; @@ -131,7 +131,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Mock private ChannelEditorDialogController mChannelEditorDialogController; @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier; @Mock private UserContextProvider mContextTracker; - @Mock private Bubbles mBubbles; + @Mock private BubblesManager mBubblesManager; @Mock(answer = Answers.RETURNS_SELF) private PriorityOnboardingDialogController.Builder mBuilder; private Provider<PriorityOnboardingDialogController.Builder> mProvider = () -> mBuilder; @@ -156,7 +156,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { () -> mStatusBar, mHandler, mHandler, mAccessibilityManager, mHighPriorityProvider, mINotificationManager, mLauncherApps, mShortcutManager, mChannelEditorDialogController, mContextTracker, mProvider, - mAssistantFeedbackController, Optional.of(mBubbles), + mAssistantFeedbackController, Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback); mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer, mCheckSaveListener, mOnSettingsClickListener); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 847e0a474a6a..48375e02f64f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -70,6 +70,7 @@ import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.InflatedSmartReplies; +import com.android.systemui.wmshell.BubblesManager; import org.mockito.ArgumentCaptor; @@ -121,7 +122,7 @@ public class NotificationTestHelper { mGroupMembershipManager = new NotificationGroupManagerLegacy( mStatusBarStateController, () -> mock(PeopleNotificationIdentifier.class), - Optional.of(() -> mock(Bubbles.class))); + Optional.of((mock(Bubbles.class)))); mGroupExpansionManager = mGroupMembershipManager; mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarStateController, mock(KeyguardBypassController.class), mock(NotificationGroupManagerLegacy.class), @@ -430,7 +431,8 @@ public class NotificationTestHelper { mock(FalsingManager.class), mStatusBarStateController, mPeopleNotificationIdentifier, - mock(OnUserInteractionCallback.class)); + mock(OnUserInteractionCallback.class), + Optional.of(mock(BubblesManager.class))); row.setAboveShelfChangedListener(aboveShelf -> { }); mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java index 7d84f86cc7b3..b0086ef1d4fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java @@ -92,7 +92,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { mGroupManager = new NotificationGroupManagerLegacy( mock(StatusBarStateController.class), () -> mock(PeopleNotificationIdentifier.class), - Optional.of(() -> mock(Bubbles.class))); + Optional.of(mock(Bubbles.class))); mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, mGroupManager); mGroupManager.setHeadsUpManager(mHeadsUpManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java index 29e445a13e24..f81672ab6a37 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java @@ -70,7 +70,7 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase { mGroupManager = new NotificationGroupManagerLegacy( mock(StatusBarStateController.class), () -> mock(PeopleNotificationIdentifier.class), - Optional.of(() -> mock(Bubbles.class))); + Optional.of(mock(Bubbles.class))); mGroupManager.setHeadsUpManager(mHeadsUpManager); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index f7489b1c164a..1f31fcd2a2bf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -53,7 +53,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.systemui.ActivityIntentHelper; import com.android.systemui.SysuiTestCase; import com.android.systemui.assist.AssistManager; -import com.android.systemui.bubbles.Bubbles; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; @@ -77,6 +76,7 @@ import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.wmshell.BubblesManager; import org.junit.Before; import org.junit.Test; @@ -116,7 +116,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { @Mock private Handler mHandler; @Mock - private Bubbles mBubbles; + private BubblesManager mBubblesManager; @Mock private ShadeControllerImpl mShadeController; @Mock @@ -193,7 +193,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mStatusBarKeyguardViewManager, mock(KeyguardManager.class), mock(IDreamManager.class), - Optional.of(mBubbles), + Optional.of(mBubblesManager), () -> mAssistManager, mRemoteInputManager, mock(NotificationGroupManagerLegacy.class), @@ -280,7 +280,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow); // Then - verify(mBubbles).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry())); + verify(mBubblesManager).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry())); // This is called regardless, and simply short circuits when there is nothing to do. verify(mShadeController, atLeastOnce()).collapsePanel(); @@ -312,7 +312,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow); // Then - verify(mBubbles).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry()); + verify(mBubblesManager).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry())); verify(mShadeController, atLeastOnce()).collapsePanel(); @@ -342,7 +342,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow); // Then - verify(mBubbles).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry()); + verify(mBubblesManager).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry()); verify(mShadeController, atLeastOnce()).collapsePanel(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index d6a7acbcbd78..9f096dada6f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -145,6 +145,7 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.volume.VolumeComponent; +import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; @@ -223,6 +224,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private UserSwitcherController mUserSwitcherController; @Mock private NetworkController mNetworkController; @Mock private VibratorHelper mVibratorHelper; + @Mock private BubblesManager mBubblesManager; @Mock private Bubbles mBubbles; @Mock private NotificationShadeWindowController mNotificationShadeWindowController; @Mock private NotificationIconAreaController mNotificationIconAreaController; @@ -377,6 +379,7 @@ public class StatusBarTest extends SysuiTestCase { wakefulnessLifecycle, mStatusBarStateController, mVibratorHelper, + Optional.of(mBubblesManager), Optional.of(mBubbles), mVisualStabilityManager, mDeviceProvisionedController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index d9e9a8b26f0f..88d04011e738 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.bubbles; +package com.android.systemui.wmshell; import static android.app.Notification.FLAG_BUBBLE; import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; @@ -64,6 +64,14 @@ import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.SysuiTestCase; +import com.android.systemui.bubbles.Bubble; +import com.android.systemui.bubbles.BubbleData; +import com.android.systemui.bubbles.BubbleDataRepository; +import com.android.systemui.bubbles.BubbleEntry; +import com.android.systemui.bubbles.BubbleLogger; +import com.android.systemui.bubbles.BubblePositioner; +import com.android.systemui.bubbles.BubbleStackView; +import com.android.systemui.bubbles.Bubbles; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -112,12 +120,12 @@ import java.util.List; /** * Tests the NotificationEntryManager setup with BubbleController. * The {@link NotifPipeline} setup with BubbleController is tested in - * {@link NewNotifPipelineBubbleControllerTest}. + * {@link NewNotifPipelineBubblesTest}. */ @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) -public class BubbleControllerTest extends SysuiTestCase { +public class BubblesTest extends SysuiTestCase { @Mock private NotificationEntryManager mNotificationEntryManager; @Mock @@ -157,6 +165,7 @@ public class BubbleControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor; + private BubblesManager mBubblesManager; private TestableBubbleController mBubbleController; private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; private NotificationEntryListener mEntryListener; @@ -167,9 +176,12 @@ public class BubbleControllerTest extends SysuiTestCase { private ExpandableNotificationRow mRow2; private ExpandableNotificationRow mRow3; private ExpandableNotificationRow mNonBubbleNotifRow; + private BubbleEntry mBubbleEntry; + private BubbleEntry mBubbleEntry2; + private BubbleEntry mBubbleEntry3; @Mock - private BubbleController.BubbleExpandListener mBubbleExpandListener; + private Bubbles.BubbleExpandListener mBubbleExpandListener; @Mock private PendingIntent mDeleteIntent; @Mock @@ -226,6 +238,9 @@ public class BubbleControllerTest extends SysuiTestCase { mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent); mRow3 = mNotificationTestHelper.createBubble(mDeleteIntent); mNonBubbleNotifRow = mNotificationTestHelper.createRow(); + mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow.getEntry()); + mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2.getEntry()); + mBubbleEntry3 = BubblesManager.notifToBubbleEntry(mRow3.getEntry()); // Return non-null notification data from the NEM when(mNotificationEntryManager @@ -258,26 +273,13 @@ public class BubbleControllerTest extends SysuiTestCase { mock(HeadsUpManager.class), mock(Handler.class) ); + when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false); mBubbleController = new TestableBubbleController( mContext, - mNotificationShadeWindowController, - mStatusBarStateController, - mShadeController, mBubbleData, - mConfigurationController, - interruptionStateProvider, - mZenModeController, - mLockscreenUserManager, - mNotificationGroupManager, - mNotificationEntryManager, - mNotifPipeline, - mFeatureFlagsOldPipeline, - mDumpManager, mFloatingContentCoordinator, mDataRepository, - mSysUiState, - mock(INotificationManager.class), mStatusBarService, mWindowManager, mWindowManagerShellWrapper, @@ -288,6 +290,25 @@ public class BubbleControllerTest extends SysuiTestCase { mPositioner); mBubbleController.setExpandListener(mBubbleExpandListener); + mBubblesManager = new BubblesManager( + mContext, + mBubbleController, + mNotificationShadeWindowController, + mStatusBarStateController, + mShadeController, + mConfigurationController, + mStatusBarService, + mock(INotificationManager.class), + interruptionStateProvider, + mZenModeController, + mLockscreenUserManager, + mNotificationGroupManager, + mNotificationEntryManager, + mNotifPipeline, + mSysUiState, + mFeatureFlagsOldPipeline, + mDumpManager); + // Get a reference to the BubbleController's entry listener verify(mNotificationEntryManager, atLeastOnce()) .addNotificationEntryListener(mEntryListenerCaptor.capture()); @@ -300,7 +321,7 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testAddBubble() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mSysUiStateBubblesExpanded); @@ -309,20 +330,20 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testHasBubbles() { assertFalse(mBubbleController.hasBubbles()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mSysUiStateBubblesExpanded); } @Test public void testRemoveBubble() { - mBubbleController.updateBubble(mRow.getEntry()); - assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); + mBubbleController.updateBubble(mBubbleEntry); + assertNotNull(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey())); assertTrue(mBubbleController.hasBubbles()); verify(mNotificationEntryManager).updateNotifications(any()); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); verify(mNotificationEntryManager, times(2)).updateNotifications(anyString()); @@ -331,14 +352,14 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testPromoteBubble_autoExpand() throws Exception { - mBubbleController.updateBubble(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry2); + mBubbleController.updateBubble(mBubbleEntry); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey())) .thenReturn(mRow.getEntry()); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey())) .thenReturn(mRow2.getEntry()); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getEntry().getKey()); assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b)); @@ -361,18 +382,18 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testCancelOverflowBubble() { - mBubbleController.updateBubble(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry(), /* suppressFlyout */ + mBubbleController.updateBubble(mBubbleEntry2); + mBubbleController.updateBubble(mBubbleEntry, /* suppressFlyout */ false, /* showInShade */ true); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey())) .thenReturn(mRow.getEntry()); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey())) .thenReturn(mRow2.getEntry()); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL); + mRow.getEntry().getKey(), Bubbles.DISMISS_NOTIF_CANCEL); verify(mNotificationEntryManager, times(1)).performRemoveNotification( eq(mRow.getEntry().getSbn()), any(), anyInt()); assertThat(mBubbleData.getOverflowBubbles()).isEmpty(); @@ -381,11 +402,11 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testUserChange_doesNotRemoveNotif() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_CHANGED); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_CHANGED); verify(mNotificationEntryManager, never()).performRemoveNotification( eq(mRow.getEntry().getSbn()), any(), anyInt()); assertFalse(mBubbleController.hasBubbles()); @@ -395,15 +416,15 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testDismissStack() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); verify(mNotificationEntryManager, times(1)).updateNotifications(any()); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry2); verify(mNotificationEntryManager, times(2)).updateNotifications(any()); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey())); assertTrue(mBubbleController.hasBubbles()); - mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); verify(mNotificationEntryManager, times(3)).updateNotifications(any()); assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey())); @@ -417,12 +438,12 @@ public class BubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Expand the stack BubbleStackView stackView = mBubbleController.getStackView(); @@ -434,7 +455,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Make sure the notif is suppressed assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Collapse mBubbleController.collapseStack(); @@ -450,15 +471,15 @@ public class BubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); mEntryListener.onPendingEntryAdded(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow2.getEntry())); + mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); // Expand BubbleStackView stackView = mBubbleController.getStackView(); @@ -472,7 +493,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Last added is the one that is expanded assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow2.getEntry())); + mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); // Switch which bubble is expanded mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey( @@ -481,7 +502,7 @@ public class BubbleControllerTest extends SysuiTestCase { assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // collapse for previous bubble verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( @@ -501,12 +522,12 @@ public class BubbleControllerTest extends SysuiTestCase { public void testExpansionRemovesShowInShadeAndDot() { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -520,7 +541,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); } @@ -529,12 +550,12 @@ public class BubbleControllerTest extends SysuiTestCase { public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -548,7 +569,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -558,7 +579,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Nothing should have changed // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); } @@ -568,8 +589,8 @@ public class BubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onPendingEntryAdded(mRow.getEntry()); mEntryListener.onPendingEntryAdded(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); // Expand BubbleStackView stackView = mBubbleController.getStackView(); @@ -584,13 +605,13 @@ public class BubbleControllerTest extends SysuiTestCase { assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow2.getEntry())); + mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); // Dismiss currently expanded mBubbleController.removeBubble( mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey(), - BubbleController.DISMISS_USER_GESTURE); + Bubbles.DISMISS_USER_GESTURE); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey()); // Make sure first bubble is selected @@ -602,7 +623,7 @@ public class BubbleControllerTest extends SysuiTestCase { mBubbleController.removeBubble( mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey(), - BubbleController.DISMISS_USER_GESTURE); + Bubbles.DISMISS_USER_GESTURE); // Make sure state changes and collapse happens verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey()); @@ -619,7 +640,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Add the auto expand bubble mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Expansion shouldn't change verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */, @@ -636,7 +657,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Add the auto expand bubble mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Expansion should change verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */, @@ -653,11 +674,11 @@ public class BubbleControllerTest extends SysuiTestCase { // Add the suppress notif bubble mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Notif should be suppressed because we were foreground assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout()); @@ -667,22 +688,22 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testSuppressNotif_onUpdateNotif() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Should not be suppressed assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Should show dot assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); // Update to suppress notif setMetadataFlags(mRow.getEntry(), Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Notif should be suppressed assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout()); @@ -695,13 +716,13 @@ public class BubbleControllerTest extends SysuiTestCase { final String key = mRow.getEntry().getKey(); mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Simulate notification cancellation. mRemoveInterceptor.onNotificationRemoveRequested( mRow.getEntry().getKey(), mRow.getEntry(), REASON_APP_CANCEL); - mBubbleController.expandStackAndSelectBubble(mRow.getEntry()); + mBubbleController.expandStackAndSelectBubble(mBubbleEntry); assertTrue(mSysUiStateBubblesExpanded); } @@ -710,7 +731,7 @@ public class BubbleControllerTest extends SysuiTestCase { public void testMarkNewNotificationAsShowInShade() { mEntryListener.onPendingEntryAdded(mRow.getEntry()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -726,31 +747,31 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_AGED); verify(mDeleteIntent, never()).send(); } @Test public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); verify(mDeleteIntent, times(1)).send(); } @Test public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); - mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); + mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); verify(mDeleteIntent, times(2)).send(); } @Test public void testRemoveBubble_noLongerBubbleAfterUpdate() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); mRow.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE; @@ -766,7 +787,7 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testRemoveBubble_succeeds_appCancel() { mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); @@ -780,7 +801,7 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testRemoveBubble_entryListenerRemove() { mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); @@ -792,11 +813,11 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void removeBubble_clearAllIntercepted() { mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL_ALL); @@ -805,17 +826,17 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(intercepted); // Should update show in shade state assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); } @Test public void removeBubble_userDismissNotifIntercepted() { mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL); @@ -824,21 +845,21 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(intercepted); // Should update show in shade state assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); } @Test public void removeNotif_inOverflow_intercepted() { // Get bubble with notif in shade. mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dismiss the bubble into overflow. mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); assertFalse(mBubbleController.hasBubbles()); boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( @@ -852,14 +873,14 @@ public class BubbleControllerTest extends SysuiTestCase { public void removeNotif_notInOverflow_notIntercepted() { // Get bubble with notif in shade. mEntryListener.onPendingEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_NO_LONGER_BUBBLE); + mRow.getEntry().getKey(), Bubbles.DISMISS_NO_LONGER_BUBBLE); assertFalse(mBubbleController.hasBubbles()); boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested( @@ -872,11 +893,11 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testOverflowBubble_maxReached_notInShade_bubbleRemoved() { mBubbleController.updateBubble( - mRow.getEntry(), /* suppressFlyout */ false, /* showInShade */ false); + mBubbleEntry, /* suppressFlyout */ false, /* showInShade */ false); mBubbleController.updateBubble( - mRow2.getEntry(), /* suppressFlyout */ false, /* showInShade */ false); + mBubbleEntry2, /* suppressFlyout */ false, /* showInShade */ false); mBubbleController.updateBubble( - mRow3.getEntry(), /* suppressFlyout */ false, /* showInShade */ false); + mBubbleEntry3, /* suppressFlyout */ false, /* showInShade */ false); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey())) .thenReturn(mRow.getEntry()); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey())) @@ -887,12 +908,12 @@ public class BubbleControllerTest extends SysuiTestCase { mBubbleData.setMaxOverflowBubbles(1); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); assertEquals(mBubbleData.getBubbles().size(), 2); assertEquals(mBubbleData.getOverflowBubbles().size(), 1); mBubbleController.removeBubble( - mRow2.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow2.getEntry().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.getEntry().getSbn()), any(), eq(REASON_CANCEL)); @@ -902,22 +923,22 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testNotifyShadeSuppressionChange_notificationDismiss() { - BubbleController.NotificationSuppressionChangedListener listener = - mock(BubbleController.NotificationSuppressionChangedListener.class); + Bubbles.NotificationSuppressionChangedListener listener = + mock(Bubbles.NotificationSuppressionChangedListener.class); mBubbleData.setSuppressionChangedListener(listener); mEntryListener.onPendingEntryAdded(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mRemoveInterceptor.onNotificationRemoveRequested( mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL); // Should update show in shade state assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Should notify delegate that shade state changed verify(listener).onBubbleNotificationSuppressionChange( @@ -926,21 +947,21 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void testNotifyShadeSuppressionChange_bubbleExpanded() { - BubbleController.NotificationSuppressionChangedListener listener = - mock(BubbleController.NotificationSuppressionChangedListener.class); + Bubbles.NotificationSuppressionChangedListener listener = + mock(Bubbles.NotificationSuppressionChangedListener.class); mBubbleData.setSuppressionChangedListener(listener); mEntryListener.onPendingEntryAdded(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mBubbleData.setExpanded(true); // Once a bubble is expanded the notif is suppressed assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Should notify delegate that shade state changed verify(listener).onBubbleNotificationSuppressionChange( @@ -959,11 +980,11 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); // WHEN the summary is dismissed - mBubbleController.handleDismissalInterception(groupSummary.getEntry()); + mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // THEN the summary and bubbled child are suppressed from the shade assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupedBubble.getEntry())); + groupedBubble.getEntry().getKey(), groupSummary.getEntry().getSbn().getGroupKey())); assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey())); } @@ -979,7 +1000,7 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); // GIVEN the summary is dismissed - mBubbleController.handleDismissalInterception(groupSummary.getEntry()); + mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // WHEN the summary is cancelled by the app mEntryListener.onEntryRemoved(groupSummary.getEntry(), null, false, REASON_APP_CANCEL); @@ -1002,7 +1023,7 @@ public class BubbleControllerTest extends SysuiTestCase { groupSummary.addChildNotification(groupedBubble); // WHEN the summary is dismissed - mBubbleController.handleDismissalInterception(groupSummary.getEntry()); + mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // THEN only the NON-bubble children are dismissed List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren(); @@ -1017,7 +1038,8 @@ public class BubbleControllerTest extends SysuiTestCase { // THEN the bubble child is suppressed from the shade assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupedBubble.getEntry())); + groupedBubble.getEntry().getKey(), + groupedBubble.getEntry().getSbn().getGroupKey())); // THEN the summary is removed from GroupManager verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry()); @@ -1031,18 +1053,18 @@ public class BubbleControllerTest extends SysuiTestCase { @Test public void test_notVisuallyInterruptive_updateOverflowBubble_notAdded() { // Setup - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); assertTrue(mBubbleController.hasBubbles()); // Overflow it mBubbleData.dismissBubbleWithKey(mRow.getEntry().getKey(), - BubbleController.DISMISS_USER_GESTURE); + Bubbles.DISMISS_USER_GESTURE); assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse(); assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey())).isTrue(); // Test - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java index b9394ff3f26a..99c8ca417778 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.bubbles; +package com.android.systemui.wmshell; import static android.app.Notification.FLAG_BUBBLE; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; @@ -46,7 +46,6 @@ import android.content.res.Configuration; import android.graphics.Insets; import android.graphics.Rect; import android.hardware.display.AmbientDisplayConfiguration; -import android.hardware.face.FaceManager; import android.os.Handler; import android.os.PowerManager; import android.service.dreams.IDreamManager; @@ -60,8 +59,14 @@ import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.statusbar.IStatusBarService; -import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; +import com.android.systemui.bubbles.BubbleData; +import com.android.systemui.bubbles.BubbleDataRepository; +import com.android.systemui.bubbles.BubbleEntry; +import com.android.systemui.bubbles.BubbleLogger; +import com.android.systemui.bubbles.BubblePositioner; +import com.android.systemui.bubbles.BubbleStackView; +import com.android.systemui.bubbles.Bubbles; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -69,9 +74,7 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.RankingBuilder; -import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; @@ -81,10 +84,8 @@ import com.android.systemui.statusbar.notification.collection.legacy.Notificatio import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; -import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; import com.android.systemui.statusbar.phone.NotificationShadeWindowView; import com.android.systemui.statusbar.phone.ShadeController; @@ -92,7 +93,6 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.systemui.util.InjectionInflationController; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -111,18 +111,18 @@ import java.util.List; /** * Tests the NotifPipeline setup with BubbleController. * The NotificationEntryManager setup with BubbleController is tested in - * {@link BubbleControllerTest}. + * {@link BubblesTest}. */ @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) -public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { +public class NewNotifPipelineBubblesTest extends SysuiTestCase { @Mock private NotificationEntryManager mNotificationEntryManager; @Mock private NotificationGroupManagerLegacy mNotificationGroupManager; @Mock - private BubbleController.NotifCallback mNotifCallback; + private BubblesManager.NotifCallback mNotifCallback; @Mock private WindowManager mWindowManager; @Mock @@ -136,8 +136,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Mock private ZenModeConfig mZenModeConfig; @Mock - private FaceManager mFaceManager; - @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private SysuiStatusBarStateController mStatusBarStateController; @@ -156,6 +154,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor; + private BubblesManager mBubblesManager; private TestableBubbleController mBubbleController; private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; private NotifCollectionListener mEntryListener; @@ -163,8 +162,10 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { private ExpandableNotificationRow mRow; private ExpandableNotificationRow mRow2; private ExpandableNotificationRow mNonBubbleNotifRow; + private BubbleEntry mBubbleEntry; + private BubbleEntry mBubbleEntry2; @Mock - private BubbleController.BubbleExpandListener mBubbleExpandListener; + private Bubbles.BubbleExpandListener mBubbleExpandListener; @Mock private PendingIntent mDeleteIntent; @Mock @@ -174,16 +175,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Mock private ShadeController mShadeController; @Mock - private NotificationShelfComponent mNotificationShelfComponent; - @Mock private NotifPipeline mNotifPipeline; @Mock private FeatureFlags mFeatureFlagsNewPipeline; @Mock private DumpManager mDumpManager; @Mock - private LockscreenLockIconController mLockIconController; - @Mock private IStatusBarService mStatusBarService; @Mock private LauncherApps mLauncherApps; @@ -197,7 +194,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { private BubbleData mBubbleData; private TestableLooper mTestableLooper; - private SuperStatusBarViewFactory mSuperStatusBarViewFactory; @Before public void setUp() throws Exception { @@ -205,26 +201,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mTestableLooper = TestableLooper.get(this); - mContext.addMockSystemService(FaceManager.class, mFaceManager); when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); - mSuperStatusBarViewFactory = new SuperStatusBarViewFactory(mContext, - new InjectionInflationController(SystemUIFactory.getInstance().getSysUIComponent() - .createViewInstanceCreatorFactory()), - new NotificationShelfComponent.Builder() { - @Override - public NotificationShelfComponent.Builder notificationShelf( - NotificationShelf view) { - return this; - } - - @Override - public NotificationShelfComponent build() { - return mNotificationShelfComponent; - } - }, - mLockIconController); - mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, @@ -240,6 +218,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mRow = mNotificationTestHelper.createBubble(mDeleteIntent); mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent); mNonBubbleNotifRow = mNotificationTestHelper.createRow(); + mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow.getEntry()); + mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2.getEntry()); mZenModeConfig.suppressedVisualEffects = 0; when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); @@ -265,23 +245,9 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true); mBubbleController = new TestableBubbleController( mContext, - mNotificationShadeWindowController, - mStatusBarStateController, - mShadeController, mBubbleData, - mConfigurationController, - interruptionStateProvider, - mZenModeController, - mLockscreenUserManager, - mNotificationGroupManager, - mNotificationEntryManager, - mNotifPipeline, - mFeatureFlagsNewPipeline, - mDumpManager, mFloatingContentCoordinator, mDataRepository, - mSysUiState, - mock(INotificationManager.class), mStatusBarService, mWindowManager, mWindowManagerShellWrapper, @@ -290,9 +256,28 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mock(Handler.class), mock(ShellTaskOrganizer.class), mPositioner); - mBubbleController.addNotifCallback(mNotifCallback); mBubbleController.setExpandListener(mBubbleExpandListener); + mBubblesManager = new BubblesManager( + mContext, + mBubbleController, + mNotificationShadeWindowController, + mStatusBarStateController, + mShadeController, + mConfigurationController, + mStatusBarService, + mock(INotificationManager.class), + interruptionStateProvider, + mZenModeController, + mLockscreenUserManager, + mNotificationGroupManager, + mNotificationEntryManager, + mNotifPipeline, + mSysUiState, + mFeatureFlagsNewPipeline, + mDumpManager); + mBubblesManager.addNotifCallback(mNotifCallback); + // Get a reference to the BubbleController's entry listener verify(mNotifPipeline, atLeastOnce()) .addCollectionListener(mNotifListenerCaptor.capture()); @@ -301,26 +286,26 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testAddBubble() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); } @Test public void testHasBubbles() { assertFalse(mBubbleController.hasBubbles()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); } @Test public void testRemoveBubble() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); assertTrue(mBubbleController.hasBubbles()); verify(mNotifCallback, times(1)).invalidateNotifications(anyString()); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); verify(mNotifCallback, times(2)).invalidateNotifications(anyString()); } @@ -328,17 +313,18 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testRemoveBubble_withDismissedNotif_inOverflow() { mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry())); + assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Make it look like dismissed notif mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true); // Now remove the bubble mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey())); // We don't remove the notification since the bubble is still in overflow. @@ -349,19 +335,20 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testRemoveBubble_withDismissedNotif_notInOverflow() { mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey())) .thenReturn(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry())); + assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Make it look like dismissed notif mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true); // Now remove the bubble mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL); + mRow.getEntry().getKey(), Bubbles.DISMISS_NOTIF_CANCEL); assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey())); // Since the notif is dismissed and not in overflow, once the bubble is removed, @@ -373,15 +360,15 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testDismissStack() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); verify(mNotifCallback, times(1)).invalidateNotifications(anyString()); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry2); verify(mNotifCallback, times(2)).invalidateNotifications(anyString()); assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey())); assertTrue(mBubbleController.hasBubbles()); - mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); verify(mNotifCallback, times(3)).invalidateNotifications(anyString()); assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey())); @@ -393,12 +380,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Expand the stack BubbleStackView stackView = mBubbleController.getStackView(); @@ -407,7 +394,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); // Make sure the notif is suppressed - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry())); + assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Collapse mBubbleController.collapseStack(); @@ -421,15 +409,15 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onEntryAdded(mRow.getEntry()); mEntryListener.onEntryAdded(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow2.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Expand BubbleStackView stackView = mBubbleController.getStackView(); @@ -440,15 +428,17 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Last added is the one that is expanded assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey()); - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry())); + assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( + mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); // Switch which bubble is expanded - mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); + mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey( + mRow.getEntry().getKey())); mBubbleData.setExpanded(true); assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // collapse for previous bubble verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( @@ -467,11 +457,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { public void testExpansionRemovesShowInShadeAndDot() { // Mark it as a bubble and add it explicitly mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); - assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry())); + assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -483,7 +474,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); } @@ -492,12 +483,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() { // Mark it as a bubble and add it explicitly mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // We should have bubbles & their notifs should not be suppressed assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -509,7 +500,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -519,7 +510,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Nothing should have changed // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Notif shouldn't show dot after expansion assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); } @@ -529,8 +520,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Mark it as a bubble and add it explicitly mEntryListener.onEntryAdded(mRow.getEntry()); mEntryListener.onEntryAdded(mRow2.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); // Expand BubbleStackView stackView = mBubbleController.getStackView(); @@ -543,13 +534,13 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow2.getEntry())); + mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey())); // Dismiss currently expanded mBubbleController.removeBubble( mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey(), - BubbleController.DISMISS_USER_GESTURE); + Bubbles.DISMISS_USER_GESTURE); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey()); // Make sure first bubble is selected @@ -561,7 +552,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mBubbleController.removeBubble( mBubbleData.getBubbleInStackWithKey( stackView.getExpandedBubble().getKey()).getKey(), - BubbleController.DISMISS_USER_GESTURE); + Bubbles.DISMISS_USER_GESTURE); // Make sure state changes and collapse happens verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey()); @@ -576,7 +567,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Add the auto expand bubble mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Expansion shouldn't change verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */, @@ -591,7 +582,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Add the auto expand bubble mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Expansion should change verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */, @@ -606,11 +597,11 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Add the suppress notif bubble mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Notif should be suppressed because we were foreground assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout()); @@ -618,22 +609,22 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testSuppressNotif_onUpdateNotif() { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Should not be suppressed assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Should show dot assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); // Update to suppress notif setMetadataFlags(mRow.getEntry(), Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); // Notif should be suppressed assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout()); @@ -643,7 +634,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { public void testMarkNewNotificationAsShowInShade() { mEntryListener.onEntryAdded(mRow.getEntry()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mTestableLooper.processAllMessages(); assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot()); @@ -659,31 +650,31 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_AGED); verify(mDeleteIntent, never()).send(); } @Test public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); verify(mDeleteIntent, times(1)).send(); } @Test public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); - mBubbleController.updateBubble(mRow2.getEntry()); - mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleController.updateBubble(mBubbleEntry2); + mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE); verify(mDeleteIntent, times(2)).send(); } @Test public void testRemoveBubble_noLongerBubbleAfterUpdate() throws PendingIntent.CanceledException { - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); mRow.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE; @@ -699,7 +690,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testRemoveBubble_entryListenerRemove() { mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); @@ -711,36 +702,36 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void removeBubble_intercepted() { mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); - boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry()); + boolean intercepted = mBubblesManager.handleDismissalInterception(mRow.getEntry()); // Intercept! assertTrue(intercepted); // Should update show in shade state - assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry())); + assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); } @Test public void removeBubble_dismissIntoOverflow_intercepted() { mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dismiss the bubble - mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); + mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE); assertFalse(mBubbleController.hasBubbles()); // Dismiss the notification - boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry()); + boolean intercepted = mBubblesManager.handleDismissalInterception(mRow.getEntry()); // Intercept dismissal since bubble is going into overflow assertTrue(intercepted); @@ -749,19 +740,18 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void removeBubble_notIntercepted() { mEntryListener.onEntryAdded(mRow.getEntry()); - mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Dismiss the bubble - mBubbleController.removeBubble( - mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL); + mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_NOTIF_CANCEL); assertFalse(mBubbleController.hasBubbles()); // Dismiss the notification - boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry()); + boolean intercepted = mBubblesManager.handleDismissalInterception(mRow.getEntry()); // Not a bubble anymore so we don't intercept dismissal. assertFalse(intercepted); @@ -769,21 +759,21 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testNotifyShadeSuppressionChange_notificationDismiss() { - BubbleController.NotificationSuppressionChangedListener listener = - mock(BubbleController.NotificationSuppressionChangedListener.class); + Bubbles.NotificationSuppressionChangedListener listener = + mock(Bubbles.NotificationSuppressionChangedListener.class); mBubbleData.setSuppressionChangedListener(listener); mEntryListener.onEntryAdded(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); - mBubbleController.handleDismissalInterception(mRow.getEntry()); + mBubblesManager.handleDismissalInterception(mRow.getEntry()); // Should update show in shade state assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Should notify delegate that shade state changed verify(listener).onBubbleNotificationSuppressionChange( @@ -792,21 +782,21 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Test public void testNotifyShadeSuppressionChange_bubbleExpanded() { - BubbleController.NotificationSuppressionChangedListener listener = - mock(BubbleController.NotificationSuppressionChangedListener.class); + Bubbles.NotificationSuppressionChangedListener listener = + mock(Bubbles.NotificationSuppressionChangedListener.class); mBubbleData.setSuppressionChangedListener(listener); mEntryListener.onEntryAdded(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); mBubbleData.setExpanded(true); // Once a bubble is expanded the notif is suppressed assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - mRow.getEntry())); + mBubbleEntry.getKey(), mBubbleEntry.getGroupKey())); // Should notify delegate that shade state changed verify(listener).onBubbleNotificationSuppressionChange( @@ -825,11 +815,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); // WHEN the summary is dismissed - mBubbleController.handleDismissalInterception(groupSummary.getEntry()); + mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // THEN the summary and bubbled child are suppressed from the shade assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupedBubble.getEntry())); + groupedBubble.getEntry().getKey(), + groupedBubble.getEntry().getSbn().getGroupKey())); assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey())); } @@ -845,7 +836,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); // GIVEN the summary is dismissed - mBubbleController.handleDismissalInterception(groupSummary.getEntry()); + mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // WHEN the summary is cancelled by the app mEntryListener.onEntryRemoved(groupSummary.getEntry(), 0); @@ -868,7 +859,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { groupSummary.addChildNotification(groupedBubble); // WHEN the summary is dismissed - mBubbleController.handleDismissalInterception(groupSummary.getEntry()); + mBubblesManager.handleDismissalInterception(groupSummary.getEntry()); // THEN only the NON-bubble children are dismissed List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren(); @@ -882,11 +873,13 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // THEN the bubble child still exists as a bubble and is suppressed from the shade assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupedBubble.getEntry())); + groupedBubble.getEntry().getKey(), + groupedBubble.getEntry().getSbn().getGroupKey())); // THEN the summary is also suppressed from the shade assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( - groupSummary.getEntry())); + groupSummary.getEntry().getKey(), + groupSummary.getEntry().getSbn().getGroupKey())); } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java new file mode 100644 index 000000000000..2273bc48ce8b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wmshell; + +import android.content.Context; +import android.content.pm.LauncherApps; +import android.os.Handler; +import android.view.WindowManager; + +import com.android.internal.statusbar.IStatusBarService; +import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.bubbles.BubbleData; +import com.android.systemui.bubbles.BubbleDataRepository; +import com.android.systemui.bubbles.BubbleLogger; +import com.android.systemui.bubbles.BubblePositioner; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.WindowManagerShellWrapper; +import com.android.wm.shell.common.FloatingContentCoordinator; + +/** + * Testable BubbleController subclass that immediately synchronizes surfaces. + */ +public class TestableBubbleController extends BubbleController { + + // Let's assume surfaces can be synchronized immediately. + TestableBubbleController(Context context, + BubbleData data, + FloatingContentCoordinator floatingContentCoordinator, + BubbleDataRepository dataRepository, + IStatusBarService statusBarService, + WindowManager windowManager, + WindowManagerShellWrapper windowManagerShellWrapper, + LauncherApps launcherApps, + BubbleLogger bubbleLogger, + Handler mainHandler, + ShellTaskOrganizer shellTaskOrganizer, + BubblePositioner positioner) { + super(context, data, Runnable::run, floatingContentCoordinator, dataRepository, + statusBarService, windowManager, windowManagerShellWrapper, launcherApps, + bubbleLogger, mainHandler, shellTaskOrganizer, positioner); + setInflateSynchronously(true); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java index 17dc76b38a56..7847c57dbc32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.bubbles; +package com.android.systemui.wmshell; import android.content.ContentResolver; import android.hardware.display.AmbientDisplayConfiguration; |