Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1ef99ff8 authored by Orhan Uysal's avatar Orhan Uysal
Browse files

Prevent bounds restoring when user changes them

With IME moving and restoring bounds of the focused tasks,
if a user changes the bounds we shouldn't restore them and leave them as
be.

This cl makes it so that:
1) We store the ImeTarget and it's pre-IME
bounds and post-IME bounds.
2) When IME is disappearing, if any of the IME targets have different
   current bounds then the post-IME bounds, do not restore them to do
previous bounds.

Test: atest DesktopImeHandlerTest
Flag: com.android.window.flags.enable_desktop_ime_bugfix
Bug: 388570293
Change-Id: Id0823a92aec9f17624c40a0e18ae52fd40abe485
parent 1418b9e4
Loading
Loading
Loading
Loading
+48 −28
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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,
@@ -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 =
@@ -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,
@@ -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
    }
}
+86 −27
Original line number Diff line number Diff line
@@ -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,
@@ -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,
@@ -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)
@@ -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,
@@ -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,
@@ -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,
@@ -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) ->