diff options
| author | 2022-12-06 11:08:46 -0800 | |
|---|---|---|
| committer | 2022-12-08 17:43:17 -0800 | |
| commit | e57c2641312bc7b0a6cac2fbe2a13c12c06bc112 (patch) | |
| tree | d76f4b7cb534b811ec6953d3ac4d14fdf1e2b7a4 | |
| parent | 5e432142561a06ee8dcdf492afe2c0754a3626b4 (diff) | |
Create controller for desktop prototype 2
Create the controller class, set up dagger.
Ensure controller is not initialized when flag is off.
Bug: 260645044
Test: atest DesktopTasksControllerTest
Change-Id: I0435e07a22e379f403ae5d1b23f39a67a12e8a6d
5 files changed, 259 insertions, 2 deletions
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 c743582c3264..09f5cf1d31e4 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<DesktopMode> provideDesktopMode( - Optional<DesktopModeController> desktopModeController) { + Optional<DesktopModeController> desktopModeController, + Optional<DesktopTasksController> desktopTasksController) { + if (DesktopModeStatus.isProto2Enabled()) { + return desktopTasksController.map(DesktopTasksController::asDesktopMode); + } return desktopModeController.map(DesktopModeController::asDesktopMode); } @@ -700,6 +705,23 @@ public abstract class WMShellBaseModule { @BindsOptionalOf @DynamicOverride + abstract DesktopTasksController optionalDesktopTasksController(); + + @WMSingleton + @Provides + static Optional<DesktopTasksController> providesDesktopTasksController( + @DynamicOverride Optional<Lazy<DesktopTasksController>> 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(); @WMSingleton 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 6be83054ae59..8a6fe437e652 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; @@ -616,6 +617,22 @@ public abstract class WMShellModule { @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 static DesktopModeTaskRepository provideDesktopModeTaskRepository() { return new DesktopModeTaskRepository(); } 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 67f4a1914c49..055949fd8c89 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 000000000000..736aa7e9aeca --- /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<DesktopTasksController> { + + 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 000000000000..15155f80247b --- /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<DesktopTasksController>()) + } + + @Test + fun instantiate_flagOff_doNotAddInitCallback() { + whenever(DesktopModeStatus.isProto2Enabled()).thenReturn(false) + clearInvocations(shellInit) + + createController() + + verify(shellInit, never()).addInitCallback(any(), any<DesktopTasksController>()) + } +}
\ No newline at end of file |