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

Commit e8402bc4 authored by Jiaming Liu's avatar Jiaming Liu Committed by Android (Google) Code Review
Browse files

Merge "[Bubbles] Fix bubble disappear issue when quickly switching" into main

parents 7fd0e9c0 fbb42267
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -306,6 +306,42 @@ class BubbleBarAnimationHelperTest {
        assertThat(afterCalled).isTrue()
    }

    @Test
    fun animateExpansion_withPendingAnimation() {
        val bubble = createBubble("key").initialize(container)
        val bbev = bubble.bubbleBarExpandedView!!

        val semaphore = Semaphore(0)
        var afterCalled = false
        val after = Runnable {
            afterCalled = true
            semaphore.release()
        }

        var expandedViewWithPendingAnimationBefore: BubbleBarExpandedView? = null
        var expandedViewWithPendingAnimationAfter: BubbleBarExpandedView? = null

        activityScenario.onActivity {
            bbev.onTaskCreated()
            // Make the TaskView invisible so that the animation waits on TaskView visibility.
            bbev.bubbleTaskView!!.listener
                .onTaskVisibilityChanged(0, false /* visible */)
            animationHelper.animateExpansion(bubble, after)
            expandedViewWithPendingAnimationBefore =
                animationHelper.expandedViewWithPendingAnimation

            animationHelper.cancelAnimations()
            expandedViewWithPendingAnimationAfter =
                animationHelper.expandedViewWithPendingAnimation
        }
        getInstrumentation().waitForIdleSync()

        assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
        assertThat(afterCalled).isTrue()
        assertThat(expandedViewWithPendingAnimationBefore).isEqualTo(bbev)
        assertThat(expandedViewWithPendingAnimationAfter).isNull()
    }

    @Test
    fun onImeTopChanged_noOverlap() {
        val bubble = createBubble(key = "b1").initialize(container)
+66 −3
Original line number Diff line number Diff line
@@ -159,10 +159,18 @@ class BubbleBarExpandedViewTest {
    @Test
    fun animateExpansion_waitsUntilTaskCreated() {
        var animated = false
        bubbleExpandedView.animateExpansionWhenTaskViewVisible { animated = true }
        var endRunnableRun = false
        bubbleExpandedView.animateExpansionWhenTaskViewVisible(
            { animated = true },
            { endRunnableRun = true }
        )
        assertThat(animated).isFalse()
        assertThat(endRunnableRun).isFalse()
        bubbleExpandedView.onTaskCreated()
        assertThat(animated).isTrue()
        // The end runnable should not be run unless the animation is canceled by
        // cancelPendingAnimation.
        assertThat(endRunnableRun).isFalse()
    }

    @Test
@@ -189,8 +197,15 @@ class BubbleBarExpandedViewTest {
        assertThat(taskView.taskView.parent).isEqualTo(expandedView)

        var animated = false
        expandedView.animateExpansionWhenTaskViewVisible { animated = true }
        var endRunnableRun = false
        expandedView.animateExpansionWhenTaskViewVisible(
            { animated = true },
            { endRunnableRun = true }
        )
        assertThat(animated).isTrue()
        // The end runnable should not be run unless the animation is canceled by
        // cancelPendingAnimation.
        assertThat(endRunnableRun).isFalse()
    }

    @Test
@@ -219,13 +234,61 @@ class BubbleBarExpandedViewTest {
        assertThat(taskView.taskView.parent).isEqualTo(expandedView)

        var animated = false
        expandedView.animateExpansionWhenTaskViewVisible { animated = true }
        var endRunnableRun = false
        expandedView.animateExpansionWhenTaskViewVisible(
            { animated = true },
            { endRunnableRun = true }
        )
        assertThat(animated).isFalse()
        assertThat(endRunnableRun).isFalse()

        // send a visible signal to simulate a new surface getting created
        expandedView.onContentVisibilityChanged(true)

        assertThat(animated).isTrue()
        assertThat(endRunnableRun).isFalse()
    }


    @Test
    fun animateExpansion_taskViewAttachedAndInvisibleThenAnimationCanceled() {
        val inflater = LayoutInflater.from(context)
        val expandedView = inflater.inflate(
            R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */
        ) as BubbleBarExpandedView
        val taskView = bubbleTaskViewFactory.create()
        val taskViewParent = FrameLayout(context)
        taskViewParent.addView(taskView.taskView)
        taskView.listener.onTaskCreated(666, ComponentName(context, "BubbleBarExpandedViewTest"))
        assertThat(taskView.isVisible).isTrue()
        taskView.listener.onTaskVisibilityChanged(666, false)
        assertThat(taskView.isVisible).isFalse()

        expandedView.initialize(
            expandedViewManager,
            positioner,
            false /* isOverflow */,
            bubble,
            taskView,
        )

        // the task view should be added to the expanded view
        assertThat(taskView.taskView.parent).isEqualTo(expandedView)

        var animated = false
        var endRunnableRun = false
        expandedView.animateExpansionWhenTaskViewVisible(
            { animated = true },
            { endRunnableRun = true }
        )
        assertThat(animated).isFalse()
        assertThat(endRunnableRun).isFalse()

        // Cancel the pending animation.
        expandedView.cancelPendingAnimation()

        assertThat(animated).isFalse()
        assertThat(endRunnableRun).isTrue()
    }

    private fun BubbleBarExpandedView.menuView(): BubbleBarMenuView {
+58 −25
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.view.SurfaceControl;
import android.widget.FrameLayout;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import com.android.app.animation.Interpolators;
import com.android.internal.protolog.ProtoLog;
@@ -110,6 +111,7 @@ public class BubbleBarAnimationHelper {

    // TODO(b/381936992): remove expanded bubble state from this helper class
    private BubbleViewProvider mExpandedBubble;
    private BubbleBarExpandedView mExpandedViewWithPendingAnimation;

    public BubbleBarAnimationHelper(Context context, BubblePositioner positioner) {
        mPositioner = positioner;
@@ -142,8 +144,6 @@ public class BubbleBarAnimationHelper {

        bbev.setAnimationMatrix(mExpandedViewContainerMatrix);

        bbev.animateExpansionWhenTaskViewVisible(() -> {
            bbev.getHandleView().setAlpha(1);
        ObjectAnimator alphaAnim = createAlphaAnimator(bbev, /* visible= */ true);
        alphaAnim.setDuration(EXPANDED_VIEW_EXPAND_ALPHA_DURATION);
        alphaAnim.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
@@ -153,6 +153,8 @@ public class BubbleBarAnimationHelper {
                bbev.setAnimating(false);
            }
        });
        Runnable animationRunnable = () -> {
            bbev.getHandleView().setAlpha(1);
            startNewAnimator(alphaAnim);

            PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
@@ -178,7 +180,8 @@ public class BubbleBarAnimationHelper {
                        }
                    })
                    .start();
        });
        };
        animateExpansionWhenTaskViewVisible(bbev, animationRunnable, endRunnable);
    }

    private void prepareForAnimateIn(BubbleBarExpandedView bbev) {
@@ -297,7 +300,6 @@ public class BubbleBarAnimationHelper {
        toBbev.getHandleView().setAlpha(0f);
        toBbev.getHandleView().setHandleInitialColor(fromBbev.getHandleView().getHandleColor());

        toBbev.animateExpansionWhenTaskViewVisible(() -> {
        AnimatorSet switchAnim = new AnimatorSet();
        switchAnim.playTogether(
                switchOutAnimator(fromBbev), switchInAnimator(toBbev, startTx, endTx));
@@ -311,8 +313,22 @@ public class BubbleBarAnimationHelper {
                }
            }
        });
            startNewAnimator(switchAnim);
        });
        Runnable animationRunnable = () -> startNewAnimator(switchAnim);
        animateExpansionWhenTaskViewVisible(toBbev, animationRunnable, endRunnable);
    }

    private void animateExpansionWhenTaskViewVisible(@NonNull BubbleBarExpandedView bbev,
            @NonNull Runnable animationRunnable, @Nullable Runnable endRunnable) {
        // When a new animation is requested, the old pending animation should be canceled,
        // regardless of whether the new animation is run immediately or not.
        cancelPendingAnimation();
        boolean pending = bbev.animateExpansionWhenTaskViewVisible(() -> {
            mExpandedViewWithPendingAnimation = null;
            animationRunnable.run();
        }, endRunnable);
        if (pending) {
            mExpandedViewWithPendingAnimation = bbev;
        }
    }

    private float getSwitchAnimationInitialTx(float endTx) {
@@ -680,6 +696,7 @@ public class BubbleBarAnimationHelper {
            }
            mRunningAnimator = null;
        }
        cancelPendingAnimation();
    }

    /** Handles IME position changes. */
