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

Commit b794cbc3 authored by Ats Jenk's avatar Ats Jenk
Browse files

Clear out IME runnable if updating expanded state

When we update the stack expanded state, we first check for IME
visibility before performing the update.
If IME is visible, we try to hide IME first and then continue with
expand or collapse.
In case the IME callback does not trigger or takes too long, we can end
up in a state where the second call to expand/collapse runs before the
initial. Because the BubblePositioner state is updated immediately,
second time we immediately run the expand/collapse operation.
But if the IME callback finishes later, the runnable is still active in
BubbleController. And that runnable can run at a later stage.
This could cause for UI state to go out of sync with the state in
BubbleData. Where we think the bubble stack is collapsed, where it
actually was expanded.
Make sure the pending runnable is cleared out when a second call to
expand/collapse comes in and IME is already hidden.

Bug: 391909607
Flag: com.android.wm.shell.enable_bubble_swipe_up_cleanup
Test: atest WMShellMultivalentTestsOnDevice:com.android.wm.shell.bubbles.BubbleStackViewTest
Change-Id: I35a40cfedad74cf5be4ba3aef73df63ec0716e3b
parent a4ec5882
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.After
import org.junit.Assert.fail
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -311,6 +312,73 @@ class BubbleStackViewTest {
        }
    }

    @EnableFlags(Flags.FLAG_ENABLE_BUBBLE_SWIPE_UP_CLEANUP)
    @Test
    fun expandStack_clearsImeRunnable() {
        val bubble = createAndInflateBubble()

        InstrumentationRegistry.getInstrumentation().runOnMainSync {
            bubbleStackView.addBubble(bubble)
        }

        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
        assertThat(bubbleStackView.bubbleCount).isEqualTo(1)

        // Set up a pending runnable to be cleared
        bubbleStackViewManager.onImeHidden = Runnable {
            fail("IME runnable should not be called when IME is hidden")
        }

        positioner.setImeVisible(false, 0)

        InstrumentationRegistry.getInstrumentation().runOnMainSync {
            // simulate a request from the bubble data listener to expand the stack
            bubbleStackView.isExpanded = true
            verify(sysuiProxy).onStackExpandChanged(true)
            shellExecutor.flushAll()
        }

        // Ime runnable is reset
        assertThat(bubbleStackViewManager.onImeHidden).isNull()
    }

    @EnableFlags(Flags.FLAG_ENABLE_BUBBLE_SWIPE_UP_CLEANUP)
    @Test
    fun collapseStack_clearsImeRunnable() {
        val bubble = createAndInflateBubble()

        InstrumentationRegistry.getInstrumentation().runOnMainSync {
            bubbleStackView.addBubble(bubble)
        }

        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
        assertThat(bubbleStackView.bubbleCount).isEqualTo(1)

        positioner.setImeVisible(false, 0)

        InstrumentationRegistry.getInstrumentation().runOnMainSync {
            // simulate a request from the bubble data listener to expand the stack
            bubbleStackView.isExpanded = true
            verify(sysuiProxy).onStackExpandChanged(true)
            shellExecutor.flushAll()
        }

        // Set up a pending runnable to be cleared
        bubbleStackViewManager.onImeHidden = Runnable {
            fail("IME runnable should not be called when IME is hidden")
        }

        InstrumentationRegistry.getInstrumentation().runOnMainSync {
            // simulate a request from the bubble data listener to collapse the stack
            bubbleStackView.isExpanded = false
            verify(sysuiProxy).onStackExpandChanged(false)
            shellExecutor.flushAll()
        }

        // Check that the runnable is cleared
        assertThat(bubbleStackViewManager.onImeHidden).isNull()
    }

    @Test
    fun tapDifferentBubble_shouldReorder() {
        surfaceSynchronizer.isActive = false
@@ -707,6 +775,10 @@ class BubbleStackViewTest {
        override fun hideCurrentInputMethod(onImeHidden: Runnable?) {
            this.onImeHidden = onImeHidden
        }

        override fun clearImeHiddenRunnable() {
            this.onImeHidden = null
        }
    }

    private class FakeBubbleExpandListener : BubbleExpandListener {
+8 −0
Original line number Diff line number Diff line
@@ -672,6 +672,14 @@ public class BubbleController implements ConfigurationChangeListener,
        }
    }

    /**
     * Allows callers to reset runnable scheduled to run after IME is hidden by
     * {@link #hideCurrentInputMethod(Runnable)}
     */
    void clearImeHiddenRunnable() {
        mOnImeHidden = null;
    }

    /**
     * Called when the status bar has become visible or invisible (either permanently or
     * temporarily).
+7 −0
Original line number Diff line number Diff line
@@ -2300,6 +2300,13 @@ public class BubbleStackView extends FrameLayout
        if (mPositioner.isImeVisible()) {
            hideCurrentInputMethod(onImeHidden);
        } else {
            if (Flags.enableBubbleSwipeUpCleanup()) {
                // Clear out the existing runnable if one was scheduled to run after IME was hidden.
                // IME hide action can take time or in some cases not trigger at all. And we can
                // get a second call to expand in during it. Make sure we don't run a previous
                // runnable in that case.
                mManager.clearImeHiddenRunnable();
            }
            // the IME is already hidden, so run the runnable immediately
            onImeHidden.run();
        }
+7 −0
Original line number Diff line number Diff line
@@ -36,6 +36,9 @@ interface BubbleStackViewManager {
    /** Requests to hide the current input method. */
    fun hideCurrentInputMethod(onImeHidden: Runnable?)

    /** Allows callers to clear the runnable set by [hideCurrentInputMethod]. */
    fun clearImeHiddenRunnable()

    companion object {

        @JvmStatic
@@ -55,6 +58,10 @@ interface BubbleStackViewManager {
            override fun hideCurrentInputMethod(onImeHidden: Runnable?) {
                controller.hideCurrentInputMethod(onImeHidden)
            }

            override fun clearImeHiddenRunnable() {
                controller.clearImeHiddenRunnable()
            }
        }
    }
}