Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +33 −17 Original line number Diff line number Diff line Loading @@ -486,7 +486,9 @@ class DesktopTasksController( "DesktopTasksController: cancelDragToDesktop taskId=%d", task.taskId ) dragToDesktopTransitionHandler.cancelDragToDesktopTransition() dragToDesktopTransitionHandler.cancelDragToDesktopTransition( DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL ) } private fun moveToFullscreenWithAnimation(task: RunningTaskInfo, position: Point) { Loading Loading @@ -1105,12 +1107,22 @@ class DesktopTasksController( @JvmOverloads fun requestSplit( taskInfo: RunningTaskInfo, leftOrTop: Boolean = false, ) { val windowingMode = taskInfo.windowingMode if ( windowingMode == WINDOWING_MODE_FULLSCREEN || windowingMode == WINDOWING_MODE_FREEFORM leftOrTop: Boolean = false ) { // If a drag to desktop is in progress, we want to enter split select // even if the requesting task is already in split. val isDragging = dragToDesktopTransitionHandler.inProgress val shouldRequestSplit = taskInfo.isFullscreen || taskInfo.isFreeform || isDragging if (shouldRequestSplit) { if (isDragging) { releaseVisualIndicator() val cancelState = if (leftOrTop) { DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_LEFT } else { DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_RIGHT } dragToDesktopTransitionHandler.cancelDragToDesktopTransition(cancelState) } else { val wct = WindowContainerTransaction() addMoveToSplitChanges(wct, taskInfo) splitScreenController.requestEnterSplitSelect( Loading @@ -1121,6 +1133,7 @@ class DesktopTasksController( ) } } } private fun getDefaultDensityDpi(): Int { return context.resources.displayMetrics.densityDpi Loading Loading @@ -1247,7 +1260,10 @@ class DesktopTasksController( * @param taskInfo the task being dragged. * @param y height of drag, to be checked against status bar height. */ fun onDragPositioningEndThroughStatusBar(inputCoordinates: PointF, taskInfo: RunningTaskInfo) { fun onDragPositioningEndThroughStatusBar( inputCoordinates: PointF, taskInfo: RunningTaskInfo, ) { val indicator = getVisualIndicator() ?: return val indicatorType = indicator.updateIndicatorType(inputCoordinates, taskInfo.windowingMode) when (indicatorType) { Loading @@ -1264,10 +1280,10 @@ class DesktopTasksController( cancelDragToDesktop(taskInfo) } DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.LEFT)) requestSplit(taskInfo, leftOrTop = true) } DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.RIGHT)) requestSplit(taskInfo, leftOrTop = false) } } } Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt +140 −13 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.RectEvaluator import android.animation.ValueAnimator import android.app.ActivityManager.RunningTaskInfo import android.app.ActivityOptions import android.app.ActivityOptions.SourceInfo import android.app.ActivityTaskManager.INVALID_TASK_ID Loading @@ -12,9 +13,11 @@ import android.app.PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT import android.app.PendingIntent.FLAG_MUTABLE import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW import android.content.Context import android.content.Intent import android.content.Intent.FILL_IN_COMPONENT import android.graphics.PointF import android.graphics.Rect import android.os.Bundle import android.os.IBinder Loading @@ -30,6 +33,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition import com.android.wm.shell.protolog.ShellProtoLogGroup import com.android.wm.shell.shared.TransitionUtil import com.android.wm.shell.splitscreen.SplitScreenController Loading Loading @@ -186,7 +190,7 @@ class DragToDesktopTransitionHandler( * outside the desktop drop zone and is instead dropped back into the status bar region that * means the user wants to remain in their current windowing mode. */ fun cancelDragToDesktopTransition() { fun cancelDragToDesktopTransition(cancelState: CancelState) { if (!inProgress) { // Don't attempt to cancel a drag to desktop transition since there is no transition in // progress which means that the drag to desktop transition was never successfully Loading @@ -200,13 +204,32 @@ class DragToDesktopTransitionHandler( clearState() return } state.cancelled = true if (state.draggedTaskChange != null) { state.cancelState = cancelState if (state.draggedTaskChange != null && cancelState == CancelState.STANDARD_CANCEL) { // Regular case, transient launch of Home happened as is waiting for the cancel // transient to start and merge. Animate the cancellation (scale back to original // bounds) first before actually starting the cancel transition so that the wallpaper // is visible behind the animating task. startCancelAnimation() } else if ( state.draggedTaskChange != null && (cancelState == CancelState.CANCEL_SPLIT_LEFT || cancelState == CancelState.CANCEL_SPLIT_RIGHT) ) { // We have a valid dragged task, but the animation will be handled by // SplitScreenController; request the transition here. @SplitPosition val splitPosition = if (cancelState == CancelState.CANCEL_SPLIT_LEFT) { SPLIT_POSITION_TOP_OR_LEFT } else { SPLIT_POSITION_BOTTOM_OR_RIGHT } val wct = WindowContainerTransaction() restoreWindowOrder(wct, state) state.startTransitionFinishTransaction?.apply() state.startTransitionFinishCb?.onTransitionFinished(null /* wct */) requestSplitFromScaledTask(splitPosition, wct) clearState() } else { // There's no dragged task, this can happen when the "cancel" happened too quickly // before the "start" transition is even ready (like on a fling gesture). The Loading @@ -217,6 +240,54 @@ class DragToDesktopTransitionHandler( } } /** Calculate the bounds of a scaled task, then use those bounds to request split select. */ private fun requestSplitFromScaledTask( @SplitPosition splitPosition: Int, wct: WindowContainerTransaction ) { val state = requireTransitionState() val taskInfo = state.draggedTaskChange?.taskInfo ?: error("Expected non-null taskInfo") val taskBounds = Rect(taskInfo.configuration.windowConfiguration.bounds) val taskScale = state.dragAnimator.scale val scaledWidth = taskBounds.width() * taskScale val scaledHeight = taskBounds.height() * taskScale val dragPosition = PointF(state.dragAnimator.position) state.dragAnimator.cancelAnimator() val animatedTaskBounds = Rect( dragPosition.x.toInt(), dragPosition.y.toInt(), (dragPosition.x + scaledWidth).toInt(), (dragPosition.y + scaledHeight).toInt() ) requestSplitSelect(wct, taskInfo, splitPosition, animatedTaskBounds) } private fun requestSplitSelect( wct: WindowContainerTransaction, taskInfo: RunningTaskInfo, @SplitPosition splitPosition: Int, taskBounds: Rect = Rect(taskInfo.configuration.windowConfiguration.bounds) ) { // Prepare to exit split in order to enter split select. if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) { splitScreenController.prepareExitSplitScreen( wct, splitScreenController.getStageOfTask(taskInfo.taskId), SplitScreenController.EXIT_REASON_DESKTOP_MODE ) splitScreenController.transitionHandler.onSplitToDesktop() } wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_MULTI_WINDOW) wct.setDensityDpi(taskInfo.token, context.resources.displayMetrics.densityDpi) splitScreenController.requestEnterSplitSelect( taskInfo, wct, splitPosition, taskBounds ) } override fun startAnimation( transition: IBinder, info: TransitionInfo, Loading Loading @@ -261,7 +332,7 @@ class DragToDesktopTransitionHandler( is TransitionState.FromSplit -> { state.splitRootChange = change val layer = if (!state.cancelled) { if (state.cancelState == CancelState.NO_CANCEL) { // Normal case, split root goes to the bottom behind everything // else. appLayers - i Loading Loading @@ -311,8 +382,18 @@ class DragToDesktopTransitionHandler( // Do not do this in the cancel-early case though, since in that case nothing should // happen on screen so the layering will remain the same as if no transition // occurred. if (change.taskInfo?.taskId == state.draggedTaskId && !state.cancelled) { if ( change.taskInfo?.taskId == state.draggedTaskId && state.cancelState != CancelState.STANDARD_CANCEL ) { // We need access to the dragged task's change in both non-cancel and split // cancel cases. state.draggedTaskChange = change } if ( change.taskInfo?.taskId == state.draggedTaskId && state.cancelState == CancelState.NO_CANCEL ) { taskDisplayAreaOrganizer.reparentToDisplayArea( change.endDisplayId, change.leash, Loading @@ -331,11 +412,11 @@ class DragToDesktopTransitionHandler( state.startTransitionFinishTransaction = finishTransaction startTransaction.apply() if (!state.cancelled) { if (state.cancelState == CancelState.NO_CANCEL) { // Normal case, start animation to scale down the dragged task. It'll also be moved to // follow the finger and when released we'll start the next phase/transition. state.dragAnimator.startAnimation() } else { } else if (state.cancelState == CancelState.STANDARD_CANCEL) { // Cancel-early case, the state was flagged was cancelled already, which means the // gesture ended in the cancel region. This can happen even before the start transition // is ready/animate here when cancelling quickly like with a fling. There's no point Loading @@ -343,6 +424,26 @@ class DragToDesktopTransitionHandler( // directly into starting the cancel transition to restore WM order. Surfaces should // not move as if no transition happened. startCancelDragToDesktopTransition() } else if ( state.cancelState == CancelState.CANCEL_SPLIT_LEFT || state.cancelState == CancelState.CANCEL_SPLIT_RIGHT ){ // Cancel-early case for split-cancel. The state was flagged already as a cancel for // requesting split select. Similar to the above, this can happen due to quick fling // gestures. We can simply request split here without needing to calculate animated // task bounds as the task has not shrunk at all. val splitPosition = if (state.cancelState == CancelState.CANCEL_SPLIT_LEFT) { SPLIT_POSITION_TOP_OR_LEFT } else { SPLIT_POSITION_BOTTOM_OR_RIGHT } val taskInfo = state.draggedTaskChange?.taskInfo ?: error("Expected non-null task info.") val wct = WindowContainerTransaction() restoreWindowOrder(wct) state.startTransitionFinishTransaction?.apply() state.startTransitionFinishCb?.onTransitionFinished(null /* wct */) requestSplitSelect(wct, taskInfo, splitPosition) } return true } Loading @@ -355,6 +456,12 @@ class DragToDesktopTransitionHandler( finishCallback: Transitions.TransitionFinishCallback ) { val state = requireTransitionState() // We don't want to merge the split select animation if that's what we requested. if (state.cancelState == CancelState.CANCEL_SPLIT_LEFT || state.cancelState == CancelState.CANCEL_SPLIT_RIGHT) { clearState() return } val isCancelTransition = info.type == TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP && transition == state.cancelTransitionToken && Loading Loading @@ -552,6 +659,17 @@ class DragToDesktopTransitionHandler( private fun startCancelDragToDesktopTransition() { val state = requireTransitionState() val wct = WindowContainerTransaction() restoreWindowOrder(wct, state) state.cancelTransitionToken = transitions.startTransition( TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this ) } private fun restoreWindowOrder( wct: WindowContainerTransaction, state: TransitionState = requireTransitionState() ) { when (state) { is TransitionState.FromFullscreen -> { // There may have been tasks sent behind home that are not the dragged task (like Loading Loading @@ -580,9 +698,6 @@ class DragToDesktopTransitionHandler( } val homeWc = state.homeToken ?: error("Home task should be non-null before cancelling") wct.restoreTransientOrder(homeWc) state.cancelTransitionToken = transitions.startTransition(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this) } private fun clearState() { Loading Loading @@ -624,7 +739,7 @@ class DragToDesktopTransitionHandler( abstract var cancelTransitionToken: IBinder? abstract var homeToken: WindowContainerToken? abstract var draggedTaskChange: Change? abstract var cancelled: Boolean abstract var cancelState: CancelState abstract var startAborted: Boolean data class FromFullscreen( Loading @@ -636,7 +751,7 @@ class DragToDesktopTransitionHandler( override var cancelTransitionToken: IBinder? = null, override var homeToken: WindowContainerToken? = null, override var draggedTaskChange: Change? = null, override var cancelled: Boolean = false, override var cancelState: CancelState = CancelState.NO_CANCEL, override var startAborted: Boolean = false, var otherRootChanges: MutableList<Change> = mutableListOf() ) : TransitionState() Loading @@ -650,13 +765,25 @@ class DragToDesktopTransitionHandler( override var cancelTransitionToken: IBinder? = null, override var homeToken: WindowContainerToken? = null, override var draggedTaskChange: Change? = null, override var cancelled: Boolean = false, override var cancelState: CancelState = CancelState.NO_CANCEL, override var startAborted: Boolean = false, var splitRootChange: Change? = null, var otherSplitTask: Int ) : TransitionState() } /** Enum to provide context on cancelling a drag to desktop event. */ enum class CancelState { /** No cancel case; this drag is not flagged for a cancel event. */ NO_CANCEL, /** A standard cancel event; should restore task to previous windowing mode. */ STANDARD_CANCEL, /** A cancel event where the task will request to enter split on the left side. */ CANCEL_SPLIT_LEFT, /** A cancel event where the task will request to enter split on the right side. */ CANCEL_SPLIT_RIGHT } companion object { /** The duration of the animation to commit or cancel the drag-to-desktop gesture. */ private const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt +115 −46 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +33 −17 Original line number Diff line number Diff line Loading @@ -486,7 +486,9 @@ class DesktopTasksController( "DesktopTasksController: cancelDragToDesktop taskId=%d", task.taskId ) dragToDesktopTransitionHandler.cancelDragToDesktopTransition() dragToDesktopTransitionHandler.cancelDragToDesktopTransition( DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL ) } private fun moveToFullscreenWithAnimation(task: RunningTaskInfo, position: Point) { Loading Loading @@ -1105,12 +1107,22 @@ class DesktopTasksController( @JvmOverloads fun requestSplit( taskInfo: RunningTaskInfo, leftOrTop: Boolean = false, ) { val windowingMode = taskInfo.windowingMode if ( windowingMode == WINDOWING_MODE_FULLSCREEN || windowingMode == WINDOWING_MODE_FREEFORM leftOrTop: Boolean = false ) { // If a drag to desktop is in progress, we want to enter split select // even if the requesting task is already in split. val isDragging = dragToDesktopTransitionHandler.inProgress val shouldRequestSplit = taskInfo.isFullscreen || taskInfo.isFreeform || isDragging if (shouldRequestSplit) { if (isDragging) { releaseVisualIndicator() val cancelState = if (leftOrTop) { DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_LEFT } else { DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_RIGHT } dragToDesktopTransitionHandler.cancelDragToDesktopTransition(cancelState) } else { val wct = WindowContainerTransaction() addMoveToSplitChanges(wct, taskInfo) splitScreenController.requestEnterSplitSelect( Loading @@ -1121,6 +1133,7 @@ class DesktopTasksController( ) } } } private fun getDefaultDensityDpi(): Int { return context.resources.displayMetrics.densityDpi Loading Loading @@ -1247,7 +1260,10 @@ class DesktopTasksController( * @param taskInfo the task being dragged. * @param y height of drag, to be checked against status bar height. */ fun onDragPositioningEndThroughStatusBar(inputCoordinates: PointF, taskInfo: RunningTaskInfo) { fun onDragPositioningEndThroughStatusBar( inputCoordinates: PointF, taskInfo: RunningTaskInfo, ) { val indicator = getVisualIndicator() ?: return val indicatorType = indicator.updateIndicatorType(inputCoordinates, taskInfo.windowingMode) when (indicatorType) { Loading @@ -1264,10 +1280,10 @@ class DesktopTasksController( cancelDragToDesktop(taskInfo) } DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.LEFT)) requestSplit(taskInfo, leftOrTop = true) } DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.RIGHT)) requestSplit(taskInfo, leftOrTop = false) } } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt +140 −13 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.RectEvaluator import android.animation.ValueAnimator import android.app.ActivityManager.RunningTaskInfo import android.app.ActivityOptions import android.app.ActivityOptions.SourceInfo import android.app.ActivityTaskManager.INVALID_TASK_ID Loading @@ -12,9 +13,11 @@ import android.app.PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT import android.app.PendingIntent.FLAG_MUTABLE import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW import android.content.Context import android.content.Intent import android.content.Intent.FILL_IN_COMPONENT import android.graphics.PointF import android.graphics.Rect import android.os.Bundle import android.os.IBinder Loading @@ -30,6 +33,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition import com.android.wm.shell.protolog.ShellProtoLogGroup import com.android.wm.shell.shared.TransitionUtil import com.android.wm.shell.splitscreen.SplitScreenController Loading Loading @@ -186,7 +190,7 @@ class DragToDesktopTransitionHandler( * outside the desktop drop zone and is instead dropped back into the status bar region that * means the user wants to remain in their current windowing mode. */ fun cancelDragToDesktopTransition() { fun cancelDragToDesktopTransition(cancelState: CancelState) { if (!inProgress) { // Don't attempt to cancel a drag to desktop transition since there is no transition in // progress which means that the drag to desktop transition was never successfully Loading @@ -200,13 +204,32 @@ class DragToDesktopTransitionHandler( clearState() return } state.cancelled = true if (state.draggedTaskChange != null) { state.cancelState = cancelState if (state.draggedTaskChange != null && cancelState == CancelState.STANDARD_CANCEL) { // Regular case, transient launch of Home happened as is waiting for the cancel // transient to start and merge. Animate the cancellation (scale back to original // bounds) first before actually starting the cancel transition so that the wallpaper // is visible behind the animating task. startCancelAnimation() } else if ( state.draggedTaskChange != null && (cancelState == CancelState.CANCEL_SPLIT_LEFT || cancelState == CancelState.CANCEL_SPLIT_RIGHT) ) { // We have a valid dragged task, but the animation will be handled by // SplitScreenController; request the transition here. @SplitPosition val splitPosition = if (cancelState == CancelState.CANCEL_SPLIT_LEFT) { SPLIT_POSITION_TOP_OR_LEFT } else { SPLIT_POSITION_BOTTOM_OR_RIGHT } val wct = WindowContainerTransaction() restoreWindowOrder(wct, state) state.startTransitionFinishTransaction?.apply() state.startTransitionFinishCb?.onTransitionFinished(null /* wct */) requestSplitFromScaledTask(splitPosition, wct) clearState() } else { // There's no dragged task, this can happen when the "cancel" happened too quickly // before the "start" transition is even ready (like on a fling gesture). The Loading @@ -217,6 +240,54 @@ class DragToDesktopTransitionHandler( } } /** Calculate the bounds of a scaled task, then use those bounds to request split select. */ private fun requestSplitFromScaledTask( @SplitPosition splitPosition: Int, wct: WindowContainerTransaction ) { val state = requireTransitionState() val taskInfo = state.draggedTaskChange?.taskInfo ?: error("Expected non-null taskInfo") val taskBounds = Rect(taskInfo.configuration.windowConfiguration.bounds) val taskScale = state.dragAnimator.scale val scaledWidth = taskBounds.width() * taskScale val scaledHeight = taskBounds.height() * taskScale val dragPosition = PointF(state.dragAnimator.position) state.dragAnimator.cancelAnimator() val animatedTaskBounds = Rect( dragPosition.x.toInt(), dragPosition.y.toInt(), (dragPosition.x + scaledWidth).toInt(), (dragPosition.y + scaledHeight).toInt() ) requestSplitSelect(wct, taskInfo, splitPosition, animatedTaskBounds) } private fun requestSplitSelect( wct: WindowContainerTransaction, taskInfo: RunningTaskInfo, @SplitPosition splitPosition: Int, taskBounds: Rect = Rect(taskInfo.configuration.windowConfiguration.bounds) ) { // Prepare to exit split in order to enter split select. if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) { splitScreenController.prepareExitSplitScreen( wct, splitScreenController.getStageOfTask(taskInfo.taskId), SplitScreenController.EXIT_REASON_DESKTOP_MODE ) splitScreenController.transitionHandler.onSplitToDesktop() } wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_MULTI_WINDOW) wct.setDensityDpi(taskInfo.token, context.resources.displayMetrics.densityDpi) splitScreenController.requestEnterSplitSelect( taskInfo, wct, splitPosition, taskBounds ) } override fun startAnimation( transition: IBinder, info: TransitionInfo, Loading Loading @@ -261,7 +332,7 @@ class DragToDesktopTransitionHandler( is TransitionState.FromSplit -> { state.splitRootChange = change val layer = if (!state.cancelled) { if (state.cancelState == CancelState.NO_CANCEL) { // Normal case, split root goes to the bottom behind everything // else. appLayers - i Loading Loading @@ -311,8 +382,18 @@ class DragToDesktopTransitionHandler( // Do not do this in the cancel-early case though, since in that case nothing should // happen on screen so the layering will remain the same as if no transition // occurred. if (change.taskInfo?.taskId == state.draggedTaskId && !state.cancelled) { if ( change.taskInfo?.taskId == state.draggedTaskId && state.cancelState != CancelState.STANDARD_CANCEL ) { // We need access to the dragged task's change in both non-cancel and split // cancel cases. state.draggedTaskChange = change } if ( change.taskInfo?.taskId == state.draggedTaskId && state.cancelState == CancelState.NO_CANCEL ) { taskDisplayAreaOrganizer.reparentToDisplayArea( change.endDisplayId, change.leash, Loading @@ -331,11 +412,11 @@ class DragToDesktopTransitionHandler( state.startTransitionFinishTransaction = finishTransaction startTransaction.apply() if (!state.cancelled) { if (state.cancelState == CancelState.NO_CANCEL) { // Normal case, start animation to scale down the dragged task. It'll also be moved to // follow the finger and when released we'll start the next phase/transition. state.dragAnimator.startAnimation() } else { } else if (state.cancelState == CancelState.STANDARD_CANCEL) { // Cancel-early case, the state was flagged was cancelled already, which means the // gesture ended in the cancel region. This can happen even before the start transition // is ready/animate here when cancelling quickly like with a fling. There's no point Loading @@ -343,6 +424,26 @@ class DragToDesktopTransitionHandler( // directly into starting the cancel transition to restore WM order. Surfaces should // not move as if no transition happened. startCancelDragToDesktopTransition() } else if ( state.cancelState == CancelState.CANCEL_SPLIT_LEFT || state.cancelState == CancelState.CANCEL_SPLIT_RIGHT ){ // Cancel-early case for split-cancel. The state was flagged already as a cancel for // requesting split select. Similar to the above, this can happen due to quick fling // gestures. We can simply request split here without needing to calculate animated // task bounds as the task has not shrunk at all. val splitPosition = if (state.cancelState == CancelState.CANCEL_SPLIT_LEFT) { SPLIT_POSITION_TOP_OR_LEFT } else { SPLIT_POSITION_BOTTOM_OR_RIGHT } val taskInfo = state.draggedTaskChange?.taskInfo ?: error("Expected non-null task info.") val wct = WindowContainerTransaction() restoreWindowOrder(wct) state.startTransitionFinishTransaction?.apply() state.startTransitionFinishCb?.onTransitionFinished(null /* wct */) requestSplitSelect(wct, taskInfo, splitPosition) } return true } Loading @@ -355,6 +456,12 @@ class DragToDesktopTransitionHandler( finishCallback: Transitions.TransitionFinishCallback ) { val state = requireTransitionState() // We don't want to merge the split select animation if that's what we requested. if (state.cancelState == CancelState.CANCEL_SPLIT_LEFT || state.cancelState == CancelState.CANCEL_SPLIT_RIGHT) { clearState() return } val isCancelTransition = info.type == TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP && transition == state.cancelTransitionToken && Loading Loading @@ -552,6 +659,17 @@ class DragToDesktopTransitionHandler( private fun startCancelDragToDesktopTransition() { val state = requireTransitionState() val wct = WindowContainerTransaction() restoreWindowOrder(wct, state) state.cancelTransitionToken = transitions.startTransition( TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this ) } private fun restoreWindowOrder( wct: WindowContainerTransaction, state: TransitionState = requireTransitionState() ) { when (state) { is TransitionState.FromFullscreen -> { // There may have been tasks sent behind home that are not the dragged task (like Loading Loading @@ -580,9 +698,6 @@ class DragToDesktopTransitionHandler( } val homeWc = state.homeToken ?: error("Home task should be non-null before cancelling") wct.restoreTransientOrder(homeWc) state.cancelTransitionToken = transitions.startTransition(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this) } private fun clearState() { Loading Loading @@ -624,7 +739,7 @@ class DragToDesktopTransitionHandler( abstract var cancelTransitionToken: IBinder? abstract var homeToken: WindowContainerToken? abstract var draggedTaskChange: Change? abstract var cancelled: Boolean abstract var cancelState: CancelState abstract var startAborted: Boolean data class FromFullscreen( Loading @@ -636,7 +751,7 @@ class DragToDesktopTransitionHandler( override var cancelTransitionToken: IBinder? = null, override var homeToken: WindowContainerToken? = null, override var draggedTaskChange: Change? = null, override var cancelled: Boolean = false, override var cancelState: CancelState = CancelState.NO_CANCEL, override var startAborted: Boolean = false, var otherRootChanges: MutableList<Change> = mutableListOf() ) : TransitionState() Loading @@ -650,13 +765,25 @@ class DragToDesktopTransitionHandler( override var cancelTransitionToken: IBinder? = null, override var homeToken: WindowContainerToken? = null, override var draggedTaskChange: Change? = null, override var cancelled: Boolean = false, override var cancelState: CancelState = CancelState.NO_CANCEL, override var startAborted: Boolean = false, var splitRootChange: Change? = null, var otherSplitTask: Int ) : TransitionState() } /** Enum to provide context on cancelling a drag to desktop event. */ enum class CancelState { /** No cancel case; this drag is not flagged for a cancel event. */ NO_CANCEL, /** A standard cancel event; should restore task to previous windowing mode. */ STANDARD_CANCEL, /** A cancel event where the task will request to enter split on the left side. */ CANCEL_SPLIT_LEFT, /** A cancel event where the task will request to enter split on the right side. */ CANCEL_SPLIT_RIGHT } companion object { /** The duration of the animation to commit or cancel the drag-to-desktop gesture. */ private const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt +115 −46 File changed.Preview size limit exceeded, changes collapsed. Show changes