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
This commit is contained in:
@@ -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 =
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
+110
@@ -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<Context>()
|
||||
private val systemUiProxy = mock<SystemUiProxy>()
|
||||
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<TransitionFilter>()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user