From 631da72a2a5495bc63be6cf3e757c4898f307a55 Mon Sep 17 00:00:00 2001 From: mpodolian Date: Fri, 27 Dec 2024 17:10:54 -0800 Subject: [PATCH 1/3] Fix arrow animation when the new bubble added and old bubble removed. The AddingAndRemoving animation now uses a newly selected bubble index and added bubble index arguments. Test: BubbleAnimatorTest Test: Manual. Enable optional overflow bubble. Create a bubble bar with overflow containing a single bubble. Open the overflow menu and click an overflow bubble icon. Observe that a new bubble is added and immediately expands while the overflow bubble is collapsing. Observe that the arrow animates to the new bubble's position. Test: Manual. Have few bubbles in the bubble bar. Collapse any bubble. Observe arrow animated correctly to the newly selected bubble position. Flag: com.android.wm.shell.enable_bubble_bar Bug: 359952121 Change-Id: I8321e2f532189e7599b029083199e034e38080e3 --- .../taskbar/bubbles/BubbleBarView.java | 37 +++++---- .../bubbles/animation/BubbleAnimator.kt | 79 ++++++++++++++----- .../bubbles/animation/BubbleAnimatorTest.kt | 4 +- 3 files changed, 83 insertions(+), 37 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index aa6ad25425..a1ac28c2a7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -744,32 +744,36 @@ public class BubbleBarView extends FrameLayout { Runnable onEndRunnable) { FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams((int) mIconSize, (int) mIconSize, Gravity.LEFT); - boolean isOverflowSelected = - mSelectedBubbleView != null && mSelectedBubbleView.isOverflow(); boolean removingOverflow = removedBubble.isOverflow(); boolean addingOverflow = addedBubble.isOverflow(); - + int addedIndex = addingOverflow ? getChildCount() : 0; if (!isExpanded()) { removeView(removedBubble); - int index = addingOverflow ? getChildCount() : 0; - addView(addedBubble, index, lp); + addView(addedBubble, addedIndex, lp); if (onEndRunnable != null) { onEndRunnable.run(); } return; } - int index = addingOverflow ? getChildCount() : 0; addedBubble.setScaleX(0f); addedBubble.setScaleY(0f); - addView(addedBubble, index, lp); - - if (isOverflowSelected && removingOverflow) { - // The added bubble will be selected - mSelectedBubbleView = addedBubble; - } - int indexOfSelectedBubble = indexOfChild(mSelectedBubbleView); + addView(addedBubble, addedIndex, lp); + int indexOfCurrentSelectedBubble = indexOfChild(mSelectedBubbleView); int indexOfBubbleToRemove = indexOfChild(removedBubble); - + int indexOfNewlySelectedBubble = indexOfCurrentSelectedBubble; + boolean removingSelectedBubble = mSelectedBubbleView == removedBubble; + if (removingSelectedBubble) { + if (removingOverflow) { + // The added bubble will be selected + mSelectedBubbleView = addedBubble; + indexOfNewlySelectedBubble = indexOfChild(mSelectedBubbleView); + } else { + boolean isRemovedNextToOverflow = + indexOfBubbleToRemove == getChildCount() - (hasOverflow() ? 2 : 1); + indexOfNewlySelectedBubble = + indexOfCurrentSelectedBubble + (isRemovedNextToOverflow ? -1 : 1); + } + } mBubbleAnimator = new BubbleAnimator(mIconSize, mExpandedBarIconsSpacing, getChildCount(), mBubbleBarLocation.isOnLeft(isLayoutRtl())); BubbleAnimator.Listener listener = new BubbleAnimator.Listener() { @@ -802,8 +806,9 @@ public class BubbleBarView extends FrameLayout { invalidate(); } }; - mBubbleAnimator.animateNewAndRemoveOld(indexOfSelectedBubble, indexOfBubbleToRemove, - listener); + //TODO (b/359952121) fix scenario when overflow is being added + mBubbleAnimator.animateNewAndRemoveOld(indexOfCurrentSelectedBubble, + indexOfNewlySelectedBubble, indexOfBubbleToRemove, addedIndex, listener); } @Override 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 944e80656e..84e13ba56b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt @@ -70,14 +70,18 @@ class BubbleAnimator( fun animateNewAndRemoveOld( selectedBubbleIndex: Int, + newlySelectedBubbleIndex: Int, removedBubbleIndex: Int, + addedBubbleIndex: Int, listener: Listener, ) { animator = createAnimator(listener) state = State.AddingAndRemoving( selectedBubbleIndex = selectedBubbleIndex, + newlySelectedBubbleIndex = newlySelectedBubbleIndex, removedBubbleIndex = removedBubbleIndex, + addedBubbleIndex = addedBubbleIndex, ) animator.start() } @@ -187,17 +191,7 @@ class BubbleAnimator( State.Idle -> 0f is State.AddingBubble -> getArrowPositionWhenAddingBubble(state) is State.RemovingBubble -> getArrowPositionWhenRemovingBubble(state) - is State.AddingAndRemoving -> { - // we never remove the selected bubble, so the arrow stays pointing to its center - val tx = - getBubbleTranslationXWhileAddingBubbleAtLimit( - bubbleIndex = state.selectedBubbleIndex, - removedBubbleIndex = state.removedBubbleIndex, - addedBubbleScale = animator.animatedFraction, - removedBubbleScale = 1 - animator.animatedFraction, - ) - tx + iconSize / 2f - } + is State.AddingAndRemoving -> getArrowPositionWhenAddingAndRemovingBubble(state) } } @@ -210,12 +204,11 @@ class BubbleAnimator( ) + iconSize / 2f if (state.newlySelectedBubbleIndex != null) { val selectedBubbleScale = if (state.newlySelectedBubbleIndex == 0) scale else 1f - val finalTx = - getBubbleTranslationXWhileScalingBubble( - bubbleIndex = state.newlySelectedBubbleIndex, - scalingBubbleIndex = 0, - bubbleScale = 1f, - ) + iconSize * selectedBubbleScale / 2f + val finalTx = getBubbleTranslationXWhileScalingBubble( + bubbleIndex = state.newlySelectedBubbleIndex, + scalingBubbleIndex = 0, + bubbleScale = scale + ) + iconSize * selectedBubbleScale / 2f tx += (finalTx - tx) * animator.animatedFraction } return tx @@ -266,6 +259,45 @@ class BubbleAnimator( } } + private fun getArrowPositionWhenAddingAndRemovingBubble(state: State.AddingAndRemoving): Float { + // The bubble bar keeps constant width while adding and removing bubble. So we just need to + // find selected bubble arrow position on the animation start and newly selected bubble + // arrow position on the animation end interpolating the arrow between these positions + // during the animation. + // The indexes in the state are provided for the bubble bar containing all bubbles. So for + // certain circumstances indexes should be adjusted. + // When animation is started added bubble has zero scale as well as removed bubble when the + // animation is ended, so for both cases we should compute translation as it is one less + // bubble. + val bubbleCountOnEnd = bubbleCount - 1 + var selectedIndex = state.selectedBubbleIndex + // We only need to adjust the selected index if added bubble was added before the selected. + if (selectedIndex > state.addedBubbleIndex) { + // If the selectedIndex is higher index than the added bubble index, we need to reduce + // selectedIndex by one because the added bubble has zero scale when animation is + // started. + selectedIndex-- + } + var newlySelectedIndex = state.newlySelectedBubbleIndex + // We only need to adjust newlySelectedIndex if removed bubble was removed before the newly + // selected bubble. + if (newlySelectedIndex > state.removedBubbleIndex) { + // If the newlySelectedIndex is higher index than the removed bubble index, we need to + // reduce newlySelectedIndex by one because the removed bubble has zero scale when + // animation is ended. + newlySelectedIndex-- + } + val iconAndSpacing: Float = iconSize + expandedBarIconSpacing + val startTx = getBubblesToTheLeft(selectedIndex, bubbleCountOnEnd) * iconAndSpacing + val endTx = getBubblesToTheLeft(newlySelectedIndex, bubbleCountOnEnd) * iconAndSpacing + val tx = startTx + (endTx - startTx) * animator.animatedFraction + return tx + iconSize / 2f + } + + private fun getBubblesToTheLeft(bubbleIndex: Int, bubbleCount: Int): Int = + if (onLeft) bubbleCount - bubbleIndex - 1 else bubbleIndex + + /** * Returns the translation X for the bubble at index {@code bubbleIndex} when the bubble bar is * expanded and a bubble is animating in or out. @@ -413,10 +445,17 @@ class BubbleAnimator( val removingLastRemainingBubble: Boolean, ) : State - // TODO add index where bubble is being added, and index for newly selected bubble /** A new bubble is being added and an old bubble is being removed from the bubble bar. */ - data class AddingAndRemoving(val selectedBubbleIndex: Int, val removedBubbleIndex: Int) : - State + data class AddingAndRemoving( + /** The index of the selected bubble. */ + val selectedBubbleIndex: Int, + /** The index of the newly selected bubble. */ + val newlySelectedBubbleIndex: Int, + /** The index of the bubble being removed. */ + val removedBubbleIndex: Int, + /** The index of the added bubble. */ + val addedBubbleIndex: Int, + ) : State } /** Callbacks for the animation. */ 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 3ca36ec8d5..da362bdfb3 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 @@ -94,7 +94,9 @@ class BubbleAnimatorTest { InstrumentationRegistry.getInstrumentation().runOnMainSync { bubbleAnimator.animateNewAndRemoveOld( selectedBubbleIndex = 3, - removedBubbleIndex = 2, + newlySelectedBubbleIndex = 2, + removedBubbleIndex = 1, + addedBubbleIndex = 3, listener, ) } From db7aa238a01e5723981c6335d6f5c2a76223bc55 Mon Sep 17 00:00:00 2001 From: mpodolian Date: Thu, 2 Jan 2025 16:25:46 -0800 Subject: [PATCH 2/3] Fix bubbles animation when the new bubble added and old bubble removed. The AddingAndRemoving animation now uses a newly selected bubble index and added bubble index arguments. Test: BubbleAnimatorTest Test: Manual. Enable optional overflow bubble. Create a bubble bar with overflow containing a single bubble. Open the overflow menu and click an overflow bubble icon. Observe that a new bubble is added and immediately expands while the overflow bubble is collapsing. Observe that bubbles animation. Test: Manual. Have few bubbles in the bubble bar. Collapse any bubble. Observe bubbles are animated correctly. Video: (slowed down 10 times) http://recall/-/gx8ASgewUeUS3QYohfrd1J/emOREYETIeV5ktcNZLETYa Flag: com.android.wm.shell.enable_bubble_bar Fixes: 359952121 Change-Id: If2366ec12cb196757ba6439c5c9306959d4191a9 --- .../taskbar/bubbles/BubbleBarView.java | 1 - .../bubbles/animation/BubbleAnimator.kt | 101 ++++++++++-------- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index a1ac28c2a7..c9575e84aa 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -806,7 +806,6 @@ public class BubbleBarView extends FrameLayout { invalidate(); } }; - //TODO (b/359952121) fix scenario when overflow is being added mBubbleAnimator.animateNewAndRemoveOld(indexOfCurrentSelectedBubble, indexOfNewlySelectedBubble, indexOfBubbleToRemove, addedIndex, listener); } 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 84e13ba56b..3751ab2bc3 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt @@ -18,6 +18,8 @@ package com.android.launcher3.taskbar.bubbles.animation import androidx.core.animation.Animator import androidx.core.animation.ValueAnimator +import kotlin.math.max +import kotlin.math.min /** * Animates individual bubbles within the bubble bar while the bubble bar is expanded. @@ -141,6 +143,7 @@ class BubbleAnimator( getBubbleTranslationXWhileAddingBubbleAtLimit( bubbleIndex = bubbleIndex, removedBubbleIndex = state.removedBubbleIndex, + addedBubbleIndex = state.addedBubbleIndex, addedBubbleScale = animator.animatedFraction, removedBubbleScale = 1 - animator.animatedFraction, ) @@ -294,7 +297,9 @@ class BubbleAnimator( return tx + iconSize / 2f } - private fun getBubblesToTheLeft(bubbleIndex: Int, bubbleCount: Int): Int = + private fun getBubblesToTheLeft(bubbleIndex: Int, bubbleCount: Int = this.bubbleCount): Int = + // when bar is on left the index - 0 corresponds to the right - most bubble and when the + // bubble bar is on the right - 0 corresponds to the left - most bubble. if (onLeft) bubbleCount - bubbleIndex - 1 else bubbleIndex @@ -322,6 +327,7 @@ class BubbleAnimator( // the bar is on the left and the current bubble is to the right of the scaling // bubble so account for its scale (bubbleCount - bubbleIndex - 2 + bubbleScale) * iconAndSpacing + bubbleIndex == scalingBubbleIndex -> { // the bar is on the left and this is the scaling bubble val totalIconSize = (bubbleCount - bubbleIndex - 1) * iconSize @@ -331,6 +337,7 @@ class BubbleAnimator( val scaledSpace = bubbleScale * expandedBarIconSpacing totalIconSize + totalSpacing + scaledSpace + pivotAdjustment } + else -> // the bar is on the left and the scaling bubble is on the right. the current // bubble is unaffected by the scaling bubble @@ -342,10 +349,12 @@ class BubbleAnimator( // the bar is on the right and the scaling bubble is on the right. the current // bubble is unaffected by the scaling bubble iconAndSpacing * bubbleIndex + bubbleIndex == scalingBubbleIndex -> // the bar is on the right, and this is the animating bubble. it only needs to // be adjusted for the scaling pivot. iconAndSpacing * bubbleIndex + pivotAdjustment + else -> // the bar is on the right and the scaling bubble is on the left so account for // its scale @@ -357,61 +366,67 @@ class BubbleAnimator( private fun getBubbleTranslationXWhileAddingBubbleAtLimit( bubbleIndex: Int, removedBubbleIndex: Int, + addedBubbleIndex: Int, addedBubbleScale: Float, removedBubbleScale: Float, ): Float { val iconAndSpacing = iconSize + expandedBarIconSpacing // the bubbles are scaling from the center, so we need to adjust their translation so // that the distance to the adjacent bubble scales at the same rate. - val addedBubblePivotAdjustment = -(1 - addedBubbleScale) * iconSize / 2f - val removedBubblePivotAdjustment = -(1 - removedBubbleScale) * iconSize / 2f + val addedBubblePivotAdjustment = (addedBubbleScale - 1) * iconSize / 2f + val removedBubblePivotAdjustment = (removedBubbleScale - 1) * iconSize / 2f - return if (onLeft) { - // this is how many bubbles there are to the left of the current bubble. - // when the bubble bar is on the right the added bubble is the right-most bubble so it - // doesn't affect the translation of any other bubble. - // when the removed bubble is to the left of the current bubble, we need to subtract it - // from bubblesToLeft and use removedBubbleScale instead when calculating the - // translation. - val bubblesToLeft = bubbleCount - bubbleIndex - 1 - when { - bubbleIndex == 0 -> - // this is the added bubble and it's the right-most bubble. account for all the - // other bubbles -- including the removed bubble -- and adjust for the added - // bubble pivot. - (bubblesToLeft - 1 + removedBubbleScale) * iconAndSpacing + - addedBubblePivotAdjustment - bubbleIndex < removedBubbleIndex -> + val minAddedRemovedIndex = min(addedBubbleIndex, removedBubbleIndex) + val maxAddedRemovedIndex = max(addedBubbleIndex, removedBubbleIndex) + val isBetweenAddedAndRemoved = + bubbleIndex in (minAddedRemovedIndex + 1).. { + if (isRemovedBubbleToLeftOfAddedBubble) { // the removed bubble is to the left so account for it (bubblesToLeft - 1 + removedBubbleScale) * iconAndSpacing - bubbleIndex == removedBubbleIndex -> { - // this is the removed bubble. all the bubbles to the left are at full scale - // but we need to scale the spacing between the removed bubble and the bubble to - // its left because the removed bubble disappears towards the left side + } else { + // the added bubble is to the left so account for it + (bubblesToLeft - 1 + addedBubbleScale) * iconAndSpacing + } + } + + bubbleIndex == addedBubbleIndex -> { + if (isRemovedBubbleToLeftOfAddedBubble) { + // the removed bubble is to the left so account for it + (bubblesToLeft - 1 + removedBubbleScale) * iconAndSpacing + } else { + // it's the left-most scaling bubble, all bubbles on the left are at full scale + bubblesToLeft * iconAndSpacing + } + addedBubblePivotAdjustment + } + + bubbleIndex == removedBubbleIndex -> { + if (isRemovedBubbleToLeftOfAddedBubble) { + // All the bubbles to the left are at full scale, but we need to scale the + // spacing between the removed bubble and the bubble next to it val totalIconSize = bubblesToLeft * iconSize val totalSpacing = (bubblesToLeft - 1 + removedBubbleScale) * expandedBarIconSpacing - totalIconSize + totalSpacing + removedBubblePivotAdjustment - } - else -> - // both added and removed bubbles are to the right so they don't affect the tx - bubblesToLeft * iconAndSpacing + totalIconSize + totalSpacing + } else { + // The added bubble is to the left, so account for it + (bubblesToLeft - 1 + addedBubbleScale) * iconAndSpacing + } + removedBubblePivotAdjustment } - } else { - when { - bubbleIndex == 0 -> addedBubblePivotAdjustment // we always add bubbles at index 0 - bubbleIndex < removedBubbleIndex -> - // the bar is on the right and the removed bubble is on the right. the current - // bubble is unaffected by the removed bubble. only need to factor in the added - // bubble's scale. - iconAndSpacing * (bubbleIndex - 1 + addedBubbleScale) - bubbleIndex == removedBubbleIndex -> - // the bar is on the right, and this is the animating bubble. - iconAndSpacing * (bubbleIndex - 1 + addedBubbleScale) + - removedBubblePivotAdjustment - else -> - // both the added and the removed bubbles are to the left of the current bubble - iconAndSpacing * (bubbleIndex - 2 + addedBubbleScale + removedBubbleScale) + + else -> { + // if bubble index is on the right side of the animated bubbles, we need to deduct + // one, since both the added and the removed bubbles takes a single place + val onTheRightOfAnimatedBubbles = + if (onLeft) { + bubbleIndex < minAddedRemovedIndex + } else { + bubbleIndex > maxAddedRemovedIndex + } + (bubblesToLeft - if (onTheRightOfAnimatedBubbles) 1 else 0) * iconAndSpacing } } } From f62b45e5f0f7a1ca0e98ddf33dd1087d867ec5f8 Mon Sep 17 00:00:00 2001 From: mpodolian Date: Fri, 3 Jan 2025 10:19:43 -0800 Subject: [PATCH 3/3] Take bubble selection from the shell update. Prior to this change, the bubble bar controller determined which bubble to select. With this change, the selection is now determined by the update received from the shell. Test: Manual. Tested the add overflow and remove bubble flows, add bubble and remove overflow flows, and add bubble and remove bubble flows Flag: com.android.wm.shell.enable_bubble_bar Fixes: 359952121 Change-Id: I8ec01d6976b1e1cd0129071fd1765041eeb10b83 --- .../taskbar/bubbles/BubbleBarController.java | 9 ++++--- .../taskbar/bubbles/BubbleBarView.java | 27 +++++++------------ .../bubbles/BubbleBarViewController.java | 21 +++++++++------ .../bubbles/animation/BubbleAnimator.kt | 23 ++++++++-------- 4 files changed, 40 insertions(+), 40 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java index 3dcf2b4ebf..7d39bf8d4d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java @@ -342,10 +342,10 @@ public class BubbleBarController extends IBubblesListener.Stub { mBubbleBarViewController.showOverflow(update.showOverflow); } - BubbleBarBubble bubbleToSelect = null; if (update.addedBubble != null) { mBubbles.put(update.addedBubble.getKey(), update.addedBubble); } + BubbleBarBubble bubbleToSelect = null; if (update.selectedBubbleKey != null) { if (mSelectedBubble == null || !update.selectedBubbleKey.equals(mSelectedBubble.getKey())) { @@ -363,7 +363,7 @@ public class BubbleBarController extends IBubblesListener.Stub { && update.removedBubbles.isEmpty() && !mBubbles.isEmpty()) { // A bubble was added from the overflow (& now it's empty / not showing) - mBubbleBarViewController.removeOverflowAndAddBubble(update.addedBubble); + mBubbleBarViewController.removeOverflowAndAddBubble(update.addedBubble, bubbleToSelect); } else if (update.addedBubble != null && update.removedBubbles.size() == 1) { // we're adding and removing a bubble at the same time. handle this as a single update. RemovedBubble removedBubble = update.removedBubbles.get(0); @@ -371,7 +371,8 @@ public class BubbleBarController extends IBubblesListener.Stub { boolean showOverflow = update.showOverflowChanged && update.showOverflow; if (bubbleToRemove != null) { mBubbleBarViewController.addBubbleAndRemoveBubble(update.addedBubble, - bubbleToRemove, isExpanding, suppressAnimation, showOverflow); + bubbleToRemove, bubbleToSelect, isExpanding, suppressAnimation, + showOverflow); } else { mBubbleBarViewController.addBubble(update.addedBubble, isExpanding, suppressAnimation, bubbleToSelect); @@ -387,7 +388,7 @@ public class BubbleBarController extends IBubblesListener.Stub { if (bubble != null && overflowNeedsToBeAdded) { // First removal, show the overflow overflowNeedsToBeAdded = false; - mBubbleBarViewController.addOverflowAndRemoveBubble(bubble); + mBubbleBarViewController.addOverflowAndRemoveBubble(bubble, bubbleToSelect); } else if (bubble != null) { mBubbleBarViewController.removeBubble(bubble); } else { diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index c9575e84aa..2d4d279564 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -741,12 +741,10 @@ public class BubbleBarView extends FrameLayout { /** Add a new bubble and remove an old bubble from the bubble bar. */ public void addBubbleAndRemoveBubble(BubbleView addedBubble, BubbleView removedBubble, - Runnable onEndRunnable) { + @Nullable BubbleView bubbleToSelect, Runnable onEndRunnable) { FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams((int) mIconSize, (int) mIconSize, Gravity.LEFT); - boolean removingOverflow = removedBubble.isOverflow(); - boolean addingOverflow = addedBubble.isOverflow(); - int addedIndex = addingOverflow ? getChildCount() : 0; + int addedIndex = addedBubble.isOverflow() ? getChildCount() : 0; if (!isExpanded()) { removeView(removedBubble); addView(addedBubble, addedIndex, lp); @@ -760,19 +758,14 @@ public class BubbleBarView extends FrameLayout { addView(addedBubble, addedIndex, lp); int indexOfCurrentSelectedBubble = indexOfChild(mSelectedBubbleView); int indexOfBubbleToRemove = indexOfChild(removedBubble); - int indexOfNewlySelectedBubble = indexOfCurrentSelectedBubble; - boolean removingSelectedBubble = mSelectedBubbleView == removedBubble; - if (removingSelectedBubble) { - if (removingOverflow) { - // The added bubble will be selected - mSelectedBubbleView = addedBubble; - indexOfNewlySelectedBubble = indexOfChild(mSelectedBubbleView); - } else { - boolean isRemovedNextToOverflow = - indexOfBubbleToRemove == getChildCount() - (hasOverflow() ? 2 : 1); - indexOfNewlySelectedBubble = - indexOfCurrentSelectedBubble + (isRemovedNextToOverflow ? -1 : 1); - } + int indexOfNewlySelectedBubble = bubbleToSelect == null + ? indexOfCurrentSelectedBubble : indexOfChild(bubbleToSelect); + // Since removed bubble is kept till the end of the animation we should check if there are + // more than one bubble. In such a case the bar will remain open without the selected bubble + if (mSelectedBubbleView == removedBubble + && bubbleToSelect == null + && getBubbleChildCount() > 1) { + Log.w(TAG, "Remove the currently selected bubble without selecting a new one."); } mBubbleAnimator = new BubbleAnimator(mIconSize, mExpandedBarIconsSpacing, getChildCount(), mBubbleBarLocation.isOnLeft(isLayoutRtl())); diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index 5685093097..d85f7b4e1c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -843,11 +843,12 @@ public class BubbleBarViewController { } /** Adds a new bubble and removes an old bubble at the same time. */ - public void addBubbleAndRemoveBubble(BubbleBarBubble addedBubble, - BubbleBarBubble removedBubble, boolean isExpanding, boolean suppressAnimation, - boolean addOverflowToo) { + public void addBubbleAndRemoveBubble(BubbleBarBubble addedBubble, BubbleBarBubble removedBubble, + @Nullable BubbleBarBubble bubbleToSelect, boolean isExpanding, + boolean suppressAnimation, boolean addOverflowToo) { + BubbleView bubbleToSelectView = bubbleToSelect == null ? null : bubbleToSelect.getView(); mBarView.addBubbleAndRemoveBubble(addedBubble.getView(), removedBubble.getView(), - addOverflowToo ? () -> showOverflow(true) : null); + bubbleToSelectView, addOverflowToo ? () -> showOverflow(true) : null); addedBubble.getView().setOnClickListener(mBubbleClickListener); addedBubble.getView().setController(mBubbleViewController); removedBubble.getView().setController(null); @@ -878,22 +879,26 @@ public class BubbleBarViewController { } /** Adds the overflow view to the bubble bar while animating a view away. */ - public void addOverflowAndRemoveBubble(BubbleBarBubble removedBubble) { + public void addOverflowAndRemoveBubble(BubbleBarBubble removedBubble, + @Nullable BubbleBarBubble bubbleToSelect) { if (mOverflowAdded) return; mOverflowAdded = true; + BubbleView bubbleToSelectView = bubbleToSelect == null ? null : bubbleToSelect.getView(); mBarView.addBubbleAndRemoveBubble(mOverflowBubble.getView(), removedBubble.getView(), - null /* onEndRunnable */); + bubbleToSelectView, null /* onEndRunnable */); mOverflowBubble.getView().setOnClickListener(mBubbleClickListener); mOverflowBubble.getView().setController(mBubbleViewController); removedBubble.getView().setController(null); } /** Removes the overflow view to the bubble bar while animating a view in. */ - public void removeOverflowAndAddBubble(BubbleBarBubble addedBubble) { + public void removeOverflowAndAddBubble(BubbleBarBubble addedBubble, + @Nullable BubbleBarBubble bubbleToSelect) { if (!mOverflowAdded) return; mOverflowAdded = false; + BubbleView bubbleToSelectView = bubbleToSelect == null ? null : bubbleToSelect.getView(); mBarView.addBubbleAndRemoveBubble(addedBubble.getView(), mOverflowBubble.getView(), - null /* onEndRunnable */); + bubbleToSelectView, null /* onEndRunnable */); addedBubble.getView().setOnClickListener(mBubbleClickListener); addedBubble.getView().setController(mBubbleViewController); mOverflowBubble.getView().setController(null); 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 3751ab2bc3..26d6cccc7d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleAnimator.kt @@ -200,18 +200,20 @@ class BubbleAnimator( private fun getArrowPositionWhenAddingBubble(state: State.AddingBubble): Float { val scale = animator.animatedFraction - var tx = getBubbleTranslationXWhileScalingBubble( - bubbleIndex = state.selectedBubbleIndex, - scalingBubbleIndex = 0, - bubbleScale = scale - ) + iconSize / 2f + var tx = + getBubbleTranslationXWhileScalingBubble( + bubbleIndex = state.selectedBubbleIndex, + scalingBubbleIndex = 0, + bubbleScale = scale, + ) + iconSize / 2f if (state.newlySelectedBubbleIndex != null) { val selectedBubbleScale = if (state.newlySelectedBubbleIndex == 0) scale else 1f - val finalTx = getBubbleTranslationXWhileScalingBubble( - bubbleIndex = state.newlySelectedBubbleIndex, - scalingBubbleIndex = 0, - bubbleScale = scale - ) + iconSize * selectedBubbleScale / 2f + val finalTx = + getBubbleTranslationXWhileScalingBubble( + bubbleIndex = state.newlySelectedBubbleIndex, + scalingBubbleIndex = 0, + bubbleScale = scale, + ) + iconSize * selectedBubbleScale / 2f tx += (finalTx - tx) * animator.animatedFraction } return tx @@ -302,7 +304,6 @@ class BubbleAnimator( // bubble bar is on the right - 0 corresponds to the left - most bubble. if (onLeft) bubbleCount - bubbleIndex - 1 else bubbleIndex - /** * Returns the translation X for the bubble at index {@code bubbleIndex} when the bubble bar is * expanded and a bubble is animating in or out.