diff options
| author | Ats Jenk <atsjenk@google.com> | 2022-09-26 23:43:18 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2022-09-26 23:43:18 +0000 |
| commit | df2bdd51358e353bd4a261cc3fb8b260c2f83237 (patch) | |
| tree | 24c6cb0792fa289d7eb178174fdeb86786a6a78b | |
| parent | bf338c76331f1dffa3628cf30bf26150df6f0125 (diff) | |
| parent | d849498a69d0813fcb9b0522b38513f6ae064b4c (diff) | |
Merge "Add support to show apps on desktop in sysui proxy" into tm-qpr-dev
19 files changed, 361 insertions, 79 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 33074de8f583..f4dda4cc461a 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; @@ -716,15 +718,36 @@ public abstract class WMShellBaseModule { // Desktop mode (optional feature) // + @WMSingleton + @Provides + static Optional<DesktopMode> provideDesktopMode( + Optional<DesktopModeController> desktopModeController) { + return desktopModeController.map(DesktopModeController::asDesktopMode); + } + + @BindsOptionalOf + @DynamicOverride + abstract DesktopModeController optionalDesktopModeController(); + + @WMSingleton + @Provides + static Optional<DesktopModeController> providesDesktopModeController( + @DynamicOverride Optional<DesktopModeController> desktopModeController) { + if (DesktopModeStatus.IS_SUPPORTED) { + return desktopModeController; + } + return Optional.empty(); + } + @BindsOptionalOf @DynamicOverride abstract DesktopModeTaskRepository optionalDesktopModeTaskRepository(); @WMSingleton @Provides - static Optional<DesktopModeTaskRepository> providesDesktopModeTaskRepository( + static Optional<DesktopModeTaskRepository> providesDesktopTaskRepository( @DynamicOverride Optional<DesktopModeTaskRepository> 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 35e88e9abb3c..e784261daa7e 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<DesktopModeController> 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 8993d549964c..ff3be38d09e1 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 { - - /** - * 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); +@ExternalThread +public interface DesktopMode { - /** - * 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 6e44d58cffae..9474cfe916f0 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<DesktopModeController> { 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); @@ -121,6 +155,28 @@ public class DesktopModeController { } /** + * Show apps on desktop + */ + public void showDesktopApps() { + ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks(); + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size()); + ArrayList<RunningTaskInfo> 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} */ private final class SettingsObserver extends ContentObserver { @@ -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 000000000000..195ff502e7dc --- /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 000000000000..5042bd6f2d65 --- /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 f58719b225a4..e2d5a499d1e1 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 extends AutoCloseable> 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<T extends AutoCloseable> 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<T extends AutoCloseable> 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 f8799945134f..6409e70b0752 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 @@ -44,7 +44,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; @@ -287,7 +287,7 @@ public class RecentTasksController implements TaskStackListenerCallback, rawMapping.put(taskInfo.taskId, taskInfo); } - boolean desktopModeActive = DesktopMode.isActive(mContext); + boolean desktopModeActive = DesktopModeStatus.isActive(mContext); ArrayList<ActivityManager.RecentTaskInfo> freeformTasks = new ArrayList<>(); // Pull out the pairs as we iterate back in the list @@ -320,7 +320,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 e8a2cb160880..9c7131a9e02e 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<Caption private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true; - return DesktopMode.IS_SUPPORTED + return DesktopModeStatus.IS_SUPPORTED && mDisplayController.getDisplayContext(taskInfo.displayId) .getResources().getConfiguration().smallestScreenWidthDp >= 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 5040bc37c614..733f6b7d5dbf 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<WindowDecorLinearL View caption = mResult.mRootView.findViewById(R.id.caption); caption.setOnTouchListener(mOnCaptionTouchListener); View maximize = caption.findViewById(R.id.maximize_window); - if (DesktopMode.IS_SUPPORTED) { + if (DesktopModeStatus.IS_SUPPORTED) { // Hide maximize button when desktop mode is available maximize.setVisibility(View.GONE); } else { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java index c0720cf04028..3672ae386dc4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java @@ -44,6 +44,7 @@ public final class TestRunningTaskInfoBuilder { private ActivityManager.TaskDescription.Builder mTaskDescriptionBuilder = null; private final Point mPositionInParent = new Point(); private boolean mIsVisible = false; + private long mLastActiveTime; public static WindowContainerToken createMockWCToken() { final IWindowContainerToken itoken = mock(IWindowContainerToken.class); @@ -52,6 +53,11 @@ public final class TestRunningTaskInfoBuilder { return new WindowContainerToken(itoken); } + public TestRunningTaskInfoBuilder setToken(WindowContainerToken token) { + mToken = token; + return this; + } + public TestRunningTaskInfoBuilder setBounds(Rect bounds) { mBounds.set(bounds); return this; @@ -95,6 +101,11 @@ public final class TestRunningTaskInfoBuilder { return this; } + public TestRunningTaskInfoBuilder setLastActiveTime(long lastActiveTime) { + mLastActiveTime = lastActiveTime; + return this; + } + public ActivityManager.RunningTaskInfo build() { final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); info.taskId = sNextTaskId++; @@ -110,6 +121,7 @@ public final class TestRunningTaskInfoBuilder { mTaskDescriptionBuilder != null ? mTaskDescriptionBuilder.build() : null; info.positionInParent = mPositionInParent; info.isVisible = mIsVisible; + info.lastActiveTime = mLastActiveTime; return info; } } 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 c628f3994d8d..dd23d97d9199 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 @@ -19,16 +19,22 @@ package com.android.wm.shell.desktopmode; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.WindowConfiguration; +import android.app.ActivityManager; import android.os.Handler; import android.os.IBinder; import android.testing.AndroidTestingRunner; @@ -39,13 +45,17 @@ import android.window.WindowContainerTransaction.Change; import androidx.test.filters.SmallTest; +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.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; @@ -67,18 +77,38 @@ public class DesktopModeControllerTest extends ShellTestCase { private Handler mMockHandler; @Mock private Transitions mMockTransitions; + private TestShellExecutor mExecutor; private DesktopModeController mController; + private DesktopModeTaskRepository mDesktopModeTaskRepository; private ShellInit mShellInit; + private StaticMockitoSession mMockitoSession; @Before public void setUp() { + mMockitoSession = mockitoSession().mockStatic(DesktopModeStatus.class).startMocking(); + when(DesktopModeStatus.isActive(any())).thenReturn(true); + mShellInit = Mockito.spy(new ShellInit(mTestExecutor)); + mExecutor = new TestShellExecutor(); + + mDesktopModeTaskRepository = new DesktopModeTaskRepository(); mController = new DesktopModeController(mContext, mShellInit, mShellTaskOrganizer, - mRootTaskDisplayAreaOrganizer, mMockHandler, mMockTransitions); + mRootTaskDisplayAreaOrganizer, mMockTransitions, + mDesktopModeTaskRepository, mMockHandler, mExecutor); + + when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(anyInt())).thenReturn( + new WindowContainerTransaction()); mShellInit.init(); + clearInvocations(mShellTaskOrganizer); + clearInvocations(mRootTaskDisplayAreaOrganizer); + } + + @After + public void tearDown() { + mMockitoSession.finishMocking(); } @Test @@ -159,17 +189,15 @@ public class DesktopModeControllerTest extends ShellTestCase { assertThat(wct.getChanges()).hasSize(3); // Verify executed WCT has a change for setting task windowing mode to undefined - Change taskWmModeChange = wct.getChanges().get(taskWmMockToken.binder()); - assertThat(taskWmModeChange).isNotNull(); - assertThat(taskWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED); + Change taskWmMode = wct.getChanges().get(taskWmMockToken.binder()); + assertThat(taskWmMode).isNotNull(); + assertThat(taskWmMode.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED); // Verify executed WCT has a change for clearing task bounds - Change taskBoundsChange = wct.getChanges().get(taskBoundsMockToken.binder()); - assertThat(taskBoundsChange).isNotNull(); - assertThat(taskBoundsChange.getWindowSetMask() - & WindowConfiguration.WINDOW_CONFIG_BOUNDS).isNotEqualTo(0); - assertThat(taskBoundsChange.getConfiguration().windowConfiguration.getBounds().isEmpty()) - .isTrue(); + Change bounds = wct.getChanges().get(taskBoundsMockToken.binder()); + assertThat(bounds).isNotNull(); + assertThat(bounds.getWindowSetMask() & WINDOW_CONFIG_BOUNDS).isNotEqualTo(0); + assertThat(bounds.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue(); // Verify executed WCT has a change for setting display windowing mode to fullscreen Change displayWmModeChange = wct.getChanges().get(displayAreaInfo.token.asBinder()); @@ -177,6 +205,41 @@ public class DesktopModeControllerTest extends ShellTestCase { assertThat(displayWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN); } + @Test + public void testShowDesktopApps() { + // Set up two active tasks on desktop + mDesktopModeTaskRepository.addActiveTask(1); + mDesktopModeTaskRepository.addActiveTask(2); + MockToken token1 = new MockToken(); + MockToken token2 = new MockToken(); + ActivityManager.RunningTaskInfo taskInfo1 = new TestRunningTaskInfoBuilder().setToken( + token1.token()).setLastActiveTime(100).build(); + ActivityManager.RunningTaskInfo taskInfo2 = new TestRunningTaskInfoBuilder().setToken( + token2.token()).setLastActiveTime(200).build(); + when(mShellTaskOrganizer.getRunningTaskInfo(1)).thenReturn(taskInfo1); + when(mShellTaskOrganizer.getRunningTaskInfo(2)).thenReturn(taskInfo2); + + // Run show desktop apps logic + mController.showDesktopApps(); + ArgumentCaptor<WindowContainerTransaction> 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 cadfeb0de312..70fee2be90c6 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 422274469dcc..2111df501415 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 6d12485fd6b5..85278dd4b883 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 50c38e54b4d0..a21f45f701b3 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 7e30431a30c8..0d06c513d248 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<FloatingTasks> f); + @BindsInstance + Builder setDesktopMode(Optional<DesktopMode> 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 dd115490abfa..096f96949382 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<FloatingTasks> getFloatingTasks(); + + /** + * Optional {@link DesktopMode} component for interacting with desktop mode. + */ + @WMSingleton + Optional<DesktopMode> getDesktopMode(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 95edb359c53c..7e2a5c51786d 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<RecentTasks> mRecentTasks; private final Optional<BackAnimation> mBackAnimation; + private final Optional<DesktopMode> 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> recentTasks, Optional<BackAnimation> backAnimation, Optional<StartingSurface> startingSurface, + Optional<DesktopMode> 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); |
