Merge "Animate the expanded view into the dismiss target" into main
This commit is contained in:
commit
5d7c7eaceb
@ -23,6 +23,8 @@ import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.util.Log;
|
||||
import android.util.Size;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@ -33,6 +35,7 @@ import com.android.wm.shell.bubbles.BubbleOverflow;
|
||||
import com.android.wm.shell.bubbles.BubblePositioner;
|
||||
import com.android.wm.shell.bubbles.BubbleViewProvider;
|
||||
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix;
|
||||
import com.android.wm.shell.common.magnetictarget.MagnetizedObject.MagneticTarget;
|
||||
|
||||
/**
|
||||
* Helper class to animate a {@link BubbleBarExpandedView} on a bubble.
|
||||
@ -44,6 +47,13 @@ public class BubbleBarAnimationHelper {
|
||||
private static final float EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT = 0.1f;
|
||||
private static final float EXPANDED_VIEW_ANIMATE_OUT_SCALE_AMOUNT = .75f;
|
||||
private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
|
||||
private static final int EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION = 100;
|
||||
/**
|
||||
* Additional scale applied to expanded view when it is positioned inside a magnetic target.
|
||||
*/
|
||||
private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.75f;
|
||||
private static final int EXPANDED_VIEW_ANIMATE_POSITION_DURATION = 300;
|
||||
private static final int EXPANDED_VIEW_DISMISS_DURATION = 250;
|
||||
|
||||
/** Spring config for the expanded view scale-in animation. */
|
||||
private final PhysicsAnimator.SpringConfig mScaleInSpringConfig =
|
||||
@ -181,7 +191,8 @@ public class BubbleBarAnimationHelper {
|
||||
Log.w(TAG, "Trying to animate collapse without a bubble");
|
||||
return;
|
||||
}
|
||||
|
||||
bbev.setScaleX(1f);
|
||||
bbev.setScaleY(1f);
|
||||
mExpandedViewContainerMatrix.setScaleX(1f);
|
||||
mExpandedViewContainerMatrix.setScaleY(1f);
|
||||
|
||||
@ -208,12 +219,125 @@ public class BubbleBarAnimationHelper {
|
||||
mExpandedViewAlphaAnimator.reverse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates dismissal of currently expanded bubble
|
||||
*
|
||||
* @param endRunnable a runnable to run at the end of the animation
|
||||
*/
|
||||
public void animateDismiss(Runnable endRunnable) {
|
||||
mIsExpanded = false;
|
||||
final BubbleBarExpandedView bbev = getExpandedView();
|
||||
if (bbev == null) {
|
||||
Log.w(TAG, "Trying to animate dismiss without a bubble");
|
||||
return;
|
||||
}
|
||||
|
||||
int[] location = bbev.getLocationOnScreen();
|
||||
int diffFromBottom = mPositioner.getScreenRect().bottom - location[1];
|
||||
|
||||
bbev.animate()
|
||||
// 2x distance from bottom so the view flies out
|
||||
.translationYBy(diffFromBottom * 2)
|
||||
.setDuration(EXPANDED_VIEW_DISMISS_DURATION)
|
||||
.withEndAction(endRunnable)
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate current expanded bubble back to its rest position
|
||||
*/
|
||||
public void animateToRestPosition() {
|
||||
BubbleBarExpandedView bbev = getExpandedView();
|
||||
if (bbev == null) {
|
||||
Log.w(TAG, "Trying to animate expanded view to rest position without a bubble");
|
||||
return;
|
||||
}
|
||||
Point restPoint = getExpandedViewRestPosition(getExpandedViewSize());
|
||||
bbev.animate()
|
||||
.x(restPoint.x)
|
||||
.y(restPoint.y)
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.setDuration(EXPANDED_VIEW_ANIMATE_POSITION_DURATION)
|
||||
.setInterpolator(Interpolators.EMPHASIZED_DECELERATE)
|
||||
.withStartAction(() -> bbev.setAnimating(true))
|
||||
.withEndAction(() -> bbev.setAnimating(false))
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates currently expanded bubble into the given {@link MagneticTarget}.
|
||||
*
|
||||
* @param target magnetic target to snap to
|
||||
* @param endRunnable a runnable to run at the end of the animation
|
||||
*/
|
||||
public void animateIntoTarget(MagneticTarget target, @Nullable Runnable endRunnable) {
|
||||
BubbleBarExpandedView bbev = getExpandedView();
|
||||
if (bbev == null) {
|
||||
Log.w(TAG, "Trying to snap the expanded view to target without a bubble");
|
||||
return;
|
||||
}
|
||||
Point expandedViewCenter = getViewCenterOnScreen(bbev);
|
||||
|
||||
// Calculate the difference between the target's center coordinates and the object's.
|
||||
// Animating the object's x/y properties by these values will center the object on top
|
||||
// of the magnetic target.
|
||||
float xDiff = target.getCenterOnScreen().x - expandedViewCenter.x;
|
||||
float yDiff = target.getCenterOnScreen().y - expandedViewCenter.y;
|
||||
|
||||
// Calculate scale of expanded view so it fits inside the magnetic target
|
||||
float bbevMaxSide = Math.max(bbev.getWidth(), bbev.getHeight());
|
||||
float targetMaxSide = Math.max(target.getTargetView().getWidth(),
|
||||
target.getTargetView().getHeight());
|
||||
float scale = (targetMaxSide * EXPANDED_VIEW_IN_TARGET_SCALE) / bbevMaxSide;
|
||||
|
||||
bbev.animate()
|
||||
.translationX(bbev.getTranslationX() + xDiff)
|
||||
.translationY(bbev.getTranslationY() + yDiff)
|
||||
.scaleX(scale)
|
||||
.scaleY(scale)
|
||||
.setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
|
||||
.setInterpolator(Interpolators.EMPHASIZED)
|
||||
.withStartAction(() -> bbev.setAnimating(true))
|
||||
.withEndAction(() -> {
|
||||
bbev.setAnimating(false);
|
||||
if (endRunnable != null) {
|
||||
endRunnable.run();
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate currently expanded view when it is released from dismiss view
|
||||
*/
|
||||
public void animateUnstuckFromDismissView() {
|
||||
BubbleBarExpandedView expandedView = getExpandedView();
|
||||
if (expandedView == null) {
|
||||
Log.w(TAG, "Trying to unsnap the expanded view from dismiss without a bubble");
|
||||
return;
|
||||
}
|
||||
expandedView
|
||||
.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
|
||||
.setInterpolator(Interpolators.EMPHASIZED)
|
||||
.withStartAction(() -> expandedView.setAnimating(true))
|
||||
.withEndAction(() -> expandedView.setAnimating(false))
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel current animations
|
||||
*/
|
||||
public void cancelAnimations() {
|
||||
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
|
||||
mExpandedViewAlphaAnimator.cancel();
|
||||
BubbleBarExpandedView bbev = getExpandedView();
|
||||
if (bbev != null) {
|
||||
bbev.animate().cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable BubbleBarExpandedView getExpandedView() {
|
||||
@ -231,21 +355,42 @@ public class BubbleBarAnimationHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
|
||||
final int padding = mPositioner.getBubbleBarExpandedViewPadding();
|
||||
final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded);
|
||||
final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
|
||||
final Size size = getExpandedViewSize();
|
||||
Point position = getExpandedViewRestPosition(size);
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) bbev.getLayoutParams();
|
||||
lp.width = width;
|
||||
lp.height = height;
|
||||
lp.width = size.getWidth();
|
||||
lp.height = size.getHeight();
|
||||
bbev.setLayoutParams(lp);
|
||||
if (mLayerView.isOnLeft()) {
|
||||
bbev.setX(mPositioner.getInsets().left + padding);
|
||||
} else {
|
||||
bbev.setX(mPositioner.getAvailableRect().width() - width - padding);
|
||||
}
|
||||
bbev.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height);
|
||||
bbev.setX(position.x);
|
||||
bbev.setY(position.y);
|
||||
bbev.updateLocation();
|
||||
bbev.maybeShowOverflow();
|
||||
}
|
||||
|
||||
private Point getExpandedViewRestPosition(Size size) {
|
||||
final int padding = mPositioner.getBubbleBarExpandedViewPadding();
|
||||
Point point = new Point();
|
||||
if (mLayerView.isOnLeft()) {
|
||||
point.x = mPositioner.getInsets().left + padding;
|
||||
} else {
|
||||
point.x = mPositioner.getAvailableRect().width() - size.getWidth() - padding;
|
||||
}
|
||||
point.y = mPositioner.getExpandedViewBottomForBubbleBar() - size.getHeight();
|
||||
return point;
|
||||
}
|
||||
|
||||
private Size getExpandedViewSize() {
|
||||
boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
|
||||
final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded);
|
||||
final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
|
||||
return new Size(width, height);
|
||||
}
|
||||
|
||||
private Point getViewCenterOnScreen(View view) {
|
||||
Point center = new Point();
|
||||
int[] onScreenLocation = view.getLocationOnScreen();
|
||||
center.x = (int) (onScreenLocation[0] + (view.getWidth() / 2f));
|
||||
center.y = (int) (onScreenLocation[1] + (view.getHeight() / 2f));
|
||||
return center;
|
||||
}
|
||||
}
|
||||
|
@ -16,70 +16,67 @@
|
||||
|
||||
package com.android.wm.shell.bubbles.bar
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.graphics.PointF
|
||||
import android.graphics.Rect
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import com.android.wm.shell.animation.Interpolators
|
||||
import com.android.wm.shell.common.bubbles.DismissView
|
||||
import com.android.wm.shell.common.bubbles.RelativeTouchListener
|
||||
import com.android.wm.shell.common.magnetictarget.MagnetizedObject
|
||||
|
||||
/** Controller for handling drag interactions with [BubbleBarExpandedView] */
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
class BubbleBarExpandedViewDragController(
|
||||
private val expandedView: BubbleBarExpandedView,
|
||||
private val dismissView: DismissView,
|
||||
private val animationHelper: BubbleBarAnimationHelper,
|
||||
private val onDismissed: () -> Unit
|
||||
) {
|
||||
|
||||
var isStuckToDismiss: Boolean = false
|
||||
private set
|
||||
|
||||
private var expandedViewInitialTranslationX = 0f
|
||||
private var expandedViewInitialTranslationY = 0f
|
||||
private val magnetizedExpandedView: MagnetizedObject<BubbleBarExpandedView> =
|
||||
MagnetizedObject.magnetizeView(expandedView)
|
||||
private val magnetizedDismissTarget: MagnetizedObject.MagneticTarget
|
||||
|
||||
init {
|
||||
expandedView.handleView.setOnTouchListener(HandleDragListener())
|
||||
}
|
||||
|
||||
private fun finishDrag(x: Float, y: Float, viewInitialX: Float, viewInitialY: Float) {
|
||||
val dismissCircleBounds = Rect().apply { dismissView.circle.getBoundsOnScreen(this) }
|
||||
if (dismissCircleBounds.contains(x.toInt(), y.toInt())) {
|
||||
onDismissed()
|
||||
} else {
|
||||
resetExpandedViewPosition(viewInitialX, viewInitialY)
|
||||
}
|
||||
dismissView.hide()
|
||||
}
|
||||
|
||||
private fun resetExpandedViewPosition(initialX: Float, initialY: Float) {
|
||||
val listener =
|
||||
object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationStart(animation: Animator) {
|
||||
expandedView.isAnimating = true
|
||||
}
|
||||
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
expandedView.isAnimating = false
|
||||
}
|
||||
magnetizedExpandedView.magnetListener = MagnetListener()
|
||||
magnetizedExpandedView.animateStuckToTarget =
|
||||
{
|
||||
target: MagnetizedObject.MagneticTarget,
|
||||
_: Float,
|
||||
_: Float,
|
||||
_: Boolean,
|
||||
after: (() -> Unit)? ->
|
||||
animationHelper.animateIntoTarget(target, after)
|
||||
}
|
||||
expandedView
|
||||
.animate()
|
||||
.translationX(initialX)
|
||||
.translationY(initialY)
|
||||
.setDuration(RESET_POSITION_ANIM_DURATION)
|
||||
.setInterpolator(Interpolators.EMPHASIZED_DECELERATE)
|
||||
.setListener(listener)
|
||||
.start()
|
||||
|
||||
magnetizedDismissTarget =
|
||||
MagnetizedObject.MagneticTarget(dismissView.circle, dismissView.circle.width)
|
||||
magnetizedExpandedView.addTarget(magnetizedDismissTarget)
|
||||
|
||||
val dragMotionEventHandler = HandleDragListener()
|
||||
|
||||
expandedView.handleView.setOnTouchListener { view, event ->
|
||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||
expandedViewInitialTranslationX = expandedView.translationX
|
||||
expandedViewInitialTranslationY = expandedView.translationY
|
||||
}
|
||||
val magnetConsumed = magnetizedExpandedView.maybeConsumeMotionEvent(event)
|
||||
// Move events can be consumed by the magnetized object
|
||||
if (event.actionMasked == MotionEvent.ACTION_MOVE && magnetConsumed) {
|
||||
return@setOnTouchListener true
|
||||
}
|
||||
return@setOnTouchListener dragMotionEventHandler.onTouch(view, event) || magnetConsumed
|
||||
}
|
||||
}
|
||||
|
||||
private inner class HandleDragListener : RelativeTouchListener() {
|
||||
|
||||
private val expandedViewRestPosition = PointF()
|
||||
|
||||
override fun onDown(v: View, ev: MotionEvent): Boolean {
|
||||
// While animating, don't allow new touch events
|
||||
if (expandedView.isAnimating) {
|
||||
return false
|
||||
}
|
||||
expandedViewRestPosition.x = expandedView.translationX
|
||||
expandedViewRestPosition.y = expandedView.translationY
|
||||
return true
|
||||
return !expandedView.isAnimating
|
||||
}
|
||||
|
||||
override fun onMove(
|
||||
@ -90,8 +87,8 @@ class BubbleBarExpandedViewDragController(
|
||||
dx: Float,
|
||||
dy: Float
|
||||
) {
|
||||
expandedView.translationX = expandedViewRestPosition.x + dx
|
||||
expandedView.translationY = expandedViewRestPosition.y + dy
|
||||
expandedView.translationX = expandedViewInitialTranslationX + dx
|
||||
expandedView.translationY = expandedViewInitialTranslationY + dy
|
||||
dismissView.show()
|
||||
}
|
||||
|
||||
@ -105,16 +102,40 @@ class BubbleBarExpandedViewDragController(
|
||||
velX: Float,
|
||||
velY: Float
|
||||
) {
|
||||
finishDrag(ev.rawX, ev.rawY, expandedViewRestPosition.x, expandedViewRestPosition.y)
|
||||
finishDrag()
|
||||
}
|
||||
|
||||
override fun onCancel(v: View, ev: MotionEvent, viewInitialX: Float, viewInitialY: Float) {
|
||||
resetExpandedViewPosition(expandedViewRestPosition.x, expandedViewRestPosition.y)
|
||||
dismissView.hide()
|
||||
finishDrag()
|
||||
}
|
||||
|
||||
private fun finishDrag() {
|
||||
if (!isStuckToDismiss) {
|
||||
animationHelper.animateToRestPosition()
|
||||
dismissView.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val RESET_POSITION_ANIM_DURATION = 300L
|
||||
private inner class MagnetListener : MagnetizedObject.MagnetListener {
|
||||
override fun onStuckToTarget(target: MagnetizedObject.MagneticTarget) {
|
||||
isStuckToDismiss = true
|
||||
}
|
||||
|
||||
override fun onUnstuckFromTarget(
|
||||
target: MagnetizedObject.MagneticTarget,
|
||||
velX: Float,
|
||||
velY: Float,
|
||||
wasFlungOut: Boolean
|
||||
) {
|
||||
isStuckToDismiss = false
|
||||
animationHelper.animateUnstuckFromDismissView()
|
||||
}
|
||||
|
||||
override fun onReleasedInTarget(target: MagnetizedObject.MagneticTarget) {
|
||||
onDismissed()
|
||||
dismissView.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ package com.android.wm.shell.bubbles.bar;
|
||||
|
||||
import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
|
||||
import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
|
||||
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_GESTURE;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
@ -36,7 +37,6 @@ import com.android.wm.shell.bubbles.BubbleController;
|
||||
import com.android.wm.shell.bubbles.BubbleOverflow;
|
||||
import com.android.wm.shell.bubbles.BubblePositioner;
|
||||
import com.android.wm.shell.bubbles.BubbleViewProvider;
|
||||
import com.android.wm.shell.bubbles.Bubbles;
|
||||
import com.android.wm.shell.bubbles.DeviceConfig;
|
||||
import com.android.wm.shell.bubbles.DismissViewUtils;
|
||||
import com.android.wm.shell.common.bubbles.DismissView;
|
||||
@ -206,10 +206,13 @@ public class BubbleBarLayerView extends FrameLayout
|
||||
}
|
||||
});
|
||||
|
||||
mDragController = new BubbleBarExpandedViewDragController(mExpandedView, mDismissView,
|
||||
mDragController = new BubbleBarExpandedViewDragController(
|
||||
mExpandedView,
|
||||
mDismissView,
|
||||
mAnimationHelper,
|
||||
() -> {
|
||||
mBubbleController.dismissBubble(mExpandedBubble.getKey(),
|
||||
Bubbles.DISMISS_USER_GESTURE);
|
||||
DISMISS_USER_GESTURE);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
|
||||
@ -241,7 +244,11 @@ public class BubbleBarLayerView extends FrameLayout
|
||||
mIsExpanded = false;
|
||||
final BubbleBarExpandedView viewToRemove = mExpandedView;
|
||||
mEducationViewController.hideEducation(/* animated = */ true);
|
||||
mAnimationHelper.animateCollapse(() -> removeView(viewToRemove));
|
||||
if (mDragController != null && mDragController.isStuckToDismiss()) {
|
||||
mAnimationHelper.animateDismiss(() -> removeView(viewToRemove));
|
||||
} else {
|
||||
mAnimationHelper.animateCollapse(() -> removeView(viewToRemove));
|
||||
}
|
||||
mBubbleController.getSysuiProxy().onStackExpandChanged(false);
|
||||
mExpandedView = null;
|
||||
mDragController = null;
|
||||
|
Loading…
x
Reference in New Issue
Block a user