From 9ab2b8c785d0f7e119369eb0610454621b3b0f89 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Thu, 29 Aug 2024 17:48:03 -0700 Subject: [PATCH] Update bubble bar stash and unstash animation Bubble bar should stash and unstash from the handle. Update the animation to match what is used for the taskbar animation. Fix the stash handle reveal animation to animate the stash bounds to bubble bar bounds and back. This gives the effect of the handle morphing into the bubble bar and bubble bar morphing back into the handle. Bug: 345680453 Flag: com.android.wm.shell.enable_bubble_bar Test: atest TransientBubbleStashControllerTest Test: have bubbles in bubble bar and be in app, swipe up for taskbar and observe the bubble bar handle transforming into the bubble bar Test: with taskbar and bubbles expanded, check that bubble bar transforms into the handle Change-Id: I8508e9207092774cbdb990e65f7f84e56a924d0c --- quickstep/res/values/dimens.xml | 1 + .../taskbar/TaskbarActivityContext.java | 2 +- .../bubbles/BubbleBarViewController.java | 52 +++-- .../BubbleStashedHandleViewController.java | 119 +++++++---- .../bubbles/stashing/BubbleStashController.kt | 6 +- .../PersistentBubbleStashController.kt | 2 +- .../TransientBubbleStashController.kt | 192 +++++++++++------- .../PersistentBubbleStashControllerTest.kt | 2 +- .../TransientBubbleStashControllerTest.kt | 15 +- 9 files changed, 248 insertions(+), 143 deletions(-) diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index d9818828c9..9996367c80 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -437,6 +437,7 @@ 55dp @dimen/transient_taskbar_stashed_height @dimen/taskbar_stashed_handle_height + @dimen/transient_taskbar_stash_spring_velocity_dp_per_s 9dp diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index e19042e8a4..47ae741908 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -283,7 +283,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { TaskbarHotseatDimensionsProvider dimensionsProvider = new DeviceProfileDimensionsProviderAdapter(this); BubbleStashController bubbleStashController = isTransientTaskbar - ? new TransientBubbleStashController(dimensionsProvider, getResources()) + ? new TransientBubbleStashController(dimensionsProvider, this) : new PersistentBubbleStashController(dimensionsProvider); bubbleControllersOptional = Optional.of(new BubbleControllers( new BubbleBarController(this, bubbleBarView), diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index 5c1a5467d8..8b3bb4e736 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -80,12 +80,14 @@ public class BubbleBarViewController { // These are exposed to {@link BubbleStashController} to animate for stashing/un-stashing private final MultiValueAlpha mBubbleBarAlpha; - private final AnimatedFloat mBubbleBarScale = new AnimatedFloat(this::updateScale); + private final AnimatedFloat mBubbleBarScaleY = new AnimatedFloat(this::updateScaleY); private final AnimatedFloat mBubbleBarTranslationY = new AnimatedFloat( this::updateTranslationY); // Modified when swipe up is happening on the bubble bar or task bar. private float mBubbleBarSwipeUpTranslationY; + // Modified when bubble bar is springing back into the stash handle. + private float mBubbleBarStashTranslationY; // Whether the bar is hidden for a sysui state. private boolean mHiddenForSysui; @@ -125,7 +127,7 @@ public class BubbleBarViewController { onBubbleBarConfigurationChanged(/* animate= */ false); mActivity.addOnDeviceProfileChangeListener( dp -> onBubbleBarConfigurationChanged(/* animate= */ true)); - mBubbleBarScale.updateValue(1f); + mBubbleBarScaleY.updateValue(1f); mBubbleClickListener = v -> onBubbleClicked((BubbleView) v); mBubbleBarClickListener = v -> expandBubbleBar(); mBubbleDragController.setupBubbleBarView(mBarView); @@ -255,8 +257,8 @@ public class BubbleBarViewController { return mBubbleBarAlpha; } - public AnimatedFloat getBubbleBarScale() { - return mBubbleBarScale; + public AnimatedFloat getBubbleBarScaleY() { + return mBubbleBarScaleY; } public AnimatedFloat getBubbleBarTranslationY() { @@ -267,6 +269,27 @@ public class BubbleBarViewController { return mBarView.getBubbleBarCollapsedHeight(); } + /** + * @see BubbleBarView#getRelativePivotX() + */ + public float getBubbleBarRelativePivotX() { + return mBarView.getRelativePivotX(); + } + + /** + * @see BubbleBarView#getRelativePivotY() + */ + public float getBubbleBarRelativePivotY() { + return mBarView.getRelativePivotY(); + } + + /** + * @see BubbleBarView#setRelativePivot(float, float) + */ + public void setBubbleBarRelativePivot(float x, float y) { + mBarView.setRelativePivot(x, y); + } + /** * Whether the bubble bar is visible or not. */ @@ -474,17 +497,20 @@ public class BubbleBarViewController { updateTranslationY(); } - private void updateTranslationY() { - mBarView.setTranslationY(mBubbleBarTranslationY.value - + mBubbleBarSwipeUpTranslationY); + /** + * Sets the translation of the bubble bar during the stash animation. + */ + public void setTranslationYForStash(float transY) { + mBubbleBarStashTranslationY = transY; + updateTranslationY(); } - /** - * Applies scale properties for the entire bubble bar. - */ - private void updateScale() { - float scale = mBubbleBarScale.value; - mBarView.setScaleX(scale); + private void updateTranslationY() { + mBarView.setTranslationY(mBubbleBarTranslationY.value + mBubbleBarSwipeUpTranslationY + + mBubbleBarStashTranslationY); + } + + private void updateScaleY(float scale) { mBarView.setScaleY(scale); } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java index 6bfe8f40ae..5c4428c92d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java @@ -21,6 +21,7 @@ import static android.view.View.VISIBLE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.annotation.Nullable; import android.content.res.Resources; import android.graphics.Outline; import android.graphics.Rect; @@ -28,6 +29,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewOutlineProvider; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.anim.RevealOutlineAnimation; import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; @@ -49,23 +51,29 @@ public class BubbleStashedHandleViewController { private final TaskbarActivityContext mActivity; private final StashedHandleView mStashedHandleView; private final MultiValueAlpha mStashedHandleAlpha; + private float mTranslationForSwipeY; + private float mTranslationForStashY; // Initialized in init. private BubbleBarViewController mBarViewController; private BubbleStashController mBubbleStashController; private RegionSamplingHelper mRegionSamplingHelper; - private int mBarSize; - private int mStashedTaskbarHeight; + // Height of the area for the stash handle. Handle will be drawn in the center of this. + // This is also the area where touch is handled on the handle. + private int mStashedBubbleBarHeight; private int mStashedHandleWidth; private int mStashedHandleHeight; - // The bounds we want to clip to in the settled state when showing the stashed handle. + // The bounds of the stashed handle in settled state. private final Rect mStashedHandleBounds = new Rect(); + private float mStashedHandleRadius; // When the reveal animation is cancelled, we can assume it's about to create a new animation, // which should start off at the same point the cancelled one left off. private float mStartProgressForNextRevealAnim; - private boolean mWasLastRevealAnimReversed; + // Use a nullable boolean to handle initial case where the last animation direction is not known + @Nullable + private Boolean mWasLastRevealAnimReversed = null; // XXX: if there are more of these maybe do state flags instead private boolean mHiddenForSysui; @@ -77,6 +85,7 @@ public class BubbleStashedHandleViewController { mActivity = activity; mStashedHandleView = stashedHandleView; mStashedHandleAlpha = new MultiValueAlpha(mStashedHandleView, 1); + mStashedHandleAlpha.setUpdateVisibility(true); } /** Initialize controller. */ @@ -84,26 +93,31 @@ public class BubbleStashedHandleViewController { mBarViewController = bubbleControllers.bubbleBarViewController; mBubbleStashController = bubbleControllers.bubbleStashController; + DeviceProfile deviceProfile = mActivity.getDeviceProfile(); Resources resources = mActivity.getResources(); mStashedHandleHeight = resources.getDimensionPixelSize( R.dimen.bubblebar_stashed_handle_height); mStashedHandleWidth = resources.getDimensionPixelSize( R.dimen.bubblebar_stashed_handle_width); - mBarSize = resources.getDimensionPixelSize(R.dimen.bubblebar_size); - final int bottomMargin = resources.getDimensionPixelSize( - R.dimen.transient_taskbar_bottom_margin); - mStashedHandleView.getLayoutParams().height = mBarSize + bottomMargin; + int barSize = resources.getDimensionPixelSize(R.dimen.bubblebar_size); + // Use the max translation for bubble bar whether it is on the home screen or in app. + // Use values directly from device profile to avoid referencing other bubble controllers + // during init flow. + int maxTy = Math.max(deviceProfile.hotseatBarBottomSpacePx, + deviceProfile.taskbarBottomMargin); + // Adjust handle view size to accommodate the handle morphing into the bubble bar + mStashedHandleView.getLayoutParams().height = barSize + maxTy; mStashedHandleAlpha.get(0).setValue(0); - mStashedTaskbarHeight = resources.getDimensionPixelSize( + mStashedBubbleBarHeight = resources.getDimensionPixelSize( R.dimen.bubblebar_stashed_size); mStashedHandleView.setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - float stashedHandleRadius = view.getHeight() / 2f; - outline.setRoundRect(mStashedHandleBounds, stashedHandleRadius); + mStashedHandleRadius = view.getHeight() / 2f; + outline.setRoundRect(mStashedHandleBounds, mStashedHandleRadius); } }); @@ -132,28 +146,25 @@ public class BubbleStashedHandleViewController { private void updateBounds(BubbleBarLocation bubbleBarLocation) { // As more bubbles get added, the icon bounds become larger. To ensure a consistent // handle bar position, we pin it to the edge of the screen. - final int stashedCenterY = mStashedHandleView.getHeight() - mStashedTaskbarHeight / 2; + final int stashedCenterY = mStashedHandleView.getHeight() - mStashedBubbleBarHeight / 2; + final int stashedCenterX; if (bubbleBarLocation.isOnLeft(mStashedHandleView.isLayoutRtl())) { final int left = mBarViewController.getHorizontalMargin(); - mStashedHandleBounds.set( - left, - stashedCenterY - mStashedHandleHeight / 2, - left + mStashedHandleWidth, - stashedCenterY + mStashedHandleHeight / 2); - mStashedHandleView.setPivotX(0); + stashedCenterX = left + mStashedHandleWidth / 2; } else { final int right = - mActivity.getDeviceProfile().widthPx - mBarViewController.getHorizontalMargin(); - mStashedHandleBounds.set( - right - mStashedHandleWidth, - stashedCenterY - mStashedHandleHeight / 2, - right, - stashedCenterY + mStashedHandleHeight / 2); - mStashedHandleView.setPivotX(mStashedHandleView.getWidth()); + mStashedHandleView.getRight() - mBarViewController.getHorizontalMargin(); + stashedCenterX = right - mStashedHandleWidth / 2; } - + mStashedHandleBounds.set( + stashedCenterX - mStashedHandleWidth / 2, + stashedCenterY - mStashedHandleHeight / 2, + stashedCenterX + mStashedHandleWidth / 2, + stashedCenterY + mStashedHandleHeight / 2 + ); mStashedHandleView.updateSampledRegion(mStashedHandleBounds); - mStashedHandleView.setPivotY(mStashedHandleView.getHeight() - mStashedTaskbarHeight / 2f); + mStashedHandleView.setPivotX(stashedCenterX); + mStashedHandleView.setPivotY(stashedCenterY); } public void onDestroy() { @@ -168,13 +179,6 @@ public class BubbleStashedHandleViewController { return mStashedHandleHeight; } - /** - * Returns the height when the bubble bar is unstashed (so the height of the bubble bar). - */ - public int getUnstashedHeight() { - return mBarSize; - } - /** * Called when system ui state changes. Bubbles don't show when the device is locked. */ @@ -242,7 +246,20 @@ public class BubbleStashedHandleViewController { * Sets the translation of the stashed handle during the swipe up gesture. */ public void setTranslationYForSwipe(float transY) { - mStashedHandleView.setTranslationY(transY); + mTranslationForSwipeY = transY; + updateTranslationY(); + } + + /** + * Sets the translation of the stashed handle during the spring on stash animation. + */ + public void setTranslationYForStash(float transY) { + mTranslationForStashY = transY; + updateTranslationY(); + } + + private void updateTranslationY() { + mStashedHandleView.setTranslationY(mTranslationForSwipeY + mTranslationForStashY); } /** Returns the translation of the stashed handle. */ @@ -263,18 +280,17 @@ public class BubbleStashedHandleViewController { * the size of where the bubble bar icons will be. */ public Animator createRevealAnimToIsStashed(boolean isStashed) { - Rect bubbleBarBounds = new Rect(mBarViewController.getBubbleBarBounds()); + Rect bubbleBarBounds = getLocalBubbleBarBounds(); - // Account for the full visual height of the bubble bar - int heightDiff = (mBarSize - bubbleBarBounds.height()) / 2; - bubbleBarBounds.top -= heightDiff; - bubbleBarBounds.bottom += heightDiff; - float stashedHandleRadius = mStashedHandleView.getHeight() / 2f; + float bubbleBarRadius = bubbleBarBounds.height() / 2f; final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider( - stashedHandleRadius, stashedHandleRadius, bubbleBarBounds, mStashedHandleBounds); + bubbleBarRadius, mStashedHandleRadius, bubbleBarBounds, mStashedHandleBounds); boolean isReversed = !isStashed; - boolean changingDirection = mWasLastRevealAnimReversed != isReversed; + // We are only changing direction when mWasLastRevealAnimReversed is set at least once + boolean changingDirection = + mWasLastRevealAnimReversed != null && mWasLastRevealAnimReversed != isReversed; + mWasLastRevealAnimReversed = isReversed; if (changingDirection) { mStartProgressForNextRevealAnim = 1f - mStartProgressForNextRevealAnim; @@ -291,6 +307,21 @@ public class BubbleStashedHandleViewController { return revealAnim; } + /** + * Get bounds for the bubble bar in the space of the handle view + */ + private Rect getLocalBubbleBarBounds() { + // Position the bubble bar bounds to the space of handle view + Rect bubbleBarBounds = new Rect(mBarViewController.getBubbleBarBounds()); + // Start by moving bubble bar bounds to the bottom of handle view + int height = bubbleBarBounds.height(); + bubbleBarBounds.bottom = mStashedHandleView.getHeight(); + bubbleBarBounds.top = bubbleBarBounds.bottom - height; + // Then apply translation that is applied to the bubble bar + bubbleBarBounds.offset(0, (int) mBubbleStashController.getBubbleBarTranslationY()); + return bubbleBarBounds; + } + /** Checks that the stash handle is visible and that the motion event is within bounds. */ public boolean isEventOverHandle(MotionEvent ev) { if (mStashedHandleView.getVisibility() != VISIBLE) { @@ -299,7 +330,7 @@ public class BubbleStashedHandleViewController { // the bounds of the handle only include the visible part, so we check that the Y coordinate // is anywhere within the stashed height of bubble bar (same as taskbar stashed height). - final int top = mActivity.getDeviceProfile().heightPx - mStashedTaskbarHeight; + final int top = mActivity.getDeviceProfile().heightPx - mStashedBubbleBarHeight; final float x = ev.getRawX(); return ev.getRawY() >= top && x >= mStashedHandleBounds.left && x <= mStashedHandleBounds.right; diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt index 48eb7dea15..b2a88ac919 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt @@ -179,10 +179,10 @@ interface BubbleStashController { /** How long to stash/unstash. */ const val BAR_STASH_DURATION = InsetsController.ANIMATION_DURATION_RESIZE.toLong() + const val BAR_STASH_ALPHA_DURATION = 50L + const val BAR_STASH_ALPHA_DELAY = 33L + /** How long to translate Y coordinate of the BubbleBar. */ const val BAR_TRANSLATION_DURATION = 300L - - /** The scale bubble bar animates to when being stashed. */ - const val STASHED_BAR_SCALE = 0.5f } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt index 1b650195ef..3ebd97e315 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt @@ -116,7 +116,7 @@ class PersistentBubbleStashController( bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY // bubble bar has only alpha property, getting it at index 0 bubbleBarAlphaAnimator = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0) - bubbleBarScaleAnimator = bubbleBarViewController.bubbleBarScale + bubbleBarScaleAnimator = bubbleBarViewController.bubbleBarScaleY } private fun animateAfterUnlock() { 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 1a4b9823ef..b8fd1cb1ed 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt @@ -17,30 +17,36 @@ package com.android.launcher3.taskbar.bubbles.stashing import android.animation.Animator -import android.animation.AnimatorListenerAdapter import android.animation.AnimatorSet -import android.content.res.Resources +import android.content.Context import android.view.MotionEvent import android.view.View import androidx.annotation.VisibleForTesting +import androidx.core.animation.doOnEnd +import androidx.core.animation.doOnStart +import androidx.dynamicanimation.animation.SpringForce +import com.android.app.animation.Interpolators.EMPHASIZED +import com.android.app.animation.Interpolators.LINEAR import com.android.launcher3.R import com.android.launcher3.anim.AnimatedFloat -import com.android.launcher3.taskbar.StashedHandleViewController +import com.android.launcher3.anim.SpringAnimationBuilder import com.android.launcher3.taskbar.TaskbarInsetsController import com.android.launcher3.taskbar.bubbles.BubbleBarViewController import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController +import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_ALPHA_DELAY +import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_ALPHA_DURATION import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_DURATION import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_TRANSLATION_DURATION -import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.STASHED_BAR_SCALE import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.ControllersAfterInitAction import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.TaskbarHotseatDimensionsProvider import com.android.launcher3.util.MultiPropertyFactory import com.android.wm.shell.common.bubbles.BubbleBarLocation import com.android.wm.shell.shared.animation.PhysicsAnimator +import kotlin.math.max class TransientBubbleStashController( private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider, - resources: Resources + private val context: Context ) : BubbleStashController { private lateinit var bubbleBarViewController: BubbleBarViewController @@ -50,14 +56,20 @@ class TransientBubbleStashController( // stash view properties private var bubbleStashedHandleViewController: BubbleStashedHandleViewController? = null private var stashHandleViewAlpha: MultiPropertyFactory.MultiProperty? = null + private var translationYDuringStash = AnimatedFloat { transY -> + bubbleStashedHandleViewController?.setTranslationYForStash(transY) + bubbleBarViewController.setTranslationYForStash(transY) + } + private val stashHandleStashVelocity = + context.resources.getDimension(R.dimen.bubblebar_stashed_handle_spring_velocity_dp_per_s) private var stashedHeight: Int = 0 // bubble bar properties private lateinit var bubbleBarAlpha: MultiPropertyFactory.MultiProperty private lateinit var bubbleBarTranslationYAnimator: AnimatedFloat private lateinit var bubbleBarScale: AnimatedFloat - private val mHandleCenterFromScreenBottom = - resources.getDimensionPixelSize(R.dimen.bubblebar_stashed_size) / 2f + private val handleCenterFromScreenBottom = + context.resources.getDimensionPixelSize(R.dimen.bubblebar_stashed_size) / 2f private var animator: AnimatorSet? = null @@ -136,12 +148,9 @@ class TransientBubbleStashController( bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY // bubble bar has only alpha property, getting it at index 0 bubbleBarAlpha = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0) - bubbleBarScale = bubbleBarViewController.bubbleBarScale + bubbleBarScale = bubbleBarViewController.bubbleBarScaleY stashedHeight = bubbleStashedHandleViewController?.stashedHeight ?: 0 - stashHandleViewAlpha = - bubbleStashedHandleViewController - ?.stashedHandleAlpha - ?.get(StashedHandleViewController.ALPHA_INDEX_STASHED) + stashHandleViewAlpha = bubbleStashedHandleViewController?.stashedHandleAlpha?.get(0) } private fun animateAfterUnlock() { @@ -179,7 +188,7 @@ class TransientBubbleStashController( stashHandleViewAlpha?.value = 1f this.bubbleBarTranslationYAnimator.updateValue(getStashTranslation()) bubbleBarAlpha.setValue(0f) - bubbleBarScale.updateValue(STASHED_BAR_SCALE) + bubbleBarScale.updateValue(getStashScale()) isStashed = true onIsStashedChanged() } @@ -223,11 +232,11 @@ class TransientBubbleStashController( // the difference between the centers of the handle and the bubble bar is the difference // between their distance from the bottom of the screen. val barCenter: Float = bubbleBarViewController.bubbleBarCollapsedHeight / 2f - return mHandleCenterFromScreenBottom - barCenter + return handleCenterFromScreenBottom - barCenter } override fun getStashedHandleTranslationForNewBubbleAnimation(): Float { - return -mHandleCenterFromScreenBottom + return -handleCenterFromScreenBottom } override fun getStashedHandlePhysicsAnimator(): PhysicsAnimator? { @@ -245,7 +254,13 @@ class TransientBubbleStashController( override fun getHandleTranslationY(): Float? = bubbleStashedHandleViewController?.translationY private fun getStashTranslation(): Float { - return (bubbleBarViewController.bubbleBarCollapsedHeight - stashedHeight) / 2f + return bubbleBarTranslationY / 2f + } + + @VisibleForTesting + fun getStashScale(): Float { + val handleHeight = bubbleStashedHandleViewController?.stashedHeight ?: 0 + return handleHeight / bubbleBarViewController.bubbleBarCollapsedHeight } /** @@ -258,61 +273,84 @@ class TransientBubbleStashController( @Suppress("SameParameterValue") private fun createStashAnimator(isStashed: Boolean, duration: Long): AnimatorSet { val animatorSet = AnimatorSet() - val fullLengthAnimatorSet = AnimatorSet() - // Not exactly half and may overlap. See [first|second]HalfDurationScale below. - val firstHalfAnimatorSet = AnimatorSet() - val secondHalfAnimatorSet = AnimatorSet() - val firstHalfDurationScale: Float - val secondHalfDurationScale: Float - val stashHandleAlphaValue: Float - if (isStashed) { - firstHalfDurationScale = 0.75f - secondHalfDurationScale = 0.5f - stashHandleAlphaValue = 1f - fullLengthAnimatorSet.play( - bubbleBarTranslationYAnimator.animateToValue(getStashTranslation()) - ) - firstHalfAnimatorSet.playTogether( - bubbleBarAlpha.animateToValue(0f), - bubbleBarScale.animateToValue(STASHED_BAR_SCALE) - ) - } else { - firstHalfDurationScale = 0.5f - secondHalfDurationScale = 0.75f - stashHandleAlphaValue = 0f - fullLengthAnimatorSet.playTogether( - bubbleBarScale.animateToValue(1f), - bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY) - ) - secondHalfAnimatorSet.playTogether(bubbleBarAlpha.animateToValue(1f)) - } - stashHandleViewAlpha?.let { - secondHalfAnimatorSet.playTogether(it.animateToValue(stashHandleAlphaValue)) - } - bubbleStashedHandleViewController?.createRevealAnimToIsStashed(isStashed)?.let { - fullLengthAnimatorSet.play(it) - } - fullLengthAnimatorSet.setDuration(duration) - firstHalfAnimatorSet.setDuration((duration * firstHalfDurationScale).toLong()) - secondHalfAnimatorSet.setDuration((duration * secondHalfDurationScale).toLong()) - secondHalfAnimatorSet.startDelay = (duration * (1 - secondHalfDurationScale)).toLong() - animatorSet.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet, secondHalfAnimatorSet) - animatorSet.addListener( - object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - animator = null - controllersAfterInitAction.runAfterInit { - if (isStashed) { - bubbleBarViewController.isExpanded = false - } - taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged() - } - } + + val alphaDuration = if (isStashed) duration else BAR_STASH_ALPHA_DURATION + val alphaDelay = if (isStashed) BAR_STASH_ALPHA_DELAY else 0L + animatorSet.play( + createStashAlphaAnimator(isStashed).apply { + this.duration = max(0L, alphaDuration - alphaDelay) + this.startDelay = alphaDelay + this.interpolator = LINEAR } ) + + animatorSet.play( + createSpringOnStashAnimator(isStashed).apply { + this.duration = duration + this.interpolator = LINEAR + } + ) + + animatorSet.play( + bubbleStashedHandleViewController?.createRevealAnimToIsStashed(isStashed)?.apply { + this.duration = duration + this.interpolator = EMPHASIZED + } + ) + + val scaleTarget = if (isStashed) getStashScale() else 1f + animatorSet.play( + bubbleBarScale.animateToValue(scaleTarget).apply { + this.duration = duration + this.interpolator = EMPHASIZED + this.setBubbleBarPivotDuringAnim(0.5f, 1f) + } + ) + + val translationYTarget = if (isStashed) getStashTranslation() else bubbleBarTranslationY + animatorSet.play( + bubbleBarTranslationYAnimator.animateToValue(translationYTarget).apply { + this.duration = duration + this.interpolator = EMPHASIZED + } + ) + + animatorSet.doOnEnd { + animator = null + controllersAfterInitAction.runAfterInit { + if (isStashed) { + bubbleBarViewController.isExpanded = false + } + taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged() + } + } return animatorSet } + private fun createStashAlphaAnimator(isStashed: Boolean): AnimatorSet { + val stashHandleAlphaTarget = if (isStashed) 1f else 0f + val barAlphaTarget = if (isStashed) 0f else 1f + return AnimatorSet().apply { + play(bubbleBarAlpha.animateToValue(barAlphaTarget)) + play(stashHandleViewAlpha?.animateToValue(stashHandleAlphaTarget)) + } + } + + private fun createSpringOnStashAnimator(isStashed: Boolean): Animator { + if (!isStashed) { + // Animate the stash translation back to 0 + return translationYDuringStash.animateToValue(0f) + } + // Apply a spring to the handle + return SpringAnimationBuilder(context) + .setStartValue(translationYDuringStash.value) + .setEndValue(0f) + .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY) + .setStiffness(SpringForce.STIFFNESS_LOW) + .setStartVelocity(stashHandleStashVelocity) + .build(translationYDuringStash, AnimatedFloat.VALUE) + } + private fun onIsStashedChanged() { controllersAfterInitAction.runAfterInit { taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged() @@ -363,13 +401,23 @@ class TransientBubbleStashController( } private fun Animator.updateTouchRegionOnAnimationEnd(): Animator { - this.addListener( - object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - onIsStashedChanged() - } + doOnEnd { onIsStashedChanged() } + return this + } + + private fun Animator.setBubbleBarPivotDuringAnim(pivotX: Float, pivotY: Float): Animator { + var initialPivotX = Float.NaN + var initialPivotY = Float.NaN + doOnStart { + initialPivotX = bubbleBarViewController.bubbleBarRelativePivotX + initialPivotY = bubbleBarViewController.bubbleBarRelativePivotY + bubbleBarViewController.setBubbleBarRelativePivot(pivotX, pivotY) + } + doOnEnd { + if (!initialPivotX.isNaN() && !initialPivotY.isNaN()) { + bubbleBarViewController.setBubbleBarRelativePivot(initialPivotX, initialPivotY) } - ) + } return this } } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt index c0a5dfaf7b..4106a2c9a6 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt @@ -251,7 +251,7 @@ class PersistentBubbleStashControllerTest { whenever(bubbleBarViewController.hasBubbles()).thenReturn(true) whenever(bubbleBarViewController.bubbleBarTranslationY).thenReturn(translationY) - whenever(bubbleBarViewController.bubbleBarScale).thenReturn(scale) + whenever(bubbleBarViewController.bubbleBarScaleY).thenReturn(scale) whenever(bubbleBarViewController.bubbleBarAlpha).thenReturn(alpha) whenever(bubbleBarViewController.bubbleBarCollapsedHeight).thenReturn(BUBBLE_BAR_HEIGHT) } 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 b5809c2b78..63c21979c2 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 @@ -31,7 +31,6 @@ 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.stashing.BubbleStashController.Companion.STASHED_BAR_SCALE import com.android.launcher3.util.MultiValueAlpha import com.android.wm.shell.shared.animation.PhysicsAnimator import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils @@ -59,7 +58,7 @@ class TransientBubbleStashControllerTest { const val HOTSEAT_TRANSLATION_Y = -45f const val TASK_BAR_TRANSLATION_Y = -TASKBAR_BOTTOM_SPACE const val HANDLE_VIEW_HEIGHT = 4 - const val BUBBLE_BAR_STASHED_TRANSLATION_Y = 48 + const val BUBBLE_BAR_STASHED_TRANSLATION_Y = -2.5f } @get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this) @@ -90,7 +89,7 @@ class TransientBubbleStashControllerTest { val taskbarHotseatDimensionsProvider = DefaultDimensionsProvider(taskBarBottomSpace = TASKBAR_BOTTOM_SPACE) mTransientBubbleStashController = - TransientBubbleStashController(taskbarHotseatDimensionsProvider, context.resources) + TransientBubbleStashController(taskbarHotseatDimensionsProvider, context) setUpBubbleBarView() setUpBubbleBarController() setUpStashedHandleView() @@ -174,8 +173,8 @@ class TransientBubbleStashControllerTest { assertThat(mTransientBubbleStashController.isStashed).isTrue() assertThat(bubbleBarView.translationY).isEqualTo(BUBBLE_BAR_STASHED_TRANSLATION_Y) assertThat(bubbleBarView.alpha).isEqualTo(0f) - assertThat(bubbleBarView.scaleX).isEqualTo(STASHED_BAR_SCALE) - assertThat(bubbleBarView.scaleY).isEqualTo(STASHED_BAR_SCALE) + assertThat(bubbleBarView.scaleX).isEqualTo(mTransientBubbleStashController.getStashScale()) + assertThat(bubbleBarView.scaleY).isEqualTo(mTransientBubbleStashController.getStashScale()) // Handle view is visible assertThat(stashedHandleView.translationY).isEqualTo(0) assertThat(stashedHandleView.alpha).isEqualTo(1) @@ -243,8 +242,8 @@ class TransientBubbleStashControllerTest { // Then all property values are updated assertThat(bubbleBarView.translationY).isEqualTo(BUBBLE_BAR_STASHED_TRANSLATION_Y) assertThat(bubbleBarView.alpha).isEqualTo(0) - assertThat(bubbleBarView.scaleX).isEqualTo(STASHED_BAR_SCALE) - assertThat(bubbleBarView.scaleY).isEqualTo(STASHED_BAR_SCALE) + assertThat(bubbleBarView.scaleX).isEqualTo(mTransientBubbleStashController.getStashScale()) + assertThat(bubbleBarView.scaleY).isEqualTo(mTransientBubbleStashController.getStashScale()) // Handle is visible at correct Y position assertThat(stashedHandleView.alpha).isEqualTo(1) assertThat(stashedHandleView.translationY).isEqualTo(0) @@ -306,7 +305,7 @@ class TransientBubbleStashControllerTest { whenever(bubbleBarViewController.hasBubbles()).thenReturn(true) whenever(bubbleBarViewController.bubbleBarTranslationY).thenReturn(barTranslationY) - whenever(bubbleBarViewController.bubbleBarScale).thenReturn(barScale) + whenever(bubbleBarViewController.bubbleBarScaleY).thenReturn(barScale) whenever(bubbleBarViewController.bubbleBarAlpha).thenReturn(barAlpha) whenever(bubbleBarViewController.bubbleBarCollapsedHeight).thenReturn(BUBBLE_BAR_HEIGHT) }