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

Commit 6a567a57 authored by Eric Lin's avatar Eric Lin Committed by Android (Google) Code Review
Browse files

Merge "Allow launch-next-bubble transition for existing bubbles." into main

parents 5c7a645d 3c18ddf2
Loading
Loading
Loading
Loading
+80 −1
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.wm.shell.bubbles

import android.app.ActivityManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
@@ -77,9 +79,11 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.isA
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@@ -99,6 +103,7 @@ class BubbleControllerTest(flags: FlagsParameterization) {
    @get:Rule
    val setFlagsRule = SetFlagsRule(flags)

    private val bubbleTransitions = mock<BubbleTransitions>()
    private val context = ApplicationProvider.getApplicationContext<Context>()
    private val uiEventLoggerFake = UiEventLoggerFake()
    private val displayImeController = mock<DisplayImeController>()
@@ -389,6 +394,78 @@ class BubbleControllerTest(flags: FlagsParameterization) {
        assertThat(bubbleController.hasStableBubbleForTask(777)).isFalse()
    }

    @Test
    fun expandStackAndSelectBubbleForExistingTransition_reusesExistingBubble() {
        assumeTrue(BubbleAnythingFlagHelper.enableCreateAnyBubble())

        val bubble = createBubble("key", taskId = 123)
        getInstrumentation().runOnMainSync {
            bubbleData.notificationEntryUpdated(
                bubble,
                true /* suppressFlyout */,
                true /* showInShade= */,
            )
        }
        val taskInfo = ActivityManager.RunningTaskInfo().apply { taskId = 123 }

        getInstrumentation().runOnMainSync {
            bubbleController.expandStackAndSelectBubbleForExistingTransition(
                taskInfo,
                mock(), /* transition */
            ) {}
        }

        verify(bubbleTransitions).startLaunchNewTaskBubbleForExistingTransition(
            eq(bubble), /* bubble */
            any(), /* expandedViewManager */
            any(), /* factory */
            any(), /* positioner */
            any(), /* stackView */
            anyOrNull(), /* layerView */
            any(), /* iconFactory */
            any(), /* inflateSync */
            any(), /* transition */
            any(), /* onInflatedCallback */
        )
        assertThat(bubbleData.selectedBubble).isEqualTo(bubble)
        assertThat(bubbleController.isStackExpanded).isTrue()
    }

    @Test
    fun expandStackAndSelectBubbleForExistingTransition_newBubble() {
        assumeTrue(BubbleAnythingFlagHelper.enableCreateAnyBubble())

        val taskInfo = ActivityManager.RunningTaskInfo().apply {
            baseActivity = COMPONENT
            taskId = 123
        }

        getInstrumentation().runOnMainSync {
            bubbleController.expandStackAndSelectBubbleForExistingTransition(
                taskInfo,
                mock(), /* transition */
            ) {}
        }

        val bubble = argumentCaptor<Bubble>().let { bubbleCaptor ->
            verify(bubbleTransitions).startLaunchNewTaskBubbleForExistingTransition(
                bubbleCaptor.capture(), /* bubble */
                any(), /* expandedViewManager */
                any(), /* factory */
                any(), /* positioner */
                any(), /* stackView */
                anyOrNull(), /* layerView */
                any(), /* iconFactory */
                any(), /* inflateSync */
                any(), /* transition */
                any(), /* onInflatedCallback */
            )
            bubbleCaptor.lastValue
        }
        assertThat(bubble.taskId).isEqualTo(123)
        assertThat(bubble.key).isEqualTo("key_app_bubble:123")
    }

    private fun createBubble(key: String, taskId: Int = 0): Bubble {
        val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
        val shortcutInfo = ShortcutInfo.Builder(context, "fakeId").setIcon(icon).build()
@@ -459,7 +536,7 @@ class BubbleControllerTest(flags: FlagsParameterization) {
                surfaceSynchronizer,
                FloatingContentCoordinator(),
                bubbleDataRepository,
                mock<BubbleTransitions>(),
                bubbleTransitions,
                mock<IStatusBarService>(),
                windowManager,
                displayInsetsController,
@@ -500,6 +577,8 @@ class BubbleControllerTest(flags: FlagsParameterization) {
    }

    companion object {
        private val COMPONENT = ComponentName("com.example.app", "com.example.app.MainActivity")

        private fun createFakeInsetsState(imeVisible: Boolean): InsetsState {
            val insetsState = InsetsState()
            if (imeVisible) {
+22 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.wm.shell.bubbles

import android.app.ActivityManager
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.os.Binder
import android.os.IBinder
@@ -46,6 +47,7 @@ import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoInteractions
@@ -114,6 +116,26 @@ class BubbleTaskStackListenerTest {
        verify(bubbleData).setSelectedBubbleAndExpandStack(bubble)
    }

    @Test
    fun onActivityRestartAttempt_inStackAppBubbleMovingToFront_doesNothing() {
        task.configuration.windowConfiguration.activityType = ACTIVITY_TYPE_STANDARD
        bubbleController.stub {
            on { shouldBeAppBubble(task) } doReturn true
        }
        bubbleData.stub {
            on { getBubbleInStackWithTaskId(bubbleTaskId) } doReturn bubble
        }

        bubbleTaskStackListener.onActivityRestartAttempt(
            task,
            homeTaskVisible = false,
            clearedTask = false,
            wasVisible = false,
        )

        verify(bubbleData, never()).setSelectedBubbleAndExpandStack(bubble)
    }

    @Test
    @EnableFlags(
        FLAG_ENABLE_CREATE_ANY_BUBBLE,
+8 −2
Original line number Diff line number Diff line
@@ -1693,8 +1693,14 @@ public class BubbleController implements ConfigurationChangeListener,
            Consumer<Transitions.TransitionHandler> onInflatedCallback) {
        if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return null;

        // Create a new bubble and show it
        Bubble b = mBubbleData.getOrCreateBubble(taskInfo); // Removes from overflow
        Bubble b = mBubbleData.getBubbleInStackWithTaskId(taskInfo.taskId);
        if (b != null) {
            // Reuse the existing bubble
            mBubbleData.setSelectedBubbleAndExpandStack(b, BubbleBarLocation.DEFAULT);
        } else {
            // Create a new bubble and show it, remove from overflow
            b = mBubbleData.getOrCreateBubble(taskInfo);
        }
        ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubbleForExistingTransition() taskId=%s",
                taskInfo.taskId);
        b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
+13 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
package com.android.wm.shell.bubbles

import android.app.ActivityManager
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
@@ -64,7 +65,7 @@ class BubbleTaskStackListener(
            when {
                isBubbleToFullscreen(task) -> moveCollapsedInStackBubbleToFullscreen(bubble, task)
                isBubbleToSplit(task) -> return // skip split task restarts
                else -> selectAndExpandInStackBubble(bubble, task)
                !isAppBubbleMovingToFront(task) -> selectAndExpandInStackBubble(bubble, task)
            }
        }
    }
@@ -75,6 +76,17 @@ class BubbleTaskStackListener(
            .orElse(false)
    }

    /**
     * Returns whether the given bubble task restart should move the app bubble to front
     * and be handled in DefaultMixedTransition#animateEnterBubblesFromBubble.
     * This occurs when a startActivity call resolves to an existing activity, causing the
     * task to move to front, and the mixed transition will then expand the bubble.
     */
    private fun isAppBubbleMovingToFront(task: ActivityManager.RunningTaskInfo): Boolean {
        return task.activityType == ACTIVITY_TYPE_STANDARD
                && bubbleController.shouldBeAppBubble(task)
    }

    /** Selects and expands a bubble that is currently in the stack. */
    private fun selectAndExpandInStackBubble(
        bubble: Bubble,
+7 −9
Original line number Diff line number Diff line
@@ -165,12 +165,6 @@ public class BubbleTransitions {
        }
        for (IBinder cookie : info.getTriggerTask().launchCookies) {
            if (mPendingEnterTransitions.containsKey(cookie)) {
                if (hasBubbleWithTaskId(info.getTriggerTask().taskId)) {
                    // We'll let this transition fall through and let the normal TaskViewTransitions
                    // play it
                    mPendingEnterTransitions.remove(cookie);
                    return false;
                }
                return true;
            }
        }
@@ -245,11 +239,11 @@ public class BubbleTransitions {
    }

    /**
     * Called to initiate axed bubble-to-bubble launch/convert for the given transition.
     * Initiates axed bubble-to-bubble launch/existing bubble convert for the given transition.
     *
     * @return whether a new transition was started for the launch
     */
    public boolean startBubbleToBubbleLaunch(@NonNull IBinder transition,
    public boolean startBubbleToBubbleLaunchOrExistingBubbleConvert(@NonNull IBinder transition,
            @NonNull ActivityManager.RunningTaskInfo launchingTask,
            @NonNull Consumer<TransitionHandler> onInflatedCallback) {
        TransitionHandler handler =
@@ -538,6 +532,9 @@ public class BubbleTransitions {
                state.mVisible = true;
            }
            mTransitionProgress.setInflated();
            // Remove any intermediate queued transitions that were started as a result of the
            // inflation (the task view will be in the right bounds)
            mTaskViewTransitions.removePendingTransitions(tv.getController());
            mTaskViewTransitions.enqueueExternal(tv.getController(), () -> {
                return mTransition;
            });
@@ -681,7 +678,8 @@ public class BubbleTransitions {
        }

        private void playAnimation() {
            ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.playAnimation()");
            ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.playAnimation(): playConvert=%b",
                    mPlayConvertTaskAnimation);
            final TaskViewTaskController tv = mBubble.getTaskView().getController();
            final SurfaceControl.Transaction startT = new SurfaceControl.Transaction();
            // Set task position to 0,0 as it will be placed inside the TaskView
Loading