Loading libs/WindowManager/Shell/res/values/dimen.xml +0 −4 Original line number Diff line number Diff line Loading @@ -510,10 +510,6 @@ split select if dragged until the touch input is within the range. --> <dimen name="desktop_mode_transition_area_width">32dp</dimen> <!-- The height of the area at the top of the screen where a freeform task will transition to fullscreen if dragged until the top bound of the task is within the area. --> <dimen name="desktop_mode_transition_area_height">16dp</dimen> <!-- The width of the area where a desktop task will transition to fullscreen. --> <dimen name="desktop_mode_fullscreen_from_desktop_width">80dp</dimen> Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +7 −6 Original line number Diff line number Diff line Loading @@ -98,6 +98,7 @@ public class DesktopModeVisualIndicator { * Based on the coordinates of the current drag event, determine which indicator type we should * display, including no visible indicator. */ @NonNull IndicatorType updateIndicatorType(PointF inputCoordinates, int windowingMode) { final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId); // If we are in freeform, we don't want a visible indicator in the "freeform" drag zone. Loading Loading @@ -136,18 +137,18 @@ public class DesktopModeVisualIndicator { Region calculateFullscreenRegion(DisplayLayout layout, @WindowConfiguration.WindowingMode int windowingMode, int captionHeight) { final Region region = new Region(); int edgeTransitionHeight = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_height); int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM ? 2 * layout.stableInsets().top : mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height); // A thin, short Rect at the top of the screen. if (windowingMode == WINDOWING_MODE_FREEFORM) { int fromFreeformWidth = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_width); int fromFreeformHeight = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height); region.union(new Rect((layout.width() / 2) - (fromFreeformWidth / 2), -captionHeight, (layout.width() / 2) + (fromFreeformWidth / 2), fromFreeformHeight)); transitionHeight)); } // A screen-wide, shorter Rect if the task is in fullscreen or split. if (windowingMode == WINDOWING_MODE_FULLSCREEN Loading @@ -155,7 +156,7 @@ public class DesktopModeVisualIndicator { region.union(new Rect(0, -captionHeight, layout.width(), edgeTransitionHeight)); transitionHeight)); } return region; } Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +79 −56 Original line number Diff line number Diff line Loading @@ -140,7 +140,7 @@ class DesktopTasksController( private val transitionAreaHeight get() = context.resources.getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_height com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height ) private val transitionAreaWidth Loading Loading @@ -565,30 +565,7 @@ class DesktopTasksController( * @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to. */ fun snapToHalfScreen(taskInfo: RunningTaskInfo, position: SnapPosition) { val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return val stableBounds = Rect() displayLayout.getStableBounds(stableBounds) val destinationWidth = stableBounds.width() / 2 val destinationBounds = when (position) { SnapPosition.LEFT -> { Rect( stableBounds.left, stableBounds.top, stableBounds.left + destinationWidth, stableBounds.bottom ) } SnapPosition.RIGHT -> { Rect( stableBounds.right - destinationWidth, stableBounds.top, stableBounds.right, stableBounds.bottom ) } } val destinationBounds = getSnapBounds(taskInfo, position) if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return Loading @@ -613,6 +590,33 @@ class DesktopTasksController( screenBounds.centerY() - outBounds.centerY()) } private fun getSnapBounds(taskInfo: RunningTaskInfo, position: SnapPosition): Rect { val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return Rect() val stableBounds = Rect() displayLayout.getStableBounds(stableBounds) val destinationWidth = stableBounds.width() / 2 return when (position) { SnapPosition.LEFT -> { Rect( stableBounds.left, stableBounds.top, stableBounds.left + destinationWidth, stableBounds.bottom ) } SnapPosition.RIGHT -> { Rect( stableBounds.right - destinationWidth, stableBounds.top, stableBounds.right, stableBounds.bottom ) } } } /** * Get windowing move for a given `taskId` * Loading Loading @@ -646,7 +650,7 @@ class DesktopTasksController( ?.let { homeTask -> wct.reorder(homeTask.getToken(), true /* onTop */) } } private fun releaseVisualIndicator() { fun releaseVisualIndicator() { val t = SurfaceControl.Transaction() visualIndicator?.releaseVisualIndicator(t) visualIndicator = null Loading Loading @@ -927,16 +931,13 @@ class DesktopTasksController( taskSurface: SurfaceControl, inputX: Float, taskTop: Float ) { ): DesktopModeVisualIndicator.IndicatorType { // If the visual indicator does not exist, create it. if (visualIndicator == null) { visualIndicator = DesktopModeVisualIndicator( val indicator = visualIndicator ?: DesktopModeVisualIndicator( syncQueue, taskInfo, displayController, context, taskSurface, rootTaskDisplayAreaOrganizer) } // Then, update the indicator type. val indicator = visualIndicator ?: return indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode) if (visualIndicator == null) visualIndicator = indicator return indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode) } /** Loading @@ -956,20 +957,28 @@ class DesktopTasksController( if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) { return } if (taskBounds.top <= transitionAreaHeight) { val indicator = visualIndicator ?: return val indicatorType = indicator.updateIndicatorType( PointF(inputCoordinate.x, taskBounds.top.toFloat()), taskInfo.windowingMode ) when (indicatorType) { DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> { moveToFullscreenWithAnimation(taskInfo, position) return } if (inputCoordinate.x <= transitionAreaWidth) { DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { releaseVisualIndicator() snapToHalfScreen(taskInfo, SnapPosition.LEFT) return } if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width() ?.minus(transitionAreaWidth) ?: return)) { DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { releaseVisualIndicator() snapToHalfScreen(taskInfo, SnapPosition.RIGHT) return } DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR, DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR -> { releaseVisualIndicator() } } // A freeform drag-move ended, remove the indicator immediately. releaseVisualIndicator() Loading @@ -982,14 +991,28 @@ class DesktopTasksController( * @param y height of drag, to be checked against status bar height. */ fun onDragPositioningEndThroughStatusBar( inputCoordinates: PointF, taskInfo: RunningTaskInfo, freeformBounds: Rect ) { val indicator = visualIndicator ?: return val indicatorType = indicator .updateIndicatorType(inputCoordinates, taskInfo.windowingMode) when (indicatorType) { DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> { finalizeDragToDesktop(taskInfo, freeformBounds) } private fun getStatusBarHeight(taskInfo: RunningTaskInfo): Int { return displayController.getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0 DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR, DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> { cancelDragToDesktop(taskInfo) } DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.LEFT)) } DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.RIGHT)) } } } /** Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +60 −59 Original line number Diff line number Diff line Loading @@ -30,6 +30,10 @@ import static android.view.WindowInsets.Type.statusBars; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR; import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION; import static com.android.wm.shell.windowdecor.MoveToDesktopAnimator.DRAG_FREEFORM_SCALE; Loading Loading @@ -81,6 +85,7 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; Loading Loading @@ -119,9 +124,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final Choreographer mMainChoreographer; private final DisplayController mDisplayController; private final SyncTransactionQueue mSyncQueue; private final Optional<DesktopTasksController> mDesktopTasksController; private final DesktopTasksController mDesktopTasksController; private final InputManager mInputManager; private boolean mTransitionDragActive; private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>(); Loading Loading @@ -231,7 +235,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDisplayInsetsController = displayInsetsController; mSyncQueue = syncQueue; mTransitions = transitions; mDesktopTasksController = desktopTasksController; mDesktopTasksController = desktopTasksController.get(); mShellCommandHandler = shellCommandHandler; mWindowManager = windowManager; mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory; Loading @@ -248,8 +252,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mShellCommandHandler.addDumpCallback(this::dump, this); mDisplayInsetsController.addInsetsChangedListener(mContext.getDisplayId(), new DesktopModeOnInsetsChangedListener()); mDesktopTasksController.ifPresent(c -> c.setOnTaskResizeAnimationListener( new DeskopModeOnTaskResizeAnimationListener())); mDesktopTasksController.setOnTaskResizeAnimationListener( new DeskopModeOnTaskResizeAnimationListener()); try { mWindowManager.registerSystemGestureExclusionListener(mGestureExclusionListener, mContext.getDisplayId()); Loading @@ -273,7 +277,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId); if (decor != null && DesktopModeStatus.isEnabled() && decor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { mDesktopTasksController.ifPresent(c -> c.moveToSplit(decor.mTaskInfo)); mDesktopTasksController.moveToSplit(decor.mTaskInfo); } } } Loading Loading @@ -414,13 +418,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { decoration.closeHandleMenu(); } } else if (id == R.id.desktop_button) { if (mDesktopTasksController.isPresent()) { final WindowContainerTransaction wct = new WindowContainerTransaction(); // App sometimes draws before the insets from WindowDecoration#relayout have // been added, so they must be added here mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct); mDesktopTasksController.get().moveToDesktop(mTaskId, wct); } mDesktopTasksController.moveToDesktop(mTaskId, wct); decoration.closeHandleMenu(); } else if (id == R.id.fullscreen_button) { decoration.closeHandleMenu(); Loading @@ -428,42 +430,37 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mSplitScreenController.moveTaskToFullscreen(mTaskId, SplitScreenController.EXIT_REASON_DESKTOP_MODE); } else { mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId)); mDesktopTasksController.moveToFullscreen(mTaskId); } } else if (id == R.id.split_screen_button) { decoration.closeHandleMenu(); mDesktopTasksController.ifPresent(c -> { c.requestSplit(decoration.mTaskInfo); }); mDesktopTasksController.requestSplit(decoration.mTaskInfo); } else if (id == R.id.collapse_menu_button) { decoration.closeHandleMenu(); } else if (id == R.id.select_button) { if (DesktopModeStatus.IS_DISPLAY_CHANGE_ENABLED) { // TODO(b/278084491): dev option to enable display switching // remove when select is implemented mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId)); mDesktopTasksController.moveToNextDisplay(mTaskId); } } else if (id == R.id.maximize_window) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo)); mDesktopTasksController.toggleDesktopTaskSize(taskInfo); } else if (id == R.id.maximize_menu_maximize_button) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo)); mDesktopTasksController.toggleDesktopTaskSize(taskInfo); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } else if (id == R.id.maximize_menu_snap_left_button) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen( taskInfo, SnapPosition.LEFT)); mDesktopTasksController.snapToHalfScreen(taskInfo, SnapPosition.LEFT); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } else if (id == R.id.maximize_menu_snap_right_button) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen( taskInfo, SnapPosition.RIGHT)); mDesktopTasksController.snapToHalfScreen(taskInfo, SnapPosition.RIGHT); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } Loading Loading @@ -572,7 +569,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private void moveTaskToFront(RunningTaskInfo taskInfo) { if (!taskInfo.isFocused) { mDesktopTasksController.ifPresent(c -> c.moveTaskToFront(taskInfo)); mDesktopTasksController.moveTaskToFront(taskInfo); } } Loading Loading @@ -616,10 +613,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final int dragPointerIdx = e.findPointerIndex(mDragPointerId); final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningMove( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); mDesktopTasksController.ifPresent(c -> c.onDragPositioningMove(taskInfo, mDesktopTasksController.onDragPositioningMove(taskInfo, decoration.mTaskSurface, e.getRawX(dragPointerIdx), newTaskBounds)); newTaskBounds); mIsDragging = true; return true; } Loading @@ -641,10 +638,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { (int) (e.getRawY(dragPointerIdx) - e.getY(dragPointerIdx))); final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo, position, mDesktopTasksController.onDragPositioningEnd(taskInfo, position, new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)), newTaskBounds)); newTaskBounds); if (touchingButton && !mHasLongClicked) { // We need the input event to not be consumed here to end the ripple // effect on the touched button. We will reset drag state in the ensuing Loading Loading @@ -672,10 +668,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && action != MotionEvent.ACTION_CANCEL)) { return false; } mDesktopTasksController.ifPresent(c -> { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); c.toggleDesktopTaskSize(decoration.mTaskInfo); }); mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo); return true; } } Loading Loading @@ -839,20 +833,29 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return; } if (mTransitionDragActive) { final DesktopModeVisualIndicator.IndicatorType indicatorType = mDesktopTasksController.updateVisualIndicator(relevantDecor.mTaskInfo, relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY()); mTransitionDragActive = false; final int statusBarHeight = getStatusBarHeight( relevantDecor.mTaskInfo.displayId); if (ev.getRawY() > 2 * statusBarHeight) { if (indicatorType == TO_DESKTOP_INDICATOR || indicatorType == TO_SPLIT_LEFT_INDICATOR || indicatorType == TO_SPLIT_RIGHT_INDICATOR) { if (DesktopModeStatus.isEnabled()) { animateToDesktop(relevantDecor, ev); } mMoveToDesktopAnimator = null; return; } else if (mMoveToDesktopAnimator != null) { mDesktopTasksController.ifPresent( c -> c.cancelDragToDesktop(relevantDecor.mTaskInfo)); mDesktopTasksController.onDragPositioningEndThroughStatusBar( new PointF(ev.getRawX(), ev.getRawY()), relevantDecor.mTaskInfo, calculateFreeformBounds(ev.getDisplayId(), DRAG_FREEFORM_SCALE)); mMoveToDesktopAnimator = null; return; } else { // In cases where we create an indicator but do not start the // move-to-desktop animation, we need to dismiss it. mDesktopTasksController.releaseVisualIndicator(); } } relevantDecor.checkClickEvent(ev); Loading @@ -864,20 +867,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return; } if (mTransitionDragActive) { mDesktopTasksController.ifPresent( c -> c.updateVisualIndicator( final DesktopModeVisualIndicator.IndicatorType indicatorType = mDesktopTasksController.updateVisualIndicator( relevantDecor.mTaskInfo, relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY())); final int statusBarHeight = getStatusBarHeight( relevantDecor.mTaskInfo.displayId); if (ev.getRawY() > statusBarHeight) { relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY()); if (indicatorType != TO_FULLSCREEN_INDICATOR) { if (mMoveToDesktopAnimator == null) { mMoveToDesktopAnimator = new MoveToDesktopAnimator( mContext, mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo, relevantDecor.mTaskSurface); mDesktopTasksController.ifPresent( c -> c.startDragToDesktop(relevantDecor.mTaskInfo, mMoveToDesktopAnimator)); mDesktopTasksController.startDragToDesktop(relevantDecor.mTaskInfo, mMoveToDesktopAnimator); } } if (mMoveToDesktopAnimator != null) { Loading Loading @@ -923,6 +923,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { * Animates a window to the center, grows to freeform size, and transitions to Desktop Mode. * @param relevantDecor the window decor of the task to be animated * @param ev the motion event that triggers the animation * TODO(b/315527000): This animation needs to be adjusted to allow snap left/right cases. * Currently fullscreen -> split snap still animates to center screen before readjusting. */ private void centerAndMoveToDesktopWithAnimation(DesktopModeWindowDecoration relevantDecor, MotionEvent ev) { Loading @@ -946,13 +948,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mDesktopTasksController.ifPresent( c -> { c.onDragPositioningEndThroughStatusBar(relevantDecor.mTaskInfo, mDesktopTasksController.onDragPositioningEndThroughStatusBar( new PointF(ev.getRawX(), ev.getRawY()), relevantDecor.mTaskInfo, calculateFreeformBounds(ev.getDisplayId(), DesktopTasksController .DESKTOP_MODE_INITIAL_BOUNDS_SCALE)); }); } }); animator.start(); Loading Loading @@ -1082,7 +1083,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final DragPositioningCallback dragPositioningCallback; final int transitionAreaHeight = mContext.getResources().getDimensionPixelSize( R.dimen.desktop_mode_transition_area_height); R.dimen.desktop_mode_fullscreen_from_desktop_height); if (!DesktopModeStatus.isVeiledResizeEnabled()) { dragPositioningCallback = new FluidResizeTaskPositioner( mTaskOrganizer, mTransitions, windowDecoration, mDisplayController, Loading Loading @@ -1181,12 +1182,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Override public void onExclusionRegionChanged(int taskId, Region region) { mDesktopTasksController.ifPresent(d -> d.onExclusionRegionChanged(taskId, region)); mDesktopTasksController.onExclusionRegionChanged(taskId, region); } @Override public void onExclusionRegionDismissed(int taskId) { mDesktopTasksController.ifPresent(d -> d.removeExclusionRegionForTask(taskId)); mDesktopTasksController.removeExclusionRegionForTask(taskId); } } Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt +10 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
libs/WindowManager/Shell/res/values/dimen.xml +0 −4 Original line number Diff line number Diff line Loading @@ -510,10 +510,6 @@ split select if dragged until the touch input is within the range. --> <dimen name="desktop_mode_transition_area_width">32dp</dimen> <!-- The height of the area at the top of the screen where a freeform task will transition to fullscreen if dragged until the top bound of the task is within the area. --> <dimen name="desktop_mode_transition_area_height">16dp</dimen> <!-- The width of the area where a desktop task will transition to fullscreen. --> <dimen name="desktop_mode_fullscreen_from_desktop_width">80dp</dimen> Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +7 −6 Original line number Diff line number Diff line Loading @@ -98,6 +98,7 @@ public class DesktopModeVisualIndicator { * Based on the coordinates of the current drag event, determine which indicator type we should * display, including no visible indicator. */ @NonNull IndicatorType updateIndicatorType(PointF inputCoordinates, int windowingMode) { final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId); // If we are in freeform, we don't want a visible indicator in the "freeform" drag zone. Loading Loading @@ -136,18 +137,18 @@ public class DesktopModeVisualIndicator { Region calculateFullscreenRegion(DisplayLayout layout, @WindowConfiguration.WindowingMode int windowingMode, int captionHeight) { final Region region = new Region(); int edgeTransitionHeight = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_height); int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM ? 2 * layout.stableInsets().top : mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height); // A thin, short Rect at the top of the screen. if (windowingMode == WINDOWING_MODE_FREEFORM) { int fromFreeformWidth = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_width); int fromFreeformHeight = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height); region.union(new Rect((layout.width() / 2) - (fromFreeformWidth / 2), -captionHeight, (layout.width() / 2) + (fromFreeformWidth / 2), fromFreeformHeight)); transitionHeight)); } // A screen-wide, shorter Rect if the task is in fullscreen or split. if (windowingMode == WINDOWING_MODE_FULLSCREEN Loading @@ -155,7 +156,7 @@ public class DesktopModeVisualIndicator { region.union(new Rect(0, -captionHeight, layout.width(), edgeTransitionHeight)); transitionHeight)); } return region; } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +79 −56 Original line number Diff line number Diff line Loading @@ -140,7 +140,7 @@ class DesktopTasksController( private val transitionAreaHeight get() = context.resources.getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_height com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height ) private val transitionAreaWidth Loading Loading @@ -565,30 +565,7 @@ class DesktopTasksController( * @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to. */ fun snapToHalfScreen(taskInfo: RunningTaskInfo, position: SnapPosition) { val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return val stableBounds = Rect() displayLayout.getStableBounds(stableBounds) val destinationWidth = stableBounds.width() / 2 val destinationBounds = when (position) { SnapPosition.LEFT -> { Rect( stableBounds.left, stableBounds.top, stableBounds.left + destinationWidth, stableBounds.bottom ) } SnapPosition.RIGHT -> { Rect( stableBounds.right - destinationWidth, stableBounds.top, stableBounds.right, stableBounds.bottom ) } } val destinationBounds = getSnapBounds(taskInfo, position) if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return Loading @@ -613,6 +590,33 @@ class DesktopTasksController( screenBounds.centerY() - outBounds.centerY()) } private fun getSnapBounds(taskInfo: RunningTaskInfo, position: SnapPosition): Rect { val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return Rect() val stableBounds = Rect() displayLayout.getStableBounds(stableBounds) val destinationWidth = stableBounds.width() / 2 return when (position) { SnapPosition.LEFT -> { Rect( stableBounds.left, stableBounds.top, stableBounds.left + destinationWidth, stableBounds.bottom ) } SnapPosition.RIGHT -> { Rect( stableBounds.right - destinationWidth, stableBounds.top, stableBounds.right, stableBounds.bottom ) } } } /** * Get windowing move for a given `taskId` * Loading Loading @@ -646,7 +650,7 @@ class DesktopTasksController( ?.let { homeTask -> wct.reorder(homeTask.getToken(), true /* onTop */) } } private fun releaseVisualIndicator() { fun releaseVisualIndicator() { val t = SurfaceControl.Transaction() visualIndicator?.releaseVisualIndicator(t) visualIndicator = null Loading Loading @@ -927,16 +931,13 @@ class DesktopTasksController( taskSurface: SurfaceControl, inputX: Float, taskTop: Float ) { ): DesktopModeVisualIndicator.IndicatorType { // If the visual indicator does not exist, create it. if (visualIndicator == null) { visualIndicator = DesktopModeVisualIndicator( val indicator = visualIndicator ?: DesktopModeVisualIndicator( syncQueue, taskInfo, displayController, context, taskSurface, rootTaskDisplayAreaOrganizer) } // Then, update the indicator type. val indicator = visualIndicator ?: return indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode) if (visualIndicator == null) visualIndicator = indicator return indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode) } /** Loading @@ -956,20 +957,28 @@ class DesktopTasksController( if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) { return } if (taskBounds.top <= transitionAreaHeight) { val indicator = visualIndicator ?: return val indicatorType = indicator.updateIndicatorType( PointF(inputCoordinate.x, taskBounds.top.toFloat()), taskInfo.windowingMode ) when (indicatorType) { DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> { moveToFullscreenWithAnimation(taskInfo, position) return } if (inputCoordinate.x <= transitionAreaWidth) { DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { releaseVisualIndicator() snapToHalfScreen(taskInfo, SnapPosition.LEFT) return } if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width() ?.minus(transitionAreaWidth) ?: return)) { DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { releaseVisualIndicator() snapToHalfScreen(taskInfo, SnapPosition.RIGHT) return } DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR, DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR -> { releaseVisualIndicator() } } // A freeform drag-move ended, remove the indicator immediately. releaseVisualIndicator() Loading @@ -982,14 +991,28 @@ class DesktopTasksController( * @param y height of drag, to be checked against status bar height. */ fun onDragPositioningEndThroughStatusBar( inputCoordinates: PointF, taskInfo: RunningTaskInfo, freeformBounds: Rect ) { val indicator = visualIndicator ?: return val indicatorType = indicator .updateIndicatorType(inputCoordinates, taskInfo.windowingMode) when (indicatorType) { DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> { finalizeDragToDesktop(taskInfo, freeformBounds) } private fun getStatusBarHeight(taskInfo: RunningTaskInfo): Int { return displayController.getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0 DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR, DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> { cancelDragToDesktop(taskInfo) } DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.LEFT)) } DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.RIGHT)) } } } /** Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +60 −59 Original line number Diff line number Diff line Loading @@ -30,6 +30,10 @@ import static android.view.WindowInsets.Type.statusBars; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR; import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION; import static com.android.wm.shell.windowdecor.MoveToDesktopAnimator.DRAG_FREEFORM_SCALE; Loading Loading @@ -81,6 +85,7 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; Loading Loading @@ -119,9 +124,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final Choreographer mMainChoreographer; private final DisplayController mDisplayController; private final SyncTransactionQueue mSyncQueue; private final Optional<DesktopTasksController> mDesktopTasksController; private final DesktopTasksController mDesktopTasksController; private final InputManager mInputManager; private boolean mTransitionDragActive; private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>(); Loading Loading @@ -231,7 +235,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDisplayInsetsController = displayInsetsController; mSyncQueue = syncQueue; mTransitions = transitions; mDesktopTasksController = desktopTasksController; mDesktopTasksController = desktopTasksController.get(); mShellCommandHandler = shellCommandHandler; mWindowManager = windowManager; mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory; Loading @@ -248,8 +252,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mShellCommandHandler.addDumpCallback(this::dump, this); mDisplayInsetsController.addInsetsChangedListener(mContext.getDisplayId(), new DesktopModeOnInsetsChangedListener()); mDesktopTasksController.ifPresent(c -> c.setOnTaskResizeAnimationListener( new DeskopModeOnTaskResizeAnimationListener())); mDesktopTasksController.setOnTaskResizeAnimationListener( new DeskopModeOnTaskResizeAnimationListener()); try { mWindowManager.registerSystemGestureExclusionListener(mGestureExclusionListener, mContext.getDisplayId()); Loading @@ -273,7 +277,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId); if (decor != null && DesktopModeStatus.isEnabled() && decor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { mDesktopTasksController.ifPresent(c -> c.moveToSplit(decor.mTaskInfo)); mDesktopTasksController.moveToSplit(decor.mTaskInfo); } } } Loading Loading @@ -414,13 +418,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { decoration.closeHandleMenu(); } } else if (id == R.id.desktop_button) { if (mDesktopTasksController.isPresent()) { final WindowContainerTransaction wct = new WindowContainerTransaction(); // App sometimes draws before the insets from WindowDecoration#relayout have // been added, so they must be added here mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct); mDesktopTasksController.get().moveToDesktop(mTaskId, wct); } mDesktopTasksController.moveToDesktop(mTaskId, wct); decoration.closeHandleMenu(); } else if (id == R.id.fullscreen_button) { decoration.closeHandleMenu(); Loading @@ -428,42 +430,37 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mSplitScreenController.moveTaskToFullscreen(mTaskId, SplitScreenController.EXIT_REASON_DESKTOP_MODE); } else { mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId)); mDesktopTasksController.moveToFullscreen(mTaskId); } } else if (id == R.id.split_screen_button) { decoration.closeHandleMenu(); mDesktopTasksController.ifPresent(c -> { c.requestSplit(decoration.mTaskInfo); }); mDesktopTasksController.requestSplit(decoration.mTaskInfo); } else if (id == R.id.collapse_menu_button) { decoration.closeHandleMenu(); } else if (id == R.id.select_button) { if (DesktopModeStatus.IS_DISPLAY_CHANGE_ENABLED) { // TODO(b/278084491): dev option to enable display switching // remove when select is implemented mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId)); mDesktopTasksController.moveToNextDisplay(mTaskId); } } else if (id == R.id.maximize_window) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo)); mDesktopTasksController.toggleDesktopTaskSize(taskInfo); } else if (id == R.id.maximize_menu_maximize_button) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo)); mDesktopTasksController.toggleDesktopTaskSize(taskInfo); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } else if (id == R.id.maximize_menu_snap_left_button) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen( taskInfo, SnapPosition.LEFT)); mDesktopTasksController.snapToHalfScreen(taskInfo, SnapPosition.LEFT); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } else if (id == R.id.maximize_menu_snap_right_button) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen( taskInfo, SnapPosition.RIGHT)); mDesktopTasksController.snapToHalfScreen(taskInfo, SnapPosition.RIGHT); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } Loading Loading @@ -572,7 +569,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private void moveTaskToFront(RunningTaskInfo taskInfo) { if (!taskInfo.isFocused) { mDesktopTasksController.ifPresent(c -> c.moveTaskToFront(taskInfo)); mDesktopTasksController.moveTaskToFront(taskInfo); } } Loading Loading @@ -616,10 +613,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final int dragPointerIdx = e.findPointerIndex(mDragPointerId); final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningMove( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); mDesktopTasksController.ifPresent(c -> c.onDragPositioningMove(taskInfo, mDesktopTasksController.onDragPositioningMove(taskInfo, decoration.mTaskSurface, e.getRawX(dragPointerIdx), newTaskBounds)); newTaskBounds); mIsDragging = true; return true; } Loading @@ -641,10 +638,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { (int) (e.getRawY(dragPointerIdx) - e.getY(dragPointerIdx))); final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo, position, mDesktopTasksController.onDragPositioningEnd(taskInfo, position, new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)), newTaskBounds)); newTaskBounds); if (touchingButton && !mHasLongClicked) { // We need the input event to not be consumed here to end the ripple // effect on the touched button. We will reset drag state in the ensuing Loading Loading @@ -672,10 +668,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && action != MotionEvent.ACTION_CANCEL)) { return false; } mDesktopTasksController.ifPresent(c -> { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); c.toggleDesktopTaskSize(decoration.mTaskInfo); }); mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo); return true; } } Loading Loading @@ -839,20 +833,29 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return; } if (mTransitionDragActive) { final DesktopModeVisualIndicator.IndicatorType indicatorType = mDesktopTasksController.updateVisualIndicator(relevantDecor.mTaskInfo, relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY()); mTransitionDragActive = false; final int statusBarHeight = getStatusBarHeight( relevantDecor.mTaskInfo.displayId); if (ev.getRawY() > 2 * statusBarHeight) { if (indicatorType == TO_DESKTOP_INDICATOR || indicatorType == TO_SPLIT_LEFT_INDICATOR || indicatorType == TO_SPLIT_RIGHT_INDICATOR) { if (DesktopModeStatus.isEnabled()) { animateToDesktop(relevantDecor, ev); } mMoveToDesktopAnimator = null; return; } else if (mMoveToDesktopAnimator != null) { mDesktopTasksController.ifPresent( c -> c.cancelDragToDesktop(relevantDecor.mTaskInfo)); mDesktopTasksController.onDragPositioningEndThroughStatusBar( new PointF(ev.getRawX(), ev.getRawY()), relevantDecor.mTaskInfo, calculateFreeformBounds(ev.getDisplayId(), DRAG_FREEFORM_SCALE)); mMoveToDesktopAnimator = null; return; } else { // In cases where we create an indicator but do not start the // move-to-desktop animation, we need to dismiss it. mDesktopTasksController.releaseVisualIndicator(); } } relevantDecor.checkClickEvent(ev); Loading @@ -864,20 +867,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return; } if (mTransitionDragActive) { mDesktopTasksController.ifPresent( c -> c.updateVisualIndicator( final DesktopModeVisualIndicator.IndicatorType indicatorType = mDesktopTasksController.updateVisualIndicator( relevantDecor.mTaskInfo, relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY())); final int statusBarHeight = getStatusBarHeight( relevantDecor.mTaskInfo.displayId); if (ev.getRawY() > statusBarHeight) { relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY()); if (indicatorType != TO_FULLSCREEN_INDICATOR) { if (mMoveToDesktopAnimator == null) { mMoveToDesktopAnimator = new MoveToDesktopAnimator( mContext, mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo, relevantDecor.mTaskSurface); mDesktopTasksController.ifPresent( c -> c.startDragToDesktop(relevantDecor.mTaskInfo, mMoveToDesktopAnimator)); mDesktopTasksController.startDragToDesktop(relevantDecor.mTaskInfo, mMoveToDesktopAnimator); } } if (mMoveToDesktopAnimator != null) { Loading Loading @@ -923,6 +923,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { * Animates a window to the center, grows to freeform size, and transitions to Desktop Mode. * @param relevantDecor the window decor of the task to be animated * @param ev the motion event that triggers the animation * TODO(b/315527000): This animation needs to be adjusted to allow snap left/right cases. * Currently fullscreen -> split snap still animates to center screen before readjusting. */ private void centerAndMoveToDesktopWithAnimation(DesktopModeWindowDecoration relevantDecor, MotionEvent ev) { Loading @@ -946,13 +948,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mDesktopTasksController.ifPresent( c -> { c.onDragPositioningEndThroughStatusBar(relevantDecor.mTaskInfo, mDesktopTasksController.onDragPositioningEndThroughStatusBar( new PointF(ev.getRawX(), ev.getRawY()), relevantDecor.mTaskInfo, calculateFreeformBounds(ev.getDisplayId(), DesktopTasksController .DESKTOP_MODE_INITIAL_BOUNDS_SCALE)); }); } }); animator.start(); Loading Loading @@ -1082,7 +1083,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final DragPositioningCallback dragPositioningCallback; final int transitionAreaHeight = mContext.getResources().getDimensionPixelSize( R.dimen.desktop_mode_transition_area_height); R.dimen.desktop_mode_fullscreen_from_desktop_height); if (!DesktopModeStatus.isVeiledResizeEnabled()) { dragPositioningCallback = new FluidResizeTaskPositioner( mTaskOrganizer, mTransitions, windowDecoration, mDisplayController, Loading Loading @@ -1181,12 +1182,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Override public void onExclusionRegionChanged(int taskId, Region region) { mDesktopTasksController.ifPresent(d -> d.onExclusionRegionChanged(taskId, region)); mDesktopTasksController.onExclusionRegionChanged(taskId, region); } @Override public void onExclusionRegionDismissed(int taskId) { mDesktopTasksController.ifPresent(d -> d.removeExclusionRegionForTask(taskId)); mDesktopTasksController.removeExclusionRegionForTask(taskId); } } Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt +10 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes