From 39c006e792adaee31d800f1f6b9be14ce594071e Mon Sep 17 00:00:00 2001 From: Gustav Sennton Date: Tue, 29 Oct 2024 13:48:44 +0000 Subject: [PATCH] Register transitions for Desktop app launch + minimize animations In other CLs we pass specific RemoteTransitions to Shell for app launches and unminimize actions. Those CLs cover Launcher actions like the keyboard alt-tab shortcut, or launching an app from Taskbar. This CL covers launches that don't originate from Launcher, e.g. launches through intents. Test: DesktopAppLaunchTransitionManagerTest Bug: 369966416 Flag: com.android.window.flags.enable_desktop_app_launch_transitions Change-Id: I82fd72f870c1e38efe5a8ce533a60f282c10203c --- .../desktop/DesktopAppLaunchTransition.kt | 1 + .../DesktopAppLaunchTransitionManager.kt | 88 ++++++++++++++ .../quickstep/TouchInteractionService.java | 9 ++ .../DesktopAppLaunchTransitionManagerTest.kt | 110 ++++++++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt create mode 100644 quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchTransitionManagerTest.kt diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt index e160f8293d..87a82f0af1 100644 --- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt +++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt @@ -143,6 +143,7 @@ class DesktopAppLaunchTransition( } companion object { + /** Change modes that represent a task becoming visible / launching in Desktop mode. */ val LAUNCH_CHANGE_MODES = intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT) private val launchBoundsAnimationDef = diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt new file mode 100644 index 0000000000..e32bcd1fdc --- /dev/null +++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2024 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.launcher3.desktop + +import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.content.Context +import android.window.DesktopModeFlags +import android.window.RemoteTransition +import android.window.TransitionFilter +import android.window.TransitionFilter.CONTAINER_ORDER_TOP +import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType +import com.android.launcher3.util.Executors.MAIN_EXECUTOR +import com.android.quickstep.SystemUiProxy +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus + +/** Manages transitions related to app launches in Desktop Mode. */ +class DesktopAppLaunchTransitionManager( + private val context: Context, + private val systemUiProxy: SystemUiProxy, +) { + private var remoteWindowLimitUnminimizeTransition: RemoteTransition? = null + + /** + * Register a [RemoteTransition] supporting Desktop app launches, and window limit + * minimizations. + */ + fun registerTransitions() { + if (!shouldRegisterTransitions()) { + return + } + remoteWindowLimitUnminimizeTransition = + RemoteTransition( + DesktopAppLaunchTransition(context, MAIN_EXECUTOR, AppLaunchType.UNMINIMIZE) + ) + systemUiProxy.registerRemoteTransition( + remoteWindowLimitUnminimizeTransition, + buildAppLaunchFilter(), + ) + } + + /** + * Unregister the [RemoteTransition] supporting Desktop app launches and window limit + * minimizations. + */ + fun unregisterTransitions() { + if (!shouldRegisterTransitions()) { + return + } + systemUiProxy.unregisterRemoteTransition(remoteWindowLimitUnminimizeTransition) + remoteWindowLimitUnminimizeTransition = null + } + + private fun shouldRegisterTransitions(): Boolean = + DesktopModeStatus.canEnterDesktopMode(context) && + DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue + + companion object { + private fun buildAppLaunchFilter(): TransitionFilter { + val openRequirement = + TransitionFilter.Requirement().apply { + mActivityType = ACTIVITY_TYPE_STANDARD + mWindowingMode = WINDOWING_MODE_FREEFORM + mModes = DesktopAppLaunchTransition.LAUNCH_CHANGE_MODES + mMustBeTask = true + mOrder = CONTAINER_ORDER_TOP + } + return TransitionFilter().apply { + mTypeSet = DesktopAppLaunchTransition.LAUNCH_CHANGE_MODES + mRequirements = arrayOf(openRequirement) + } + } + } +} diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index fcc512119f..ad5720f5d5 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -85,6 +85,7 @@ import com.android.launcher3.EncryptionType; import com.android.launcher3.Flags; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.anim.AnimatedFloat; +import com.android.launcher3.desktop.DesktopAppLaunchTransitionManager; import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.statehandlers.DesktopVisibilityController; import com.android.launcher3.statemanager.StatefulActivity; @@ -662,6 +663,7 @@ public class TouchInteractionService extends Service { private NavigationMode mGestureStartNavMode = null; private DesktopVisibilityController mDesktopVisibilityController; + private DesktopAppLaunchTransitionManager mDesktopAppLaunchTransitionManager; @Override public void onCreate() { @@ -686,6 +688,9 @@ public class TouchInteractionService extends Service { mDesktopVisibilityController = new DesktopVisibilityController(this); mTaskbarManager = new TaskbarManager( this, mAllAppsActionManager, mNavCallbacks, mDesktopVisibilityController); + mDesktopAppLaunchTransitionManager = + new DesktopAppLaunchTransitionManager(this, SystemUiProxy.INSTANCE.get(this)); + mDesktopAppLaunchTransitionManager.registerTransitions(); if (Flags.enableLauncherOverviewInWindow() || Flags.enableFallbackOverviewInWindow()) { mRecentsWindowManager = new RecentsWindowManager(this); } @@ -851,6 +856,10 @@ public class TouchInteractionService extends Service { if (mRecentsWindowManager != null) { mRecentsWindowManager.destroy(); } + if (mDesktopAppLaunchTransitionManager != null) { + mDesktopAppLaunchTransitionManager.unregisterTransitions(); + } + mDesktopAppLaunchTransitionManager = null; mDesktopVisibilityController.onDestroy(); sConnected = false; diff --git a/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchTransitionManagerTest.kt b/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchTransitionManagerTest.kt new file mode 100644 index 0000000000..26189df281 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchTransitionManagerTest.kt @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2024 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.launcher3.desktop + +import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.content.Context +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule +import android.view.WindowManager.TRANSIT_OPEN +import android.view.WindowManager.TRANSIT_TO_FRONT +import android.window.TransitionFilter +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession +import com.android.quickstep.SystemUiProxy +import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness + +@SmallTest +@RunWith(AndroidJUnit4::class) +class DesktopAppLaunchTransitionManagerTest { + + @get:Rule val mSetFlagsRule = SetFlagsRule() + + private val mockitoSession = + mockitoSession() + .strictness(Strictness.LENIENT) + .mockStatic(DesktopModeStatus::class.java) + .startMocking() + + private val context = mock() + private val systemUiProxy = mock() + private lateinit var transitionManager: DesktopAppLaunchTransitionManager + + @Before + fun setUp() { + whenever(context.resources).thenReturn(mock()) + whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true) + transitionManager = DesktopAppLaunchTransitionManager(context, systemUiProxy) + } + + @After + fun tearDown() { + mockitoSession.finishMocking() + } + + @Test + @EnableFlags(FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS) + fun registerTransitions_appLaunchFlagEnabled_registersTransition() { + transitionManager.registerTransitions() + + verify(systemUiProxy, times(1)).registerRemoteTransition(any(), any()) + } + + @Test + @DisableFlags(FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS) + fun registerTransitions_appLaunchFlagDisabled_doesntRegisterTransition() { + transitionManager.registerTransitions() + + verify(systemUiProxy, times(0)).registerRemoteTransition(any(), any()) + } + + @Test + @EnableFlags(FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS) + fun registerTransitions_usesCorrectFilter() { + transitionManager.registerTransitions() + val filterArgumentCaptor = argumentCaptor() + + verify(systemUiProxy, times(1)) + .registerRemoteTransition(any(), filterArgumentCaptor.capture()) + + assertThat(filterArgumentCaptor.lastValue).isNotNull() + assertThat(filterArgumentCaptor.lastValue.mTypeSet) + .isEqualTo(intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT)) + assertThat(filterArgumentCaptor.lastValue.mRequirements).hasLength(1) + val launchRequirement = filterArgumentCaptor.lastValue.mRequirements!![0] + assertThat(launchRequirement.mModes).isEqualTo(intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT)) + assertThat(launchRequirement.mActivityType).isEqualTo(ACTIVITY_TYPE_STANDARD) + assertThat(launchRequirement.mWindowingMode).isEqualTo(WINDOWING_MODE_FREEFORM) + } +}