Merge "Animate the stash handle for new bubbles" into main

This commit is contained in:
Liran Binyamin
2024-04-05 00:49:39 +00:00
committed by Android (Google) Code Review
5 changed files with 224 additions and 34 deletions

View File

@@ -96,6 +96,8 @@ public class BubbleBarView extends FrameLayout {
private final BubbleBarBackground mBubbleBarBackground;
private boolean mIsAnimatingNewBubble = false;
/**
* The current bounds of all the bubble bar. Note that these bounds may not account for
* translation. The bounds should be retrieved using {@link #getBubbleBarBounds()} which
@@ -457,6 +459,7 @@ public class BubbleBarView extends FrameLayout {
/** Prepares for animating a bubble while being stashed. */
public void prepareForAnimatingBubbleWhileStashed(String bubbleKey) {
mIsAnimatingNewBubble = true;
// we're about to animate the new bubble in. the new bubble has already been added to this
// view, but we're currently stashed, so before we can start the animation we need make
// everything else in the bubble bar invisible, except for the bubble that's being animated.
@@ -477,6 +480,9 @@ public class BubbleBarView extends FrameLayout {
/** Resets the state after the bubble animation completed. */
public void onAnimatingBubbleCompleted() {
mIsAnimatingNewBubble = false;
// setting the background triggers relayout so no need to explicitly invalidate after the
// animation
setBackground(mBubbleBarBackground);
for (int i = 0; i < getChildCount(); i++) {
final BubbleView view = (BubbleView) getChildAt(i);
@@ -521,6 +527,12 @@ public class BubbleBarView extends FrameLayout {
* on the expanded state.
*/
private void updateChildrenRenderNodeProperties() {
if (mIsAnimatingNewBubble) {
// don't update bubbles if a new bubble animation is playing.
// the bubble bar will redraw itself via onLayout after the animation.
return;
}
final float widthState = (float) mWidthAnimator.getAnimatedValue();
final float currentWidth = getWidth();
final float expandedWidth = expandedWidth();

View File

@@ -51,6 +51,15 @@ public class BubbleStashController {
*/
private static final float STASHED_BAR_SCALE = 0.5f;
/** The duration of hiding and showing the stashed handle as part of a new bubble animation. */
private static final long NEW_BUBBLE_HANDLE_ANIMATION_DURATION_MS = 200;
/** The translation Y value the handle animates to when hiding it for a new bubble. */
private static final int NEW_BUBBLE_HIDE_HANDLE_ANIMATION_TRANSLATION_Y = -20;
/** The alpha value the handle animates to when hiding it for a new bubble. */
public static final float NEW_BUBBLE_HIDE_HANDLE_ANIMATION_ALPHA = 0.5f;
protected final TaskbarActivityContext mActivity;
// Initialized in init.
@@ -64,6 +73,7 @@ public class BubbleStashController {
private AnimatedFloat mIconScaleForStash;
private AnimatedFloat mIconTranslationYForStash;
private MultiPropertyFactory.MultiProperty mBubbleStashedHandleAlpha;
private AnimatedFloat mBubbleStashedHandleTranslationY;
private boolean mRequestedStashState;
private boolean mRequestedExpandedState;
@@ -95,6 +105,7 @@ public class BubbleStashController {
mBubbleStashedHandleAlpha = mHandleViewController.getStashedHandleAlpha().get(
StashedHandleViewController.ALPHA_INDEX_STASHED);
mBubbleStashedHandleTranslationY = mHandleViewController.getStashedHandleTranslationY();
mStashedHeight = mHandleViewController.getStashedHeight();
mUnstashedHeight = mHandleViewController.getUnstashedHeight();
@@ -362,4 +373,35 @@ public class BubbleStashController {
public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
mHandleViewController.setBubbleBarLocation(bubbleBarLocation);
}
/** Returns the x position of the center of the stashed handle. */
public float getStashedHandleCenterX() {
return mHandleViewController.getStashedHandleCenterX();
}
/** Returns the animation for hiding the handle before a new bubble animates in. */
public AnimatorSet buildHideHandleAnimationForNewBubble() {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
mBubbleStashedHandleTranslationY.animateToValue(
NEW_BUBBLE_HIDE_HANDLE_ANIMATION_TRANSLATION_Y),
mBubbleStashedHandleAlpha.animateToValue(NEW_BUBBLE_HIDE_HANDLE_ANIMATION_ALPHA));
animatorSet.setDuration(NEW_BUBBLE_HANDLE_ANIMATION_DURATION_MS);
return animatorSet;
}
/** Sets the alpha value of the stashed handle. */
public void setStashAlpha(float alpha) {
mBubbleStashedHandleAlpha.setValue(alpha);
}
/** Returns the animation for showing the handle after a new bubble animated in. */
public AnimatorSet buildShowHandleAnimationForNewBubble() {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
mBubbleStashedHandleTranslationY.animateToValue(0),
mBubbleStashedHandleAlpha.animateToValue(1));
animatorSet.setDuration(NEW_BUBBLE_HANDLE_ANIMATION_DURATION_MS);
return animatorSet;
}
}

View File

@@ -29,6 +29,7 @@ import android.view.View;
import android.view.ViewOutlineProvider;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.taskbar.StashedHandleView;
@@ -47,7 +48,7 @@ public class BubbleStashedHandleViewController {
private final TaskbarActivityContext mActivity;
private final StashedHandleView mStashedHandleView;
private final MultiValueAlpha mTaskbarStashedHandleAlpha;
private final MultiValueAlpha mStashedHandleAlpha;
// Initialized in init.
private BubbleBarViewController mBarViewController;
@@ -58,6 +59,12 @@ public class BubbleStashedHandleViewController {
private int mStashedHandleWidth;
private int mStashedHandleHeight;
private final AnimatedFloat mStashedHandleTranslationY =
new AnimatedFloat(this::updateTranslationY);
// Modified when swipe up is happening on the stashed handle or task bar.
private float mSwipeUpTranslationY;
// The bounds we want to clip to in the settled state when showing the stashed handle.
private final Rect mStashedHandleBounds = new Rect();
@@ -75,7 +82,7 @@ public class BubbleStashedHandleViewController {
StashedHandleView stashedHandleView) {
mActivity = activity;
mStashedHandleView = stashedHandleView;
mTaskbarStashedHandleAlpha = new MultiValueAlpha(mStashedHandleView, 1);
mStashedHandleAlpha = new MultiValueAlpha(mStashedHandleView, 1);
}
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
@@ -93,7 +100,7 @@ public class BubbleStashedHandleViewController {
R.dimen.transient_taskbar_bottom_margin);
mStashedHandleView.getLayoutParams().height = mBarSize + bottomMargin;
mTaskbarStashedHandleAlpha.get(0).setValue(0);
mStashedHandleAlpha.get(0).setValue(0);
mStashedTaskbarHeight = resources.getDimensionPixelSize(
R.dimen.bubblebar_stashed_size);
@@ -231,18 +238,33 @@ public class BubbleStashedHandleViewController {
}
}
/** Returns an animator for translation Y. */
public AnimatedFloat getStashedHandleTranslationY() {
return mStashedHandleTranslationY;
}
/**
* Sets the translation of the stashed handle during the swipe up gesture.
*/
public void setTranslationYForSwipe(float transY) {
mStashedHandleView.setTranslationY(transY);
mSwipeUpTranslationY = transY;
updateTranslationY();
}
private void updateTranslationY() {
mStashedHandleView.setTranslationY(mStashedHandleTranslationY.value + mSwipeUpTranslationY);
}
/**
* Used by {@link BubbleStashController} to animate the handle when stashing or un stashing.
*/
public MultiPropertyFactory<View> getStashedHandleAlpha() {
return mTaskbarStashedHandleAlpha;
return mStashedHandleAlpha;
}
/** Returns the x position of the center of the stashed handle. */
public float getStashedHandleCenterX() {
return mStashedHandleBounds.exactCenterX();
}
/**

View File

@@ -18,12 +18,16 @@ package com.android.launcher3.taskbar.bubbles.animation
import android.view.View
import android.view.View.VISIBLE
import androidx.core.animation.AnimatorSet
import androidx.core.animation.ObjectAnimator
import androidx.core.animation.doOnEnd
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.launcher3.taskbar.bubbles.BubbleBarBubble
import com.android.launcher3.taskbar.bubbles.BubbleBarView
import com.android.launcher3.taskbar.bubbles.BubbleStashController
import com.android.launcher3.taskbar.bubbles.BubbleView
import com.android.systemui.util.doOnEnd
import com.android.wm.shell.shared.animation.PhysicsAnimator
/** Handles animations for bubble bar bubbles. */
@@ -39,7 +43,17 @@ constructor(
/** The time to show the flyout. */
const val FLYOUT_DELAY_MS: Long = 2500
/** The translation Y the new bubble will animate to. */
const val BUBBLE_ANIMATION_TRANSLATION_Y = -50f
const val BUBBLE_ANIMATION_FINAL_TRANSLATION_Y = -50f
/** The initial translation Y value the new bubble is set to before the animation starts. */
// TODO(liranb): get rid of this and calculate this based on the y-distance between the
// bubble and the stash handle.
const val BUBBLE_ANIMATION_INITIAL_TRANSLATION_Y = 50f
/** The initial scale Y value that the new bubble is set to before the animation starts. */
const val BUBBLE_ANIMATION_INITIAL_SCALE_Y = 0.3f
/** The initial alpha value that the new bubble is set to before the animation starts. */
const val BUBBLE_ANIMATION_INITIAL_ALPHA = 0.5f
/** The duration of the hide bubble animation. */
const val HIDE_BUBBLE_ANIMATION_DURATION_MS = 250L
}
/** An interface for scheduling jobs. */
@@ -78,41 +92,94 @@ constructor(
// the animation of a new bubble is divided into 2 parts. The first part shows the bubble
// and the second part hides it after a delay.
val showAnimation = buildShowAnimation(bubbleView, b.key, animator)
val hideAnimation = buildHideAnimation(animator)
val hideAnimation = buildHideAnimation(bubbleView)
scheduler.post(showAnimation)
scheduler.postDelayed(FLYOUT_DELAY_MS, hideAnimation)
}
/** Returns a lambda that starts the animation that shows the new bubble. */
/**
* Returns a lambda that starts the animation that shows the new bubble.
*
* The animation is divided into 2 parts. First the stash handle starts animating up and fades
* out. When it ends the bubble starts fading in. The bubble and stashed handle are aligned to
* give the impression of the stash handle morphing into the bubble.
*/
private fun buildShowAnimation(
bubbleView: BubbleView,
key: String,
animator: PhysicsAnimator<BubbleView>
bubbleAnimator: PhysicsAnimator<BubbleView>
): () -> Unit = {
// calculate the initial translation x the bubble should have in order to align it with the
// stash handle.
val initialTranslationX =
bubbleStashController.stashedHandleCenterX - bubbleView.centerXOnScreen
bubbleBarView.prepareForAnimatingBubbleWhileStashed(key)
animator.setDefaultSpringConfig(springConfig)
animator
bubbleAnimator.setDefaultSpringConfig(springConfig)
bubbleAnimator
.spring(DynamicAnimation.ALPHA, 1f)
.spring(DynamicAnimation.TRANSLATION_Y, BUBBLE_ANIMATION_TRANSLATION_Y)
.spring(DynamicAnimation.TRANSLATION_Y, BUBBLE_ANIMATION_FINAL_TRANSLATION_Y)
.spring(DynamicAnimation.SCALE_Y, 1f)
// prepare the bubble for the animation
bubbleView.alpha = 0f
bubbleView.translationX = initialTranslationX
bubbleView.translationY = BUBBLE_ANIMATION_INITIAL_TRANSLATION_Y
bubbleView.scaleY = BUBBLE_ANIMATION_INITIAL_SCALE_Y
bubbleView.visibility = VISIBLE
animator.start()
// start the stashed handle animation. when it ends, start the bubble animation.
val stashedHandleAnimation = bubbleStashController.buildHideHandleAnimationForNewBubble()
stashedHandleAnimation.doOnEnd {
bubbleView.alpha = BUBBLE_ANIMATION_INITIAL_ALPHA
bubbleAnimator.start()
bubbleStashController.setStashAlpha(0f)
}
stashedHandleAnimation.start()
}
/** Returns a lambda that starts the animation that hides the new bubble. */
private fun buildHideAnimation(animator: PhysicsAnimator<BubbleView>): () -> Unit = {
animator.setDefaultSpringConfig(springConfig)
animator
.spring(DynamicAnimation.ALPHA, 0f)
.spring(DynamicAnimation.TRANSLATION_Y, 0f)
.addEndListener { _, _, _, canceled, _, _, allRelevantPropertyAnimsEnded ->
if (!canceled && allRelevantPropertyAnimsEnded) {
if (bubbleStashController.isStashed) {
bubbleBarView.alpha = 0f
}
bubbleBarView.onAnimatingBubbleCompleted()
}
/**
* Returns a lambda that starts the animation that hides the new bubble.
*
* Similarly to the show animation, this is divided into 2 parts. We first animate the bubble
* out, and then animate the stash handle in. At the end of the animation we reset the values of
* the bubble.
*/
private fun buildHideAnimation(bubbleView: BubbleView): () -> Unit = {
val stashAnimation = bubbleStashController.buildShowHandleAnimationForNewBubble()
val alphaAnimator =
ObjectAnimator.ofFloat(bubbleView, View.ALPHA, BUBBLE_ANIMATION_INITIAL_ALPHA)
val translationYAnimator =
ObjectAnimator.ofFloat(
bubbleView,
View.TRANSLATION_Y,
BUBBLE_ANIMATION_INITIAL_TRANSLATION_Y
)
val scaleYAnimator =
ObjectAnimator.ofFloat(bubbleView, View.SCALE_Y, BUBBLE_ANIMATION_INITIAL_SCALE_Y)
val hideBubbleAnimation = AnimatorSet()
hideBubbleAnimation.playTogether(alphaAnimator, translationYAnimator, scaleYAnimator)
hideBubbleAnimation.duration = HIDE_BUBBLE_ANIMATION_DURATION_MS
hideBubbleAnimation.doOnEnd {
// the bubble is now hidden, start the stash handle animation and reset bubble
// properties
bubbleStashController.setStashAlpha(
BubbleStashController.NEW_BUBBLE_HIDE_HANDLE_ANIMATION_ALPHA
)
bubbleView.alpha = 0f
stashAnimation.start()
bubbleView.translationY = 0f
bubbleView.scaleY = 1f
if (bubbleStashController.isStashed) {
bubbleBarView.alpha = 0f
}
animator.start()
bubbleBarView.onAnimatingBubbleCompleted()
}
hideBubbleAnimation.start()
}
}
/** The X position in screen coordinates of the center of the bubble. */
private val BubbleView.centerXOnScreen: Float
get() {
val screenCoordinates = IntArray(2)
getLocationOnScreen(screenCoordinates)
return screenCoordinates[0] + width / 2f
}