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

Commit 29be714f authored by Chris Li's avatar Chris Li
Browse files

Suppress the animation for launcher bubble bar jumpcut switch

Before, when launch a Task trampoline app into Bubble, the Bubble bar
will:
1. Show one Bubble icon
2. Show another Bubble icon (2 visible at same time)
3. Dismiss the first Bubble icon

After, it will show the first Bubble icon opening animation, and then
jumpcut to the second Bubble icon.

To achieve that:
1. Pass a signal to Launcher that such a Bubble update should suppress
   animation.
2. Make sure the first Bubble removal signal is sent with the second
   Bubble open signal in the same Bubble update.
3. Make sure the dispatching of Bubble removal signal will not cleanup
   the TaskView immediately, because we need the TaskView to be visible
   until the opening Bubble is visible.

Bug: 417848405
Test: atest WMShellUnitTests:BubbleDataTest
Flag: com.android.window.flags.fix_bubble_trampoline_animation
Change-Id: I584e45d51606bdf1c3c0003348521d8779efa848
parent e90beaf7
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -54,6 +54,10 @@ public class BubbleBarUpdate implements Parcelable {
    public Point expandedViewDropTargetSize;
    public boolean showOverflowChanged;
    public boolean showOverflow;
    /**
     * Whether to suppress the animation of this update. Currently, this is only used for jumpcut.
     */
    public boolean suppressAnimation;

    // This is only populated if bubbles have been removed.
    public List<RemovedBubble> removedBubbles = new ArrayList<>();
@@ -96,6 +100,7 @@ public class BubbleBarUpdate implements Parcelable {
                Point.class);
        showOverflowChanged = parcel.readBoolean();
        showOverflow = parcel.readBoolean();
        suppressAnimation = parcel.readBoolean();
    }

    /**
@@ -135,6 +140,7 @@ public class BubbleBarUpdate implements Parcelable {
                + " expandedViewDropTargetSize=" + expandedViewDropTargetSize
                + " showOverflowChanged=" + showOverflowChanged
                + " showOverflow=" + showOverflow
                + " suppressAnimation=" + suppressAnimation
                + " }";
    }

@@ -161,6 +167,7 @@ public class BubbleBarUpdate implements Parcelable {
        parcel.writeParcelable(expandedViewDropTargetSize, flags);
        parcel.writeBoolean(showOverflowChanged);
        parcel.writeBoolean(showOverflow);
        parcel.writeBoolean(suppressAnimation);
    }

    /**
+34 −0
Original line number Diff line number Diff line
@@ -98,6 +98,14 @@ public class BubbleData {
        BubbleBarLocation mBubbleBarLocation;
        // Pair with Bubble and @DismissReason Integer
        final List<Pair<Bubble, Integer>> removedBubbles = new ArrayList<>();
        /**
         * The closing Bubble of a jumpcut Bubble switch transition animation.
         *
         * This is different from {@link #removedBubbles} that the applying of this change will not
         * cleanup the TaskView, but will only notify the listener about the removal.
         */
        @Nullable
        Bubble jumpcutBubbleSwitchClosingBubble;

        // A read-only view of the bubbles list, changes there will be reflected here.
        final List<Bubble> bubbles;
