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

Commit c4bff889 authored by Orhan Uysal's avatar Orhan Uysal
Browse files

Do not trigger IME task move on caption hold.

When a task is about to moved up due to IME, if a user is still holding
the caption down, do not move the task. This prevents a flicker that
happens when the transition is started but the user is dragging the
window around.

Test: Manual & atest DesktopImeHandlerTest
Fix: 415804441
Flag: com.android.window.flags.enable_desktop_ime_bugfix
Change-Id: Id3b1080cc69fd49419a3eed2224263214bde0c4d
parent 2583ac54
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -1807,6 +1807,7 @@ public abstract class WMShellModule {
            Optional<DesktopUserRepositories> desktopUserRepositories,
            FocusTransitionObserver focusTransitionObserver,
            DisplayImeController displayImeController,
            Optional<DesktopModeWindowDecorViewModel> desktopModeWindowDecorViewModel,
            DisplayController displayController,
            ShellTaskOrganizer shellTaskOrganizer,
            Transitions transitions,
@@ -1821,7 +1822,8 @@ public abstract class WMShellModule {
        return Optional.of(
                new DesktopImeHandler(desktopTasksController.get(), desktopUserRepositories.get(),
                        focusTransitionObserver, shellTaskOrganizer,
                        displayImeController, displayController, transitions, mainExecutor,
                        displayImeController, desktopModeWindowDecorViewModel, displayController,
                        transitions, mainExecutor,
                        animExecutor, context, shellInit));
    }

+28 −3
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ import com.android.wm.shell.shared.animation.WindowAnimator
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.FocusTransitionObserver
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel
import java.util.Optional

/** Handles the interactions between IME and desktop tasks */
class DesktopImeHandler(
@@ -48,13 +50,17 @@ class DesktopImeHandler(
    private val focusTransitionObserver: FocusTransitionObserver,
    private val shellTaskOrganizer: ShellTaskOrganizer,
    private val displayImeController: DisplayImeController,
    private val desktopModeWindowDecorViewModel: Optional<DesktopModeWindowDecorViewModel>,
    private val displayController: DisplayController,
    private val transitions: Transitions,
    private val mainExecutor: ShellExecutor,
    private val animExecutor: ShellExecutor,
    private val context: Context,
    shellInit: ShellInit,
) : DisplayImeController.ImePositionProcessor, Transitions.TransitionHandler {
) :
    DisplayImeController.ImePositionProcessor,
    Transitions.TransitionHandler,
    DesktopModeWindowDecorViewModel.CaptionTouchStatusListener {

    init {
        shellInit.addInitCallback(::onInit, this)
@@ -63,11 +69,16 @@ class DesktopImeHandler(
    private fun onInit() {
        if (DesktopExperienceFlags.ENABLE_DESKTOP_IME_BUGFIX.isTrue()) {
            displayImeController.addPositionProcessor(this)
            desktopModeWindowDecorViewModel.ifPresent { it ->
                it.registerCaptionTouchStatusListener(this)
            }
        }
    }

    private val taskToImeTarget = mutableMapOf<Int, ImeTarget>()
    private var imeTriggeredTransition: IBinder? = null
    /** Responsible for tracking whether app header (not app handle) is pressed. */
    private var isCaptionPressed: Boolean = false

    data class ImeTarget(
        var topTask: ActivityManager.RunningTaskInfo,
@@ -87,9 +98,15 @@ class DesktopImeHandler(
            return IME_ANIMATION_DEFAULT
        }

        if (showing) {
        // Only get the top task when the IME will be showing. Otherwise just restore
        // previously manipulated task.
        if (showing) {
            // If user is still pressing the caption, we shouldn't move the task.
            if (isCaptionPressed) {
                logD("Caption is pressed, do not move task with IME")
                return IME_ANIMATION_DEFAULT
            }

            val currentTopTask = getFocusedTask(displayId) ?: return IME_ANIMATION_DEFAULT
            if (!userRepositories.current.isActiveTask(currentTopTask.taskId))
                return IME_ANIMATION_DEFAULT
@@ -265,6 +282,14 @@ class DesktopImeHandler(
        ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
    }

    override fun onCaptionPressed() {
        isCaptionPressed = true
    }

    override fun onCaptionReleased() {
        isCaptionPressed = false
    }

    private companion object {
        private const val TAG = "DesktopImeHandler"
        // NOTE: All these constants came from InsetsController.
+30 −0
Original line number Diff line number Diff line
@@ -264,6 +264,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
    private final LatencyTracker mLatencyTracker;
    private final CompatUIHandler mCompatUI;
    private final UserProfileContexts mUserProfileContexts;
    private CaptionTouchStatusListener mCaptionTouchStatusListener;

    public DesktopModeWindowDecorViewModel(
            Context context,
@@ -1037,6 +1038,18 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
        mDesktopTilingDecorViewModel.onDeskRemoved(deskId);
    }

    /** Registers a {@link CaptionTouchStatusListener}. */
    public void registerCaptionTouchStatusListener(CaptionTouchStatusListener l) {
        mCaptionTouchStatusListener = l;
    }

    /** Listener for caption touch events. */
    public interface CaptionTouchStatusListener {
        /** Called when the caption is pressed. */
        void onCaptionPressed();
        /** Called when the caption is released. */
        void onCaptionReleased();
    }
    @VisibleForTesting
    public class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
            implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
@@ -1357,6 +1370,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,

        private boolean handleFreeformMotionEvent(DesktopModeWindowDecoration decoration,
                RunningTaskInfo taskInfo, View v, MotionEvent e) {
            updateTouchStatus(e);
            final int id = v.getId();
            if (mGestureDetector.onTouchEvent(e)) {
                return true;
@@ -1505,6 +1519,22 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
            }
        }

        private void updateTouchStatus(MotionEvent e) {
            if (mCaptionTouchStatusListener == null) {
                return;
            }
            switch (e.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    mCaptionTouchStatusListener.onCaptionPressed();
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL: {
                    mCaptionTouchStatusListener.onCaptionReleased();
                    break;
                }
            }
        }

        /**
         * Perform a task size toggle on release of the double-tap, assuming no drag event
         * was handled during the double-tap.
+67 −0
Original line number Diff line number Diff line
@@ -40,7 +40,9 @@ import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.FocusTransitionObserver
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlin.test.Test
import org.junit.Before
import org.mockito.ArgumentCaptor
@@ -69,6 +71,7 @@ class DesktopImeHandlerTest : ShellTestCase() {
    private val focusTransitionObserver = mock<FocusTransitionObserver>()
    private val desktopUserRepositories = mock<DesktopUserRepositories>()
    private val tasksRepository = mock<DesktopRepository>()
    private val desktopModeWindowDecorViewModel = mock<DesktopModeWindowDecorViewModel>()

    private lateinit var imeHandler: DesktopImeHandler
    private lateinit var shellInit: ShellInit
@@ -89,6 +92,7 @@ class DesktopImeHandlerTest : ShellTestCase() {
                focusTransitionObserver,
                shellTaskOrganizer,
                displayImeController,
                Optional.of(desktopModeWindowDecorViewModel),
                displayController,
                transitions,
                mainExecutor = mock(),
@@ -290,6 +294,69 @@ class DesktopImeHandlerTest : ShellTestCase() {
        verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull())
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_IME_BUGFIX)
    @DisableFlags(Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS)
    fun onImeStartPositioning_captionPressed_noOp() {
        setUpLandscapeDisplay()
        val wct = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
        val taskBounds = Rect(0, 400, 500, 1600)
        var freeformTask = createFreeformTask(DEFAULT_DISPLAY, taskBounds)
        freeformTask.isFocused = true
        whenever(shellTaskOrganizer.getRunningTasks(any())).thenReturn(arrayListOf(freeformTask))
        whenever(shellTaskOrganizer.getRunningTaskInfo(freeformTask.taskId))
            .thenReturn(freeformTask)

        imeHandler.onCaptionPressed()
        imeHandler.onImeStartPositioning(
            DEFAULT_DISPLAY,
            hiddenTop = DISPLAY_DIMENSION_SHORT,
            shownTop = IME_HEIGHT,
            showing = true,
            isFloating = false,
            t = mock(),
        )

        // No transition is started because the IME is floating
        verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), wct.capture(), anyOrNull())
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_IME_BUGFIX)
    @DisableFlags(Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS)
    fun onImeStartPositioning_captionPressedAndReleased_movesTask() {
        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.onCaptionPressed()
        imeHandler.onCaptionReleased()
        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)
    }

    @Test
    @EnableFlags(
        Flags.FLAG_ENABLE_DESKTOP_IME_BUGFIX,