From 5ecf77d2ceec9cb78f492e8e2169597e12549a1b Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Wed, 14 Dec 2022 20:42:34 +0000 Subject: [PATCH 1/4] Revert^2 "Create controller for desktop prototype 2" f3c7cc853a1e99bfb44f98191124d7235ffb67e1 Change-Id: I23ae45dad7570a89e3650564ec4def3af65f1576 --- .../wm/shell/dagger/WMShellBaseModule.java | 24 +++- .../wm/shell/dagger/WMShellModule.java | 17 +++ .../shell/desktopmode/DesktopModeStatus.java | 6 +- .../desktopmode/DesktopTasksController.kt | 116 ++++++++++++++++++ .../desktopmode/DesktopTasksControllerTest.kt | 98 +++++++++++++++ 5 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt create mode 100644 libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index c743582c32641..09f5cf1d31e48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -61,6 +61,7 @@ import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; +import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.displayareahelper.DisplayAreaHelperController; import com.android.wm.shell.draganddrop.DragAndDropController; @@ -677,7 +678,11 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static Optional provideDesktopMode( - Optional desktopModeController) { + Optional desktopModeController, + Optional desktopTasksController) { + if (DesktopModeStatus.isProto2Enabled()) { + return desktopTasksController.map(DesktopTasksController::asDesktopMode); + } return desktopModeController.map(DesktopModeController::asDesktopMode); } @@ -698,6 +703,23 @@ public abstract class WMShellBaseModule { return Optional.empty(); } + @BindsOptionalOf + @DynamicOverride + abstract DesktopTasksController optionalDesktopTasksController(); + + @WMSingleton + @Provides + static Optional providesDesktopTasksController( + @DynamicOverride Optional> desktopTasksController) { + // Use optional-of-lazy for the dependency that this provider relies on. + // Lazy ensures that this provider will not be the cause the dependency is created + // when it will not be returned due to the condition below. + if (DesktopModeStatus.isProto2Enabled()) { + return desktopTasksController.map(Lazy::get); + } + return Optional.empty(); + } + @BindsOptionalOf @DynamicOverride abstract DesktopModeTaskRepository optionalDesktopModeTaskRepository(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 6be83054ae597..8a6fe437e652d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -50,6 +50,7 @@ import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; +import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.freeform.FreeformTaskListener; @@ -613,6 +614,22 @@ public abstract class WMShellModule { mainExecutor); } + @WMSingleton + @Provides + @DynamicOverride + static DesktopTasksController provideDesktopTasksController( + Context context, + ShellInit shellInit, + ShellController shellController, + ShellTaskOrganizer shellTaskOrganizer, + Transitions transitions, + @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository, + @ShellMainThread ShellExecutor mainExecutor + ) { + return new DesktopTasksController(context, shellInit, shellController, shellTaskOrganizer, + transitions, desktopModeTaskRepository, mainExecutor); + } + @WMSingleton @Provides @DynamicOverride diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java index 67f4a1914c492..055949fd8c89d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java @@ -70,9 +70,13 @@ public class DesktopModeStatus { * @return {@code true} if active */ public static boolean isActive(Context context) { - if (!IS_SUPPORTED) { + if (!isAnyEnabled()) { return false; } + if (isProto2Enabled()) { + // Desktop mode is always active in prototype 2 + return true; + } try { int result = Settings.System.getIntForUser(context.getContentResolver(), Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt new file mode 100644 index 0000000000000..736aa7e9aeca3 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2022 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.wm.shell.desktopmode + +import android.content.Context +import androidx.annotation.BinderThread +import com.android.internal.protolog.common.ProtoLog +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.common.ExternalInterfaceBinder +import com.android.wm.shell.common.RemoteCallable +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.common.annotations.ExternalThread +import com.android.wm.shell.common.annotations.ShellMainThread +import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.sysui.ShellController +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.sysui.ShellSharedConstants +import com.android.wm.shell.transition.Transitions +import java.util.concurrent.Executor + +/** Handles moving tasks in and out of desktop */ +class DesktopTasksController( + private val context: Context, + shellInit: ShellInit, + private val shellController: ShellController, + private val shellTaskOrganizer: ShellTaskOrganizer, + private val transitions: Transitions, + private val desktopModeTaskRepository: DesktopModeTaskRepository, + @ShellMainThread private val mainExecutor: ShellExecutor +) : RemoteCallable { + + private val desktopMode: DesktopModeImpl + + init { + desktopMode = DesktopModeImpl() + if (DesktopModeStatus.isProto2Enabled()) { + shellInit.addInitCallback({ onInit() }, this) + } + } + + private fun onInit() { + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopTasksController") + shellController.addExternalInterface( + ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE, + { createExternalInterface() }, + this + ) + } + + override fun getContext(): Context { + return context + } + + override fun getRemoteCallExecutor(): ShellExecutor { + return mainExecutor + } + + /** Creates a new instance of the external interface to pass to another process. */ + private fun createExternalInterface(): ExternalInterfaceBinder { + return IDesktopModeImpl(this) + } + + /** Get connection interface between sysui and shell */ + fun asDesktopMode(): DesktopMode { + return desktopMode + } + + /** + * Adds a listener to find out about changes in the visibility of freeform tasks. + * + * @param listener the listener to add. + * @param callbackExecutor the executor to call the listener on. + */ + fun addListener(listener: VisibleTasksListener, callbackExecutor: Executor) { + desktopModeTaskRepository.addVisibleTasksListener(listener, callbackExecutor) + } + + /** The interface for calls from outside the shell, within the host process. */ + @ExternalThread + private inner class DesktopModeImpl : DesktopMode { + override fun addListener(listener: VisibleTasksListener, callbackExecutor: Executor) { + mainExecutor.execute { + this@DesktopTasksController.addListener(listener, callbackExecutor) + } + } + } + + /** The interface for calls from outside the host process. */ + @BinderThread + private class IDesktopModeImpl(private var controller: DesktopTasksController?) : + IDesktopMode.Stub(), ExternalInterfaceBinder { + /** Invalidates this instance, preventing future calls from updating the controller. */ + override fun invalidate() { + controller = null + } + + override fun showDesktopApps() { + // TODO + } + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt new file mode 100644 index 0000000000000..15155f80247bf --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2022 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.wm.shell.desktopmode + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession +import com.android.dx.mockito.inline.extended.ExtendedMockito.never +import com.android.dx.mockito.inline.extended.StaticMockitoSession +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.TestShellExecutor +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.sysui.ShellController +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.Transitions +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.any +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever + + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class DesktopTasksControllerTest : ShellTestCase() { + + @Mock + lateinit var testExecutor: ShellExecutor + @Mock + lateinit var shellController: ShellController + @Mock + lateinit var shellTaskOrganizer: ShellTaskOrganizer + @Mock + lateinit var transitions: Transitions + + lateinit var mockitoSession: StaticMockitoSession + lateinit var controller: DesktopTasksController + lateinit var shellInit: ShellInit + lateinit var desktopModeTaskRepository: DesktopModeTaskRepository + + @Before + fun setUp() { + mockitoSession = mockitoSession().mockStatic(DesktopModeStatus::class.java).startMocking() + whenever(DesktopModeStatus.isProto2Enabled()).thenReturn(true) + + shellInit = Mockito.spy(ShellInit(testExecutor)) + desktopModeTaskRepository = DesktopModeTaskRepository() + + controller = createController() + + shellInit.init() + } + + private fun createController(): DesktopTasksController { + return DesktopTasksController(context, shellInit, shellController, + shellTaskOrganizer, transitions, desktopModeTaskRepository, TestShellExecutor()) + } + + @After + fun tearDown() { + mockitoSession.finishMocking() + } + + @Test + fun instantiate_addInitCallback() { + verify(shellInit).addInitCallback(any(), any()) + } + + @Test + fun instantiate_flagOff_doNotAddInitCallback() { + whenever(DesktopModeStatus.isProto2Enabled()).thenReturn(false) + clearInvocations(shellInit) + + createController() + + verify(shellInit, never()).addInitCallback(any(), any()) + } +} \ No newline at end of file From 7e40f7b4d1daa2fe9f9a9cf51922c28f9cf74aae Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Wed, 14 Dec 2022 20:42:34 +0000 Subject: [PATCH 2/4] Revert^2 "Implement showDesktopApps" 6fadd9130f6927c49690fae870803e37a7a0a846 Change-Id: I54f8ea80b094e6d0bc18273022e27b8dc0be6fb5 --- .../desktopmode/DesktopTasksController.kt | 60 ++++++- .../DesktopModeControllerTest.java | 52 +----- .../desktopmode/DesktopTasksControllerTest.kt | 150 ++++++++++++++++-- .../shell/desktopmode/DesktopTestHelpers.kt | 61 +++++++ .../wm/shell/desktopmode/MockToken.java | 40 +++++ 5 files changed, 303 insertions(+), 60 deletions(-) create mode 100644 libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt create mode 100644 libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 736aa7e9aeca3..8ded092f53830 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -16,10 +16,14 @@ package com.android.wm.shell.desktopmode +import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME import android.content.Context +import android.view.WindowManager +import android.window.WindowContainerTransaction import androidx.annotation.BinderThread import com.android.internal.protolog.common.ProtoLog import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.common.ExecutorUtils import com.android.wm.shell.common.ExternalInterfaceBinder import com.android.wm.shell.common.RemoteCallable import com.android.wm.shell.common.ShellExecutor @@ -32,6 +36,7 @@ import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.sysui.ShellSharedConstants import com.android.wm.shell.transition.Transitions import java.util.concurrent.Executor +import java.util.function.Consumer /** Handles moving tasks in and out of desktop */ class DesktopTasksController( @@ -62,6 +67,55 @@ class DesktopTasksController( ) } + /** Show all tasks, that are part of the desktop, on top of launcher */ + fun showDesktopApps() { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "showDesktopApps") + val wct = WindowContainerTransaction() + + bringDesktopAppsToFront(wct) + + // Execute transaction if there are pending operations + if (!wct.isEmpty) { + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + transitions.startTransition(WindowManager.TRANSIT_TO_FRONT, wct, null /* handler */) + } else { + shellTaskOrganizer.applyTransaction(wct) + } + } + } + + private fun bringDesktopAppsToFront(wct: WindowContainerTransaction) { + val activeTasks = desktopModeTaskRepository.getActiveTasks() + + // Skip if all tasks are already visible + if (activeTasks.isNotEmpty() && activeTasks.all(desktopModeTaskRepository::isVisibleTask)) { + ProtoLog.d( + WM_SHELL_DESKTOP_MODE, + "bringDesktopAppsToFront: active tasks are already in front, skipping." + ) + return + } + + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront") + + // First move home to front and then other tasks on top of it + moveHomeTaskToFront(wct) + + val allTasksInZOrder = desktopModeTaskRepository.getFreeformTasksInZOrder() + activeTasks + // Sort descending as the top task is at index 0. It should be ordered to top last + .sortedByDescending { taskId -> allTasksInZOrder.indexOf(taskId) } + .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) } + .forEach { task -> wct.reorder(task.token, true /* onTop */) } + } + + private fun moveHomeTaskToFront(wct: WindowContainerTransaction) { + shellTaskOrganizer + .getRunningTasks(context.displayId) + .firstOrNull { task -> task.activityType == ACTIVITY_TYPE_HOME } + ?.let { homeTask -> wct.reorder(homeTask.getToken(), true /* onTop */) } + } + override fun getContext(): Context { return context } @@ -110,7 +164,11 @@ class DesktopTasksController( } override fun showDesktopApps() { - // TODO + ExecutorUtils.executeRemoteCallWithTaskPermission( + controller, + "showDesktopApps", + Consumer(DesktopTasksController::showDesktopApps) + ) } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java index 707c04948b6f9..a3ba7677feff4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java @@ -16,8 +16,6 @@ package com.android.wm.shell.desktopmode; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -29,6 +27,9 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask; +import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask; +import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createHomeTask; import static com.google.common.truth.Truth.assertThat; @@ -48,7 +49,6 @@ import android.os.IBinder; import android.testing.AndroidTestingRunner; import android.window.DisplayAreaInfo; import android.window.TransitionRequestInfo; -import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import android.window.WindowContainerTransaction.Change; import android.window.WindowContainerTransaction.HierarchyOp; @@ -59,7 +59,6 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.sysui.ShellController; @@ -355,7 +354,7 @@ public class DesktopModeControllerTest extends ShellTestCase { @Test public void testHandleTransitionRequest_taskOpen_returnsWct() { RunningTaskInfo trigger = new RunningTaskInfo(); - trigger.token = new MockToken().mToken; + trigger.token = new MockToken().token(); trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); WindowContainerTransaction wct = mController.handleRequest( mock(IBinder.class), @@ -381,40 +380,13 @@ public class DesktopModeControllerTest extends ShellTestCase { } private DisplayAreaInfo createMockDisplayArea() { - DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(new MockToken().mToken, + DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(new MockToken().token(), mContext.getDisplayId(), 0); when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId())) .thenReturn(displayAreaInfo); return displayAreaInfo; } - private RunningTaskInfo createFreeformTask() { - return new TestRunningTaskInfoBuilder() - .setToken(new MockToken().token()) - .setActivityType(ACTIVITY_TYPE_STANDARD) - .setWindowingMode(WINDOWING_MODE_FREEFORM) - .setLastActiveTime(100) - .build(); - } - - private RunningTaskInfo createFullscreenTask() { - return new TestRunningTaskInfoBuilder() - .setToken(new MockToken().token()) - .setActivityType(ACTIVITY_TYPE_STANDARD) - .setWindowingMode(WINDOWING_MODE_FULLSCREEN) - .setLastActiveTime(100) - .build(); - } - - private RunningTaskInfo createHomeTask() { - return new TestRunningTaskInfoBuilder() - .setToken(new MockToken().token()) - .setActivityType(ACTIVITY_TYPE_HOME) - .setWindowingMode(WINDOWING_MODE_FULLSCREEN) - .setLastActiveTime(100) - .build(); - } - private WindowContainerTransaction getDesktopModeSwitchTransaction() { ArgumentCaptor arg = ArgumentCaptor.forClass( WindowContainerTransaction.class); @@ -442,18 +414,4 @@ public class DesktopModeControllerTest extends ShellTestCase { assertThat(change.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue(); } - private static class MockToken { - private final WindowContainerToken mToken; - private final IBinder mBinder; - - MockToken() { - mToken = mock(WindowContainerToken.class); - mBinder = mock(IBinder.class); - when(mToken.asBinder()).thenReturn(mBinder); - } - - WindowContainerToken token() { - return mToken; - } - } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 15155f80247bf..4e0a5b59cbdcb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -16,7 +16,10 @@ package com.android.wm.shell.desktopmode +import android.app.ActivityManager.RunningTaskInfo import android.testing.AndroidTestingRunner +import android.window.WindowContainerTransaction +import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.dx.mockito.inline.extended.ExtendedMockito.never @@ -25,39 +28,44 @@ import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestShellExecutor import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.isNull import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.any +import org.mockito.Mockito.anyInt import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever - @SmallTest @RunWith(AndroidTestingRunner::class) class DesktopTasksControllerTest : ShellTestCase() { - @Mock - lateinit var testExecutor: ShellExecutor - @Mock - lateinit var shellController: ShellController - @Mock - lateinit var shellTaskOrganizer: ShellTaskOrganizer - @Mock - lateinit var transitions: Transitions + @Mock lateinit var testExecutor: ShellExecutor + @Mock lateinit var shellController: ShellController + @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer + @Mock lateinit var transitions: Transitions lateinit var mockitoSession: StaticMockitoSession lateinit var controller: DesktopTasksController lateinit var shellInit: ShellInit lateinit var desktopModeTaskRepository: DesktopModeTaskRepository + // Mock running tasks are registered here so we can get the list from mock shell task organizer + private val runningTasks = mutableListOf() + @Before fun setUp() { mockitoSession = mockitoSession().mockStatic(DesktopModeStatus::class.java).startMocking() @@ -66,19 +74,30 @@ class DesktopTasksControllerTest : ShellTestCase() { shellInit = Mockito.spy(ShellInit(testExecutor)) desktopModeTaskRepository = DesktopModeTaskRepository() + whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks } + controller = createController() shellInit.init() } private fun createController(): DesktopTasksController { - return DesktopTasksController(context, shellInit, shellController, - shellTaskOrganizer, transitions, desktopModeTaskRepository, TestShellExecutor()) + return DesktopTasksController( + context, + shellInit, + shellController, + shellTaskOrganizer, + transitions, + desktopModeTaskRepository, + TestShellExecutor() + ) } @After fun tearDown() { mockitoSession.finishMocking() + + runningTasks.clear() } @Test @@ -95,4 +114,111 @@ class DesktopTasksControllerTest : ShellTestCase() { verify(shellInit, never()).addInitCallback(any(), any()) } -} \ No newline at end of file + + @Test + fun showDesktopApps_allAppsInvisible_bringsToFront() { + val homeTask = setUpHomeTask() + val task1 = setUpDesktopTask() + val task2 = setUpDesktopTask() + markTaskHidden(task1) + markTaskHidden(task2) + + controller.showDesktopApps() + + val wct = getLatestWct() + assertThat(wct.hierarchyOps).hasSize(3) + // Expect order to be from bottom: home, task1, task2 + wct.assertReorderAt(index = 0, homeTask) + wct.assertReorderAt(index = 1, task1) + wct.assertReorderAt(index = 2, task2) + } + + @Test + fun showDesktopApps_appsAlreadyVisible_doesNothing() { + setUpHomeTask() + val task1 = setUpDesktopTask() + val task2 = setUpDesktopTask() + markTaskVisible(task1) + markTaskVisible(task2) + + controller.showDesktopApps() + + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + verify(transitions, never()).startTransition(anyInt(), any(), isNull()) + } else { + verify(shellTaskOrganizer, never()).applyTransaction(any()) + } + } + + @Test + fun showDesktopApps_someAppsInvisible_reordersAll() { + val homeTask = setUpHomeTask() + val task1 = setUpDesktopTask() + val task2 = setUpDesktopTask() + markTaskHidden(task1) + markTaskVisible(task2) + + controller.showDesktopApps() + + val wct = getLatestWct() + assertThat(wct.hierarchyOps).hasSize(3) + // Expect order to be from bottom: home, task1, task2 + wct.assertReorderAt(index = 0, homeTask) + wct.assertReorderAt(index = 1, task1) + wct.assertReorderAt(index = 2, task2) + } + + @Test + fun showDesktopApps_noActiveTasks_reorderHomeToTop() { + val homeTask = setUpHomeTask() + + controller.showDesktopApps() + + val wct = getLatestWct() + assertThat(wct.hierarchyOps).hasSize(1) + wct.assertReorderAt(index = 0, homeTask) + } + + private fun setUpDesktopTask(): RunningTaskInfo { + val task = createFreeformTask() + whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + desktopModeTaskRepository.addActiveTask(task.taskId) + desktopModeTaskRepository.addOrMoveFreeformTaskToTop(task.taskId) + runningTasks.add(task) + return task + } + + private fun setUpHomeTask(): RunningTaskInfo { + val task = createHomeTask() + whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + runningTasks.add(task) + return task + } + + private fun markTaskVisible(task: RunningTaskInfo) { + desktopModeTaskRepository.updateVisibleFreeformTasks(task.taskId, visible = true) + } + + private fun markTaskHidden(task: RunningTaskInfo) { + desktopModeTaskRepository.updateVisibleFreeformTasks(task.taskId, visible = false) + } + + private fun getLatestWct(): WindowContainerTransaction { + val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + verify(transitions).startTransition(anyInt(), arg.capture(), isNull()) + } else { + verify(shellTaskOrganizer).applyTransaction(arg.capture()) + } + return arg.value + } +} + +private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) { + assertWithMessage("WCT does not have a hierarchy operation at index $index") + .that(hierarchyOps.size) + .isGreaterThan(index) + val op = hierarchyOps[index] + assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REORDER) + assertThat(op.container).isEqualTo(task.token.asBinder()) +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt new file mode 100644 index 0000000000000..dc91d756842ec --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 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.wm.shell.desktopmode + +import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME +import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import com.android.wm.shell.TestRunningTaskInfoBuilder + +class DesktopTestHelpers { + companion object { + /** Create a task that has windowing mode set to [WINDOWING_MODE_FREEFORM] */ + @JvmStatic + fun createFreeformTask(): RunningTaskInfo { + return TestRunningTaskInfoBuilder() + .setToken(MockToken().token()) + .setActivityType(ACTIVITY_TYPE_STANDARD) + .setWindowingMode(WINDOWING_MODE_FREEFORM) + .setLastActiveTime(100) + .build() + } + + /** Create a task that has windowing mode set to [WINDOWING_MODE_FULLSCREEN] */ + @JvmStatic + fun createFullscreenTask(): RunningTaskInfo { + return TestRunningTaskInfoBuilder() + .setToken(MockToken().token()) + .setActivityType(ACTIVITY_TYPE_STANDARD) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) + .setLastActiveTime(100) + .build() + } + + /** Create a new home task */ + @JvmStatic + fun createHomeTask(): RunningTaskInfo { + return TestRunningTaskInfoBuilder() + .setToken(MockToken().token()) + .setActivityType(ACTIVITY_TYPE_HOME) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) + .setLastActiveTime(100) + .build() + } + } +} \ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java new file mode 100644 index 0000000000000..09d474d1f97c4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 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.wm.shell.desktopmode; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.os.IBinder; +import android.window.WindowContainerToken; + +/** + * {@link WindowContainerToken} wrapper that supports a mock binder + */ +class MockToken { + private final WindowContainerToken mToken; + + MockToken() { + mToken = mock(WindowContainerToken.class); + IBinder binder = mock(IBinder.class); + when(mToken.asBinder()).thenReturn(binder); + } + + WindowContainerToken token() { + return mToken; + } +} From fab81f10e53335cdabcfa21e18f7ab6d9e34c806 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Wed, 14 Dec 2022 20:42:34 +0000 Subject: [PATCH 3/4] Revert^2 "Implement move to desktop and fullscreen" 27ab8c084b36064543e42c6766a71d83dfdbfb66 Change-Id: I38187ab6c54ce80193902cebcabced662f79a6ec --- .../wm/shell/dagger/WMShellModule.java | 6 +- .../desktopmode/DesktopTasksController.kt | 57 +++++++++++++ .../CaptionWindowDecorViewModel.java | 85 +++++++++++++++---- .../windowdecor/CaptionWindowDecoration.java | 46 +++++++--- .../desktopmode/DesktopTasksControllerTest.kt | 81 +++++++++++++++--- .../CaptionWindowDecorViewModelTests.java | 3 + 6 files changed, 235 insertions(+), 43 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 8a6fe437e652d..701a3a42fadd3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -190,7 +190,8 @@ public abstract class WMShellModule { ShellTaskOrganizer taskOrganizer, DisplayController displayController, SyncTransactionQueue syncQueue, - Optional desktopModeController) { + Optional desktopModeController, + Optional desktopTasksController) { return new CaptionWindowDecorViewModel( context, mainHandler, @@ -198,7 +199,8 @@ public abstract class WMShellModule { taskOrganizer, displayController, syncQueue, - desktopModeController); + desktopModeController, + desktopTasksController); } // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 8ded092f53830..b075b14fb0a48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -16,7 +16,11 @@ package com.android.wm.shell.desktopmode +import android.app.ActivityManager +import android.app.WindowConfiguration import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME +import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED +import android.app.WindowConfiguration.WindowingMode import android.content.Context import android.view.WindowManager import android.window.WindowContainerTransaction @@ -84,6 +88,59 @@ class DesktopTasksController( } } + /** Move a task with given `taskId` to desktop */ + fun moveToDesktop(taskId: Int) { + shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveToDesktop(task) } + } + + /** Move a task to desktop */ + fun moveToDesktop(task: ActivityManager.RunningTaskInfo) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToDesktop: %d", task.taskId) + + val wct = WindowContainerTransaction() + // Bring other apps to front first + bringDesktopAppsToFront(wct) + + wct.setWindowingMode(task.getToken(), WindowConfiguration.WINDOWING_MODE_FREEFORM) + wct.reorder(task.getToken(), true /* onTop */) + + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + transitions.startTransition(WindowManager.TRANSIT_CHANGE, wct, null /* handler */) + } else { + shellTaskOrganizer.applyTransaction(wct) + } + } + + /** Move a task with given `taskId` to fullscreen */ + fun moveToFullscreen(taskId: Int) { + shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveToFullscreen(task) } + } + + /** Move a task to fullscreen */ + fun moveToFullscreen(task: ActivityManager.RunningTaskInfo) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToFullscreen: %d", task.taskId) + + val wct = WindowContainerTransaction() + wct.setWindowingMode(task.getToken(), WindowConfiguration.WINDOWING_MODE_FULLSCREEN) + wct.setBounds(task.getToken(), null) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + transitions.startTransition(WindowManager.TRANSIT_CHANGE, wct, null /* handler */) + } else { + shellTaskOrganizer.applyTransaction(wct) + } + } + + /** + * Get windowing move for a given `taskId` + * + * @return [WindowingMode] for the task or [WINDOWING_MODE_UNDEFINED] if task is not found + */ + @WindowingMode + fun getTaskWindowingMode(taskId: Int): Int { + return shellTaskOrganizer.getRunningTaskInfo(taskId)?.windowingMode + ?: WINDOWING_MODE_UNDEFINED + } + private fun bringDesktopAppsToFront(wct: WindowContainerTransaction) { val activeTasks = desktopModeTaskRepository.getActiveTasks() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index 42e2b3fadf19e..299284f5bda6e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -53,6 +53,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeStatus; +import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.transition.Transitions; @@ -75,6 +76,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { private final SyncTransactionQueue mSyncQueue; private FreeformTaskTransitionStarter mTransitionStarter; private Optional mDesktopModeController; + private Optional mDesktopTasksController; private boolean mTransitionDragActive; private SparseArray mEventReceiversByDisplay = new SparseArray<>(); @@ -90,7 +92,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { ShellTaskOrganizer taskOrganizer, DisplayController displayController, SyncTransactionQueue syncQueue, - Optional desktopModeController) { + Optional desktopModeController, + Optional desktopTasksController) { this( context, mainHandler, @@ -99,6 +102,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { displayController, syncQueue, desktopModeController, + desktopTasksController, new CaptionWindowDecoration.Factory(), new InputMonitorFactory()); } @@ -112,6 +116,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { DisplayController displayController, SyncTransactionQueue syncQueue, Optional desktopModeController, + Optional desktopTasksController, CaptionWindowDecoration.Factory captionWindowDecorFactory, InputMonitorFactory inputMonitorFactory) { mContext = context; @@ -122,6 +127,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mDisplayController = displayController; mSyncQueue = syncQueue; mDesktopModeController = desktopModeController; + mDesktopTasksController = desktopTasksController; mCaptionWindowDecorFactory = captionWindowDecorFactory; mInputMonitorFactory = inputMonitorFactory; @@ -242,11 +248,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { decoration.createHandleMenu(); } else if (id == R.id.desktop_button) { mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); + mDesktopTasksController.ifPresent(c -> c.moveToDesktop(mTaskId)); decoration.closeHandleMenu(); } else if (id == R.id.fullscreen_button) { mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false)); + mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId)); decoration.closeHandleMenu(); - decoration.setButtonVisibility(); + decoration.setButtonVisibility(false); } } @@ -299,8 +307,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { */ private void handleEventForMove(MotionEvent e) { RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId); - if (mDesktopModeController.isPresent() - && mDesktopModeController.get().getDisplayAreaWindowingMode(taskInfo.displayId) + if (DesktopModeStatus.isProto2Enabled() + && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { + return; + } + if (DesktopModeStatus.isProto1Enabled() && mDesktopModeController.isPresent() + && mDesktopModeController.get().getDisplayAreaWindowingMode( + taskInfo.displayId) == WINDOWING_MODE_FULLSCREEN) { return; } @@ -324,9 +337,20 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { .stableInsets().top; mDragResizeCallback.onDragResizeEnd( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); - if (e.getRawY(dragPointerIdx) <= statusBarHeight - && DesktopModeStatus.isActive(mContext)) { - mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false)); + if (e.getRawY(dragPointerIdx) <= statusBarHeight) { + if (DesktopModeStatus.isProto2Enabled()) { + if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { + // Switch a single task to fullscreen + mDesktopTasksController.ifPresent( + c -> c.moveToFullscreen(taskInfo)); + } + } else if (DesktopModeStatus.isProto1Enabled()) { + if (DesktopModeStatus.isActive(mContext)) { + // Turn off desktop mode + mDesktopModeController.ifPresent( + c -> c.setDesktopModeActive(false)); + } + } } break; } @@ -408,13 +432,27 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { * @param ev the {@link MotionEvent} received by {@link EventReceiver} */ private void handleReceivedMotionEvent(MotionEvent ev, InputMonitor inputMonitor) { - if (!DesktopModeStatus.isActive(mContext)) { - handleCaptionThroughStatusBar(ev); + if (DesktopModeStatus.isProto2Enabled()) { + CaptionWindowDecoration focusedDecor = getFocusedDecor(); + if (focusedDecor == null + || focusedDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) { + handleCaptionThroughStatusBar(ev); + } + } else if (DesktopModeStatus.isProto1Enabled()) { + if (!DesktopModeStatus.isActive(mContext)) { + handleCaptionThroughStatusBar(ev); + } } handleEventOutsideFocusedCaption(ev); // Prevent status bar from reacting to a caption drag. - if (mTransitionDragActive && !DesktopModeStatus.isActive(mContext)) { - inputMonitor.pilferPointers(); + if (DesktopModeStatus.isProto2Enabled()) { + if (mTransitionDragActive) { + inputMonitor.pilferPointers(); + } + } else if (DesktopModeStatus.isProto1Enabled()) { + if (mTransitionDragActive && !DesktopModeStatus.isActive(mContext)) { + inputMonitor.pilferPointers(); + } } } @@ -443,9 +481,20 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { case MotionEvent.ACTION_DOWN: { // Begin drag through status bar if applicable. CaptionWindowDecoration focusedDecor = getFocusedDecor(); - if (focusedDecor != null && !DesktopModeStatus.isActive(mContext) - && focusedDecor.checkTouchEventInHandle(ev)) { - mTransitionDragActive = true; + if (focusedDecor != null) { + boolean dragFromStatusBarAllowed = false; + if (DesktopModeStatus.isProto2Enabled()) { + // In proto2 any full screen task can be dragged to freeform + dragFromStatusBarAllowed = focusedDecor.mTaskInfo.getWindowingMode() + == WINDOWING_MODE_FULLSCREEN; + } else if (DesktopModeStatus.isProto1Enabled()) { + // In proto1 task can be dragged to freeform when not in desktop mode + dragFromStatusBarAllowed = !DesktopModeStatus.isActive(mContext); + } + + if (dragFromStatusBarAllowed && focusedDecor.checkTouchEventInHandle(ev)) { + mTransitionDragActive = true; + } } break; } @@ -460,7 +509,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { int statusBarHeight = mDisplayController .getDisplayLayout(focusedDecor.mTaskInfo.displayId).stableInsets().top; if (ev.getY() > statusBarHeight) { - mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); + if (DesktopModeStatus.isProto2Enabled()) { + mDesktopTasksController.ifPresent( + c -> c.moveToDesktop(focusedDecor.mTaskInfo)); + } else if (DesktopModeStatus.isProto1Enabled()) { + mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); + } + return; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 037ca2031254f..f7c7a87e66596 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -16,8 +16,9 @@ package com.android.wm.shell.windowdecor; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; + import android.app.ActivityManager; -import android.app.WindowConfiguration; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources; @@ -117,7 +118,7 @@ public class CaptionWindowDecoration extends WindowDecoration Date: Wed, 14 Dec 2022 17:22:52 -0800 Subject: [PATCH 4/4] Fix test after MockToken refactor MockToken class was extracted from DesktopModeControllerTest. But conflicting changes landed which broke the build and the changes were reverted. Applying the changes again by reverting the revert and fixing the test. Bug: 260645044,261234251,260645044 Test: atest DesktopModeControllerTest DesktopTasksControllerTest Change-Id: I95f66c35c671e774c3a4f7ba6b64167595f69b60 --- .../android/wm/shell/desktopmode/DesktopModeControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java index a3ba7677feff4..08af3d3eecfe3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java @@ -365,7 +365,7 @@ public class DesktopModeControllerTest extends ShellTestCase { @Test public void testHandleTransitionRequest_taskToFront_returnsWct() { RunningTaskInfo trigger = new RunningTaskInfo(); - trigger.token = new MockToken().mToken; + trigger.token = new MockToken().token(); trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); WindowContainerTransaction wct = mController.handleRequest( mock(IBinder.class),