diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 74240920c3..ce3f3ac561 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 65f857eb16..2502e4a775 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 00cc269e9f..16af248fbf 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) }