Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt +48 −28 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.graphics.Rect import android.os.IBinder import android.view.SurfaceControl.Transaction import android.view.WindowManager.TRANSIT_CHANGE import android.view.animation.PathInterpolator import android.window.TransitionInfo import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction Loading @@ -35,7 +36,6 @@ import com.android.wm.shell.common.DisplayImeController import com.android.wm.shell.common.DisplayImeController.ImePositionProcessor.IME_ANIMATION_DEFAULT import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.animation.Interpolators import com.android.wm.shell.shared.animation.WindowAnimator import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.FocusTransitionObserver Loading Loading @@ -66,8 +66,13 @@ class DesktopImeHandler( } } var topTask: ActivityManager.RunningTaskInfo? = null var previousBounds: Rect? = null private val taskToImeTarget = mutableMapOf<Int, ImeTarget>() data class ImeTarget( var topTask: ActivityManager.RunningTaskInfo, var previousBounds: Rect? = null, var newBounds: Rect? = null, ) override fun onImeStartPositioning( displayId: Int, Loading @@ -84,27 +89,22 @@ class DesktopImeHandler( if (showing) { // Only get the top task when the IME will be showing. Otherwise just restore // previously manipulated task. val currentTopTask = if (Flags.enableDisplayFocusInShellTransitions()) { shellTaskOrganizer.getRunningTaskInfo( focusTransitionObserver.globallyFocusedTaskId ) } else { shellTaskOrganizer.getRunningTasks(displayId).find { taskInfo -> taskInfo.isFocused } } ?: return IME_ANIMATION_DEFAULT val currentTopTask = getFocusedTask(displayId) ?: return IME_ANIMATION_DEFAULT if (!userRepositories.current.isActiveTask(currentTopTask.taskId)) return IME_ANIMATION_DEFAULT topTask = currentTopTask val taskBounds = currentTopTask.configuration.windowConfiguration?.bounds ?: return IME_ANIMATION_DEFAULT val token = currentTopTask.token // We have already moved this task, do not move it multiple times during the same IME // session. if (taskToImeTarget[currentTopTask.taskId] != null) return IME_ANIMATION_DEFAULT val imeTarget = ImeTarget(currentTopTask, Rect(taskBounds)) taskToImeTarget[currentTopTask.taskId] = imeTarget // Save the previous bounds to restore after IME disappears previousBounds = Rect(taskBounds) val taskHeight = taskBounds.height() val stableBounds = Rect() val displayLayout = Loading @@ -129,22 +129,39 @@ class DesktopImeHandler( val finalBounds = Rect(taskBounds.left, finalTop, taskBounds.right, finalBottom) logD("Moving task %d due to IME", currentTopTask.taskId) val wct = WindowContainerTransaction().setBounds(token, finalBounds) logD("Moving task %d due to IME", imeTarget.topTask.taskId) val wct = WindowContainerTransaction().setBounds(imeTarget.topTask.token, finalBounds) transitions.startTransition(TRANSIT_CHANGE, wct, this) taskToImeTarget[currentTopTask.taskId]?.newBounds = finalBounds } else { val wct = WindowContainerTransaction() taskToImeTarget.forEach { taskId, imeTarget -> val task = shellTaskOrganizer.getRunningTaskInfo(taskId) ?: return@forEach // If the current bounds are not equal to the newBounds we have saved, the task // must have moved by other means. if (task.configuration.windowConfiguration.bounds == imeTarget.newBounds) { val finalBounds = imeTarget.previousBounds ?: return@forEach // Restore the previous bounds if they exist val finalBounds = previousBounds ?: return IME_ANIMATION_DEFAULT val previousTask = topTask ?: return IME_ANIMATION_DEFAULT logD("Restoring bounds of task %d due to IME", previousTask.taskId) val wct = WindowContainerTransaction().setBounds(previousTask.token, finalBounds) wct.setBounds(imeTarget.topTask.token, finalBounds) } } if (!wct.isEmpty) { transitions.startTransition(TRANSIT_CHANGE, wct, this) } // Ime has disappeared let's remove all the targets. taskToImeTarget.clear() } return IME_ANIMATION_DEFAULT } private fun getFocusedTask(displayId: Int): ActivityManager.RunningTaskInfo? = if (Flags.enableDisplayFocusInShellTransitions()) { shellTaskOrganizer.getRunningTaskInfo(focusTransitionObserver.globallyFocusedTaskId) } else { shellTaskOrganizer.getRunningTasks(displayId).find { taskInfo -> taskInfo.isFocused } } override fun startAnimation( transition: IBinder, info: TransitionInfo, Loading Loading @@ -228,12 +245,15 @@ class DesktopImeHandler( private companion object { private const val TAG = "DesktopImeHandler" // NOTE: All these constants came from InsetsController. private const val ANIMATION_DURATION_SHOW_MS = 275L private const val ANIMATION_DURATION_HIDE_MS = 340L private val INTERPOLATOR = PathInterpolator(0.4f, 0f, 0.2f, 1f) private val boundsChangeAnimatorDef = WindowAnimator.BoundsAnimationParams( durationMs = RESIZE_DURATION_MS, interpolator = Interpolators.STANDARD_ACCELERATE, durationMs = ANIMATION_DURATION_SHOW_MS, interpolator = INTERPOLATOR, ) private const val RESIZE_DURATION_MS = 300L } } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImeHandlerTest.kt +86 −27 Original line number Diff line number Diff line Loading @@ -119,9 +119,18 @@ class DesktopImeHandlerTest : ShellTestCase() { setUpLandscapeDisplay() val wct = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) val taskBounds = Rect(0, 400, 500, 1600) val expectedBounds = Rect( taskBounds.left, STATUS_BAR_HEIGHT, taskBounds.right, STATUS_BAR_HEIGHT + taskBounds.height(), ) var freeformTask = createFreeformTask(DEFAULT_DISPLAY, taskBounds) freeformTask.isFocused = true whenever(shellTaskOrganizer.getRunningTasks(any())).thenReturn(arrayListOf(freeformTask)) whenever(shellTaskOrganizer.getRunningTaskInfo(freeformTask.taskId)) .thenReturn(freeformTask) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, Loading @@ -134,15 +143,10 @@ class DesktopImeHandlerTest : ShellTestCase() { // Moves the task up to the top of stable bounds verify(transitions).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) assertThat(findBoundsChange(wct.value, freeformTask)) .isEqualTo( Rect( taskBounds.left, STATUS_BAR_HEIGHT, taskBounds.right, STATUS_BAR_HEIGHT + taskBounds.height(), ) ) assertThat(findBoundsChange(wct.value, freeformTask)).isEqualTo(expectedBounds) // Update the freeform task bounds due to above transition freeformTask.configuration.windowConfiguration.setBounds(expectedBounds) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, Loading @@ -168,6 +172,13 @@ class DesktopImeHandlerTest : ShellTestCase() { setUpLandscapeDisplay() val wct = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) val taskBounds = Rect(0, 400, 500, 1600) val expectedBounds = Rect( taskBounds.left, STATUS_BAR_HEIGHT, taskBounds.right, STATUS_BAR_HEIGHT + taskBounds.height(), ) var freeformTask = createFreeformTask(DEFAULT_DISPLAY, taskBounds) freeformTask.isFocused = true whenever(focusTransitionObserver.globallyFocusedTaskId).thenReturn(freeformTask.taskId) Loading @@ -185,15 +196,10 @@ class DesktopImeHandlerTest : ShellTestCase() { // Moves the task up to the top of stable bounds verify(transitions).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) assertThat(findBoundsChange(wct.value, freeformTask)) .isEqualTo( Rect( taskBounds.left, STATUS_BAR_HEIGHT, taskBounds.right, STATUS_BAR_HEIGHT + taskBounds.height(), ) ) assertThat(findBoundsChange(wct.value, freeformTask)).isEqualTo(expectedBounds) // Update the freeform task bounds due to above transition freeformTask.configuration.windowConfiguration.setBounds(expectedBounds) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, Loading @@ -217,9 +223,13 @@ class DesktopImeHandlerTest : ShellTestCase() { setUpLandscapeDisplay() val wct = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) val taskBounds = Rect(0, 1000, 500, 1600) val expectedBounds = Rect(taskBounds.left, IME_HEIGHT - taskBounds.height(), taskBounds.right, IME_HEIGHT) var freeformTask = createFreeformTask(DEFAULT_DISPLAY, taskBounds) freeformTask.isFocused = true whenever(shellTaskOrganizer.getRunningTasks(any())).thenReturn(arrayListOf(freeformTask)) whenever(shellTaskOrganizer.getRunningTaskInfo(freeformTask.taskId)) .thenReturn(freeformTask) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, Loading @@ -232,15 +242,10 @@ class DesktopImeHandlerTest : ShellTestCase() { // Moves the task up to the top of stable bounds verify(transitions).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) assertThat(findBoundsChange(wct.value, freeformTask)) .isEqualTo( Rect( taskBounds.left, IME_HEIGHT - taskBounds.height(), taskBounds.right, IME_HEIGHT, ) ) assertThat(findBoundsChange(wct.value, freeformTask)).isEqualTo(expectedBounds) // Update the freeform task bounds due to above transition freeformTask.configuration.windowConfiguration.setBounds(expectedBounds) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, Loading Loading @@ -280,6 +285,60 @@ class DesktopImeHandlerTest : ShellTestCase() { verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_IME_BUGFIX, Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS, ) fun onImeStartPositioning_changeTaskPositionManually_doesNotRestorePreImeBounds() { setUpLandscapeDisplay() val wct = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) val taskBounds = Rect(0, 400, 500, 1600) val expectedBounds = Rect( taskBounds.left, STATUS_BAR_HEIGHT, taskBounds.right, STATUS_BAR_HEIGHT + taskBounds.height(), ) var freeformTask = createFreeformTask(DEFAULT_DISPLAY, taskBounds) freeformTask.isFocused = true whenever(focusTransitionObserver.globallyFocusedTaskId).thenReturn(freeformTask.taskId) whenever(shellTaskOrganizer.getRunningTaskInfo(freeformTask.taskId)) .thenReturn(freeformTask) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, hiddenTop = DISPLAY_DIMENSION_SHORT, shownTop = IME_HEIGHT, showing = true, isFloating = false, t = mock(), ) // Moves the task up to the top of stable bounds verify(transitions).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) assertThat(findBoundsChange(wct.value, freeformTask)).isEqualTo(expectedBounds) // Update the freeform task bounds to some other bounds that might happen due to user // action. expectedBounds.offset(100, 100) freeformTask.configuration.windowConfiguration.setBounds(expectedBounds) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, hiddenTop = DISPLAY_DIMENSION_SHORT, shownTop = IME_HEIGHT, showing = false, isFloating = false, t = mock(), ) // Task is not moved back to original position with a new transition. verify(transitions, times(1)) .startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) } private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? = wct.changes.entries .find { (token, change) -> Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt +48 −28 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.graphics.Rect import android.os.IBinder import android.view.SurfaceControl.Transaction import android.view.WindowManager.TRANSIT_CHANGE import android.view.animation.PathInterpolator import android.window.TransitionInfo import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction Loading @@ -35,7 +36,6 @@ import com.android.wm.shell.common.DisplayImeController import com.android.wm.shell.common.DisplayImeController.ImePositionProcessor.IME_ANIMATION_DEFAULT import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.animation.Interpolators import com.android.wm.shell.shared.animation.WindowAnimator import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.FocusTransitionObserver Loading Loading @@ -66,8 +66,13 @@ class DesktopImeHandler( } } var topTask: ActivityManager.RunningTaskInfo? = null var previousBounds: Rect? = null private val taskToImeTarget = mutableMapOf<Int, ImeTarget>() data class ImeTarget( var topTask: ActivityManager.RunningTaskInfo, var previousBounds: Rect? = null, var newBounds: Rect? = null, ) override fun onImeStartPositioning( displayId: Int, Loading @@ -84,27 +89,22 @@ class DesktopImeHandler( if (showing) { // Only get the top task when the IME will be showing. Otherwise just restore // previously manipulated task. val currentTopTask = if (Flags.enableDisplayFocusInShellTransitions()) { shellTaskOrganizer.getRunningTaskInfo( focusTransitionObserver.globallyFocusedTaskId ) } else { shellTaskOrganizer.getRunningTasks(displayId).find { taskInfo -> taskInfo.isFocused } } ?: return IME_ANIMATION_DEFAULT val currentTopTask = getFocusedTask(displayId) ?: return IME_ANIMATION_DEFAULT if (!userRepositories.current.isActiveTask(currentTopTask.taskId)) return IME_ANIMATION_DEFAULT topTask = currentTopTask val taskBounds = currentTopTask.configuration.windowConfiguration?.bounds ?: return IME_ANIMATION_DEFAULT val token = currentTopTask.token // We have already moved this task, do not move it multiple times during the same IME // session. if (taskToImeTarget[currentTopTask.taskId] != null) return IME_ANIMATION_DEFAULT val imeTarget = ImeTarget(currentTopTask, Rect(taskBounds)) taskToImeTarget[currentTopTask.taskId] = imeTarget // Save the previous bounds to restore after IME disappears previousBounds = Rect(taskBounds) val taskHeight = taskBounds.height() val stableBounds = Rect() val displayLayout = Loading @@ -129,22 +129,39 @@ class DesktopImeHandler( val finalBounds = Rect(taskBounds.left, finalTop, taskBounds.right, finalBottom) logD("Moving task %d due to IME", currentTopTask.taskId) val wct = WindowContainerTransaction().setBounds(token, finalBounds) logD("Moving task %d due to IME", imeTarget.topTask.taskId) val wct = WindowContainerTransaction().setBounds(imeTarget.topTask.token, finalBounds) transitions.startTransition(TRANSIT_CHANGE, wct, this) taskToImeTarget[currentTopTask.taskId]?.newBounds = finalBounds } else { val wct = WindowContainerTransaction() taskToImeTarget.forEach { taskId, imeTarget -> val task = shellTaskOrganizer.getRunningTaskInfo(taskId) ?: return@forEach // If the current bounds are not equal to the newBounds we have saved, the task // must have moved by other means. if (task.configuration.windowConfiguration.bounds == imeTarget.newBounds) { val finalBounds = imeTarget.previousBounds ?: return@forEach // Restore the previous bounds if they exist val finalBounds = previousBounds ?: return IME_ANIMATION_DEFAULT val previousTask = topTask ?: return IME_ANIMATION_DEFAULT logD("Restoring bounds of task %d due to IME", previousTask.taskId) val wct = WindowContainerTransaction().setBounds(previousTask.token, finalBounds) wct.setBounds(imeTarget.topTask.token, finalBounds) } } if (!wct.isEmpty) { transitions.startTransition(TRANSIT_CHANGE, wct, this) } // Ime has disappeared let's remove all the targets. taskToImeTarget.clear() } return IME_ANIMATION_DEFAULT } private fun getFocusedTask(displayId: Int): ActivityManager.RunningTaskInfo? = if (Flags.enableDisplayFocusInShellTransitions()) { shellTaskOrganizer.getRunningTaskInfo(focusTransitionObserver.globallyFocusedTaskId) } else { shellTaskOrganizer.getRunningTasks(displayId).find { taskInfo -> taskInfo.isFocused } } override fun startAnimation( transition: IBinder, info: TransitionInfo, Loading Loading @@ -228,12 +245,15 @@ class DesktopImeHandler( private companion object { private const val TAG = "DesktopImeHandler" // NOTE: All these constants came from InsetsController. private const val ANIMATION_DURATION_SHOW_MS = 275L private const val ANIMATION_DURATION_HIDE_MS = 340L private val INTERPOLATOR = PathInterpolator(0.4f, 0f, 0.2f, 1f) private val boundsChangeAnimatorDef = WindowAnimator.BoundsAnimationParams( durationMs = RESIZE_DURATION_MS, interpolator = Interpolators.STANDARD_ACCELERATE, durationMs = ANIMATION_DURATION_SHOW_MS, interpolator = INTERPOLATOR, ) private const val RESIZE_DURATION_MS = 300L } }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImeHandlerTest.kt +86 −27 Original line number Diff line number Diff line Loading @@ -119,9 +119,18 @@ class DesktopImeHandlerTest : ShellTestCase() { setUpLandscapeDisplay() val wct = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) val taskBounds = Rect(0, 400, 500, 1600) val expectedBounds = Rect( taskBounds.left, STATUS_BAR_HEIGHT, taskBounds.right, STATUS_BAR_HEIGHT + taskBounds.height(), ) var freeformTask = createFreeformTask(DEFAULT_DISPLAY, taskBounds) freeformTask.isFocused = true whenever(shellTaskOrganizer.getRunningTasks(any())).thenReturn(arrayListOf(freeformTask)) whenever(shellTaskOrganizer.getRunningTaskInfo(freeformTask.taskId)) .thenReturn(freeformTask) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, Loading @@ -134,15 +143,10 @@ class DesktopImeHandlerTest : ShellTestCase() { // Moves the task up to the top of stable bounds verify(transitions).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) assertThat(findBoundsChange(wct.value, freeformTask)) .isEqualTo( Rect( taskBounds.left, STATUS_BAR_HEIGHT, taskBounds.right, STATUS_BAR_HEIGHT + taskBounds.height(), ) ) assertThat(findBoundsChange(wct.value, freeformTask)).isEqualTo(expectedBounds) // Update the freeform task bounds due to above transition freeformTask.configuration.windowConfiguration.setBounds(expectedBounds) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, Loading @@ -168,6 +172,13 @@ class DesktopImeHandlerTest : ShellTestCase() { setUpLandscapeDisplay() val wct = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) val taskBounds = Rect(0, 400, 500, 1600) val expectedBounds = Rect( taskBounds.left, STATUS_BAR_HEIGHT, taskBounds.right, STATUS_BAR_HEIGHT + taskBounds.height(), ) var freeformTask = createFreeformTask(DEFAULT_DISPLAY, taskBounds) freeformTask.isFocused = true whenever(focusTransitionObserver.globallyFocusedTaskId).thenReturn(freeformTask.taskId) Loading @@ -185,15 +196,10 @@ class DesktopImeHandlerTest : ShellTestCase() { // Moves the task up to the top of stable bounds verify(transitions).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) assertThat(findBoundsChange(wct.value, freeformTask)) .isEqualTo( Rect( taskBounds.left, STATUS_BAR_HEIGHT, taskBounds.right, STATUS_BAR_HEIGHT + taskBounds.height(), ) ) assertThat(findBoundsChange(wct.value, freeformTask)).isEqualTo(expectedBounds) // Update the freeform task bounds due to above transition freeformTask.configuration.windowConfiguration.setBounds(expectedBounds) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, Loading @@ -217,9 +223,13 @@ class DesktopImeHandlerTest : ShellTestCase() { setUpLandscapeDisplay() val wct = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) val taskBounds = Rect(0, 1000, 500, 1600) val expectedBounds = Rect(taskBounds.left, IME_HEIGHT - taskBounds.height(), taskBounds.right, IME_HEIGHT) var freeformTask = createFreeformTask(DEFAULT_DISPLAY, taskBounds) freeformTask.isFocused = true whenever(shellTaskOrganizer.getRunningTasks(any())).thenReturn(arrayListOf(freeformTask)) whenever(shellTaskOrganizer.getRunningTaskInfo(freeformTask.taskId)) .thenReturn(freeformTask) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, Loading @@ -232,15 +242,10 @@ class DesktopImeHandlerTest : ShellTestCase() { // Moves the task up to the top of stable bounds verify(transitions).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) assertThat(findBoundsChange(wct.value, freeformTask)) .isEqualTo( Rect( taskBounds.left, IME_HEIGHT - taskBounds.height(), taskBounds.right, IME_HEIGHT, ) ) assertThat(findBoundsChange(wct.value, freeformTask)).isEqualTo(expectedBounds) // Update the freeform task bounds due to above transition freeformTask.configuration.windowConfiguration.setBounds(expectedBounds) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, Loading Loading @@ -280,6 +285,60 @@ class DesktopImeHandlerTest : ShellTestCase() { verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_IME_BUGFIX, Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS, ) fun onImeStartPositioning_changeTaskPositionManually_doesNotRestorePreImeBounds() { setUpLandscapeDisplay() val wct = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) val taskBounds = Rect(0, 400, 500, 1600) val expectedBounds = Rect( taskBounds.left, STATUS_BAR_HEIGHT, taskBounds.right, STATUS_BAR_HEIGHT + taskBounds.height(), ) var freeformTask = createFreeformTask(DEFAULT_DISPLAY, taskBounds) freeformTask.isFocused = true whenever(focusTransitionObserver.globallyFocusedTaskId).thenReturn(freeformTask.taskId) whenever(shellTaskOrganizer.getRunningTaskInfo(freeformTask.taskId)) .thenReturn(freeformTask) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, hiddenTop = DISPLAY_DIMENSION_SHORT, shownTop = IME_HEIGHT, showing = true, isFloating = false, t = mock(), ) // Moves the task up to the top of stable bounds verify(transitions).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) assertThat(findBoundsChange(wct.value, freeformTask)).isEqualTo(expectedBounds) // Update the freeform task bounds to some other bounds that might happen due to user // action. expectedBounds.offset(100, 100) freeformTask.configuration.windowConfiguration.setBounds(expectedBounds) imeHandler.onImeStartPositioning( DEFAULT_DISPLAY, hiddenTop = DISPLAY_DIMENSION_SHORT, shownTop = IME_HEIGHT, showing = false, isFloating = false, t = mock(), ) // Task is not moved back to original position with a new transition. verify(transitions, times(1)) .startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull()) } private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? = wct.changes.entries .find { (token, change) -> Loading