Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 413f63f0 authored by Louis Chang's avatar Louis Chang
Browse files

Reordering the app bubble root task when needed

Make the bubble root task always-on-top while any bubble expands.
Reorder the bubble root task to bottom when all bubbles collapse.

Bug: 407669465
Test: BubbleControllerTest
Test: BubbleUtilsTest
Test: start an app into bubble
Flag: com.android.window.flags.root_task_for_bubble
Change-Id: Idf7c168a1e78579d162123870e00c55b56aed982
parent d55f80cc
Loading
Loading
Loading
Loading
+8 −3
Original line number Diff line number Diff line
@@ -391,6 +391,7 @@ class BubbleControllerTest(flags: FlagsParameterization) {
                true, /* reorder */
                false, /* syncHiddenWithVisibilityOnReorder */
                false, /* nonBlockingIfPossible */
                null, /* overrideTransaction */
            )
        } else {
            verify(baseTransitions).setTaskViewVisible(taskView, true /* visible */)
@@ -413,6 +414,7 @@ class BubbleControllerTest(flags: FlagsParameterization) {
            any(), /* reorder */
            any(), /* syncHiddenWithVisibilityOnReorder */
            any(), /* nonBlockingIfPossible */
            any(), /* overrideTransaction */
        )
    }

@@ -460,7 +462,7 @@ class BubbleControllerTest(flags: FlagsParameterization) {
        assertThat(bubbleController.hasStableBubbleForTask(777)).isFalse()
    }

    @EnableFlags(FLAG_ROOT_TASK_FOR_BUBBLE)
    @EnableFlags(FLAG_ENABLE_CREATE_ANY_BUBBLE, FLAG_ROOT_TASK_FOR_BUBBLE)
    @Test
    fun shouldBeAppBubble_parentTaskMatchesBubbleRootTask_returnsTrue() {
        val bubbleController = createBubbleControllerWithRootTask(bubbleRootTaskId = 777)
@@ -469,7 +471,7 @@ class BubbleControllerTest(flags: FlagsParameterization) {
        assertThat(bubbleController.shouldBeAppBubble(taskInfo)).isTrue()
    }

    @EnableFlags(FLAG_ROOT_TASK_FOR_BUBBLE)
    @EnableFlags(FLAG_ENABLE_CREATE_ANY_BUBBLE, FLAG_ROOT_TASK_FOR_BUBBLE)
    @Test
    fun shouldBeAppBubble_parentTaskDoesNotMatchesBubbleRootTask_returnsFalse() {
        val bubbleController = createBubbleControllerWithRootTask(bubbleRootTaskId = 123)
@@ -979,7 +981,10 @@ class BubbleControllerTest(flags: FlagsParameterization) {
            captor.lastValue
        }

        val bubbleRootTask = ActivityManager.RunningTaskInfo().apply { taskId = bubbleRootTaskId }
        val bubbleRootTask = ActivityManager.RunningTaskInfo().apply {
            taskId = bubbleRootTaskId
            token = mock<WindowContainerToken>()
        }
        rootTaskListener.onTaskAppeared(bubbleRootTask, null /* leash */)

        return bubbleController
+4 −0
Original line number Diff line number Diff line
@@ -43,6 +43,10 @@ public class BubbleAnythingFlagHelper {

    /** Whether creating a root task to manage the bubble tasks in the Core. */
    public static boolean enableRootTaskForBubble() {
        if (!Flags.enableCreateAnyBubble()) {
            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;
+34 −2
Original line number Diff line number Diff line
@@ -641,6 +641,12 @@ public class BubbleController implements ConfigurationChangeListener,
                                return;
                            }
                            mAppBubbleRootTaskInfo = taskInfo;

                            final WindowContainerTransaction wct = new WindowContainerTransaction();
                            wct.reorder(taskInfo.token, false /* onTop */);
                            wct.setInterceptBackPressedOnTaskRoot(taskInfo.token,
                                    true /* interceptBackPressed */);
                            mTaskOrganizer.applyTransaction(wct);
                        }
                    });
        }
