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 c9d523c779ea9..befac1e1df3ab 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 @@ -57,6 +57,8 @@ import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; import com.android.wm.shell.compatui.CompatUIController; 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.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.displayareahelper.DisplayAreaHelperController; @@ -714,15 +716,36 @@ public abstract class WMShellBaseModule { // Desktop mode (optional feature) // + @WMSingleton + @Provides + static Optional provideDesktopMode( + Optional desktopModeController) { + return desktopModeController.map(DesktopModeController::asDesktopMode); + } + + @BindsOptionalOf + @DynamicOverride + abstract DesktopModeController optionalDesktopModeController(); + + @WMSingleton + @Provides + static Optional providesDesktopModeController( + @DynamicOverride Optional desktopModeController) { + if (DesktopModeStatus.IS_SUPPORTED) { + return desktopModeController; + } + return Optional.empty(); + } + @BindsOptionalOf @DynamicOverride abstract DesktopModeTaskRepository optionalDesktopModeTaskRepository(); @WMSingleton @Provides - static Optional providesDesktopModeTaskRepository( + static Optional providesDesktopTaskRepository( @DynamicOverride Optional desktopModeTaskRepository) { - if (DesktopMode.IS_SUPPORTED) { + if (DesktopModeStatus.IS_SUPPORTED) { return desktopModeTaskRepository; } return Optional.empty(); 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 35e88e9abb3c1..e784261daa7e9 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 @@ -48,7 +48,6 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; -import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.draganddrop.DragAndDropController; @@ -595,19 +594,18 @@ public abstract class WMShellModule { @WMSingleton @Provides - static Optional provideDesktopModeController( - Context context, ShellInit shellInit, + @DynamicOverride + static DesktopModeController provideDesktopModeController(Context context, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + Transitions transitions, + @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository, @ShellMainThread Handler mainHandler, - Transitions transitions + @ShellMainThread ShellExecutor mainExecutor ) { - if (DesktopMode.IS_SUPPORTED) { - return Optional.of(new DesktopModeController(context, shellInit, shellTaskOrganizer, - rootTaskDisplayAreaOrganizer, mainHandler, transitions)); - } else { - return Optional.empty(); - } + return new DesktopModeController(context, shellInit, shellTaskOrganizer, + rootTaskDisplayAreaOrganizer, transitions, desktopModeTaskRepository, mainHandler, + mainExecutor); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java index 8993d549964cd..ff3be38d09e1b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java @@ -16,43 +16,16 @@ package com.android.wm.shell.desktopmode; -import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; - -import android.content.Context; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.provider.Settings; - -import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.common.annotations.ExternalThread; /** - * Constants for desktop mode feature + * Interface to interact with desktop mode feature in shell. */ -public class DesktopMode { +@ExternalThread +public interface DesktopMode { - /** - * Flag to indicate whether desktop mode is available on the device - */ - public static final boolean IS_SUPPORTED = SystemProperties.getBoolean( - "persist.wm.debug.desktop_mode", false); - - /** - * Check if desktop mode is active - * - * @return {@code true} if active - */ - public static boolean isActive(Context context) { - if (!IS_SUPPORTED) { - return false; - } - try { - int result = Settings.System.getIntForUser(context.getContentResolver(), - Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT); - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result); - return result != 0; - } catch (Exception e) { - ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e); - return false; - } + /** Returns a binder that can be passed to an external process to manipulate DesktopMode. */ + default IDesktopMode createExternalInterface() { + return null; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java index 6e44d58cffaea..9474cfe916f01 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java @@ -20,8 +20,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.TRANSIT_CHANGE; +import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; +import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration; import android.content.Context; import android.database.ContentObserver; @@ -29,51 +31,83 @@ import android.net.Uri; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; +import android.util.ArraySet; import android.window.DisplayAreaInfo; import android.window.WindowContainerTransaction; +import androidx.annotation.BinderThread; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; +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.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; +import java.util.ArrayList; +import java.util.Comparator; + /** * Handles windowing changes when desktop mode system setting changes */ -public class DesktopModeController { +public class DesktopModeController implements RemoteCallable { private final Context mContext; private final ShellTaskOrganizer mShellTaskOrganizer; private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; - private final SettingsObserver mSettingsObserver; private final Transitions mTransitions; + private final DesktopModeTaskRepository mDesktopModeTaskRepository; + private final ShellExecutor mMainExecutor; + private final DesktopMode mDesktopModeImpl = new DesktopModeImpl(); + private final SettingsObserver mSettingsObserver; public DesktopModeController(Context context, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + Transitions transitions, + DesktopModeTaskRepository desktopModeTaskRepository, @ShellMainThread Handler mainHandler, - Transitions transitions) { + @ShellMainThread ShellExecutor mainExecutor) { mContext = context; mShellTaskOrganizer = shellTaskOrganizer; mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; - mSettingsObserver = new SettingsObserver(mContext, mainHandler); mTransitions = transitions; + mDesktopModeTaskRepository = desktopModeTaskRepository; + mMainExecutor = mainExecutor; + mSettingsObserver = new SettingsObserver(mContext, mainHandler); shellInit.addInitCallback(this::onInit, this); } private void onInit() { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopModeController"); mSettingsObserver.observe(); - if (DesktopMode.isActive(mContext)) { + if (DesktopModeStatus.isActive(mContext)) { updateDesktopModeActive(true); } } + @Override + public Context getContext() { + return mContext; + } + + @Override + public ShellExecutor getRemoteCallExecutor() { + return mMainExecutor; + } + + /** + * Get connection interface between sysui and shell + */ + public DesktopMode asDesktopMode() { + return mDesktopModeImpl; + } + @VisibleForTesting void updateDesktopModeActive(boolean active) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeActive: active=%s", active); @@ -120,6 +154,28 @@ public class DesktopModeController { wct.setWindowingMode(displayAreaInfo.token, windowingMode); } + /** + * Show apps on desktop + */ + public void showDesktopApps() { + ArraySet activeTasks = mDesktopModeTaskRepository.getActiveTasks(); + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size()); + ArrayList taskInfos = new ArrayList<>(); + for (Integer taskId : activeTasks) { + RunningTaskInfo taskInfo = mShellTaskOrganizer.getRunningTaskInfo(taskId); + if (taskInfo != null) { + taskInfos.add(taskInfo); + } + } + // Order by lastActiveTime, descending + taskInfos.sort(Comparator.comparingLong(task -> -task.lastActiveTime)); + WindowContainerTransaction wct = new WindowContainerTransaction(); + for (RunningTaskInfo task : taskInfos) { + wct.reorder(task.token, true); + } + mShellTaskOrganizer.applyTransaction(wct); + } + /** * A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE} */ @@ -150,8 +206,51 @@ public class DesktopModeController { } private void desktopModeSettingChanged() { - boolean enabled = DesktopMode.isActive(mContext); + boolean enabled = DesktopModeStatus.isActive(mContext); updateDesktopModeActive(enabled); } } + + /** + * The interface for calls from outside the shell, within the host process. + */ + @ExternalThread + private final class DesktopModeImpl implements DesktopMode { + + private IDesktopModeImpl mIDesktopMode; + + @Override + public IDesktopMode createExternalInterface() { + if (mIDesktopMode != null) { + mIDesktopMode.invalidate(); + } + mIDesktopMode = new IDesktopModeImpl(DesktopModeController.this); + return mIDesktopMode; + } + } + + /** + * The interface for calls from outside the host process. + */ + @BinderThread + private static class IDesktopModeImpl extends IDesktopMode.Stub { + + private DesktopModeController mController; + + IDesktopModeImpl(DesktopModeController controller) { + mController = controller; + } + + /** + * Invalidates this instance, preventing future calls from updating the controller. + */ + void invalidate() { + mController = null; + } + + public void showDesktopApps() { + executeRemoteCallWithTaskPermission(mController, "showDesktopApps", + DesktopModeController::showDesktopApps); + } + } } 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 new file mode 100644 index 0000000000000..195ff502e7dc9 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java @@ -0,0 +1,58 @@ +/* + * 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 com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; + +import android.content.Context; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.provider.Settings; + +import com.android.internal.protolog.common.ProtoLog; + +/** + * Constants for desktop mode feature + */ +public class DesktopModeStatus { + + /** + * Flag to indicate whether desktop mode is available on the device + */ + public static final boolean IS_SUPPORTED = SystemProperties.getBoolean( + "persist.wm.debug.desktop_mode", false); + + /** + * Check if desktop mode is active + * + * @return {@code true} if active + */ + public static boolean isActive(Context context) { + if (!IS_SUPPORTED) { + return false; + } + try { + int result = Settings.System.getIntForUser(context.getContentResolver(), + Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT); + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result); + return result != 0; + } catch (Exception e) { + ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e); + return false; + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl new file mode 100644 index 0000000000000..5042bd6f2d659 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl @@ -0,0 +1,26 @@ +/* + * 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; + +/** + * Interface that is exposed to remote callers to manipulate desktop mode features. + */ +interface IDesktopMode { + + /** Show apps on the desktop */ + void showDesktopApps(); +} \ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java index f58719b225a45..e2d5a499d1e11 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java @@ -28,7 +28,7 @@ import androidx.annotation.Nullable; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.desktopmode.DesktopMode; +import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.sysui.ShellInit; @@ -90,7 +90,7 @@ public class FreeformTaskListener t.apply(); } - if (DesktopMode.IS_SUPPORTED && taskInfo.isVisible) { + if (DesktopModeStatus.IS_SUPPORTED && taskInfo.isVisible) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Adding active freeform task: #%d", taskInfo.taskId); mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTask(taskInfo.taskId)); @@ -123,7 +123,7 @@ public class FreeformTaskListener taskInfo.taskId); mTasks.remove(taskInfo.taskId); - if (DesktopMode.IS_SUPPORTED) { + if (DesktopModeStatus.IS_SUPPORTED) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Removing active freeform task: #%d", taskInfo.taskId); mDesktopModeTaskRepository.ifPresent(it -> it.removeActiveTask(taskInfo.taskId)); @@ -150,7 +150,7 @@ public class FreeformTaskListener mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration); } - if (DesktopMode.IS_SUPPORTED) { + if (DesktopModeStatus.IS_SUPPORTED) { if (taskInfo.isVisible) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Adding active freeform task: #%d", taskInfo.taskId); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index ff4b2edb7310d..f47d2d3e8d953 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -43,7 +43,7 @@ import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.common.annotations.ShellMainThread; -import com.android.wm.shell.desktopmode.DesktopMode; +import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.sysui.ShellCommandHandler; @@ -286,7 +286,7 @@ public class RecentTasksController implements TaskStackListenerCallback, rawMapping.put(taskInfo.taskId, taskInfo); } - boolean desktopModeActive = DesktopMode.isActive(mContext); + boolean desktopModeActive = DesktopModeStatus.isActive(mContext); ArrayList freeformTasks = new ArrayList<>(); // Pull out the pairs as we iterate back in the list @@ -319,7 +319,6 @@ public class RecentTasksController implements TaskStackListenerCallback, // Add a special entry for freeform tasks if (!freeformTasks.isEmpty()) { - // First task is added separately recentTasks.add(0, GroupedRecentTaskInfo.forFreeformTasks( freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0]))); } 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 e8a2cb1608804..9c7131a9e02e2 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 @@ -36,7 +36,7 @@ import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.desktopmode.DesktopMode; +import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.transition.Transitions; @@ -239,7 +239,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel= 600; } 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 5040bc37c6146..733f6b7d5dbf7 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 @@ -34,7 +34,7 @@ import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.desktopmode.DesktopMode; +import com.android.wm.shell.desktopmode.DesktopModeStatus; /** * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with @@ -164,7 +164,7 @@ public class CaptionWindowDecoration extends WindowDecoration wctCaptor = ArgumentCaptor.forClass( + WindowContainerTransaction.class); + verify(mShellTaskOrganizer).applyTransaction(wctCaptor.capture()); + WindowContainerTransaction wct = wctCaptor.getValue(); + + // Check wct has reorder calls + assertThat(wct.getHierarchyOps()).hasSize(2); + + // Task 2 has activity later, must be first + WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0); + assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); + assertThat(op1.getContainer()).isEqualTo(token2.binder()); + + // Task 1 should be second + WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(0); + assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); + assertThat(op2.getContainer()).isEqualTo(token2.binder()); + } + private static class MockToken { private final WindowContainerToken mToken; private final IBinder mBinder; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java index cadfeb0de3126..70fee2be90c6a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java @@ -54,7 +54,7 @@ import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerImpl; -import com.android.wm.shell.desktopmode.DesktopMode; +import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellInit; @@ -190,8 +190,8 @@ public class RecentTasksControllerTest extends ShellTestCase { @Test public void testGetRecentTasks_groupActiveFreeformTasks() { StaticMockitoSession mockitoSession = mockitoSession().mockStatic( - DesktopMode.class).startMocking(); - when(DesktopMode.isActive(any())).thenReturn(true); + DesktopModeStatus.class).startMocking(); + when(DesktopModeStatus.isActive(any())).thenReturn(true); ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1); ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java index 422274469dcc9..2111df501415f 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java @@ -237,6 +237,13 @@ public class Task { public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData(); + /** + * Indicates that this task for the desktop tile in recents. + * + * Used when desktop mode feature is enabled. + */ + public boolean desktopTile; + public Task() { // Do nothing } @@ -267,6 +274,7 @@ public class Task { this(other.key, other.colorPrimary, other.colorBackground, other.isDockable, other.isLocked, other.taskDescription, other.topActivity); lastSnapshotData.set(other.lastSnapshotData); + desktopTile = other.desktopTile; } /** diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 6d12485fd6b5f..85278dd4b8837 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -62,6 +62,8 @@ public class QuickStepContract { // See IRecentTasks.aidl public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks"; public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation"; + // See IDesktopMode.aidl + public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode"; public static final String NAV_BAR_MODE_3BUTTON_OVERLAY = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY; diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java index 50c38e54b4d0e..a21f45f701b34 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java @@ -97,7 +97,8 @@ public abstract class SystemUIInitializer { .setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper()) .setRecentTasks(mWMComponent.getRecentTasks()) .setBackAnimation(mWMComponent.getBackAnimation()) - .setFloatingTasks(mWMComponent.getFloatingTasks()); + .setFloatingTasks(mWMComponent.getFloatingTasks()) + .setDesktopMode(mWMComponent.getDesktopMode()); // Only initialize when not starting from tests since this currently initializes some // components that shouldn't be run in the test environment @@ -117,7 +118,8 @@ public abstract class SystemUIInitializer { .setStartingSurface(Optional.ofNullable(null)) .setRecentTasks(Optional.ofNullable(null)) .setBackAnimation(Optional.ofNullable(null)) - .setFloatingTasks(Optional.ofNullable(null)); + .setFloatingTasks(Optional.ofNullable(null)) + .setDesktopMode(Optional.ofNullable(null)); } mSysUIComponent = builder.build(); if (initializeComponents) { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 7e30431a30c83..0d06c513d248b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -40,6 +40,7 @@ import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider; import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.bubbles.Bubbles; +import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.floating.FloatingTasks; import com.android.wm.shell.onehanded.OneHanded; @@ -113,6 +114,9 @@ public interface SysUIComponent { @BindsInstance Builder setFloatingTasks(Optional f); + @BindsInstance + Builder setDesktopMode(Optional d); + SysUIComponent build(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index dd115490abfa3..096f969493826 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -30,6 +30,7 @@ import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.dagger.TvWMShellModule; import com.android.wm.shell.dagger.WMShellModule; import com.android.wm.shell.dagger.WMSingleton; +import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.floating.FloatingTasks; import com.android.wm.shell.onehanded.OneHanded; @@ -112,4 +113,10 @@ public interface WMComponent { @WMSingleton Optional getFloatingTasks(); + + /** + * Optional {@link DesktopMode} component for interacting with desktop mode. + */ + @WMSingleton + Optional getDesktopMode(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 95edb359c53cf..7e2a5c51786d5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -27,6 +27,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION; +import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_DESKTOP_MODE; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_FLOATING_TASKS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP; @@ -110,6 +111,7 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.CallbackController; import com.android.wm.shell.back.BackAnimation; +import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.floating.FloatingTasks; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; @@ -169,6 +171,7 @@ public class OverviewProxyService extends CurrentUserTracker implements private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController; private final Optional mRecentTasks; private final Optional mBackAnimation; + private final Optional mDesktopModeOptional; private final UiEventLogger mUiEventLogger; private Region mActiveNavBarRegion; @@ -488,6 +491,9 @@ public class OverviewProxyService extends CurrentUserTracker implements mBackAnimation.ifPresent((backAnimation) -> params.putBinder( KEY_EXTRA_SHELL_BACK_ANIMATION, backAnimation.createExternalInterface().asBinder())); + mDesktopModeOptional.ifPresent((desktopMode -> params.putBinder( + KEY_EXTRA_SHELL_DESKTOP_MODE, + desktopMode.createExternalInterface().asBinder()))); try { Log.d(TAG_OPS, "OverviewProxyService connected, initializing overview proxy"); @@ -573,6 +579,7 @@ public class OverviewProxyService extends CurrentUserTracker implements Optional recentTasks, Optional backAnimation, Optional startingSurface, + Optional desktopModeOptional, BroadcastDispatcher broadcastDispatcher, ShellTransitions shellTransitions, ScreenLifecycle screenLifecycle, @@ -607,6 +614,7 @@ public class OverviewProxyService extends CurrentUserTracker implements mShellTransitions = shellTransitions; mRecentTasks = recentTasks; mBackAnimation = backAnimation; + mDesktopModeOptional = desktopModeOptional; mUiEventLogger = uiEventLogger; dumpManager.registerDumpable(getClass().getSimpleName(), this);