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
This commit is contained in:
Mykola Podolian
2025-03-20 09:39:35 -07:00
parent 3a4595c207
commit 6ddfa5dfd5
7 changed files with 255 additions and 78 deletions

View File

@@ -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(

View File

@@ -510,6 +510,8 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> 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<BaseTaskbarContext> 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<BaseTaskbarContext> 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) {

View File

@@ -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}.

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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<View>.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)
}
}