diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index 14f6e3ab23..ae88f1d04d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -187,6 +187,9 @@ public class BubbleBarView extends FrameLayout { private BubbleView mDismissedByDragBubbleView; private float mAlphaDuringDrag = 1f; + /** Additional translation in the y direction that is applied to each bubble */ + private float mBubbleOffsetY; + private Controller mController; private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED; @@ -334,6 +337,16 @@ public class BubbleBarView extends FrameLayout { mBubbleBarBackground.setAlpha((int) (255 * alpha)); } + /** + * Sets offset of each bubble view in the y direction from the base position in the bar. + */ + public void setBubbleOffsetY(float offsetY) { + mBubbleOffsetY = offsetY; + for (int i = 0; i < getChildCount(); i++) { + getChildAt(i).setTranslationY(getBubbleTranslationY()); + } + } + /** * Sets new icon sizes and newBubbleBarPadding between icons and bubble bar borders. * @@ -997,10 +1010,7 @@ public class BubbleBarView extends FrameLayout { final float expandedWidth = expandedWidth(); final float collapsedWidth = collapsedWidth(); int childCount = getChildCount(); - float viewBottom = mBubbleBarBounds.height() + (isExpanded() ? mPointerSize : 0); - float bubbleBarAnimatedTop = viewBottom - getBubbleBarHeight(); - // When translating X & Y the scale is ignored, so need to deduct it from the translations - final float ty = bubbleBarAnimatedTop + mBubbleBarPadding - getScaleIconShift(); + final float ty = getBubbleTranslationY(); final boolean onLeft = bubbleBarLocation.isOnLeft(isLayoutRtl()); // elevation state is opposite to widthState - when expanded all icons are flat float elevationState = (1 - widthState); @@ -1134,6 +1144,13 @@ public class BubbleBarView extends FrameLayout { return mBubbleBarPadding + translationX - getScaleIconShift(); } + private float getBubbleTranslationY() { + float viewBottom = mBubbleBarBounds.height() + (isExpanded() ? mPointerSize : 0); + float bubbleBarAnimatedTop = viewBottom - getBubbleBarHeight(); + // When translating X & Y the scale is ignored, so need to deduct it from the translations + return mBubbleOffsetY + bubbleBarAnimatedTop + mBubbleBarPadding - getScaleIconShift(); + } + /** * Reorders the views to match the provided list. */ diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index 570b1b9b4e..ed08de5741 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -18,11 +18,8 @@ package com.android.launcher3.taskbar.bubbles; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; -import static com.android.launcher3.taskbar.bubbles.BubbleView.STASH_TRANSLATION_Y; - import android.animation.Animator; import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; @@ -97,6 +94,8 @@ public class BubbleBarViewController { this::updateBackgroundScaleY); private final AnimatedFloat mBubbleBarTranslationY = new AnimatedFloat( this::updateTranslationY); + private final AnimatedFloat mBubbleOffsetY = new AnimatedFloat( + this::updateBubbleOffsetY); // Modified when swipe up is happening on the bubble bar or task bar. private float mBubbleBarSwipeUpTranslationY; @@ -299,6 +298,10 @@ public class BubbleBarViewController { return mBubbleBarTranslationY; } + public AnimatedFloat getBubbleOffsetY() { + return mBubbleOffsetY; + } + public float getBubbleBarCollapsedWidth() { return mBarView.collapsedWidth(); } @@ -576,6 +579,10 @@ public class BubbleBarViewController { mBarView.setBubbleAlpha(alpha); } + private void updateBubbleOffsetY(float transY) { + mBarView.setBubbleOffsetY(transY); + } + private void updateBackgroundAlpha(float alpha) { mBarView.setBackgroundAlpha(alpha); } @@ -874,14 +881,9 @@ public class BubbleBarViewController { mBubbleStashController.getHandleBounds(stashedHandleBounds); int childCount = mBarView.getChildCount(); float newChildWidth = (float) stashedHandleBounds.width() / childCount; - float stashTranslationY = -mBubbleStashController.getBubbleBarTranslationY(); AnimatorSet animatorSet = new AnimatorSet(); for (int i = 0; i < childCount; i++) { BubbleView child = (BubbleView) mBarView.getChildAt(i); - final float startTransY = isStashed ? 0f : stashTranslationY; - final float endTransY = isStashed ? stashTranslationY : 0f; - animatorSet.play( - ObjectAnimator.ofFloat(child, STASH_TRANSLATION_Y, startTransY, endTransY)); animatorSet.play( createRevealAnimForBubble(child, isStashed, stashedHandleBounds, newChildWidth)); diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java index cc6b49a5c7..561df5c2ee 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java @@ -27,7 +27,6 @@ import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; import android.text.TextUtils; import android.util.AttributeSet; -import android.util.FloatProperty; import android.view.LayoutInflater; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ImageView; @@ -50,27 +49,12 @@ public class BubbleView extends ConstraintLayout { public static final int DEFAULT_PATH_SIZE = 100; - public static FloatProperty STASH_TRANSLATION_Y = new FloatProperty<>( - "stashTranslationY") { - @Override - public void setValue(BubbleView bubbleView, float transY) { - bubbleView.setStashTranslationY(transY); - } - - @Override - public Float get(BubbleView bubbleView) { - return bubbleView.mStashTranslationY; - } - }; - private final ImageView mBubbleIcon; private final ImageView mAppIcon; private int mBubbleSize; private float mDragTranslationX; private float mOffsetX; - private float mTranslationY; - private float mStashTranslationY; private DotRenderer mDotRenderer; private DotRenderer.DrawParams mDrawParams; @@ -177,24 +161,6 @@ public class BubbleView extends ConstraintLayout { setTranslationX(mDragTranslationX + mOffsetX); } - /** - * Set translation in y direction during stash and unstash from handle - */ - public void setStashTranslationY(float translationY) { - mStashTranslationY = translationY; - applyTranslationY(); - } - - @Override - public void setTranslationY(float translationY) { - mTranslationY = translationY; - applyTranslationY(); - } - - private void applyTranslationY() { - super.setTranslationY(mTranslationY + mStashTranslationY); - } - @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt index 5d1e890762..4f0337ddc1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt @@ -47,7 +47,7 @@ import kotlin.math.max class TransientBubbleStashController( private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider, - private val context: Context + private val context: Context, ) : BubbleStashController { private lateinit var bubbleBarViewController: BubbleBarViewController @@ -70,6 +70,7 @@ class TransientBubbleStashController( private lateinit var bubbleBarBubbleAlpha: AnimatedFloat private lateinit var bubbleBarBackgroundAlpha: AnimatedFloat private lateinit var bubbleBarTranslationYAnimator: AnimatedFloat + private lateinit var bubbleBarBubbleTranslationY: AnimatedFloat private lateinit var bubbleBarBackgroundScaleX: AnimatedFloat private lateinit var bubbleBarBackgroundScaleY: AnimatedFloat private val handleCenterFromScreenBottom = @@ -143,13 +144,14 @@ class TransientBubbleStashController( taskbarInsetsController: TaskbarInsetsController, bubbleBarViewController: BubbleBarViewController, bubbleStashedHandleViewController: BubbleStashedHandleViewController?, - controllersAfterInitAction: ControllersAfterInitAction + controllersAfterInitAction: ControllersAfterInitAction, ) { this.taskbarInsetsController = taskbarInsetsController this.bubbleBarViewController = bubbleBarViewController this.bubbleStashedHandleViewController = bubbleStashedHandleViewController this.controllersAfterInitAction = controllersAfterInitAction bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY + bubbleBarBubbleTranslationY = bubbleBarViewController.bubbleOffsetY // bubble bar has only alpha property, getting it at index 0 bubbleBarAlpha = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0) bubbleBarBubbleAlpha = bubbleBarViewController.bubbleBarBubbleAlpha @@ -170,7 +172,7 @@ class TransientBubbleStashController( bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY), bubbleBarAlpha.animateToValue(1f), bubbleBarBubbleAlpha.animateToValue(1f), - bubbleBarBackgroundAlpha.animateToValue(1f) + bubbleBarBackgroundAlpha.animateToValue(1f), ) } else { isStashed = true @@ -301,23 +303,24 @@ class TransientBubbleStashController( private fun createStashAnimator(isStashed: Boolean, duration: Long): AnimatorSet { val animatorSet = AnimatorSet() - val alphaDuration = if (isStashed) duration else TASKBAR_STASH_ALPHA_DURATION - val alphaDelay = if (isStashed) TASKBAR_STASH_ALPHA_START_DELAY else 0L animatorSet.play( createBackgroundAlphaAnimator(isStashed).apply { + val alphaDuration = if (isStashed) duration else TASKBAR_STASH_ALPHA_DURATION + val alphaDelay = if (isStashed) TASKBAR_STASH_ALPHA_START_DELAY else 0L this.duration = max(0L, alphaDuration - alphaDelay) this.startDelay = alphaDelay this.interpolator = LINEAR } ) - val iconAlphaTarget = if (isStashed) 0f else 1f animatorSet.play( - bubbleBarBubbleAlpha.animateToValue(iconAlphaTarget).apply { - this.duration = TASKBAR_STASH_ALPHA_DURATION - this.startDelay = TASKBAR_STASH_ALPHA_START_DELAY - this.interpolator = LINEAR - } + bubbleBarBubbleAlpha + .animateToValue(getBarAlphaStart(isStashed), getBarAlphaEnd(isStashed)) + .apply { + this.duration = TASKBAR_STASH_ALPHA_DURATION + this.startDelay = TASKBAR_STASH_ALPHA_START_DELAY + this.interpolator = LINEAR + } ) animatorSet.play( @@ -334,6 +337,16 @@ class TransientBubbleStashController( } ) + // Animate bubble translation to keep reveal animation in the bounds of the bar + val bubbleTyStart = if (isStashed) 0f else -bubbleBarTranslationY + val bubbleTyEnd = if (isStashed) -bubbleBarTranslationY else 0f + animatorSet.play( + bubbleBarBubbleTranslationY.animateToValue(bubbleTyStart, bubbleTyEnd).apply { + this.duration = duration + this.interpolator = EMPHASIZED + } + ) + animatorSet.play( bubbleStashedHandleViewController?.createRevealAnimToIsStashed(isStashed)?.apply { this.duration = duration @@ -359,11 +372,15 @@ class TransientBubbleStashController( ) animatorSet.doOnStart { - if (!isStashed) { - bubbleBarBackgroundAlpha.updateValue(0f) - bubbleBarBubbleAlpha.updateValue(0f) - bubbleBarAlpha.value = 1f - } + // Update the start value for bubble view and background alpha when the entire animation + // begins. + // Alpha animation has a delay, and if we set the initial values at the start of the + // alpha animation, it will cause flickers. + bubbleBarBubbleAlpha.updateValue(getBarAlphaStart(isStashed)) + bubbleBarBackgroundAlpha.updateValue(getBarAlphaStart(isStashed)) + // We animate alpha for background and bubble views separately. Make sure the container + // is always visible. + bubbleBarAlpha.value = 1f } animatorSet.doOnEnd { animator = null @@ -373,6 +390,9 @@ class TransientBubbleStashController( // reset bubble view alpha bubbleBarBubbleAlpha.updateValue(1f) bubbleBarBackgroundAlpha.updateValue(1f) + // reset stash translation + translationYDuringStash.updateValue(0f) + bubbleBarBubbleTranslationY.updateValue(0f) bubbleBarViewController.isExpanded = false } taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged() @@ -382,14 +402,29 @@ class TransientBubbleStashController( } private fun createBackgroundAlphaAnimator(isStashed: Boolean): AnimatorSet { - val stashHandleAlphaTarget = if (isStashed) 1f else 0f - val barAlphaTarget = if (isStashed) 0f else 1f return AnimatorSet().apply { - play(bubbleBarBackgroundAlpha.animateToValue(barAlphaTarget)) - play(stashHandleViewAlpha?.animateToValue(stashHandleAlphaTarget)) + play( + bubbleBarBackgroundAlpha.animateToValue( + getBarAlphaStart(isStashed), + getBarAlphaEnd(isStashed), + ) + ) + play(stashHandleViewAlpha?.animateToValue(getHandleAlphaEnd(isStashed))) } } + private fun getBarAlphaStart(isStashed: Boolean): Float { + return if (isStashed) 1f else 0f + } + + private fun getBarAlphaEnd(isStashed: Boolean): Float { + return if (isStashed) 0f else 1f + } + + private fun getHandleAlphaEnd(isStashed: Boolean): Float { + return if (isStashed) 1f else 0f + } + private fun createSpringOnStashAnimator(isStashed: Boolean): Animator { if (!isStashed) { // Animate the stash translation back to 0 diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt index 1d13956bd8..d4a3b3aee9 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt @@ -32,6 +32,7 @@ import com.android.launcher3.taskbar.TaskbarInsetsController import com.android.launcher3.taskbar.bubbles.BubbleBarView import com.android.launcher3.taskbar.bubbles.BubbleBarViewController import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController +import com.android.launcher3.taskbar.bubbles.BubbleView import com.android.launcher3.util.MultiValueAlpha import com.android.wm.shell.shared.animation.PhysicsAnimator import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils @@ -55,8 +56,8 @@ class TransientBubbleStashControllerTest { companion object { const val TASKBAR_BOTTOM_SPACE = 5 - const val BUBBLE_BAR_WIDTH = 200f - const val BUBBLE_BAR_HEIGHT = 100f + const val BUBBLE_BAR_WIDTH = 200 + const val BUBBLE_BAR_HEIGHT = 100 const val HOTSEAT_TRANSLATION_Y = -45f const val TASK_BAR_TRANSLATION_Y = -TASKBAR_BOTTOM_SPACE const val HANDLE_VIEW_WIDTH = 150 @@ -77,10 +78,12 @@ class TransientBubbleStashControllerTest { private val context = ApplicationProvider.getApplicationContext() private lateinit var bubbleBarView: BubbleBarView private lateinit var stashedHandleView: StashedHandleView + private lateinit var bubbleView: BubbleView private lateinit var barTranslationY: AnimatedFloat private lateinit var barScaleX: AnimatedFloat private lateinit var barScaleY: AnimatedFloat private lateinit var barAlpha: MultiValueAlpha + private lateinit var bubbleOffsetY: AnimatedFloat private lateinit var bubbleAlpha: AnimatedFloat private lateinit var backgroundAlpha: AnimatedFloat private lateinit var stashedHandleAlpha: MultiValueAlpha @@ -105,7 +108,7 @@ class TransientBubbleStashControllerTest { taskbarInsetsController, bubbleBarViewController, bubbleStashedHandleViewController, - ImmediateAction() + ImmediateAction(), ) } @@ -161,11 +164,13 @@ class TransientBubbleStashControllerTest { mTransientBubbleStashController.isStashed = false whenever(bubbleBarViewController.isHiddenForNoBubbles).thenReturn(false) + val bubbleInitialTranslation = bubbleView.translationY + // When stash getInstrumentation().runOnMainSync { mTransientBubbleStashController.updateStashedAndExpandedState( stash = true, - expand = false + expand = false, ) } @@ -181,9 +186,13 @@ class TransientBubbleStashControllerTest { assertThat(bubbleBarView.alpha).isEqualTo(0f) assertThat(bubbleBarView.scaleX).isEqualTo(mTransientBubbleStashController.getStashScaleX()) assertThat(bubbleBarView.scaleY).isEqualTo(mTransientBubbleStashController.getStashScaleY()) + assertThat(bubbleBarView.background.alpha).isEqualTo(255) // Handle view is visible assertThat(stashedHandleView.translationY).isEqualTo(0) assertThat(stashedHandleView.alpha).isEqualTo(1) + // Bubble view is reset + assertThat(bubbleView.translationY).isEqualTo(bubbleInitialTranslation) + assertThat(bubbleView.alpha).isEqualTo(1f) } @Test @@ -274,7 +283,7 @@ class TransientBubbleStashControllerTest { val height = mTransientBubbleStashController.getTouchableHeight() // Then bubble bar height is returned - assertThat(height).isEqualTo(BUBBLE_BAR_HEIGHT.toInt()) + assertThat(height).isEqualTo(BUBBLE_BAR_HEIGHT) } private fun advanceTimeBy(advanceMs: Long) { @@ -285,20 +294,26 @@ class TransientBubbleStashControllerTest { private fun setUpBubbleBarView() { getInstrumentation().runOnMainSync { bubbleBarView = BubbleBarView(context) - bubbleBarView.layoutParams = FrameLayout.LayoutParams(0, 0) + bubbleBarView.layoutParams = + FrameLayout.LayoutParams(BUBBLE_BAR_WIDTH, BUBBLE_BAR_HEIGHT) + bubbleView = BubbleView(context) + bubbleBarView.addBubble(bubbleView) + bubbleBarView.layout(0, 0, BUBBLE_BAR_WIDTH, BUBBLE_BAR_HEIGHT) } } private fun setUpStashedHandleView() { getInstrumentation().runOnMainSync { stashedHandleView = StashedHandleView(context) - stashedHandleView.layoutParams = FrameLayout.LayoutParams(0, 0) + stashedHandleView.layoutParams = + FrameLayout.LayoutParams(HANDLE_VIEW_WIDTH, HANDLE_VIEW_HEIGHT) } } private fun setUpBubbleBarController() { barTranslationY = AnimatedFloat(Runnable { bubbleBarView.translationY = barTranslationY.value }) + bubbleOffsetY = AnimatedFloat { value -> bubbleBarView.setBubbleOffsetY(value) } barScaleX = AnimatedFloat { value -> bubbleBarView.scaleX = value } barScaleY = AnimatedFloat { value -> bubbleBarView.scaleY = value } barAlpha = MultiValueAlpha(bubbleBarView, 1 /* num alpha channels */) @@ -307,13 +322,16 @@ class TransientBubbleStashControllerTest { whenever(bubbleBarViewController.hasBubbles()).thenReturn(true) whenever(bubbleBarViewController.bubbleBarTranslationY).thenReturn(barTranslationY) + whenever(bubbleBarViewController.bubbleOffsetY).thenReturn(bubbleOffsetY) whenever(bubbleBarViewController.bubbleBarBackgroundScaleX).thenReturn(barScaleX) whenever(bubbleBarViewController.bubbleBarBackgroundScaleY).thenReturn(barScaleY) whenever(bubbleBarViewController.bubbleBarAlpha).thenReturn(barAlpha) whenever(bubbleBarViewController.bubbleBarBubbleAlpha).thenReturn(bubbleAlpha) whenever(bubbleBarViewController.bubbleBarBackgroundAlpha).thenReturn(backgroundAlpha) - whenever(bubbleBarViewController.bubbleBarCollapsedWidth).thenReturn(BUBBLE_BAR_WIDTH) - whenever(bubbleBarViewController.bubbleBarCollapsedHeight).thenReturn(BUBBLE_BAR_HEIGHT) + whenever(bubbleBarViewController.bubbleBarCollapsedWidth) + .thenReturn(BUBBLE_BAR_WIDTH.toFloat()) + whenever(bubbleBarViewController.bubbleBarCollapsedHeight) + .thenReturn(BUBBLE_BAR_HEIGHT.toFloat()) whenever(bubbleBarViewController.createRevealAnimatorForStashChange(any())) .thenReturn(AnimatorSet()) }