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 afefd5dc63442..3cb40c543a821 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 @@ -52,6 +52,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; @@ -76,6 +77,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<>(); @@ -91,7 +93,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { ShellTaskOrganizer taskOrganizer, DisplayController displayController, SyncTransactionQueue syncQueue, - Optional desktopModeController) { + Optional desktopModeController, + Optional desktopTasksController) { this( context, mainHandler, @@ -100,6 +103,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { displayController, syncQueue, desktopModeController, + desktopTasksController, new CaptionWindowDecoration.Factory(), InputManager::getInstance); } @@ -112,6 +116,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { DisplayController displayController, SyncTransactionQueue syncQueue, Optional desktopModeController, + Optional desktopTasksController, CaptionWindowDecoration.Factory captionWindowDecorFactory, Supplier inputManagerSupplier) { @@ -123,6 +128,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mDisplayController = displayController; mSyncQueue = syncQueue; mDesktopModeController = desktopModeController; + mDesktopTasksController = desktopTasksController; mCaptionWindowDecorFactory = captionWindowDecorFactory; mInputManagerSupplier = inputManagerSupplier; @@ -248,11 +254,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); } } @@ -305,8 +313,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; } @@ -330,9 +343,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; } @@ -420,13 +444,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(); + } } } @@ -455,9 +493,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; } @@ -472,7 +521,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(mMockInputManagers, () -> mock(InputManager.class))); mCaptionWindowDecorViewModel.setEventReceiverFactory(mEventReceiverFactory);