@@ -114,6 +122,7 @@ public class BubbleData {
                    || addedBubble != null
                    || updatedBubble != null
                    || !removedBubbles.isEmpty()
                    || jumpcutBubbleSwitchClosingBubble != null
                    || addedOverflowBubble != null
                    || removedOverflowBubble != null
                    || orderChanged
@@ -129,6 +138,10 @@ public class BubbleData {
            removedBubbles.add(new Pair<>(bubbleToRemove, reason));
        }

        void setJumpcutBubbleSwitchClosingBubble(Bubble closingBubble) {
            jumpcutBubbleSwitchClosingBubble = closingBubble;
        }

        /**
         * Converts the update to a {@link BubbleBarUpdate} which contains updates relevant
         * to the bubble bar. Only used when {@link BubbleController#isShowingAsBubbleBar()} is
@@ -167,6 +180,11 @@ public class BubbleData {
                            new RemovedBubble(pair.first.getKey(), pair.second));
                }
            }
            if (jumpcutBubbleSwitchClosingBubble != null) {
                bubbleBarUpdate.removedBubbles.add(new RemovedBubble(
                        jumpcutBubbleSwitchClosingBubble.getKey(),
                        Bubbles.DISMISS_JUMPCUT_BUBBLE_SWITCH));
            }
            if (orderChanged) {
                // Include the new order
                for (int i = 0; i < bubbles.size(); i++) {
@@ -176,6 +194,8 @@ public class BubbleData {
            bubbleBarUpdate.showOverflowChanged = showOverflowChanged;
            bubbleBarUpdate.showOverflow = !overflowBubbles.isEmpty();
            bubbleBarUpdate.bubbleBarLocation = mBubbleBarLocation;
            bubbleBarUpdate.suppressAnimation = addedBubble != null
                    && addedBubble.isJumpcutBubbleSwitching();
            return bubbleBarUpdate;
        }

@@ -613,6 +633,20 @@ public class BubbleData {
        dispatchPendingChanges();
    }

    /**
     * Notifies about the jumpcut Bubble switching, which contains
     *  - All info in the opening Bubble has completed loading.
     *  - The BubbleBar Icon of the closing Bubble can be removed.
     */
    void jumpcutBubbleSwitch(Bubble openingBubble, Bubble closingBubble) {
        // Notify launcher about the closing Bubble, but don't actually remove its TaskView yet
        // because we still need it to be visible until the opening Bubble is fully visible.
        // The cleanup will be done onTaskVanished.
        mStateChange.setJumpcutBubbleSwitchClosingBubble(closingBubble);
        notificationEntryUpdated(openingBubble, /* suppressFlyout= */ true,
                /* showInShade= */ false);
    }

    /** Dismisses the bubble with the matching key, if it exists. */
    public void dismissBubbleWithKey(String key, @DismissReason int reason) {
        dismissBubbleWithKey(key, reason, mTimeSource.currentTimeMillis());
+1 −2
Original line number Diff line number Diff line
@@ -954,8 +954,7 @@ public class BubbleTransitions {
            mFinishCb = finishCallback;
            mTaskLeash = enterBubbleTask.getLeash();

            mBubbleData.notificationEntryUpdated(mOpeningBubble, /* suppressFlyout= */ true,
                    /* showInShade= */ false);
            mBubbleData.jumpcutBubbleSwitch(mOpeningBubble, mClosingBubble);

            // Keep showing the closing Bubble Task within the closing Bubble TaskView until the
            // opening Bubble TaskView is ready.
+1 −0
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@ public interface Bubbles {
    int DISMISS_USER_ACCOUNT_REMOVED = 16;
    int DISMISS_SWITCH_TO_STACK = 17;
    int DISMISS_USER_GESTURE_FROM_LAUNCHER = 18;
    int DISMISS_JUMPCUT_BUBBLE_SWITCH = 19;

    /** Returns a binder that can be passed to an external process to manipulate Bubbles. */
    default IBubbles createExternalInterface() {
+33 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.wm.shell.bubbles;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.wm.shell.Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW;

import static com.google.common.truth.Truth.assertThat;
@@ -25,6 +26,7 @@ import static junit.framework.Assert.assertNotNull;
import static junit.framework.TestCase.assertEquals;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -1498,6 +1500,37 @@ public class BubbleDataTest extends ShellTestCase {
        assertThat(mBubbleData.isExpanded()).isTrue();
    }

    @Test
    public void testJumpcutBubbleSwitch() {
        mBubbleData.setListener(mListener);

        mBubbleData.jumpcutBubbleSwitch(mBubbleA1, mBubbleC1);

        verifyUpdateReceived();
        BubbleData.Update update = mUpdateCaptor.getValue();
        assertThat(update.addedBubble).isEqualTo(mBubbleA1);
        assertThat(update.jumpcutBubbleSwitchClosingBubble).isEqualTo(mBubbleC1);
        assertThat(update.removedBubbles).isEmpty();
    }

    @Test
    public void testToBubbleBarUpdate_suppressAnimationForJumpcutBubbleSwitch() {
        spyOn(mBubbleA1);
        doReturn(true).when(mBubbleA1).isJumpcutBubbleSwitching();
        mBubbleData.setListener(mListener);
        mBubbleData.jumpcutBubbleSwitch(mBubbleA1, mBubbleC1);

        verifyUpdateReceived();
        BubbleData.Update update = mUpdateCaptor.getValue();
        BubbleBarUpdate bubbleBarUpdate = update.toBubbleBarUpdate();

        assertThat(bubbleBarUpdate.suppressAnimation).isTrue();
        assertThat(bubbleBarUpdate.removedBubbles).hasSize(1);
        assertThat(bubbleBarUpdate.removedBubbles.get(0).getKey()).isEqualTo(mBubbleC1.getKey());
        assertThat(bubbleBarUpdate.removedBubbles.get(0).getRemovalReason())
                .isEqualTo(Bubbles.DISMISS_JUMPCUT_BUBBLE_SWITCH);
    }

    private void verifyUpdateReceived() {
        verify(mListener).applyUpdate(mUpdateCaptor.capture());
        reset(mListener);
Loading