@@ -1469,7 +1475,7 @@ public class BubbleController implements ConfigurationChangeListener,

    /** Returns whether the given task should be an App Bubble */
    public boolean shouldBeAppBubble(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
        if (com.android.window.flags.Flags.rootTaskForBubble()) {
        if (BubbleAnythingFlagHelper.enableRootTaskForBubble()) {
            return mAppBubbleRootTaskInfo != null
                    && taskInfo.parentTaskId == mAppBubbleRootTaskInfo.taskId;
        }
@@ -2919,6 +2925,16 @@ public class BubbleController implements ConfigurationChangeListener,
        return mAppBubbleRootTaskInfo;
    }

    @Nullable
    public WindowContainerToken getAppBubbleRootTaskToken() {
        return mAppBubbleRootTaskInfo != null ? mAppBubbleRootTaskInfo.token : null;
    }

    @Nullable
    public boolean isAppBubbleRootTask(int taskId) {
        return mAppBubbleRootTaskInfo != null && mAppBubbleRootTaskInfo.taskId == taskId;
    }

    /**
     * Returns the id of the display to which the current Bubble view is attached if it is currently
     * showing, {@link INVALID_DISPLAY} otherwise.
@@ -3758,12 +3774,28 @@ public class BubbleController implements ConfigurationChangeListener,
                if (!visible && !mBubbleData.hasBubbleInStackWithTaskView(taskView)) {
                    return;
                }

                final WindowContainerTransaction wct;
                if (BubbleAnythingFlagHelper.enableRootTaskForBubble() && shouldBeAppBubble(
                        taskView.getTaskInfo())) {
                    wct = new WindowContainerTransaction();
                    if (visible) {
                        wct.reorder(taskView.getTaskInfo().token, true /* onTop */);
                        wct.setAlwaysOnTop(mAppBubbleRootTaskInfo.token, true /* alwaysOnTop */);
                    } else if (!mBubbleData.isExpanded()) {
                        wct.setAlwaysOnTop(mAppBubbleRootTaskInfo.token, false /* alwaysOnTop */);
                        wct.reorder(mAppBubbleRootTaskInfo.token, false /* onTop */);
                    }
                } else {
                    wct = null;
                }

                // The transaction to hide the TaskView can be executed on the executor to avoid
                // blocking the calling thread.
                final boolean nonBlocking = !visible;
                // Use reorder instead of always-on-top with hidden.
                mBaseTransitions.setTaskViewVisible(taskView, visible, true /* reorder */,
                        false /* toggleHiddenOnReorder */, nonBlocking);
                        false /* toggleHiddenOnReorder */, nonBlocking, wct);
            } else {
                mBaseTransitions.setTaskViewVisible(taskView, visible);
            }
+5 −5
Original line number Diff line number Diff line
@@ -231,7 +231,10 @@ public class BubbleExpandedView extends LinearLayout {
                    Rect launchBounds = new Rect();
                    mTaskView.getBoundsOnScreen(launchBounds);

                    final WindowContainerToken rootToken = mManager.getAppBubbleRootTaskToken();
                    if (rootToken == null) {
                        options.setTaskAlwaysOnTop(true /* alwaysOnTop */);
                    }
                    options.setPendingIntentBackgroundActivityStartMode(
                            MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);

@@ -252,7 +255,6 @@ 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 {
@@ -265,8 +267,6 @@ public class BubbleExpandedView extends LinearLayout {
                            options.setLaunchedFromBubble(true);
                            options.setApplyActivityFlagsForBubbles(true);
                        } else {
                            final WindowContainerToken rootToken =
                                    mManager.getAppBubbleRootTaskToken();
                            if (rootToken != null) {
                                options.setLaunchRootTask(rootToken);
                            } else {
@@ -321,7 +321,7 @@ public class BubbleExpandedView extends LinearLayout {
            final boolean isAppBubble = mBubble != null
                    && (mBubble.isApp() || mBubble.isShortcut());
            final WindowContainerTransaction wct = getEnterBubbleTransaction(
                    tvc.getTaskToken(), isAppBubble);
                    tvc.getTaskToken(), mManager.getAppBubbleRootTaskToken(), isAppBubble);
            tvc.getTaskOrganizer().applyTransaction(wct);

            // With the task org, the taskAppeared callback will only happen once the task has
+1 −1
Original line number Diff line number Diff line
@@ -97,7 +97,7 @@ interface BubbleExpandedViewManager {
                }

                override fun getAppBubbleRootTaskToken(): WindowContainerToken? =
                    controller.appBubbleRootTaskInfo?.token
                    controller.appBubbleRootTaskToken

                override fun shouldBeAppBubble(taskInfo: ActivityManager.RunningTaskInfo): Boolean =
                    controller.shouldBeAppBubble(taskInfo)
Loading