Loading core/java/android/app/ActivityOptions.java +1 −0 Original line number Diff line number Diff line Loading @@ -596,6 +596,7 @@ public class ActivityOptions extends ComponentOptions { private boolean mIsEligibleForLegacyPermissionPrompt; private boolean mRemoveWithTaskOrganizer; private boolean mLaunchedFromBubble; // TODO(b/407669465): remove it once migrated to the new approach private boolean mLaunchNextToBubble; private boolean mTransientLaunch; private PictureInPictureParams mLaunchIntoPipParams; Loading libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleExpandedViewManager.kt +5 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.wm.shell.bubbles import android.window.WindowContainerToken import com.android.wm.shell.shared.bubbles.BubbleBarLocation import java.util.Collections Loading Loading @@ -51,4 +52,8 @@ class FakeBubbleExpandedViewManager(var bubbleBar: Boolean = false, var expanded override fun hideCurrentInputMethod() {} override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {} override fun getAppBubbleRootTaskToken(): WindowContainerToken? { return null } } libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleAnythingFlagHelper.java +20 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,26 @@ public class BubbleAnythingFlagHelper { && Flags.enableCreateAnyBubble()); } /** Whether creating a root task to manage the bubble tasks in the Core. */ public static boolean enableRootTaskForBubble() { // This is needed to prevent tasks being hidden and re-parented to TDA when move-to-back. if (!enableCreateAnyBubbleWithForceExcludedFromRecents()) { return false; } // This is needed to allow the activity behind the root task remains in RESUMED state. if (!com.android.window.flags.Flags.enableSeeThroughTaskFragments()) { return false; } // This is needed to allow the leaf task can be started in expected bounds. if (!com.android.window.flags.Flags.respectLeafTaskBounds()) { return false; } return com.android.window.flags.Flags.rootTaskForBubble(); } /** Whether the overall bubble anything feature is enabled. */ public static boolean enableBubbleAnything() { return Flags.enableBubbleAnything(); Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +26 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.wm.shell.bubbles; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED; import static android.service.notification.NotificationListenerService.REASON_CANCEL; Loading Loading @@ -231,6 +232,7 @@ public class BubbleController implements ConfigurationChangeListener, private final BubbleData mBubbleData; @Nullable private BubbleStackView mStackView; @Nullable private BubbleBarLayerView mLayerView; @Nullable private ActivityManager.RunningTaskInfo mAppBubbleRootTaskInfo; private BubbleIconFactory mBubbleIconFactory; private final BubblePositioner mBubblePositioner; private Bubbles.SysuiProxy mSysuiProxy; Loading Loading @@ -563,6 +565,25 @@ public class BubbleController implements ConfigurationChangeListener, Slog.e(TAG, "Failed to register Bubble multitasking delegate.", e); } } if (BubbleAnythingFlagHelper.enableRootTaskForBubble()) { // Create a root-task in WM Core. The app bubble tasks will be positioned as the leaf // tasks under this root-task. // The app bubble should be dismissed with proper transition (such as need to convert // it to fullscreen) if the bubble task is no longer be a leaf task under this leaf // task. mTaskOrganizer.createRootTask(mContext.getDisplayId(), WINDOWING_MODE_MULTI_WINDOW, new ShellTaskOrganizer.TaskListener() { @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { if (mAppBubbleRootTaskInfo != null) { return; } mAppBubbleRootTaskInfo = taskInfo; } }); } } private ExternalInterfaceBinder createExternalInterface() { Loading Loading @@ -2723,6 +2744,11 @@ public class BubbleController implements ConfigurationChangeListener, return mLayerView; } @Nullable public ActivityManager.RunningTaskInfo getAppBubbleRootTaskInfo() { return mAppBubbleRootTaskInfo; } /** * Returns the id of the display to which the current Bubble view is attached if it is currently * showing, {@link INVALID_DISPLAY} otherwise. Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +14 −2 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.window.ScreenCapture; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import androidx.annotation.Nullable; Loading Loading @@ -250,15 +251,26 @@ public class BubbleExpandedView extends LinearLayout { // Needs to be mutable for the fillInIntent PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, /* options= */ null); final WindowContainerToken rootToken = mManager.getAppBubbleRootTaskToken(); if (rootToken != null) { options.setLaunchRootTask(rootToken); } else { options.setLaunchNextToBubble(true /* launchNextToBubble */); } mTaskView.startActivity(pi, fillInIntent, options, launchBounds); } else if (!mIsOverflow && isShortcutBubble) { ProtoLog.v(WM_SHELL_BUBBLES, "startingShortcutBubble=%s", getBubbleKey()); if (mBubble.isChat()) { options.setLaunchedFromBubble(true); options.setApplyActivityFlagsForBubbles(true); } else { final WindowContainerToken rootToken = mManager.getAppBubbleRootTaskToken(); if (rootToken != null) { options.setLaunchRootTask(rootToken); } else { options.setLaunchNextToBubble(true /* launchNextToBubble */); } options.setApplyMultipleTaskFlagForShortcut(true); } mTaskView.startShortcutActivity(mBubble.getShortcutInfo(), Loading Loading
core/java/android/app/ActivityOptions.java +1 −0 Original line number Diff line number Diff line Loading @@ -596,6 +596,7 @@ public class ActivityOptions extends ComponentOptions { private boolean mIsEligibleForLegacyPermissionPrompt; private boolean mRemoveWithTaskOrganizer; private boolean mLaunchedFromBubble; // TODO(b/407669465): remove it once migrated to the new approach private boolean mLaunchNextToBubble; private boolean mTransientLaunch; private PictureInPictureParams mLaunchIntoPipParams; Loading
libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleExpandedViewManager.kt +5 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.wm.shell.bubbles import android.window.WindowContainerToken import com.android.wm.shell.shared.bubbles.BubbleBarLocation import java.util.Collections Loading Loading @@ -51,4 +52,8 @@ class FakeBubbleExpandedViewManager(var bubbleBar: Boolean = false, var expanded override fun hideCurrentInputMethod() {} override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {} override fun getAppBubbleRootTaskToken(): WindowContainerToken? { return null } }
libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleAnythingFlagHelper.java +20 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,26 @@ public class BubbleAnythingFlagHelper { && Flags.enableCreateAnyBubble()); } /** Whether creating a root task to manage the bubble tasks in the Core. */ public static boolean enableRootTaskForBubble() { // This is needed to prevent tasks being hidden and re-parented to TDA when move-to-back. if (!enableCreateAnyBubbleWithForceExcludedFromRecents()) { return false; } // This is needed to allow the activity behind the root task remains in RESUMED state. if (!com.android.window.flags.Flags.enableSeeThroughTaskFragments()) { return false; } // This is needed to allow the leaf task can be started in expected bounds. if (!com.android.window.flags.Flags.respectLeafTaskBounds()) { return false; } return com.android.window.flags.Flags.rootTaskForBubble(); } /** Whether the overall bubble anything feature is enabled. */ public static boolean enableBubbleAnything() { return Flags.enableBubbleAnything(); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +26 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.wm.shell.bubbles; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED; import static android.service.notification.NotificationListenerService.REASON_CANCEL; Loading Loading @@ -231,6 +232,7 @@ public class BubbleController implements ConfigurationChangeListener, private final BubbleData mBubbleData; @Nullable private BubbleStackView mStackView; @Nullable private BubbleBarLayerView mLayerView; @Nullable private ActivityManager.RunningTaskInfo mAppBubbleRootTaskInfo; private BubbleIconFactory mBubbleIconFactory; private final BubblePositioner mBubblePositioner; private Bubbles.SysuiProxy mSysuiProxy; Loading Loading @@ -563,6 +565,25 @@ public class BubbleController implements ConfigurationChangeListener, Slog.e(TAG, "Failed to register Bubble multitasking delegate.", e); } } if (BubbleAnythingFlagHelper.enableRootTaskForBubble()) { // Create a root-task in WM Core. The app bubble tasks will be positioned as the leaf // tasks under this root-task. // The app bubble should be dismissed with proper transition (such as need to convert // it to fullscreen) if the bubble task is no longer be a leaf task under this leaf // task. mTaskOrganizer.createRootTask(mContext.getDisplayId(), WINDOWING_MODE_MULTI_WINDOW, new ShellTaskOrganizer.TaskListener() { @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { if (mAppBubbleRootTaskInfo != null) { return; } mAppBubbleRootTaskInfo = taskInfo; } }); } } private ExternalInterfaceBinder createExternalInterface() { Loading Loading @@ -2723,6 +2744,11 @@ public class BubbleController implements ConfigurationChangeListener, return mLayerView; } @Nullable public ActivityManager.RunningTaskInfo getAppBubbleRootTaskInfo() { return mAppBubbleRootTaskInfo; } /** * Returns the id of the display to which the current Bubble view is attached if it is currently * showing, {@link INVALID_DISPLAY} otherwise. Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +14 −2 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.window.ScreenCapture; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import androidx.annotation.Nullable; Loading Loading @@ -250,15 +251,26 @@ public class BubbleExpandedView extends LinearLayout { // Needs to be mutable for the fillInIntent PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, /* options= */ null); final WindowContainerToken rootToken = mManager.getAppBubbleRootTaskToken(); if (rootToken != null) { options.setLaunchRootTask(rootToken); } else { options.setLaunchNextToBubble(true /* launchNextToBubble */); } mTaskView.startActivity(pi, fillInIntent, options, launchBounds); } else if (!mIsOverflow && isShortcutBubble) { ProtoLog.v(WM_SHELL_BUBBLES, "startingShortcutBubble=%s", getBubbleKey()); if (mBubble.isChat()) { options.setLaunchedFromBubble(true); options.setApplyActivityFlagsForBubbles(true); } else { final WindowContainerToken rootToken = mManager.getAppBubbleRootTaskToken(); if (rootToken != null) { options.setLaunchRootTask(rootToken); } else { options.setLaunchNextToBubble(true /* launchNextToBubble */); } options.setApplyMultipleTaskFlagForShortcut(true); } mTaskView.startShortcutActivity(mBubble.getShortcutInfo(), Loading