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

Commit 3da70d55 authored by Mady Mellor's avatar Mady Mellor
Browse files

Fix an issue where onAllBubblesAnimatedOut could be called incorrectly

There is a check for BubbleStackView having children before this
is called, however, BubbleData might have bubbles before the views
have been updated -- add a hasBubbles check which checks data.

Flag: EXEMPT very safe bug fix
Test: atest BubbleControllerTest
Test: manual - spam bubbles while dismissing them
Bug: 420487074 (not cause of this bug but found while trying to repro)
Change-Id: I354d9199f6f897bab5e889fb73c67c295f16a814
parent 269374bb
Loading
Loading
Loading
Loading
+99 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.graphics.drawable.Icon
import android.os.Handler
import android.os.UserHandle
import android.os.UserManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.platform.test.flag.junit.SetFlagsRule
@@ -37,6 +38,7 @@ import android.view.IWindowManager
import android.view.InsetsSource
import android.view.InsetsState
import android.view.SurfaceControl
import android.view.View
import android.view.WindowInsets
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CHANGE
@@ -55,6 +57,7 @@ import com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE
import com.android.wm.shell.R
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.bubbles.Bubbles.BubbleExpandListener
import com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_GESTURE
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.common.DisplayController
@@ -710,6 +713,102 @@ class BubbleControllerTest(flags: FlagsParameterization) {
        assertThat(bubble.taskView.alpha).isEqualTo(0)
    }

    @DisableFlags(FLAG_ENABLE_BUBBLE_BAR)
    @Test
    fun onAllBubblesAnimatedOut_hasBubbles() {
        val bubble = createBubble("key")
        getInstrumentation().runOnMainSync {
            bubbleController.inflateAndAdd(
                bubble,
                /* suppressFlyout= */ true,
                /* showInShade= */ true
            )
        }
        assertThat(bubbleData.hasBubbles()).isTrue()

        getInstrumentation().runOnMainSync {
            bubbleController.onAllBubblesAnimatedOut()
        }
        assertThat(bubbleController.stackView!!.visibility).isEqualTo(View.VISIBLE)
    }

    @DisableFlags(FLAG_ENABLE_BUBBLE_BAR)
    @Test
    fun onAllBubblesAnimatedOut_noBubbles() {
        val bubble = createBubble("key")
        getInstrumentation().runOnMainSync {
            bubbleController.inflateAndAdd(
                bubble,
                /* suppressFlyout= */ true,
                /* showInShade= */ true
            )
        }
        getInstrumentation().runOnMainSync {
            bubbleController.dismissBubble("key", DISMISS_USER_GESTURE)
        }
        assertThat(bubbleData.hasBubbles()).isFalse()

        getInstrumentation().runOnMainSync {
            bubbleController.onAllBubblesAnimatedOut()
        }
        assertThat(bubbleController.stackView!!.visibility).isEqualTo(View.INVISIBLE)
    }

    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
    @Test
    fun onAllBubblesAnimatedOutBubbleBar_hasBubbles() {
        bubblePositioner.update(deviceConfigUnfolded)
        bubblePositioner.isShowingInBubbleBar = true
        getInstrumentation().runOnMainSync {
            bubbleController.setLauncherHasBubbleBar(true)
            bubbleController.registerBubbleStateListener(FakeBubblesStateListener())
        }

        val bubble = createBubble("key")
        getInstrumentation().runOnMainSync {
            bubbleController.inflateAndAdd(
                bubble,
                /* suppressFlyout= */ true,
                /* showInShade= */ true
            )
        }
        assertThat(bubbleData.hasBubbles()).isTrue()

        getInstrumentation().runOnMainSync {
            bubbleController.onAllBubblesAnimatedOut()
        }
        assertThat(bubbleController.layerView!!.visibility).isEqualTo(View.VISIBLE)
    }

    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
    @Test
    fun onAllBubblesAnimatedOutBubbleBar_noBubbles() {
        bubblePositioner.update(deviceConfigUnfolded)
        bubblePositioner.isShowingInBubbleBar = true
        getInstrumentation().runOnMainSync {
            bubbleController.setLauncherHasBubbleBar(true)
            bubbleController.registerBubbleStateListener(FakeBubblesStateListener())
        }

        val bubble = createBubble("key")
        getInstrumentation().runOnMainSync {
            bubbleController.inflateAndAdd(
                bubble,
                /* suppressFlyout= */ true,
                /* showInShade= */ true
            )
        }
        getInstrumentation().runOnMainSync {
            bubbleController.dismissBubble("key", DISMISS_USER_GESTURE)
        }
        assertThat(bubbleData.hasBubbles()).isFalse()

        getInstrumentation().runOnMainSync {
            bubbleController.onAllBubblesAnimatedOut()
        }
        assertThat(bubbleController.layerView!!.visibility).isEqualTo(View.INVISIBLE)
    }

    private fun createBubble(key: String, taskId: Int = 0): Bubble {
        val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
        val shortcutInfo = ShortcutInfo.Builder(context, "fakeId").setIcon(icon).build()
+5 −2
Original line number Diff line number Diff line
@@ -1296,10 +1296,13 @@ public class BubbleController implements ConfigurationChangeListener,
    };

    /**
     * Called by the BubbleStackView and whenever all bubbles have animated out, and none have been
     * added in the meantime.
     * Called by the view displaying bubbles once all bubbles have finished animating out.
     */
    public void onAllBubblesAnimatedOut() {
        if (hasBubbles()) {
            // Bubbles could've been added in the time it takes to animate out the bubbles.
            return;
        }
        mBubbleViewCallback.updateVisibility(false /* visible */);
        removeFromWindowManagerMaybe();
    }