From 6ddfa5dfd5e5860de3bdaf7b2c887b6b49c674e2 Mon Sep 17 00:00:00 2001 From: Mykola Podolian Date: Thu, 20 Mar 2025 09:39:35 -0700 Subject: Drag and drop from the search view on the overview screen. When the bubble bar is stashed and an icon is dragged from the search grid towards it, the bubble bar should be un-stashed. Conversely, if the icon is dragged to the opposite location of the stashed bubble bar, the handle should teleport out, and the collapsed bubble bar should teleport in. This change implements logic that addresses this behavior. Bug: 399678274 Test: Manual. Drag and drop application icons from the search grid to the bubble bar drop areas. Flag: com.android.wm.shell.enable_create_any_bubble Change-Id: Ief9ef3e5c24f8199b2812fd0adf63ae3f6cf8f32 --- .../taskbar/BarsLocationAnimatorHelper.kt | 34 +++--- .../launcher3/taskbar/TaskbarDragController.java | 15 ++- .../launcher3/taskbar/bubbles/BubbleBarView.java | 57 ++++++--- .../taskbar/bubbles/BubbleBarViewController.java | 81 +++++++------ .../bubbles/BubbleStashedHandleViewController.java | 5 + .../bubbles/stashing/BubbleStashController.kt | 9 ++ .../stashing/TransientBubbleStashController.kt | 132 ++++++++++++++++++++- 7 files changed, 255 insertions(+), 78 deletions(-) (limited to 'quickstep/src') diff --git a/quickstep/src/com/android/launcher3/taskbar/BarsLocationAnimatorHelper.kt b/quickstep/src/com/android/launcher3/taskbar/BarsLocationAnimatorHelper.kt index b8060e1792..ad847b47ee 100644 --- a/quickstep/src/com/android/launcher3/taskbar/BarsLocationAnimatorHelper.kt +++ b/quickstep/src/com/android/launcher3/taskbar/BarsLocationAnimatorHelper.kt @@ -20,6 +20,7 @@ import android.animation.Animator import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.animation.ValueAnimator +import android.content.Context import android.view.View import androidx.dynamicanimation.animation.SpringForce import com.android.app.animation.Interpolators @@ -30,11 +31,10 @@ import com.android.wm.shell.shared.bubbles.BubbleBarLocation /** Animator helper that creates bars animators. */ object BarsLocationAnimatorHelper { - - private const val FADE_OUT_ANIM_ALPHA_DURATION_MS: Long = 50L - private const val FADE_OUT_ANIM_ALPHA_DELAY_MS: Long = 50L - private const val FADE_OUT_ANIM_POSITION_DURATION_MS: Long = 100L - private const val FADE_IN_ANIM_ALPHA_DURATION_MS: Long = 100L + const val FADE_OUT_ANIM_ALPHA_DURATION_MS: Long = 50L + const val FADE_OUT_ANIM_ALPHA_DELAY_MS: Long = 50L + const val FADE_OUT_ANIM_POSITION_DURATION_MS: Long = 100L + const val FADE_IN_ANIM_ALPHA_DURATION_MS: Long = 100L // Use STIFFNESS_MEDIUMLOW which is not defined in the API constants private const val FADE_IN_ANIM_POSITION_SPRING_STIFFNESS: Float = 400f @@ -45,13 +45,13 @@ object BarsLocationAnimatorHelper { // During fade in animation we shift the bubble bar 1/60th of the screen width private const val FADE_IN_ANIM_POSITION_SHIFT: Float = 1 / 60f - private val View.screenWidth: Int + private val Context.screenWidth: Int get() = resources.displayMetrics.widthPixels - private val View.outShift: Float + val Context.outShift: Float get() = screenWidth * FADE_OUT_ANIM_POSITION_SHIFT - private val View.inShiftX: Float + val Context.inShiftX: Float get() = screenWidth * FADE_IN_ANIM_POSITION_SHIFT /** @@ -108,7 +108,7 @@ object BarsLocationAnimatorHelper { targetViewAlphaAnim: ObjectAnimator, bubbleBarView: View, ): Animator { - val shift: Float = bubbleBarView.outShift + val shift: Float = bubbleBarView.context.outShift val onLeft = newLocation.isOnLeft(bubbleBarView.isLayoutRtl) val startTx: Float @@ -132,15 +132,19 @@ object BarsLocationAnimatorHelper { return createLocationInAnimator(startTx, finalTx, targetViewAlphaAnim, bubbleBarView) } - /** Creates an animator for the bubble bar view out part. */ + /** + * Creates an animator for the bubble bar view out part. + * + * @param targetLocation the location bubble bar should animate to. + */ @JvmStatic fun getBubbleBarLocationOutAnimator( bubbleBarView: View, - bubbleBarLocation: BubbleBarLocation, + targetLocation: BubbleBarLocation, targetViewAlphaAnim: ObjectAnimator, ): Animator { - val onLeft = bubbleBarLocation.isOnLeft(bubbleBarView.isLayoutRtl) - val shift = bubbleBarView.outShift + val onLeft = targetLocation.isOnLeft(bubbleBarView.isLayoutRtl) + val shift = bubbleBarView.context.outShift val finalTx = bubbleBarView.translationX + (if (onLeft) -shift else shift) return this.createLocationOutAnimator(finalTx, targetViewAlphaAnim, bubbleBarView) } @@ -152,7 +156,7 @@ object BarsLocationAnimatorHelper { navButtonsView: View, navBarTargetTranslationX: Float, ): Animator { - val outShift: Float = navButtonsView.outShift + val outShift: Float = navButtonsView.context.outShift val isNavBarOnRight: Boolean = location.isOnLeft(navButtonsView.isLayoutRtl) val finalOutTx = navButtonsView.translationX + (if (isNavBarOnRight) outShift else -outShift) @@ -162,7 +166,7 @@ object BarsLocationAnimatorHelper { ObjectAnimator.ofFloat(navButtonsView, LauncherAnimUtils.VIEW_ALPHA, 0f), navButtonsView, ) - val inShift: Float = navButtonsView.inShiftX + val inShift: Float = navButtonsView.context.inShiftX val inStartX = navBarTargetTranslationX + (if (isNavBarOnRight) -inShift else inShift) val fadeIn: Animator = createLocationInAnimator( diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java index 142f458b3b..4b977e0d23 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -510,6 +510,8 @@ public class TaskbarDragController extends DragController im } else { // This will take care of calling maybeOnDragEnd() after the animation animateGlobalDragViewToOriginalPosition(btv, dragEvent); + //TODO(b/399678274): hide drop target in shell + notifyBubbleBarItemDragCanceled(); } mActivity.getDragLayer().setOnDragListener(null); @@ -536,10 +538,10 @@ public class TaskbarDragController extends DragController im mControllers.taskbarAutohideSuspendController.updateFlag( TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, false); mActivity.onDragEnd(); + // If an item is dropped on the bubble bar, the bubble bar handles the drop, + // so it should not collapse along with the taskbar. + boolean droppedOnBubbleBar = notifyBubbleBarItemDropped(); if (mReturnAnimator == null) { - // If an item is dropped on the bubble bar, the bubble bar handles the drop, - // so it should not collapse along with the taskbar. - boolean droppedOnBubbleBar = notifyBubbleBarItemDropped(); // Upon successful drag, immediately stash taskbar. // Note, this must be done last to ensure no AutohideSuspendFlags are active, as // that will prevent us from stashing until the timeout. @@ -563,12 +565,17 @@ public class TaskbarDragController extends DragController im BubbleBarViewController bubbleBarViewController = bc.bubbleBarViewController; boolean showingDropTarget = bubbleBarViewController.isShowingDropTarget(); if (showingDropTarget) { - bubbleBarViewController.onItemDroppedInBubbleBarDragZone(); + bubbleBarViewController.onItemDragCompleted(); } return showingDropTarget; }).orElse(false); } + private void notifyBubbleBarItemDragCanceled() { + mControllers.bubbleControllers.ifPresent(bc -> + bc.bubbleBarViewController.onItemDraggedOutsideBubbleBarDropZone()); + } + @Override protected void endDrag() { if (mDisallowGlobalDrag && !mIsDropHandledByDropTarget) { diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index 1abef8a63f..6380c2371b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -18,6 +18,7 @@ package com.android.launcher3.taskbar.bubbles; import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.NonNull; @@ -560,30 +561,52 @@ public class BubbleBarView extends FrameLayout { // First animator hides the bar. // After it completes, bubble positions in the bar and arrow position is updated. // Second animator is started to show the bar. - ObjectAnimator alphaOutAnim = ObjectAnimator.ofFloat( - this, getLocationAnimAlphaProperty(), 0f); - mBubbleBarLocationAnimator = BarsLocationAnimatorHelper.getBubbleBarLocationOutAnimator( - this, - bubbleBarLocation, - alphaOutAnim); + mBubbleBarLocationAnimator = animateToBubbleBarLocationOut(bubbleBarLocation); mBubbleBarLocationAnimator.addListener(AnimatorListeners.forEndCallback(() -> { - updateBubblesLayoutProperties(bubbleBarLocation); - mBubbleBarBackground.setAnchorLeft(bubbleBarLocation.isOnLeft(isLayoutRtl())); - ObjectAnimator alphaInAnim = ObjectAnimator.ofFloat(BubbleBarView.this, - getLocationAnimAlphaProperty(), 1f); - // Animate it in - mBubbleBarLocationAnimator = BarsLocationAnimatorHelper.getBubbleBarLocationInAnimator( - bubbleBarLocation, - mBubbleBarLocation, - getDistanceFromOtherSide(), - alphaInAnim, - BubbleBarView.this); + mBubbleBarLocationAnimator = animateToBubbleBarLocationIn(mBubbleBarLocation, + bubbleBarLocation); mBubbleBarLocationAnimator.start(); })); mBubbleBarLocationAnimator.start(); } + /** Creates animator for animating bubble bar in. */ + public Animator animateToBubbleBarLocationIn(BubbleBarLocation fromLocation, + BubbleBarLocation toLocation) { + updateBubblesLayoutProperties(toLocation); + mBubbleBarBackground.setAnchorLeft(toLocation.isOnLeft(isLayoutRtl())); + ObjectAnimator alphaInAnim = ObjectAnimator.ofFloat(BubbleBarView.this, + getLocationAnimAlphaProperty(), 1f); + return BarsLocationAnimatorHelper.getBubbleBarLocationInAnimator(toLocation, fromLocation, + getDistanceFromOtherSide(), alphaInAnim, this); + } + + /** + * Creates animator for animating bubble bar out. + * + * @param targetLocation the location bubble br should animate to. + */ + public Animator animateToBubbleBarLocationOut(BubbleBarLocation targetLocation) { + ObjectAnimator alphaOutAnim = ObjectAnimator.ofFloat( + this, getLocationAnimAlphaProperty(), 0f); + Animator outAnimation = BarsLocationAnimatorHelper.getBubbleBarLocationOutAnimator( + this, + targetLocation, + alphaOutAnim); + outAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + // need to restore the original bar view state in case icon is dropped to the bubble + // bar original location + updateBubblesLayoutProperties(targetLocation); + mBubbleBarBackground.setAnchorLeft(targetLocation.isOnLeft(isLayoutRtl())); + setTranslationX(0f); + } + }); + return outAnimation; + } + /** * Get property that can be used to animate the alpha value for the bar. * When a bubble is being dragged, uses {@link #BUBBLE_DRAG_ALPHA}. diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index 9fb283cccf..ce4a14f551 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -194,9 +194,11 @@ public class BubbleBarViewController { private boolean mHiddenForStashed; private boolean mShouldShowEducation; public boolean mOverflowAdded; - private boolean mIsLocationUpdatedForDropTarget = false; private boolean mWasStashedBeforeEnteringBubbleDragZone = false; + /** This field is used solely to track the bubble bar location prior to the start of the drag */ + private @Nullable BubbleBarLocation mBubbleBarDragLocation; + private BubbleBarViewAnimator mBubbleBarViewAnimator; private final FrameLayout mBubbleBarContainer; private BubbleBarFlyoutController mBubbleBarFlyoutController; @@ -364,7 +366,7 @@ public class BubbleBarViewController { @Override public boolean isOnLeft() { boolean shouldRevertLocation = - mBarView.isShowingDropTarget() && mIsLocationUpdatedForDropTarget; + mBarView.isShowingDropTarget() && isLocationUpdatedForDropTarget(); boolean isOnLeft = mBarView.getBubbleBarLocation().isOnLeft(mBarView.isLayoutRtl()); return shouldRevertLocation != isOnLeft; } @@ -616,6 +618,17 @@ public class BubbleBarViewController { mBarView.animateToBubbleBarLocation(bubbleBarLocation); } + /** Return animator for animating bubble bar in. */ + public Animator animateBubbleBarLocationIn(BubbleBarLocation fromLocation, + BubbleBarLocation toLocation) { + return mBarView.animateToBubbleBarLocationIn(fromLocation, toLocation); + } + + /** Return animator for animating bubble bar out. */ + public Animator animateBubbleBarLocationOut(BubbleBarLocation toLocation) { + return mBarView.animateToBubbleBarLocationOut(toLocation); + } + /** Returns whether the Bubble Bar is currently displaying a drop target. */ public boolean isShowingDropTarget() { return mBarView.isShowingDropTarget(); @@ -628,25 +641,18 @@ public class BubbleBarViewController { * updated. */ public void onDragItemOverBubbleBarDragZone(@NonNull BubbleBarLocation bubbleBarLocation) { - Log.w("BBAnimation", "onDragItemOverBubbleBarDragZone()"); + mBubbleBarDragLocation = bubbleBarLocation; mBarView.showDropTarget(/* isDropTarget = */ true); - boolean isRtl = mBarView.isLayoutRtl(); - mIsLocationUpdatedForDropTarget = getBubbleBarLocation().isOnLeft(isRtl) - != bubbleBarLocation.isOnLeft(isRtl); mWasStashedBeforeEnteringBubbleDragZone = hasBubbles() && mBubbleStashController.isStashed(); if (mWasStashedBeforeEnteringBubbleDragZone) { - if (mIsLocationUpdatedForDropTarget) { - // bubble bar is stashed an location updated - //TODO(b/399678274) add animation - mBubbleStashController.showBubbleBarImmediate(); - animateBubbleBarLocation(bubbleBarLocation); - } else { - // bubble bar is stashed and location the same - just un-stash - mBubbleStashController.showBubbleBar(/* expandBubbles = */ false); - } + // bubble bar is stashed - un-stash at drag location + mBubbleStashController.showBubbleBarAtLocation( + /* fromLocation = */ getBubbleBarLocation(), + /* toLocation = */ mBubbleBarDragLocation + ); } else if (hasBubbles()) { - if (mIsLocationUpdatedForDropTarget) { + if (isLocationUpdatedForDropTarget()) { // bubble bar has bubbles and location is changed - animate bar to the opposite side animateBubbleBarLocation(bubbleBarLocation); } @@ -661,7 +667,12 @@ public class BubbleBarViewController { * {@link #onDragItemOverBubbleBarDragZone}}. */ public boolean isLocationUpdatedForDropTarget() { - return mIsLocationUpdatedForDropTarget; + if (mBubbleBarDragLocation == null) { + return false; + } + boolean isRtl = mBarView.isLayoutRtl(); + return getBubbleBarLocation().isOnLeft(isRtl) + != mBubbleBarDragLocation.isOnLeft(isRtl); } /** @@ -671,39 +682,34 @@ public class BubbleBarViewController { * mode and reset the value returned from {@link #isLocationUpdatedForDropTarget()} to false. */ public void onItemDraggedOutsideBubbleBarDropZone() { - Log.w("BBAnimation", "onItemDraggedOutsideBubbleBarDropZone()"); - mBarView.showDropTarget(/* isDropTarget = */ false); - if (mWasStashedBeforeEnteringBubbleDragZone) { - if (mIsLocationUpdatedForDropTarget) { - // bubble bar was stashed and location updated - //TODO(b/399678274) add animation - animateBubbleBarLocation(getBubbleBarLocation()); - mBubbleStashController.stashBubbleBarImmediate(); - } else { - // bubble bar was stashed and location the same - just stash it back - mBubbleStashController.stashBubbleBar(); - } + if (!isShowingDropTarget()) { + return; + } + if (mWasStashedBeforeEnteringBubbleDragZone && mBubbleBarDragLocation != null) { + // bubble bar was stashed - stash at original location + mBubbleStashController.stashBubbleBarToLocation( + /* fromLocation = */ mBubbleBarDragLocation, + /* toLocation = */ getBubbleBarLocation() + ); } else if (hasBubbles()) { - if (mIsLocationUpdatedForDropTarget) { - // bubble bar has bubbles and location was changed - return to the original location + if (isLocationUpdatedForDropTarget()) { + // bubble bar has bubbles and location was changed - return to the original + // location animateBubbleBarLocation(getBubbleBarLocation()); } } - mBubbleBarPinController.hideDropTarget(); - mIsLocationUpdatedForDropTarget = false; - mWasStashedBeforeEnteringBubbleDragZone = false; + onItemDragCompleted(); } /** * Notifies the controller that the drag has completed over the Bubble Bar drop zone. * The controller will hide the drop target if there are no bubbles and exit drop target mode. */ - public void onItemDroppedInBubbleBarDragZone() { - Log.w("BBAnimation", "onItemDroppedInBubbleBarDragZone()"); + public void onItemDragCompleted() { mBarView.showDropTarget(/* isDropTarget = */ false); mBubbleBarPinController.hideDropTarget(); - mIsLocationUpdatedForDropTarget = false; mWasStashedBeforeEnteringBubbleDragZone = false; + mBubbleBarDragLocation = null; } /** @@ -842,6 +848,7 @@ public class BubbleBarViewController { boolean hiddenForStashedAndNotAnimating = mHiddenForStashed && !mBubbleBarViewAnimator.isAnimating(); if (mHiddenForSysui || mHiddenForNoBubbles || hiddenForStashedAndNotAnimating) { + //TODO(b/404870188) this visibility change cause search view drag misbehavior mBarView.setVisibility(INVISIBLE); } else { mBarView.setVisibility(VISIBLE); diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java index 3640c3b60e..ec540e0088 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java @@ -272,6 +272,11 @@ public class BubbleStashedHandleViewController { updateTranslationY(); } + /** Sets translation X for stash handle. */ + public void setTranslationX(float translationX) { + mStashedHandleView.setTranslationX(translationX); + } + private void updateTranslationY() { mStashedHandleView.setTranslationY(mTranslationForSwipeY + mTranslationForStashY); } 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 fec1eaf55c..ec272ac873 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt @@ -131,6 +131,12 @@ interface BubbleStashController { */ fun stashBubbleBar() + /** + * Animates the bubble bar to the handle at provided location. Does not update bubble bar + * location. + */ + fun stashBubbleBarToLocation(fromLocation: BubbleBarLocation, toLocation: BubbleBarLocation) {} + /** Shows the bubble bar, and expands bubbles depending on [expandBubbles]. */ fun showBubbleBar(expandBubbles: Boolean) { showBubbleBar(expandBubbles = expandBubbles, bubbleBarGesture = false) @@ -144,6 +150,9 @@ interface BubbleStashController { */ fun showBubbleBar(expandBubbles: Boolean, bubbleBarGesture: Boolean) + /** Animates the bubble bar at the provided location. Does not update bubble bar location. */ + fun showBubbleBarAtLocation(fromLocation: BubbleBarLocation, toLocation: BubbleBarLocation) {} + // TODO(b/354218264): Move to BubbleBarViewAnimator /** * The difference on the Y axis between the center of the handle and the center of the bubble 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 9c148e2191..82bb071bc3 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt @@ -18,6 +18,7 @@ package com.android.launcher3.taskbar.bubbles.stashing import android.animation.Animator import android.animation.AnimatorSet +import android.animation.ValueAnimator import android.content.Context import android.graphics.Rect import android.view.MotionEvent @@ -31,6 +32,12 @@ import com.android.app.animation.Interpolators.LINEAR import com.android.launcher3.R import com.android.launcher3.anim.AnimatedFloat import com.android.launcher3.anim.SpringAnimationBuilder +import com.android.launcher3.taskbar.BarsLocationAnimatorHelper.FADE_IN_ANIM_ALPHA_DURATION_MS +import com.android.launcher3.taskbar.BarsLocationAnimatorHelper.FADE_OUT_ANIM_ALPHA_DELAY_MS +import com.android.launcher3.taskbar.BarsLocationAnimatorHelper.FADE_OUT_ANIM_ALPHA_DURATION_MS +import com.android.launcher3.taskbar.BarsLocationAnimatorHelper.FADE_OUT_ANIM_POSITION_DURATION_MS +import com.android.launcher3.taskbar.BarsLocationAnimatorHelper.inShiftX +import com.android.launcher3.taskbar.BarsLocationAnimatorHelper.outShift import com.android.launcher3.taskbar.TaskbarInsetsController import com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_ALPHA_START_DELAY import com.android.launcher3.taskbar.TaskbarStashController.TRANSIENT_TASKBAR_STASH_ALPHA_DURATION @@ -44,6 +51,7 @@ import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Task import com.android.launcher3.util.MultiPropertyFactory import com.android.wm.shell.shared.animation.PhysicsAnimator import com.android.wm.shell.shared.bubbles.BubbleBarLocation +import com.android.wm.shell.shared.bubbles.ContextUtils.isRtl import kotlin.math.max class TransientBubbleStashController( @@ -187,6 +195,11 @@ class TransientBubbleStashController( } override fun showBubbleBarImmediate(bubbleBarTranslationY: Float) { + showBubbleBarImmediateVisually(bubbleBarTranslationY) + onIsStashedChanged() + } + + private fun showBubbleBarImmediateVisually(bubbleBarTranslationY: Float) { bubbleStashedHandleViewController?.setTranslationYForSwipe(0f) stashHandleViewAlpha?.value = 0f this.bubbleBarTranslationYAnimator.updateValue(bubbleBarTranslationY) @@ -197,10 +210,14 @@ class TransientBubbleStashController( bubbleBarBackgroundScaleY.updateValue(1f) isStashed = false bubbleBarViewController.setHiddenForStashed(false) - onIsStashedChanged() } override fun stashBubbleBarImmediate() { + stashBubbleBarImmediateVisually() + onIsStashedChanged() + } + + private fun stashBubbleBarImmediateVisually() { bubbleStashedHandleViewController?.setTranslationYForSwipe(0f) stashHandleViewAlpha?.value = 1f this.bubbleBarTranslationYAnimator.updateValue(getStashTranslation()) @@ -212,7 +229,6 @@ class TransientBubbleStashController( bubbleBarBackgroundScaleY.updateValue(getStashScaleY()) isStashed = true bubbleBarViewController.setHiddenForStashed(true) - onIsStashedChanged() } override fun getTouchableHeight(): Int = @@ -247,6 +263,29 @@ class TransientBubbleStashController( updateStashedAndExpandedState(stash = true, expand = false) } + override fun stashBubbleBarToLocation( + fromLocation: BubbleBarLocation, + toLocation: BubbleBarLocation, + ) { + if (fromLocation.isSameSideWith(toLocation)) { + updateStashedAndExpandedState( + stash = true, + expand = false, + updateTouchRegionOnEnd = false, + ) + return + } + cancelAnimation() + animator = + AnimatorSet().apply { + playSequentially( + bubbleBarViewController.animateBubbleBarLocationOut(toLocation), + createHandleInAnimator(location = toLocation), + ) + start() + } + } + override fun showBubbleBar(expandBubbles: Boolean, bubbleBarGesture: Boolean) { updateStashedAndExpandedState( stash = false, @@ -255,6 +294,33 @@ class TransientBubbleStashController( ) } + override fun showBubbleBarAtLocation( + fromLocation: BubbleBarLocation, + toLocation: BubbleBarLocation, + ) { + if (fromLocation.isSameSideWith(toLocation)) { + updateStashedAndExpandedState( + stash = false, + expand = false, + updateTouchRegionOnEnd = false, + ) + return + } + cancelAnimation() + val bubbleBarInAnimation = + bubbleBarViewController.animateBubbleBarLocationIn(fromLocation, toLocation).apply { + doOnStart { showBubbleBarImmediateVisually(bubbleBarTranslationY) } + } + animator = + AnimatorSet().apply { + playSequentially( + createHandleOutAnimator(location = toLocation), + bubbleBarInAnimation, + ) + start() + } + } + override fun getDiffBetweenHandleAndBarCenters(): Float { // the difference between the centers of the handle and the bubble bar is the difference // between their distance from the bottom of the screen. @@ -392,7 +458,7 @@ class TransientBubbleStashController( bubbleBarAlpha.value = 1f } animatorSet.doOnEnd { - animator = null + cancelAnimation() controllersAfterInitAction.runAfterInit { if (isStashed) { bubbleBarAlpha.value = 0f @@ -486,6 +552,7 @@ class TransientBubbleStashController( stash: Boolean, expand: Boolean, bubbleBarGesture: Boolean = false, + updateTouchRegionOnEnd: Boolean = true, ) { if (bubbleBarViewController.isHiddenForNoBubbles) { // If there are no bubbles the bar and handle are invisible, nothing to do here. @@ -498,11 +565,13 @@ class TransientBubbleStashController( // notify the view controller that the stash state is about to change so that it can // cancel an ongoing animation if there is one. bubbleBarViewController.onStashStateChanging() - animator?.cancel() + cancelAnimation() animator = createStashAnimator(isStashed, BAR_STASH_DURATION).apply { updateBarVisibility(isStashed) - updateTouchRegionOnAnimationEnd() + if (updateTouchRegionOnEnd) { + updateTouchRegionOnAnimationEnd() + } start() } } @@ -512,6 +581,11 @@ class TransientBubbleStashController( } } + private fun cancelAnimation() { + animator?.cancel() + animator = null + } + override fun getHandleViewAlpha(): MultiPropertyFactory.MultiProperty? = // only return handle alpha if the bubble bar is stashed and has bubbles if (isStashed && bubbleBarViewController.hasBubbles()) { @@ -534,6 +608,49 @@ class TransientBubbleStashController( return this } + // TODO(b/399678274) add tests + private fun createHandleInAnimator(location: BubbleBarLocation): Animator? { + val stashHandleViewController = bubbleStashedHandleViewController ?: return null + val handleAlpha = stashHandleViewController.stashedHandleAlpha.get(0) + val shift = context.inShiftX + val startX = if (location.isOnLeft(context.isRtl)) shift else -shift + val handleTranslationX = + ValueAnimator.ofFloat(startX, 0f).apply { + addUpdateListener { v -> + stashHandleViewController.setTranslationX(v.animatedValue as Float) + } + duration = FADE_IN_ANIM_ALPHA_DURATION_MS + } + val handleAlphaAnimation = + handleAlpha.animateToValue(1f).apply { duration = FADE_IN_ANIM_ALPHA_DURATION_MS } + return AnimatorSet().apply { + playTogether(handleTranslationX, handleAlphaAnimation) + doOnStart { stashBubbleBarImmediateVisually() } + } + } + + private fun createHandleOutAnimator(location: BubbleBarLocation): Animator? { + val stashHandleViewController = bubbleStashedHandleViewController ?: return null + val handleAlpha = stashHandleViewController.stashedHandleAlpha.get(0) + val shift = context.outShift + val endX = if (location.isOnLeft(context.isRtl)) -shift else shift + val handleTranslationX = + ValueAnimator.ofFloat(0f, endX).apply { + addUpdateListener { v -> + stashHandleViewController.setTranslationX(v.animatedValue as Float) + } + duration = FADE_OUT_ANIM_POSITION_DURATION_MS + // in case item dropped to the opposite side - need to clear translation + doOnEnd { stashHandleViewController.setTranslationX(0f) } + } + val handleAlphaAnimation = + handleAlpha.animateToValue(0f).apply { + duration = FADE_OUT_ANIM_ALPHA_DURATION_MS + startDelay = FADE_OUT_ANIM_ALPHA_DELAY_MS + } + return AnimatorSet().apply { playTogether(handleTranslationX, handleAlphaAnimation) } + } + private fun Animator.setBubbleBarPivotDuringAnim(pivotX: Float, pivotY: Float): Animator { var initialPivotX = Float.NaN var initialPivotY = Float.NaN @@ -549,4 +666,9 @@ class TransientBubbleStashController( } return this } + + private fun BubbleBarLocation.isSameSideWith(anotherLocation: BubbleBarLocation): Boolean { + val isRtl = context.isRtl + return this.isOnLeft(isRtl) == anotherLocation.isOnLeft(isRtl) + } } -- cgit v1.2.3-59-g8ed1b