@@ -692,6 +709,17 @@ public class BubbleBarAnimationHelper {
        bbev.onImeTopChanged(imeTop);
    }

    /**
     * Cancels the pending animations. This also executes the endRunnable for the animation.
     * Canceling pending animations prevents out-of-order animation execution.
     */
    private void cancelPendingAnimation() {
        if (mExpandedViewWithPendingAnimation != null) {
            mExpandedViewWithPendingAnimation.cancelPendingAnimation();
            mExpandedViewWithPendingAnimation = null;
        }
    }

    private @Nullable BubbleBarExpandedView getExpandedView() {
        BubbleViewProvider bubble = mExpandedBubble;
        if (bubble != null) {
@@ -700,6 +728,11 @@ public class BubbleBarAnimationHelper {
        return null;
    }

    @VisibleForTesting
    BubbleBarExpandedView getExpandedViewWithPendingAnimation() {
        return mExpandedViewWithPendingAnimation;
    }

    private void updateExpandedView(BubbleBarExpandedView bbev) {
        if (bbev == null) {
            Log.w(TAG, "Trying to update the expanded view without a bubble");
+44 −1
Original line number Diff line number Diff line
@@ -148,6 +148,14 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
    @Nullable
    private Runnable mAnimateExpansion = null;

    /**
     * A runnable to be executed if the expansion animation is canceled before the task view is made
     * visible. If the animation is run, the end runnable is executed with the animation, so only
     * need to run the end runnable if the animation is dropped before it is ever started.
     */
    @Nullable
    private Runnable mAnimateExpansionEndRunnable = null;

    /**
     * Whether we want the {@code TaskView}'s content to be visible (alpha = 1f). If
     * {@link #mIsAnimating} is true, this may not reflect the {@code TaskView}'s actual alpha
@@ -411,11 +419,37 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
        mListener.onBackPressed();
    }

    void animateExpansionWhenTaskViewVisible(Runnable animateExpansion) {
    /**
     * Returns {@code true} if the animation is scheduled instead of being run immediately.
     *
     * @param animateExpansion the {@link Runnable} to be run when the TaskView is visible.
     * @param endRunnable the {@link Runnable} to be run only when the animation is canceled by
     *                    {@link #cancelPendingAnimation()}.
     */
    boolean animateExpansionWhenTaskViewVisible(
            @NonNull Runnable animateExpansion, @Nullable Runnable endRunnable) {
        if ((mBubbleTaskView != null && mBubbleTaskView.isVisible()) || mIsOverflow) {
            animateExpansion.run();
            return false;
        } else {
            mAnimateExpansion = animateExpansion;
            mAnimateExpansionEndRunnable = endRunnable;
            return true;
        }
    }

    /**
     * Cancels the pending animation that is waiting on TaskView becoming visible. This also
     * executes the end {@code Runnable}.
     */
    void cancelPendingAnimation() {
        if (mAnimateExpansion != null) {
            mAnimateExpansion = null;
            // The end runnable must be executed here, because it is not invoked for non-running
            // animators.
            if (mAnimateExpansionEndRunnable != null) {
                mAnimateExpansionEndRunnable.run();
            }
        }
    }

@@ -424,6 +458,9 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
        if (mAnimateExpansion != null) {
            mAnimateExpansion.run();
            mAnimateExpansion = null;
            // No need to execute the end runnable if the animation is played. It will be run in the
            // animation end callback.
            mAnimateExpansionEndRunnable = null;
        }
    }

@@ -557,6 +594,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
        return mTaskView != null && mTaskView.isZOrderedOnTop();
    }

    @VisibleForTesting
    @Nullable
    BubbleTaskView getBubbleTaskView() {
        return mBubbleTaskView;
    }

    /**
     * Sets whether the view is animating, in this case we won't change the content visibility
     * until the animation is done.