Animate the bubble bar on dismiss
When the last remaining bubble is dismissed by drag or from the menu
we now fade out the bubble bar.
Flag: com.android.wm.shell.enable_bubble_bar
Fixes: 347062801
Test: manual
- create a single bubble in the bar
- expand
- dismiss by drag or from menu
- observe animation
Change-Id: I86fcaf04c6f5949126744b81e0042b0e58318a13
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 833be61..3c9fb61 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -81,6 +81,7 @@
public static final long FADE_OUT_ANIM_POSITION_DURATION_MS = 100L;
public static final long FADE_IN_ANIM_ALPHA_DURATION_MS = 100L;
+ public static final long FADE_OUT_BUBBLE_BAR_DURATION_MS = 150L;
private static final String TAG = "BubbleBarView";
// TODO: (b/273594744) calculate the amount of space we have and base the max on that
// if it's smaller than 5.
@@ -146,6 +147,9 @@
// collapsed state and 1 to the fully expanded state.
private ValueAnimator mWidthAnimator = createExpansionAnimator(/* expanding = */ false);
+ @Nullable
+ private ValueAnimator mDismissAnimator = null;
+
/** An animator used for animating individual bubbles in the bubble bar while expanded. */
@Nullable
private BubbleAnimator mBubbleAnimator = null;
@@ -799,11 +803,11 @@
/** Removes the given bubble from the bubble bar. */
public void removeBubble(View bubble) {
if (isExpanded()) {
- // TODO b/347062801 - animate the bubble bar if the last bubble is removed
final boolean dismissedByDrag = mDraggedBubbleView == bubble;
if (dismissedByDrag) {
mDismissedByDragBubbleView = mDraggedBubbleView;
}
+ boolean removingLastRemainingBubble = getBubbleChildCount() == 1;
int bubbleCount = getChildCount();
mBubbleAnimator = new BubbleAnimator(mIconSize, mExpandedBarIconsSpacing,
bubbleCount, mBubbleBarLocation.isOnLeft(isLayoutRtl()));
@@ -841,7 +845,10 @@
: bubbleIndex == bubbleCount - 1;
mBubbleAnimator.animateRemovedBubble(
indexOfChild(bubble), indexOfChild(mSelectedBubbleView), removingLastBubble,
- listener);
+ removingLastRemainingBubble, listener);
+ if (removingLastRemainingBubble && mDismissAnimator == null) {
+ createDismissAnimator().start();
+ }
} else {
removeView(bubble);
}
@@ -862,6 +869,28 @@
updateDotsAndBadgesIfCollapsed();
}
+ private ValueAnimator createDismissAnimator() {
+ ValueAnimator animator =
+ ValueAnimator.ofFloat(0, 1).setDuration(FADE_OUT_BUBBLE_BAR_DURATION_MS);
+ animator.setInterpolator(Interpolators.EMPHASIZED);
+ Runnable onEnd = () -> {
+ mDismissAnimator = null;
+ setAlpha(0);
+ };
+ addAnimationCallBacks(animator, /* onStart= */ null, onEnd,
+ /* onUpdate= */ anim -> setAlpha(1 - anim.getAnimatedFraction()));
+ mDismissAnimator = animator;
+ return animator;
+ }
+
+ /** Dismisses the bubble bar */
+ public void dismiss(Runnable onDismissed) {
+ if (mDismissAnimator == null) {
+ createDismissAnimator().start();
+ }
+ addAnimationCallBacks(mDismissAnimator, null, onDismissed, null);
+ }
+
/**
* Return child views in the order which they are shown on the screen.
* <p>
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index fd08078..ce772a5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -586,13 +586,17 @@
public void setHiddenForBubbles(boolean hidden) {
if (mHiddenForNoBubbles != hidden) {
mHiddenForNoBubbles = hidden;
- updateVisibilityForStateChange();
if (hidden) {
- mBarView.setAlpha(0);
- mBarView.setExpanded(false);
- adjustTaskbarAndHotseatToBubbleBarState(/* isBubbleBarExpanded = */ false);
+ mBarView.dismiss(() -> {
+ updateVisibilityForStateChange();
+ mBarView.setExpanded(false);
+ adjustTaskbarAndHotseatToBubbleBarState(/* isBubbleBarExpanded= */ false);
+ mActivity.bubbleBarVisibilityChanged(/* isVisible= */ false);
+ });
+ } else {
+ updateVisibilityForStateChange();
+ mActivity.bubbleBarVisibilityChanged(/* isVisible= */ true);
}
- mActivity.bubbleBarVisibilityChanged(!hidden);
}
}
@@ -625,7 +629,6 @@
}
}
- // TODO: (b/273592694) animate it
private void updateVisibilityForStateChange() {
if (!mHiddenForSysui && !mHiddenForNoBubbles && !mHiddenForStashed) {
mBarView.setVisibility(VISIBLE);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt
index 8af8ffb..3604167 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt
@@ -49,23 +49,30 @@
bubbleIndex: Int,
selectedBubbleIndex: Int,
removingLastBubble: Boolean,
- listener: Listener
+ removingLastRemainingBubble: Boolean,
+ listener: Listener,
) {
animator = createAnimator(listener)
- state = State.RemovingBubble(bubbleIndex, selectedBubbleIndex, removingLastBubble)
+ state =
+ State.RemovingBubble(
+ bubbleIndex = bubbleIndex,
+ selectedBubbleIndex = selectedBubbleIndex,
+ removingLastBubble = removingLastBubble,
+ removingLastRemainingBubble = removingLastRemainingBubble,
+ )
animator.start()
}
fun animateNewAndRemoveOld(
selectedBubbleIndex: Int,
removedBubbleIndex: Int,
- listener: Listener
+ listener: Listener,
) {
animator = createAnimator(listener)
state =
State.AddingAndRemoving(
selectedBubbleIndex = selectedBubbleIndex,
- removedBubbleIndex = removedBubbleIndex
+ removedBubbleIndex = removedBubbleIndex,
)
animator.start()
}
@@ -111,20 +118,22 @@
getBubbleTranslationXWhileScalingBubble(
bubbleIndex = bubbleIndex,
scalingBubbleIndex = 0,
- bubbleScale = animator.animatedFraction
+ bubbleScale = animator.animatedFraction,
)
+
is State.RemovingBubble ->
getBubbleTranslationXWhileScalingBubble(
bubbleIndex = bubbleIndex,
scalingBubbleIndex = state.bubbleIndex,
- bubbleScale = 1 - animator.animatedFraction
+ bubbleScale = 1 - animator.animatedFraction,
)
+
is State.AddingAndRemoving ->
getBubbleTranslationXWhileAddingBubbleAtLimit(
bubbleIndex = bubbleIndex,
removedBubbleIndex = state.removedBubbleIndex,
addedBubbleScale = animator.animatedFraction,
- removedBubbleScale = 1 - animator.animatedFraction
+ removedBubbleScale = 1 - animator.animatedFraction,
)
}
}
@@ -176,10 +185,11 @@
getBubbleTranslationXWhileScalingBubble(
bubbleIndex = state.selectedBubbleIndex,
scalingBubbleIndex = 0,
- bubbleScale = animator.animatedFraction
+ bubbleScale = animator.animatedFraction,
)
tx + iconSize / 2f
}
+
is State.RemovingBubble -> getArrowPositionWhenRemovingBubble(state)
is State.AddingAndRemoving -> {
// we never remove the selected bubble, so the arrow stays pointing to its center
@@ -188,22 +198,23 @@
bubbleIndex = state.selectedBubbleIndex,
removedBubbleIndex = state.removedBubbleIndex,
addedBubbleScale = animator.animatedFraction,
- removedBubbleScale = 1 - animator.animatedFraction
+ removedBubbleScale = 1 - animator.animatedFraction,
)
tx + iconSize / 2f
}
}
}
- private fun getArrowPositionWhenRemovingBubble(state: State.RemovingBubble): Float {
- return if (state.selectedBubbleIndex != state.bubbleIndex) {
- // if we're not removing the selected bubble, the selected bubble doesn't change so just
- // return the translation X of the selected bubble and add half icon
+ private fun getArrowPositionWhenRemovingBubble(state: State.RemovingBubble): Float =
+ if (state.selectedBubbleIndex != state.bubbleIndex || state.removingLastRemainingBubble) {
+ // if we're not removing the selected bubble or if we're removing the last remaining
+ // bubble, the selected bubble doesn't change so just return the translation X of the
+ // selected bubble and add half icon
val tx =
getBubbleTranslationXWhileScalingBubble(
bubbleIndex = state.selectedBubbleIndex,
scalingBubbleIndex = state.bubbleIndex,
- bubbleScale = 1 - animator.animatedFraction
+ bubbleScale = 1 - animator.animatedFraction,
)
tx + iconSize / 2f
} else {
@@ -238,7 +249,6 @@
}
}
}
- }
/**
* Returns the translation X for the bubble at index {@code bubbleIndex} when the bubble bar is
@@ -251,7 +261,7 @@
private fun getBubbleTranslationXWhileScalingBubble(
bubbleIndex: Int,
scalingBubbleIndex: Int,
- bubbleScale: Float
+ bubbleScale: Float,
): Float {
val iconAndSpacing = iconSize + expandedBarIconSpacing
// the bubble is scaling from the center, so we need to adjust its translation so
@@ -300,7 +310,7 @@
bubbleIndex: Int,
removedBubbleIndex: Int,
addedBubbleScale: Float,
- removedBubbleScale: Float
+ removedBubbleScale: Float,
): Float {
val iconAndSpacing = iconSize + expandedBarIconSpacing
// the bubbles are scaling from the center, so we need to adjust their translation so
@@ -377,7 +387,9 @@
/** The index of the selected bubble. */
val selectedBubbleIndex: Int,
/** Whether the bubble being removed is also the last bubble. */
- val removingLastBubble: Boolean
+ val removingLastBubble: Boolean,
+ /** Whether we're removing the last remaining bubble. */
+ val removingLastRemainingBubble: Boolean,
) : State
/** A new bubble is being added and an old bubble is being removed from the bubble bar. */
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
index cc8582c..d7bb190 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar
+import android.animation.AnimatorTestRule
import android.content.ComponentName
import android.content.Intent
import android.platform.test.annotations.EnableFlags
@@ -75,7 +76,9 @@
@get:Rule(order = 3) val taskbarModeRule = TaskbarModeRule(context)
- @get:Rule(order = 4) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+ @get:Rule(order = 4) val animatorTestRule = AnimatorTestRule(this)
+
+ @get:Rule(order = 5) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
@InjectController lateinit var taskbarViewController: TaskbarViewController
@InjectController lateinit var recentAppsController: TaskbarRecentAppsController
@@ -208,7 +211,10 @@
val initialNumIcons = currentNumberOfTaskbarIcons
val initialMaxNumIconViews = addRunningAppsAndVerifyOverflowState(5)
- runOnMainSync { bubbleBarViewController.setHiddenForBubbles(true) }
+ runOnMainSync {
+ bubbleBarViewController.setHiddenForBubbles(true)
+ animatorTestRule.advanceTimeBy(150)
+ }
val maxNumIconViews = maxNumberOfTaskbarIcons
assertThat(maxNumIconViews).isGreaterThan(initialMaxNumIconViews)
@@ -226,7 +232,10 @@
val initialNumIcons = currentNumberOfTaskbarIcons
val initialMaxNumIconViews = addRunningAppsAndVerifyOverflowState(5)
- runOnMainSync { bubbleBarViewController.setHiddenForBubbles(true) }
+ runOnMainSync {
+ bubbleBarViewController.setHiddenForBubbles(true)
+ animatorTestRule.advanceTimeBy(150)
+ }
val maxNumIconViews = maxNumberOfTaskbarIcons
assertThat(maxNumIconViews).isGreaterThan(initialMaxNumIconViews)
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimatorTest.kt
index d5a76a2..eae181f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimatorTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimatorTest.kt
@@ -40,7 +40,7 @@
iconSize = 40f,
expandedBarIconSpacing = 10f,
bubbleCount = 5,
- onLeft = false
+ onLeft = false,
)
val listener = TestBubbleAnimatorListener()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -61,7 +61,7 @@
iconSize = 40f,
expandedBarIconSpacing = 10f,
bubbleCount = 5,
- onLeft = false
+ onLeft = false,
)
val listener = TestBubbleAnimatorListener()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -69,7 +69,8 @@
bubbleIndex = 2,
selectedBubbleIndex = 3,
removingLastBubble = false,
- listener
+ removingLastRemainingBubble = false,
+ listener,
)
}
@@ -87,14 +88,14 @@
iconSize = 40f,
expandedBarIconSpacing = 10f,
bubbleCount = 5,
- onLeft = false
+ onLeft = false,
)
val listener = TestBubbleAnimatorListener()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
bubbleAnimator.animateNewAndRemoveOld(
selectedBubbleIndex = 3,
removedBubbleIndex = 2,
- listener
+ listener,
)
}