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 bd3aa16e726aa..8f364b448bf2d 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 @@ -48,6 +48,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView; +import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; import com.android.wm.shell.common.bubbles.BubbleInfo; import java.io.PrintWriter; @@ -430,14 +431,16 @@ public 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 the bubble controller. - * @param stackView the stackView the bubble is eventually added to. + * @param stackView the view the bubble is added to, iff showing as floating. + * @param layerView the layer the bubble is added to, iff showing in the bubble bar. * @param iconFactory the icon factory use to create images for the bubble. * @param badgeIconFactory the icon factory to create app badges for the bubble. */ void inflate(BubbleViewInfoTask.Callback callback, Context context, BubbleController controller, - BubbleStackView stackView, + @Nullable BubbleStackView stackView, + @Nullable BubbleBarLayerView layerView, BubbleIconFactory iconFactory, BubbleBadgeIconFactory badgeIconFactory, boolean skipInflation) { @@ -448,6 +451,7 @@ public class Bubble implements BubbleViewProvider { context, controller, stackView, + layerView, iconFactory, badgeIconFactory, skipInflation, @@ -479,6 +483,7 @@ public class Bubble implements BubbleViewProvider { if (!isInflated()) { mIconView = info.imageView; mExpandedView = info.expandedView; + mBubbleBarExpandedView = info.bubbleBarExpandedView; } mShortcutInfo = info.shortcutInfo; @@ -489,7 +494,7 @@ public class Bubble implements BubbleViewProvider { mFlyoutMessage = info.flyoutMessage; mBadgeBitmap = info.badgeBitmap; - mRawBadgeBitmap = info.mRawBadgeBitmap; + mRawBadgeBitmap = info.rawBadgeBitmap; mBubbleBitmap = info.bubbleBitmap; mDotColor = info.dotColor; 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 8040bd72e2a3b..0bb1cabb9b5ef 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 @@ -89,6 +89,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; 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.common.DisplayController; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -201,6 +202,7 @@ public class BubbleController implements ConfigurationChangeListener, private BubbleLogger mLogger; private BubbleData mBubbleData; @Nullable private BubbleStackView mStackView; + @Nullable private BubbleBarLayerView mLayerView; private BubbleIconFactory mBubbleIconFactory; private BubbleBadgeIconFactory mBubbleBadgeIconFactory; private BubblePositioner mBubblePositioner; @@ -546,7 +548,7 @@ public class BubbleController implements ConfigurationChangeListener, } private void openBubbleOverflow() { - ensureStackViewCreated(); + ensureBubbleViewsAndWindowCreated(); mBubbleData.setShowingOverflow(true); mBubbleData.setSelectedBubble(mBubbleData.getOverflow()); mBubbleData.setExpanded(true); @@ -586,7 +588,7 @@ public class BubbleController implements ConfigurationChangeListener, expandStackAndSelectBubble(mNotifEntryToExpandOnShadeUnlock); } - updateStack(); + updateBubbleViews(); } @VisibleForTesting @@ -687,34 +689,54 @@ public class BubbleController implements ConfigurationChangeListener, } /** - * BubbleStackView is lazily created by this method the first time a Bubble is added. This - * method initializes the stack view and adds it to window manager. + * The view and window for bubbles is lazily created by this method the first time a Bubble + * is added. Depending on the device state, this method will: + * - initialize a {@link BubbleStackView} and add it to window manager OR + * - initialize a {@link com.android.wm.shell.bubbles.bar.BubbleBarLayerView} and adds + * it to window manager. */ - private void ensureStackViewCreated() { - if (mStackView == null) { - mStackView = new BubbleStackView( - mContext, this, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator, - mMainExecutor); - mStackView.onOrientationChanged(); - if (mExpandListener != null) { - mStackView.setExpandListener(mExpandListener); + private void ensureBubbleViewsAndWindowCreated() { + mBubblePositioner.setShowingInBubbleBar(isShowingAsBubbleBar()); + if (isShowingAsBubbleBar()) { + // When we're showing in launcher / bubble bar is enabled, we don't have bubble stack + // view, instead we just show the expanded bubble view as necessary. We still need a + // window to show this in, but we use a separate code path. + // TODO(b/273312602): consider foldables where we do need a stack view when folded + if (mLayerView == null) { + mLayerView = new BubbleBarLayerView(mContext, this); + } + } else { + if (mStackView == null) { + mStackView = new BubbleStackView( + mContext, this, mBubbleData, mSurfaceSynchronizer, + mFloatingContentCoordinator, + mMainExecutor); + mStackView.onOrientationChanged(); + if (mExpandListener != null) { + mStackView.setExpandListener(mExpandListener); + } + mStackView.setUnbubbleConversationCallback(mSysuiProxy::onUnbubbleConversation); } - mStackView.setUnbubbleConversationCallback(mSysuiProxy::onUnbubbleConversation); } - addToWindowManagerMaybe(); } - /** Adds the BubbleStackView to the WindowManager if it's not already there. */ + /** Adds the appropriate view to WindowManager if it's not already there. */ private void addToWindowManagerMaybe() { - // If the stack is null, or already added, don't add it. - if (mStackView == null || mAddedToWindowManager) { + // If already added, don't add it. + if (mAddedToWindowManager) { + return; + } + // If the appropriate view is null, don't add it. + if (isShowingAsBubbleBar() && mLayerView == null) { + return; + } else if (!isShowingAsBubbleBar() && mStackView == null) { return; } mWmLayoutParams = new WindowManager.LayoutParams( // Fill the screen so we can use translation animations to position the bubble - // stack. We'll use touchable regions to ignore touches that are not on the bubbles + // views. We'll use touchable regions to ignore touches that are not on the bubbles // themselves. ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, @@ -737,17 +759,30 @@ public class BubbleController implements ConfigurationChangeListener, mAddedToWindowManager = true; registerBroadcastReceiver(); mBubbleData.getOverflow().initialize(this); - mWindowManager.addView(mStackView, mWmLayoutParams); - mStackView.setOnApplyWindowInsetsListener((view, windowInsets) -> { - if (!windowInsets.equals(mWindowInsets)) { - mWindowInsets = windowInsets; - mBubblePositioner.update(); - mStackView.onDisplaySizeChanged(); - } - return windowInsets; - }); + // (TODO: b/273314541) some duplication in the inset listener + if (isShowingAsBubbleBar()) { + mWindowManager.addView(mLayerView, mWmLayoutParams); + mLayerView.setOnApplyWindowInsetsListener((view, windowInsets) -> { + if (!windowInsets.equals(mWindowInsets)) { + mWindowInsets = windowInsets; + mBubblePositioner.update(); + mLayerView.onDisplaySizeChanged(); + } + return windowInsets; + }); + } else { + mWindowManager.addView(mStackView, mWmLayoutParams); + mStackView.setOnApplyWindowInsetsListener((view, windowInsets) -> { + if (!windowInsets.equals(mWindowInsets)) { + mWindowInsets = windowInsets; + mBubblePositioner.update(); + mStackView.onDisplaySizeChanged(); + } + return windowInsets; + }); + } } catch (IllegalStateException e) { - // This means the stack has already been added. This shouldn't happen... + // This means the view has already been added. This shouldn't happen... e.printStackTrace(); } } @@ -770,7 +805,7 @@ public class BubbleController implements ConfigurationChangeListener, } } - /** Removes the BubbleStackView from the WindowManager if it's there. */ + /** Removes any bubble views from the WindowManager that exist. */ private void removeFromWindowManagerMaybe() { if (!mAddedToWindowManager) { return; @@ -791,8 +826,10 @@ public class BubbleController implements ConfigurationChangeListener, if (mStackView != null) { mWindowManager.removeView(mStackView); mBubbleData.getOverflow().cleanUpExpandedState(); - } else { - Log.w(TAG, "StackView added to WindowManager, but was null when removing!"); + } + if (mLayerView != null) { + mWindowManager.removeView(mLayerView); + mBubbleData.getOverflow().cleanUpExpandedState(); } } catch (IllegalArgumentException e) { // This means the stack has already been removed - it shouldn't happen, but ignore if it @@ -887,12 +924,22 @@ public class BubbleController implements ConfigurationChangeListener, // Reload each bubble for (Bubble b : mBubbleData.getBubbles()) { - b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory, + b.inflate(null /* callback */, + mContext, + this, + mStackView, + mLayerView, + mBubbleIconFactory, mBubbleBadgeIconFactory, false /* skipInflation */); } for (Bubble b : mBubbleData.getOverflowBubbles()) { - b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory, + b.inflate(null /* callback */, + mContext, + this, + mStackView, + mLayerView, + mBubbleIconFactory, mBubbleBadgeIconFactory, false /* skipInflation */); } @@ -959,7 +1006,7 @@ public class BubbleController implements ConfigurationChangeListener, */ @VisibleForTesting public boolean hasBubbles() { - if (mStackView == null) { + if (mStackView == null && mLayerView == null) { return false; } return mBubbleData.hasBubbles() || mBubbleData.isShowingOverflow(); @@ -1166,7 +1213,12 @@ public class BubbleController implements ConfigurationChangeListener, } bubble.inflate( (b) -> mBubbleData.overflowBubble(Bubbles.DISMISS_RELOAD_FROM_DISK, bubble), - mContext, this, mStackView, mBubbleIconFactory, mBubbleBadgeIconFactory, + mContext, + this, + mStackView, + mLayerView, + mBubbleIconFactory, + mBubbleBadgeIconFactory, true /* skipInflation */); }); return null; @@ -1238,10 +1290,11 @@ public class BubbleController implements ConfigurationChangeListener, @VisibleForTesting public void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) { // Lazy init stack view when a bubble is created - ensureStackViewCreated(); + ensureBubbleViewsAndWindowCreated(); bubble.setInflateSynchronously(mInflateSynchronously); bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade), - mContext, this, mStackView, mBubbleIconFactory, mBubbleBadgeIconFactory, + mContext, this, mStackView, mLayerView, + mBubbleIconFactory, mBubbleBadgeIconFactory, false /* skipInflation */); } @@ -1475,7 +1528,7 @@ public class BubbleController implements ConfigurationChangeListener, + " unsuppressed=" + (update.unsuppressedBubble != null)); } - ensureStackViewCreated(); + ensureBubbleViewsAndWindowCreated(); // Lazy load overflow bubbles from disk loadOverflowBubblesFromDisk(); @@ -1570,7 +1623,7 @@ public class BubbleController implements ConfigurationChangeListener, } mSysuiProxy.notifyInvalidateNotifications("BubbleData.Listener.applyUpdate"); - updateStack(); + updateBubbleViews(); // Update the cached state for queries from SysUI mImpl.mCachedState.update(update); @@ -1650,28 +1703,42 @@ public class BubbleController implements ConfigurationChangeListener, /** * 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. - * Updates bubbles' icon views clickable states + * Does not un-bubble, just hides or un-hides the views themselves. + * + * Updates view description for TalkBack focus. + * Updates bubbles' icon views clickable states (when floating). */ - public void updateStack() { - if (mStackView == null) { + public void updateBubbleViews() { + if (mStackView == null && mLayerView == null) { return; } if (!mIsStatusBarShade) { - // Bubbles don't appear over the locked shade. - mStackView.setVisibility(INVISIBLE); + // Bubbles don't appear when the device is locked. + if (mStackView != null) { + mStackView.setVisibility(INVISIBLE); + } + if (mLayerView != null) { + mLayerView.setVisibility(INVISIBLE); + } } else if (hasBubbles()) { // If we're unlocked, show the stack if we have bubbles. If we don't have bubbles, the // stack will be set to INVISIBLE in onAllBubblesAnimatedOut after the bubbles animate // out. - mStackView.setVisibility(VISIBLE); + if (mStackView != null) { + mStackView.setVisibility(VISIBLE); + } + if (mLayerView != null && isStackExpanded()) { + mLayerView.setVisibility(VISIBLE); + } } - mStackView.updateContentDescription(); - - mStackView.updateBubblesAcessibillityStates(); + if (mStackView != null) { + mStackView.updateContentDescription(); + mStackView.updateBubblesAcessibillityStates(); + } else if (mLayerView != null) { + // TODO(b/273313561): handle a11y for BubbleBarLayerView + } } @VisibleForTesting diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java index f437553337efe..1a97c0504b379 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java @@ -22,6 +22,7 @@ import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -38,12 +39,12 @@ import android.util.Log; import android.util.PathParser; import android.view.LayoutInflater; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.launcher3.icons.BitmapInfo; import com.android.wm.shell.R; +import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView; +import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; import java.lang.ref.WeakReference; import java.util.Objects; @@ -70,6 +71,7 @@ public class BubbleViewInfoTask extends AsyncTask mContext; private WeakReference mController; private WeakReference mStackView; + private WeakReference mLayerView; private BubbleIconFactory mIconFactory; private BubbleBadgeIconFactory mBadgeIconFactory; private boolean mSkipInflation; @@ -83,7 +85,8 @@ public class BubbleViewInfoTask extends AsyncTask(context); mController = new WeakReference<>(controller); mStackView = new WeakReference<>(stackView); + mLayerView = new WeakReference<>(layerView); mIconFactory = factory; mBadgeIconFactory = badgeFactory; mSkipInflation = skipInflation; @@ -102,8 +106,13 @@ public class BubbleViewInfoTask extends AsyncTask