diff options
| author | 2023-06-13 16:38:57 -0400 | |
|---|---|---|
| committer | 2023-06-20 10:21:33 -0400 | |
| commit | 815722a7e5b10fc02d4c725518111ba6180578ea (patch) | |
| tree | a331f809355af622b97934cb97df7da093e5c7ed | |
| parent | 4b3863dfe74af6ec32515706fea7f2d4a7a27ddd (diff) | |
Support switching navigation modes.
Switching navigation modes in Settings updates how bubbles are displayed.
Existing bubbles are re-added to the appropriate view.
Bug: 269670598
Test: In Settings go to Display and Navigation mode and switch between
Gesture and 3-button modes after creating bubbles.
Change-Id: I7562040ce907907325a00bac041b2eb3923d4848
12 files changed, 406 insertions, 107 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 504839fedf06..7e09c989e1b3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -422,6 +422,7 @@ public class Bubble implements BubbleViewProvider { } if (mBubbleBarExpandedView != null) { mBubbleBarExpandedView.cleanUpExpandedState(); + mBubbleBarExpandedView = null; } if (mIntent != null) { mIntent.unregisterCancelListener(mIntentCancelListener); @@ -549,10 +550,10 @@ public class Bubble implements BubbleViewProvider { /** * Set visibility of bubble in the expanded state. * - * @param visibility {@code true} if the expanded bubble should be visible on the screen. - * - * Note that this contents visibility doesn't affect visibility at {@link android.view.View}, + * <p>Note that this contents visibility doesn't affect visibility at {@link android.view.View}, * and setting {@code false} actually means rendering the expanded view in transparent. + * + * @param visibility {@code true} if the expanded bubble should be visible on the screen. */ @Override public void setTaskViewVisibility(boolean visibility) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index c48f2fdaf42f..796a98f51981 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -64,7 +64,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.service.notification.NotificationListenerService; @@ -92,6 +91,7 @@ import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; +import com.android.wm.shell.bubbles.properties.BubbleProperties; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -143,16 +143,6 @@ public class BubbleController implements ConfigurationChangeListener, private static final String SYSTEM_DIALOG_REASON_KEY = "reason"; private static final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav"; - // TODO(b/256873975) Should use proper flag when available to shell/launcher - /** - * Whether bubbles are showing in the bubble bar from launcher. This is only available - * on large screens and {@link BubbleController#isShowingAsBubbleBar()} should be used - * to check all conditions that indicate if the bubble bar is in use. - */ - private static final boolean BUBBLE_BAR_ENABLED = - SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false); - - /** * Common interface to send updates to bubble views. */ @@ -195,6 +185,7 @@ public class BubbleController implements ConfigurationChangeListener, private final ShellController mShellController; private final ShellCommandHandler mShellCommandHandler; private final IWindowManager mWmService; + private final BubbleProperties mBubbleProperties; // Used to post to main UI thread private final ShellExecutor mMainExecutor; @@ -291,7 +282,8 @@ public class BubbleController implements ConfigurationChangeListener, @ShellBackgroundThread ShellExecutor bgExecutor, TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue, - IWindowManager wmService) { + IWindowManager wmService, + BubbleProperties bubbleProperties) { mContext = context; mShellCommandHandler = shellCommandHandler; mShellController = shellController; @@ -328,6 +320,7 @@ public class BubbleController implements ConfigurationChangeListener, mDragAndDropController = dragAndDropController; mSyncQueue = syncQueue; mWmService = wmService; + mBubbleProperties = bubbleProperties; shellInit.addInitCallback(this::onInit, this); } @@ -518,11 +511,14 @@ public class BubbleController implements ConfigurationChangeListener, /** * Sets a listener to be notified of bubble updates. This is used by launcher so that * it may render bubbles in itself. Only one listener is supported. + * + * <p>If bubble bar is supported, bubble views will be updated to switch to bar mode. */ public void registerBubbleStateListener(Bubbles.BubbleStateListener listener) { - if (isShowingAsBubbleBar()) { - // Only set the listener if bubble bar is showing. + if (canShowAsBubbleBar() && listener != null) { + // Only set the listener if we can show the bubble bar. mBubbleStateListener = listener; + setUpBubbleViewsForMode(); sendInitialListenerUpdate(); } else { mBubbleStateListener = null; @@ -531,9 +527,15 @@ public class BubbleController implements ConfigurationChangeListener, /** * Unregisters the {@link Bubbles.BubbleStateListener}. + * + * <p>If there's an existing listener, then we're switching back to stack mode and bubble views + * will be updated accordingly. */ public void unregisterBubbleStateListener() { - mBubbleStateListener = null; + if (mBubbleStateListener != null) { + mBubbleStateListener = null; + setUpBubbleViewsForMode(); + } } /** @@ -645,8 +647,12 @@ public class BubbleController implements ConfigurationChangeListener, /** Whether bubbles are showing in the bubble bar. */ public boolean isShowingAsBubbleBar() { - // TODO(b/269670598): should also check that we're in gesture nav - return BUBBLE_BAR_ENABLED && mBubblePositioner.isLargeScreen(); + return canShowAsBubbleBar() && mBubbleStateListener != null; + } + + /** Whether the current configuration supports showing as bubble bar. */ + private boolean canShowAsBubbleBar() { + return mBubbleProperties.isBubbleBarEnabled() && mBubblePositioner.isLargeScreen(); } /** Whether this userId belongs to the current user. */ @@ -779,7 +785,7 @@ public class BubbleController implements ConfigurationChangeListener, if (isShowingAsBubbleBar()) { mWindowManager.addView(mLayerView, mWmLayoutParams); mLayerView.setOnApplyWindowInsetsListener((view, windowInsets) -> { - if (!windowInsets.equals(mWindowInsets)) { + if (!windowInsets.equals(mWindowInsets) && mLayerView != null) { mWindowInsets = windowInsets; mBubblePositioner.update(); mLayerView.onDisplaySizeChanged(); @@ -789,7 +795,7 @@ public class BubbleController implements ConfigurationChangeListener, } else { mWindowManager.addView(mStackView, mWmLayoutParams); mStackView.setOnApplyWindowInsetsListener((view, windowInsets) -> { - if (!windowInsets.equals(mWindowInsets)) { + if (!windowInsets.equals(mWindowInsets) && mStackView != null) { mWindowInsets = windowInsets; mBubblePositioner.update(); mStackView.onDisplaySizeChanged(); @@ -1066,9 +1072,11 @@ public class BubbleController implements ConfigurationChangeListener, * Expands and selects the provided bubble as long as it already exists in the stack or the * overflow. * - * This is used by external callers (launcher). + * <p>This is used by external callers (launcher). */ - public void expandStackAndSelectBubbleFromLauncher(String key) { + @VisibleForTesting + public void expandStackAndSelectBubbleFromLauncher(String key, boolean onLauncherHome) { + mBubblePositioner.setShowingInBubbleBar(onLauncherHome); Bubble b = mBubbleData.getAnyBubbleWithkey(key); if (b == null) { return; @@ -1286,6 +1294,50 @@ public class BubbleController implements ConfigurationChangeListener, }); } + void setUpBubbleViewsForMode() { + mBubbleViewCallback = isShowingAsBubbleBar() + ? mBubbleBarViewCallback + : mBubbleStackViewCallback; + + // reset the overflow so that it can be re-added later if needed. + if (mStackView != null) { + mStackView.resetOverflowView(); + mStackView.removeAllViews(); + } + // cleanup existing bubble views so they can be recreated later if needed. + mBubbleData.getBubbles().forEach(Bubble::cleanupViews); + + // remove the current bubble container from window manager, null it out, and create a new + // container based on the current mode. + removeFromWindowManagerMaybe(); + mLayerView = null; + mStackView = null; + ensureBubbleViewsAndWindowCreated(); + + // inflate bubble views + BubbleViewInfoTask.Callback callback = null; + if (!isShowingAsBubbleBar()) { + callback = b -> { + if (mStackView != null) { + mStackView.addBubble(b); + mStackView.setSelectedBubble(b); + } else { + Log.w(TAG, "Tried to add a bubble to the stack but the stack is null"); + } + }; + } + for (int i = mBubbleData.getBubbles().size() - 1; i >= 0; i--) { + Bubble bubble = mBubbleData.getBubbles().get(i); + bubble.inflate(callback, + mContext, + this, + mStackView, + mLayerView, + mBubbleIconFactory, + false /* skipInflation */); + } + } + /** * Adds or updates a bubble associated with the provided notification entry. * @@ -1746,7 +1798,7 @@ public class BubbleController implements ConfigurationChangeListener, // Update the cached state for queries from SysUI mImpl.mCachedState.update(update); - if (isShowingAsBubbleBar() && mBubbleStateListener != null) { + if (isShowingAsBubbleBar()) { BubbleBarUpdate bubbleBarUpdate = update.toBubbleBarUpdate(); // Some updates aren't relevant to the bubble bar so check first. if (bubbleBarUpdate.anythingChanged()) { @@ -1868,10 +1920,17 @@ public class BubbleController implements ConfigurationChangeListener, } @VisibleForTesting + @Nullable public BubbleStackView getStackView() { return mStackView; } + @VisibleForTesting + @Nullable + public BubbleBarLayerView getLayerView() { + return mLayerView; + } + /** * Check if notification panel is in an expanded state. * Makes a call to System UI process and delivers the result via {@code callback} on the @@ -2010,22 +2069,18 @@ public class BubbleController implements ConfigurationChangeListener, @Override public void registerBubbleListener(IBubblesListener listener) { - mMainExecutor.execute(() -> { - mListener.register(listener); - }); + mMainExecutor.execute(() -> mListener.register(listener)); } @Override public void unregisterBubbleListener(IBubblesListener listener) { - mMainExecutor.execute(() -> mListener.unregister()); + mMainExecutor.execute(mListener::unregister); } @Override public void showBubble(String key, boolean onLauncherHome) { - mMainExecutor.execute(() -> { - mBubblePositioner.setShowingInBubbleBar(onLauncherHome); - mController.expandStackAndSelectBubbleFromLauncher(key); - }); + mMainExecutor.execute( + () -> mController.expandStackAndSelectBubbleFromLauncher(key, onLauncherHome)); } @Override @@ -2037,11 +2092,6 @@ public class BubbleController implements ConfigurationChangeListener, public void collapseBubbles() { mMainExecutor.execute(() -> mController.collapseStack()); } - - @Override - public void onTaskbarStateChanged(int newState) { - // TODO (b/269670598) - } } private class BubblesImpl implements Bubbles { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 9860b076264b..34c4934d597a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -309,6 +309,7 @@ public class BubbleStackView extends FrameLayout String bubblesOnScreen = BubbleDebugConfig.formatBubblesString( getBubblesOnScreen(), getExpandedBubble()); + pw.print(" stack visibility : "); pw.println(getVisibility()); pw.print(" bubbles on screen: "); pw.println(bubblesOnScreen); pw.print(" gestureInProgress: "); pw.println(mIsGestureInProgress); pw.print(" showingDismiss: "); pw.println(mDismissView.isShowing()); @@ -970,6 +971,8 @@ public class BubbleStackView extends FrameLayout mBubbleContainer.bringToFront(); mBubbleOverflow = mBubbleData.getOverflow(); + + resetOverflowView(); mBubbleContainer.addView(mBubbleOverflow.getIconView(), mBubbleContainer.getChildCount() /* index */, new FrameLayout.LayoutParams(mPositioner.getBubbleSize(), @@ -3414,6 +3417,19 @@ public class BubbleStackView extends FrameLayout } /** + * Removes the overflow view from the stack. This allows for re-adding it later to a new stack. + */ + void resetOverflowView() { + BadgedImageView overflowIcon = mBubbleOverflow.getIconView(); + if (overflowIcon != null) { + PhysicsAnimationLayout parent = (PhysicsAnimationLayout) overflowIcon.getParent(); + if (parent != null) { + parent.removeViewNoAnimation(overflowIcon); + } + } + } + + /** * Holds some commonly queried information about the stack. */ public static class StackViewState { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index 4d329dd5d446..759246eb285d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -60,9 +60,11 @@ public interface Bubbles { 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, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED}) + DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED, + DISMISS_SWITCH_TO_STACK}) @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) - @interface DismissReason {} + @interface DismissReason { + } int DISMISS_USER_GESTURE = 1; int DISMISS_AGED = 2; @@ -80,6 +82,7 @@ public interface Bubbles { int DISMISS_NO_BUBBLE_UP = 14; int DISMISS_RELOAD_FROM_DISK = 15; int DISMISS_USER_REMOVED = 16; + int DISMISS_SWITCH_TO_STACK = 17; /** Returns a binder that can be passed to an external process to manipulate Bubbles. */ default IBubbles createExternalInterface() { @@ -120,8 +123,8 @@ public interface Bubbles { /** * This method has different behavior depending on: - * - if an app bubble exists - * - if an app bubble is expanded + * - if an app bubble exists + * - if an app bubble is expanded * * If no app bubble exists, this will add and expand a bubble with the provided intent. The * intent must be explicit (i.e. include a package name or fully qualified component class name) @@ -135,13 +138,13 @@ public interface Bubbles { * the bubble or bubble stack. * * Some notes: - * - Only one app bubble is supported at a time, regardless of users. Multi-users support is - * tracked in b/273533235. - * - Calling this method with a different intent than the existing app bubble will do nothing + * - Only one app bubble is supported at a time, regardless of users. Multi-users support is + * tracked in b/273533235. + * - Calling this method with a different intent than the existing app bubble will do nothing * * @param intent the intent to display in the bubble expanded view. - * @param user the {@link UserHandle} of the user to start this activity for. - * @param icon the {@link Icon} to use for the bubble view. + * @param user the {@link UserHandle} of the user to start this activity for. + * @param icon the {@link Icon} to use for the bubble view. */ void showOrHideAppBubble(Intent intent, UserHandle user, @Nullable Icon icon); @@ -172,13 +175,12 @@ 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 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(BubbleEntry entry, @Nullable List<BubbleEntry> children, @@ -200,9 +202,9 @@ public interface Bubbles { /** * Called when new notification entry updated. * - * @param entry the {@link BubbleEntry} by the notification. + * @param entry the {@link BubbleEntry} by the notification. * @param shouldBubbleUp {@code true} if this notification should bubble up. - * @param fromSystem {@code true} if this update is from NotificationManagerService. + * @param fromSystem {@code true} if this update is from NotificationManagerService. */ void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem); @@ -218,7 +220,7 @@ public interface Bubbles { * 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 + * @param rankingMap the updated ranking map from NotificationListenerService * @param entryDataByKey a map of ranking key to bubble entry and whether the entry should * bubble up */ @@ -230,9 +232,9 @@ public interface Bubbles { * Called when a notification channel is modified, in response to * {@link NotificationListenerService#onNotificationChannelModified}. * - * @param pkg the package the notification channel belongs to. - * @param user the user the notification channel belongs to. - * @param channel the channel being modified. + * @param pkg the package the notification channel belongs to. + * @param user the user the notification channel belongs to. + * @param channel the channel being modified. * @param modificationType the type of modification that occurred to the channel. */ void onNotificationChannelModified( @@ -300,7 +302,7 @@ public interface Bubbles { * 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 + * @param key the notification key associated with bubble being expanded */ void onBubbleExpandChanged(boolean isExpanding, String key); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl index 862e818a998b..20ae8469f431 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl @@ -35,6 +35,4 @@ interface IBubbles { oneway void collapseBubbles() = 5; - oneway void onTaskbarStateChanged(in int newState) = 6; - }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java index 55052e614458..3eb9a9112047 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java @@ -327,6 +327,12 @@ public class PhysicsAnimationLayout extends FrameLayout { addViewInternal(child, index, params, false /* isReorder */); } + /** Removes the child view immediately. */ + public void removeViewNoAnimation(View view) { + super.removeView(view); + view.setTag(R.id.physics_animator_tag, null); + } + @Override public void removeView(View view) { if (mController != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt new file mode 100644 index 000000000000..85aaa8ef585c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 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.wm.shell.bubbles.properties + +/** + * An interface for exposing bubble properties via flags which can be controlled easily in tests. + */ +interface BubbleProperties { + /** + * Whether bubble bar is enabled. + * + * When this is `true`, depending on additional factors, such as screen size and taskbar state, + * bubbles will be displayed in the bubble bar instead of floating. + * + * When this is `false`, bubbles will be floating. + */ + val isBubbleBarEnabled: Boolean +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt new file mode 100644 index 000000000000..9d8b9a6f3260 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 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.wm.shell.bubbles.properties + +import android.os.SystemProperties + +/** Provides bubble properties in production. */ +object ProdBubbleProperties : BubbleProperties { + + // TODO(b/256873975) Should use proper flag when available to shell/launcher + override val isBubbleBarEnabled = + SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false) +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 99959aeb0e8c..03f92aaf9ce9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -36,6 +36,7 @@ import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; import com.android.wm.shell.bubbles.BubbleLogger; import com.android.wm.shell.bubbles.BubblePositioner; +import com.android.wm.shell.bubbles.properties.ProdBubbleProperties; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; @@ -187,7 +188,7 @@ public abstract class WMShellModule { statusBarService, windowManager, windowManagerShellWrapper, userManager, launcherApps, logger, taskStackListener, organizer, positioner, displayController, oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor, - taskViewTransitions, syncQueue, wmService); + taskViewTransitions, syncQueue, wmService, ProdBubbleProperties.INSTANCE); } // @@ -219,12 +220,12 @@ public abstract class WMShellModule { desktopTasksController); } return new CaptionWindowDecorViewModel( - context, - mainHandler, - mainChoreographer, - taskOrganizer, - displayController, - syncQueue); + context, + mainHandler, + mainChoreographer, + taskOrganizer, + displayController, + syncQueue); } // @@ -584,13 +585,13 @@ public abstract class WMShellModule { animators.add(fullscreenAnimator); return new UnfoldAnimationController( - shellInit, - transactionPool, - progressProvider.get(), - animators, - unfoldTransitionHandler, - mainExecutor - ); + shellInit, + transactionPool, + progressProvider.get(), + animators, + unfoldTransitionHandler, + mainExecutor + ); } @Provides diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 5ca936222785..94689ebe8742 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -137,11 +137,13 @@ import com.android.wm.shell.bubbles.BubbleStackView; import com.android.wm.shell.bubbles.BubbleViewInfoTask; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.bubbles.StackEducationViewKt; +import com.android.wm.shell.bubbles.properties.BubbleProperties; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TaskStackListenerImpl; +import com.android.wm.shell.common.bubbles.BubbleBarUpdate; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.sysui.ShellCommandHandler; @@ -299,6 +301,8 @@ public class BubblesTest extends SysuiTestCase { private UserHandle mUser0; + private FakeBubbleProperties mBubbleProperties; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -360,6 +364,7 @@ public class BubblesTest extends SysuiTestCase { mock(UserTracker.class) ); when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor); + mBubbleProperties = new FakeBubbleProperties(); mBubbleController = new TestableBubbleController( mContext, mShellInit, @@ -384,7 +389,8 @@ public class BubblesTest extends SysuiTestCase { mock(Handler.class), mTaskViewTransitions, mock(SyncTransactionQueue.class), - mock(IWindowManager.class)); + mock(IWindowManager.class), + mBubbleProperties); mBubbleController.setExpandListener(mBubbleExpandListener); spyOn(mBubbleController); @@ -475,7 +481,7 @@ public class BubblesTest extends SysuiTestCase { public void testAddBubble() { mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -483,7 +489,7 @@ public class BubblesTest extends SysuiTestCase { assertFalse(mBubbleController.hasBubbles()); mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -498,7 +504,7 @@ public class BubblesTest extends SysuiTestCase { assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); verify(mNotifCallback, times(2)).invalidateNotifications(anyString()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -561,7 +567,7 @@ public class BubblesTest extends SysuiTestCase { assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey())); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -580,7 +586,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleData.setExpanded(true); assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Make sure the notif is suppressed assertBubbleNotificationSuppressedFromShade(mBubbleEntry); @@ -589,7 +595,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleController.collapseStack(); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey()); assertStackCollapsed(); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -611,7 +617,7 @@ public class BubblesTest extends SysuiTestCase { assertStackExpanded(); verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( true, mRow2.getKey()); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Last added is the one that is expanded assertEquals(mRow2.getKey(), mBubbleData.getSelectedBubble().getKey()); @@ -636,7 +642,7 @@ public class BubblesTest extends SysuiTestCase { // Collapse mBubbleController.collapseStack(); assertStackCollapsed(); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -656,7 +662,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleData.setExpanded(true); assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Notif is suppressed after expansion assertBubbleNotificationSuppressedFromShade(mBubbleEntry); @@ -681,7 +687,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleData.setExpanded(true); assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Notif is suppressed after expansion assertBubbleNotificationSuppressedFromShade(mBubbleEntry); @@ -710,7 +716,7 @@ public class BubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getKey()); @@ -741,7 +747,7 @@ public class BubblesTest extends SysuiTestCase { // We should be collapsed verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey()); assertFalse(mBubbleController.hasBubbles()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -754,7 +760,7 @@ public class BubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); @@ -769,7 +775,7 @@ public class BubblesTest extends SysuiTestCase { // We should be collapsed verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey()); assertFalse(mBubbleController.hasBubbles()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @@ -787,7 +793,7 @@ public class BubblesTest extends SysuiTestCase { verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */, mRow.getKey()); assertStackCollapsed(); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -803,7 +809,7 @@ public class BubblesTest extends SysuiTestCase { verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */, mRow.getKey()); assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -820,7 +826,7 @@ public class BubblesTest extends SysuiTestCase { // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -842,7 +848,7 @@ public class BubblesTest extends SysuiTestCase { // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -1247,11 +1253,11 @@ public class BubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Show the menu stackView.showManageMenu(true); - assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, true /* manageMenuExpanded */); assertTrue(stackView.isManageMenuSettingsVisible()); assertTrue(stackView.isManageMenuDontBubbleVisible()); } @@ -1265,11 +1271,11 @@ public class BubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Show the menu stackView.showManageMenu(true); - assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, true /* manageMenuExpanded */); assertFalse(stackView.isManageMenuSettingsVisible()); assertFalse(stackView.isManageMenuDontBubbleVisible()); } @@ -1283,15 +1289,15 @@ public class BubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Show the menu stackView.showManageMenu(true); - assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, true /* manageMenuExpanded */); // Hide the menu stackView.showManageMenu(false); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -1303,16 +1309,16 @@ public class BubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Show the menu stackView.showManageMenu(true); - assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, true /* manageMenuExpanded */); // Collapse the stack mBubbleData.setExpanded(false); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -1574,7 +1580,7 @@ public class BubblesTest extends SysuiTestCase { NotificationListenerService.RankingMap rankingMap = mock(NotificationListenerService.RankingMap.class); - when(rankingMap.getOrderedKeys()).thenReturn(new String[] { mBubbleEntry.getKey() }); + when(rankingMap.getOrderedKeys()).thenReturn(new String[]{mBubbleEntry.getKey()}); mBubbleController.onRankingUpdated(rankingMap, entryDataByKey); // Should no longer be in the stack @@ -1882,7 +1888,108 @@ public class BubblesTest extends SysuiTestCase { ); } - /** Creates a bubble using the userId and package. */ + @Test + public void registerBubbleBarListener_barDisabled_largeScreen_shouldBeIgnored() { + mBubbleProperties.mIsBubbleBarEnabled = false; + mPositioner.setIsLargeScreen(true); + mEntryListener.onEntryAdded(mRow); + mBubbleController.updateBubble(mBubbleEntry); + assertTrue(mBubbleController.hasBubbles()); + + assertStackMode(); + + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + assertStackMode(); + + assertThat(mBubbleController.getStackView().getBubbleCount()).isEqualTo(1); + } + + @Test + public void registerBubbleBarListener_barEnabled_smallScreen_shouldBeIgnored() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(false); + mEntryListener.onEntryAdded(mRow); + mBubbleController.updateBubble(mBubbleEntry); + assertTrue(mBubbleController.hasBubbles()); + + assertStackMode(); + + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + assertStackMode(); + + assertThat(mBubbleController.getStackView().getBubbleCount()).isEqualTo(1); + } + + @Test + public void registerBubbleBarListener_switchToBarAndBackToStack() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + mEntryListener.onEntryAdded(mRow); + mBubbleController.updateBubble(mBubbleEntry); + assertTrue(mBubbleController.hasBubbles()); + + assertStackMode(); + + assertThat(mBubbleData.getBubbles()).hasSize(1); + assertBubbleIsInflatedForStack(mBubbleData.getBubbles().get(0)); + + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + assertBarMode(); + + assertThat(mBubbleData.getBubbles()).hasSize(1); + assertBubbleIsInflatedForBar(mBubbleData.getBubbles().get(0)); + + mBubbleController.unregisterBubbleStateListener(); + + assertStackMode(); + + assertThat(mBubbleData.getBubbles()).hasSize(1); + assertBubbleIsInflatedForStack(mBubbleData.getBubbles().get(0)); + } + + @Test + public void switchBetweenBarAndStack_noBubbles_shouldBeIgnored() { + mBubbleProperties.mIsBubbleBarEnabled = false; + mPositioner.setIsLargeScreen(true); + assertFalse(mBubbleController.hasBubbles()); + + assertNoBubbleContainerViews(); + + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + assertNoBubbleContainerViews(); + + mBubbleController.unregisterBubbleStateListener(); + + assertNoBubbleContainerViews(); + } + + @Test + public void bubbleBarBubbleExpandedAndCollapsed() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + mEntryListener.onEntryAdded(mRow); + mBubbleController.updateBubble(mBubbleEntry); + + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), true); + + assertThat(mBubbleController.getLayerView().isExpanded()).isTrue(); + + mBubbleController.collapseStack(); + + assertThat(mBubbleController.getLayerView().isExpanded()).isFalse(); + } + + /** Creates a bubble using the userId and package. */ private Bubble createBubble(int userId, String pkg) { final UserHandle userHandle = new UserHandle(userId); NotificationEntry workEntry = new NotificationEntryBuilder() @@ -1998,6 +2105,38 @@ public class BubblesTest extends SysuiTestCase { assertFalse(mBubbleController.getImplCachedState().isStackExpanded()); } + /** Asserts that both the bubble stack and bar views don't exist. */ + private void assertNoBubbleContainerViews() { + assertThat(mBubbleController.getStackView()).isNull(); + assertThat(mBubbleController.getLayerView()).isNull(); + } + + /** Asserts that the stack is created and the bar is null. */ + private void assertStackMode() { + assertThat(mBubbleController.getStackView()).isNotNull(); + assertThat(mBubbleController.getLayerView()).isNull(); + } + + /** Asserts that the given bubble has the stack expanded view inflated. */ + private void assertBubbleIsInflatedForStack(Bubble b) { + assertThat(b.getIconView()).isNotNull(); + assertThat(b.getExpandedView()).isNotNull(); + assertThat(b.getBubbleBarExpandedView()).isNull(); + } + + /** Asserts that the bar is created and the stack is null. */ + private void assertBarMode() { + assertThat(mBubbleController.getStackView()).isNull(); + assertThat(mBubbleController.getLayerView()).isNotNull(); + } + + /** Asserts that the given bubble has the bar expanded view inflated. */ + private void assertBubbleIsInflatedForBar(Bubble b) { + assertThat(b.getIconView()).isNull(); + assertThat(b.getExpandedView()).isNull(); + assertThat(b.getBubbleBarExpandedView()).isNotNull(); + } + /** * Asserts that a bubble notification is suppressed from the shade and also validates the cached * state is updated. @@ -2027,4 +2166,19 @@ public class BubblesTest extends SysuiTestCase { assertThat(mSysUiStateBubblesExpanded).isEqualTo(stackExpanded); assertThat(mSysUiStateBubblesManageMenuExpanded).isEqualTo(manageMenuExpanded); } + + private static class FakeBubbleStateListener implements Bubbles.BubbleStateListener { + @Override + public void onBubbleStateChange(BubbleBarUpdate update) { + } + } + + private static class FakeBubbleProperties implements BubbleProperties { + boolean mIsBubbleBarEnabled = false; + + @Override + public boolean isBubbleBarEnabled() { + return mIsBubbleBarEnabled; + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java index 14c3f3c4ada5..5855347c203b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java @@ -31,6 +31,7 @@ import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; import com.android.wm.shell.bubbles.BubbleLogger; import com.android.wm.shell.bubbles.BubblePositioner; +import com.android.wm.shell.bubbles.properties.BubbleProperties; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; @@ -74,13 +75,14 @@ public class TestableBubbleController extends BubbleController { Handler shellMainHandler, TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue, - IWindowManager wmService) { + IWindowManager wmService, + BubbleProperties bubbleProperties) { super(context, shellInit, shellCommandHandler, shellController, data, Runnable::run, floatingContentCoordinator, dataRepository, statusBarService, windowManager, windowManagerShellWrapper, userManager, launcherApps, bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, displayController, oneHandedOptional, dragAndDropController, shellMainExecutor, shellMainHandler, - new SyncExecutor(), taskViewTransitions, syncQueue, wmService); + new SyncExecutor(), taskViewTransitions, syncQueue, wmService, bubbleProperties); setInflateSynchronously(true); onInit(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java index 6edc373d2926..047dc65c4a6f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java @@ -27,6 +27,7 @@ import com.android.wm.shell.bubbles.BubblePositioner; public class TestableBubblePositioner extends BubblePositioner { private int mMaxBubbles; + private boolean mIsLargeScreen = false; public TestableBubblePositioner(Context context, WindowManager windowManager) { @@ -46,4 +47,13 @@ public class TestableBubblePositioner extends BubblePositioner { public int getMaxBubbles() { return mMaxBubbles; } + + public void setIsLargeScreen(boolean isLargeScreen) { + mIsLargeScreen = isLargeScreen; + } + + @Override + public boolean isLargeScreen() { + return mIsLargeScreen; + } } |