Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +35 −37 Original line number Diff line number Diff line Loading @@ -58,7 +58,7 @@ import java.math.RoundingMode; /** * Renders bubbles in a stack and handles animating expanded and collapsed states. */ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.FloatingView { public class BubbleStackView extends FrameLayout { private static final String TAG = "BubbleStackView"; /** Loading Loading @@ -146,8 +146,9 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F mBubbleData = data; mInflater = LayoutInflater.from(context); mTouchHandler = new BubbleTouchHandler(context); mTouchHandler = new BubbleTouchHandler(context, this); setOnTouchListener(mTouchHandler); mInflater = LayoutInflater.from(context); Resources res = getResources(); mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); Loading Loading @@ -199,6 +200,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)); setClipChildren(false); mBubbleContainer.bringToFront(); } @Override Loading Loading @@ -484,7 +487,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F if (shouldExpand) { mBubbleContainer.setController(mExpandedAnimationController); mExpandedAnimationController.expandFromStack( mStackAnimationController.getStackPosition(), () -> { mStackAnimationController.getStackPosition(), () -> { updatePointerPosition(); updateAfter.run(); }); Loading Loading @@ -544,55 +548,49 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F : null; } @Override public void setPosition(float x, float y) { mStackAnimationController.moveFirstBubbleWithStackFollowing(x, y); public PointF getStackPosition() { return mStackAnimationController.getStackPosition(); } @Override public void setPositionX(float x) { // Unsupported, use setPosition(x, y). /** Called when a drag operation on an individual bubble has started. */ public void onBubbleDragStart(View bubble) { mExpandedAnimationController.prepareForBubbleDrag(bubble); } @Override public void setPositionY(float y) { // Unsupported, use setPosition(x, y). /** Called with the coordinates to which an individual bubble has been dragged. */ public void onBubbleDragged(View bubble, float x, float y) { if (!mIsExpanded || mIsAnimating) { return; } @Override public PointF getPosition() { return mStackAnimationController.getStackPosition(); mExpandedAnimationController.dragBubbleOut(bubble, x, y); } /** Called when a drag operation on an individual bubble has started. */ public void onBubbleDragStart(BubbleView bubble) { // TODO: Save position and snap back if not dismissed. /** Called when a drag operation on an individual bubble has finished. */ public void onBubbleDragFinish( View bubble, float x, float y, float velX, float velY, boolean dismissed) { if (!mIsExpanded || mIsAnimating) { return; } /** Called with the coordinates to which an individual bubble has been dragged. */ public void onBubbleDragged(BubbleView bubble, float x, float y) { bubble.setTranslationX(x); bubble.setTranslationY(y); if (dismissed) { mExpandedAnimationController.prepareForDismissalWithVelocity(bubble, velX, velY); } else { mExpandedAnimationController.snapBubbleBack(bubble, velX, velY); } /** Called when a drag operation on an individual bubble has finished. */ public void onBubbleDragFinish(BubbleView bubble, float x, float y, float velX, float velY) { // TODO: Add fling to bottom to dismiss. } void onDragStart() { if (mIsExpanded) { if (mIsExpanded || mIsAnimating) { return; } mStackAnimationController.cancelStackPositionAnimations(); mBubbleContainer.setController(mStackAnimationController); mIsAnimating = false; } void onDragged(float x, float y) { // TODO: We can drag if animating - just need to reroute inflight anims to drag point. if (mIsExpanded) { if (mIsExpanded || mIsAnimating) { return; } Loading Loading @@ -744,9 +742,9 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F private void updatePointerPosition() { if (mExpandedBubble != null) { float pointerPosition = mExpandedBubble.iconView.getPosition().x float pointerPosition = mExpandedBubble.iconView.getTranslationX() + (mExpandedBubble.iconView.getWidth() / 2f); mExpandedBubble.expandedView.setPointerPosition(pointerPosition); mExpandedBubble.expandedView.setPointerPosition((int) pointerPosition); } } Loading @@ -772,7 +770,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places. */ public float getNormalizedXPosition() { return new BigDecimal(getPosition().x / mDisplaySize.x) return new BigDecimal(getStackPosition().x / mDisplaySize.x) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); } Loading @@ -781,7 +779,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places. */ public float getNormalizedYPosition() { return new BigDecimal(getPosition().y / mDisplaySize.y) return new BigDecimal(getStackPosition().y / mDisplaySize.y) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); } Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java +65 −103 Original line number Diff line number Diff line Loading @@ -34,17 +34,16 @@ import com.android.systemui.pip.phone.PipDismissViewController; * dismissing, and flings. */ class BubbleTouchHandler implements View.OnTouchListener { /** Velocity required to dismiss a bubble without dragging it into the dismiss target. */ private static final float DISMISS_MIN_VELOCITY = 4000f; private final PointF mTouchDown = new PointF(); private final PointF mViewPositionOnTouchDown = new PointF(); private final BubbleStackView mStack; private BubbleController mController = Dependency.get(BubbleController.class); private PipDismissViewController mDismissViewController; // The position of the bubble on down event private float mBubbleDownPosX; private float mBubbleDownPosY; // The touch position on down event private float mDownX = -1; private float mDownY = -1; private boolean mMovedEnough; private int mTouchSlopSquared; private VelocityTracker mVelocityTracker; Loading @@ -58,65 +57,42 @@ class BubbleTouchHandler implements View.OnTouchListener { } }; // Bubble being dragged from the row of bubbles when the stack is expanded private BubbleView mBubbleDraggingOut; /** * Views movable by this touch handler should implement this interface. */ public interface FloatingView { /** * Sets the position of the view. */ void setPosition(float x, float y); /** * Sets the x position of the view. */ void setPositionX(float x); /** * Sets the y position of the view. */ void setPositionY(float y); /** * @return the position of the view. */ PointF getPosition(); } /** View that was initially touched, when we received the first ACTION_DOWN event. */ private View mTouchedView; public BubbleTouchHandler(Context context) { BubbleTouchHandler(Context context, BubbleStackView stackView) { final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mTouchSlopSquared = touchSlop * touchSlop; mDismissViewController = new PipDismissViewController(context); mStack = stackView; } @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getActionMasked(); BubbleStackView stack = (BubbleStackView) v; View targetView = mBubbleDraggingOut != null ? mBubbleDraggingOut : stack.getTargetView(event); boolean isFloating = targetView instanceof FloatingView; if (!isFloating || targetView == null || action == MotionEvent.ACTION_OUTSIDE) { stack.collapseStack(); final int action = event.getActionMasked(); // If we aren't currently in the process of touching a view, figure out what we're touching. // It'll be the stack, an individual bubble, or nothing. if (mTouchedView == null) { mTouchedView = mStack.getTargetView(event); } // If this is an ACTION_OUTSIDE event, or the stack reported that we aren't touching // anything, collapse the stack. if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) { mStack.collapseStack(); cleanUpDismissTarget(); resetTouches(); mTouchedView = null; return false; } FloatingView floatingView = (FloatingView) targetView; boolean isBubbleStack = floatingView instanceof BubbleStackView; final boolean isStack = mStack.equals(mTouchedView); final float rawX = event.getRawX(); final float rawY = event.getRawY(); PointF startPos = floatingView.getPosition(); float rawX = event.getRawX(); float rawY = event.getRawY(); float x = mBubbleDownPosX + rawX - mDownX; float y = mBubbleDownPosY + rawY - mDownY; // The coordinates of the touch event, in terms of the touched view's position. final float viewX = mViewPositionOnTouchDown.x + rawX - mTouchDown.x; final float viewY = mViewPositionOnTouchDown.y + rawY - mTouchDown.y; switch (action) { case MotionEvent.ACTION_DOWN: trackMovement(event); Loading @@ -124,87 +100,83 @@ class BubbleTouchHandler implements View.OnTouchListener { mDismissViewController.createDismissTarget(); mHandler.postDelayed(mShowDismissAffordance, SHOW_TARGET_DELAY); mBubbleDownPosX = startPos.x; mBubbleDownPosY = startPos.y; mDownX = rawX; mDownY = rawY; mMovedEnough = false; mTouchDown.set(rawX, rawY); if (isBubbleStack) { stack.onDragStart(); if (isStack) { mViewPositionOnTouchDown.set(mStack.getStackPosition()); mStack.onDragStart(); } else { stack.onBubbleDragStart((BubbleView) floatingView); mViewPositionOnTouchDown.set( mTouchedView.getTranslationX(), mTouchedView.getTranslationY()); mStack.onBubbleDragStart(mTouchedView); } break; case MotionEvent.ACTION_MOVE: trackMovement(event); final float deltaX = rawX - mTouchDown.x; final float deltaY = rawY - mTouchDown.y; if (mBubbleDownPosX == -1 || mDownX == -1) { mBubbleDownPosX = startPos.x; mBubbleDownPosY = startPos.y; mDownX = rawX; mDownY = rawY; } final float deltaX = rawX - mDownX; final float deltaY = rawY - mDownY; if ((deltaX * deltaX) + (deltaY * deltaY) > mTouchSlopSquared && !mMovedEnough) { mMovedEnough = true; } if (mMovedEnough) { if (floatingView instanceof BubbleView) { mBubbleDraggingOut = ((BubbleView) floatingView); stack.onBubbleDragged(mBubbleDraggingOut, x, y); if (isStack) { mStack.onDragged(viewX, viewY); } else { stack.onDragged(x, y); mStack.onBubbleDragged(mTouchedView, viewX, viewY); } } // TODO - when we're in the target stick to it / animate in some way? mInDismissTarget = mDismissViewController.updateTarget( isBubbleStack ? stack.getBubbleAt(0) : (View) floatingView); isStack ? mStack.getBubbleAt(0) : mTouchedView); break; case MotionEvent.ACTION_CANCEL: resetTouches(); mTouchedView = null; cleanUpDismissTarget(); break; case MotionEvent.ACTION_UP: trackMovement(event); if (mInDismissTarget) { if (isBubbleStack) { if (mInDismissTarget && isStack) { mController.dismissStack(); } else { mController.removeBubble(((BubbleView) floatingView).getKey()); } } else if (mMovedEnough) { mVelocityTracker.computeCurrentVelocity(1000); final float velX = mVelocityTracker.getXVelocity(); final float velY = mVelocityTracker.getYVelocity(); if (isBubbleStack) { stack.onDragFinish(x, y, velX, velY); if (isStack) { mStack.onDragFinish(viewX, viewY, velX, velY); } else { stack.onBubbleDragFinish(mBubbleDraggingOut, x, y, velX, velY); } } else if (floatingView.equals(stack.getExpandedBubbleView())) { stack.collapseStack(); } else if (isBubbleStack) { if (stack.isExpanded()) { stack.collapseStack(); final boolean dismissed = mInDismissTarget || velY > DISMISS_MIN_VELOCITY; mStack.onBubbleDragFinish( mTouchedView, viewX, viewY, velX, velY, /* dismissed */ dismissed); if (dismissed) { mController.removeBubble(((BubbleView) mTouchedView).getKey()); } } } else if (mTouchedView.equals(mStack.getExpandedBubbleView())) { mStack.collapseStack(); } else if (isStack) { if (mStack.isExpanded()) { mStack.collapseStack(); } else { stack.expandStack(); mStack.expandStack(); } } else { stack.setExpandedBubble(((BubbleView) floatingView).getKey()); mStack.setExpandedBubble(((BubbleView) mTouchedView).getKey()); } cleanUpDismissTarget(); mVelocityTracker.recycle(); mVelocityTracker = null; resetTouches(); mTouchedView = null; mMovedEnough = false; break; } return true; } Loading @@ -216,16 +188,6 @@ class BubbleTouchHandler implements View.OnTouchListener { mDismissViewController.destroyDismissTarget(); } /** * Resets anything we care about after a gesture is complete. */ private void resetTouches() { mDownX = -1; mDownY = -1; mBubbleDownPosX = -1; mBubbleDownPosY = -1; mBubbleDraggingOut = null; } private void trackMovement(MotionEvent event) { if (mVelocityTracker == null) { Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java +1 −23 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.graphics.Color; import android.graphics.PointF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; Loading @@ -39,7 +38,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow /** * A floating object on the screen that can post message updates. */ public class BubbleView extends FrameLayout implements BubbleTouchHandler.FloatingView { public class BubbleView extends FrameLayout { private static final String TAG = "BubbleView"; // Same value as Launcher3 badge code Loading Loading @@ -217,25 +216,4 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati // XXX: should we pull from the drawable, app icon, notif tint? return ColorUtils.blendARGB(defaultTint, Color.WHITE, WHITE_SCRIM_ALPHA); } @Override public void setPosition(float x, float y) { setPositionX(x); setPositionY(y); } @Override public void setPositionX(float x) { setTranslationX(x); } @Override public void setPositionY(float y) { setTranslationY(y); } @Override public PointF getPosition() { return new PointF(getTranslationX(), getTranslationY()); } } packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +146 −14 Original line number Diff line number Diff line Loading @@ -44,6 +44,9 @@ public class ExpandedAnimationController */ private static final int ANIMATE_TRANSLATION_FACTOR = 4; /** How much to scale down bubbles when they're animating in/out. */ private static final float ANIMATE_SCALE_PERCENT = 0.5f; /** * The stack position from which the bubbles were expanded. Saved in {@link #expandFromStack} * and used to return to stack form in {@link #collapseBackToStack}. Loading @@ -59,6 +62,22 @@ public class ExpandedAnimationController /** Height of the status bar. */ private float mStatusBarHeight; /** * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause * the rest of the bubbles to animate to fill the gap. */ private boolean mBubbleDraggedOutEnough = false; /** The bubble currently being dragged out of the row (to potentially be dismissed). */ private View mBubbleDraggingOut; /** * Drag velocities for the dragging-out bubble when the drag finished. These are used by * {@link #onChildRemoved} to animate out the bubble while respecting touch velocity. */ private float mBubbleDraggingOutVelX; private float mBubbleDraggingOutVelY; @Override protected void setLayout(PhysicsAnimationLayout layout) { super.setLayout(layout); Loading Loading @@ -102,6 +121,87 @@ public class ExpandedAnimationController runAfterTranslationsEnd(after); } /** Prepares the given bubble to be dragged out. */ public void prepareForBubbleDrag(View bubble) { mLayout.cancelAnimationsOnView(bubble); mBubbleDraggingOut = bubble; mBubbleDraggingOut.setTranslationZ(Short.MAX_VALUE); } /** * Drags an individual bubble to the given coordinates. Bubbles to the right will animate to * take its place once it's dragged out of the row of bubbles, and animate out of the way if the * bubble is dragged back into the row. */ public void dragBubbleOut(View bubbleView, float x, float y) { bubbleView.setTranslationX(x); bubbleView.setTranslationY(y); final boolean draggedOutEnough = y > getExpandedY() + mBubbleSizePx || y < getExpandedY() - mBubbleSizePx; if (draggedOutEnough != mBubbleDraggedOutEnough) { animateStackByBubbleWidthsStartingFrom( /* numBubbleWidths */ draggedOutEnough ? -1 : 0, /* startIndex */ mLayout.indexOfChild(bubbleView) + 1); mBubbleDraggedOutEnough = draggedOutEnough; } } /** * Snaps a bubble back to its position within the bubble row, and animates the rest of the * bubbles to accommodate it if it was previously dragged out past the threshold. */ public void snapBubbleBack(View bubbleView, float velX, float velY) { final int index = mLayout.indexOfChild(bubbleView); // Snap the bubble back, respecting its current velocity. mLayout.animateValueForChildAtIndex( DynamicAnimation.TRANSLATION_X, index, getXForChildAtIndex(index), velX); mLayout.animateValueForChildAtIndex( DynamicAnimation.TRANSLATION_Y, index, getExpandedY(), velY); mLayout.setEndListenerForProperties( mLayout.new OneTimeMultiplePropertyEndListener() { @Override void onAllAnimationsForPropertiesEnd() { // Reset Z translation once the bubble is done snapping back. bubbleView.setTranslationZ(0f); } }, DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); animateStackByBubbleWidthsStartingFrom( /* numBubbleWidths */ 0, /* startIndex */ index + 1); mBubbleDraggingOut = null; mBubbleDraggedOutEnough = false; } /** * Sets configuration variables so that when the given bubble is removed, the animations are * started with the given velocities. */ public void prepareForDismissalWithVelocity(View bubbleView, float velX, float velY) { mBubbleDraggingOut = bubbleView; mBubbleDraggingOutVelX = velX; mBubbleDraggingOutVelY = velY; mBubbleDraggedOutEnough = false; } /** * Animates the bubbles, starting at the given index, to the left or right by the given number * of bubble widths. Passing zero for numBubbleWidths will animate the bubbles to their normal * positions. */ private void animateStackByBubbleWidthsStartingFrom(int numBubbleWidths, int startIndex) { for (int i = startIndex; i < mLayout.getChildCount(); i++) { mLayout.animateValueForChildAtIndex( DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i + numBubbleWidths)); } } /** The Y value of the row of expanded bubbles. */ private float getExpandedY() { final WindowInsets insets = mLayout.getRootWindowInsets(); Loading Loading @@ -165,12 +265,7 @@ public class ExpandedAnimationController child.setTranslationX(getXForChildAtIndex(index)); child.setTranslationY(getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR); mLayout.animateValueForChild(DynamicAnimation.TRANSLATION_Y, child, getExpandedY()); // Animate the remaining bubbles to the correct X position. for (int i = index + 1; i < mLayout.getChildCount(); i++) { mLayout.animateValueForChildAtIndex( DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i)); } animateBubblesAfterIndexToCorrectX(index); } @Override Loading @@ -179,16 +274,36 @@ public class ExpandedAnimationController // TODO: Reverse this when bubbles are at the bottom. mLayout.animateValueForChild( DynamicAnimation.ALPHA, child, 0f, finishRemoval); // If we're removing the dragged-out bubble, that means it got dismissed. if (child.equals(mBubbleDraggingOut)) { // Throw it to the bottom of the screen, towards the center horizontally. mLayout.animateValueForChild( DynamicAnimation.TRANSLATION_X, child, mLayout.getWidth() / 2f - mBubbleSizePx / 2f, mBubbleDraggingOutVelX); mLayout.animateValueForChild( DynamicAnimation.TRANSLATION_Y, child, getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR); mLayout.getHeight() + mBubbleSizePx, mBubbleDraggingOutVelY); // Animate the remaining bubbles to the correct X position. for (int i = index; i < mLayout.getChildCount(); i++) { mLayout.animateValueForChildAtIndex( DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i)); // Scale it down a bit so it looks like it's disappearing. mLayout.animateValueForChild(DynamicAnimation.SCALE_X, child, ANIMATE_SCALE_PERCENT); mLayout.animateValueForChild(DynamicAnimation.SCALE_Y, child, ANIMATE_SCALE_PERCENT); mBubbleDraggingOut = null; } else { // If we're removing some random bubble just throw it off the top. mLayout.animateValueForChild( DynamicAnimation.TRANSLATION_Y, child, getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR); } // Animate all the other bubbles to their new positions sans this bubble. animateBubblesAfterIndexToCorrectX(index); } @Override Loading @@ -207,6 +322,23 @@ public class ExpandedAnimationController () -> super.setChildVisibility(child, index, visibility)); } /** * Animates the bubbles after the given index to the X position they should be in according to * {@link #getXForChildAtIndex}. */ private void animateBubblesAfterIndexToCorrectX(int start) { for (int i = start; i < mLayout.getChildCount(); i++) { final View bubble = mLayout.getChildAt(i); // Don't animate the dragging out bubble, or it'll jump around while being dragged. It // will be snapped to the correct X value after the drag (if it's not dismissed). if (!bubble.equals(mBubbleDraggingOut)) { mLayout.animateValueForChild( DynamicAnimation.TRANSLATION_X, bubble, getXForChildAtIndex(i)); } } } /** Returns the appropriate X translation value for a bubble at the given index. */ private float getXForChildAtIndex(int index) { return mBubblePaddingPx + (mBubbleSizePx + mBubblePaddingPx) * index; Loading packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java +72 −2 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +35 −37 Original line number Diff line number Diff line Loading @@ -58,7 +58,7 @@ import java.math.RoundingMode; /** * Renders bubbles in a stack and handles animating expanded and collapsed states. */ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.FloatingView { public class BubbleStackView extends FrameLayout { private static final String TAG = "BubbleStackView"; /** Loading Loading @@ -146,8 +146,9 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F mBubbleData = data; mInflater = LayoutInflater.from(context); mTouchHandler = new BubbleTouchHandler(context); mTouchHandler = new BubbleTouchHandler(context, this); setOnTouchListener(mTouchHandler); mInflater = LayoutInflater.from(context); Resources res = getResources(); mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); Loading Loading @@ -199,6 +200,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)); setClipChildren(false); mBubbleContainer.bringToFront(); } @Override Loading Loading @@ -484,7 +487,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F if (shouldExpand) { mBubbleContainer.setController(mExpandedAnimationController); mExpandedAnimationController.expandFromStack( mStackAnimationController.getStackPosition(), () -> { mStackAnimationController.getStackPosition(), () -> { updatePointerPosition(); updateAfter.run(); }); Loading Loading @@ -544,55 +548,49 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F : null; } @Override public void setPosition(float x, float y) { mStackAnimationController.moveFirstBubbleWithStackFollowing(x, y); public PointF getStackPosition() { return mStackAnimationController.getStackPosition(); } @Override public void setPositionX(float x) { // Unsupported, use setPosition(x, y). /** Called when a drag operation on an individual bubble has started. */ public void onBubbleDragStart(View bubble) { mExpandedAnimationController.prepareForBubbleDrag(bubble); } @Override public void setPositionY(float y) { // Unsupported, use setPosition(x, y). /** Called with the coordinates to which an individual bubble has been dragged. */ public void onBubbleDragged(View bubble, float x, float y) { if (!mIsExpanded || mIsAnimating) { return; } @Override public PointF getPosition() { return mStackAnimationController.getStackPosition(); mExpandedAnimationController.dragBubbleOut(bubble, x, y); } /** Called when a drag operation on an individual bubble has started. */ public void onBubbleDragStart(BubbleView bubble) { // TODO: Save position and snap back if not dismissed. /** Called when a drag operation on an individual bubble has finished. */ public void onBubbleDragFinish( View bubble, float x, float y, float velX, float velY, boolean dismissed) { if (!mIsExpanded || mIsAnimating) { return; } /** Called with the coordinates to which an individual bubble has been dragged. */ public void onBubbleDragged(BubbleView bubble, float x, float y) { bubble.setTranslationX(x); bubble.setTranslationY(y); if (dismissed) { mExpandedAnimationController.prepareForDismissalWithVelocity(bubble, velX, velY); } else { mExpandedAnimationController.snapBubbleBack(bubble, velX, velY); } /** Called when a drag operation on an individual bubble has finished. */ public void onBubbleDragFinish(BubbleView bubble, float x, float y, float velX, float velY) { // TODO: Add fling to bottom to dismiss. } void onDragStart() { if (mIsExpanded) { if (mIsExpanded || mIsAnimating) { return; } mStackAnimationController.cancelStackPositionAnimations(); mBubbleContainer.setController(mStackAnimationController); mIsAnimating = false; } void onDragged(float x, float y) { // TODO: We can drag if animating - just need to reroute inflight anims to drag point. if (mIsExpanded) { if (mIsExpanded || mIsAnimating) { return; } Loading Loading @@ -744,9 +742,9 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F private void updatePointerPosition() { if (mExpandedBubble != null) { float pointerPosition = mExpandedBubble.iconView.getPosition().x float pointerPosition = mExpandedBubble.iconView.getTranslationX() + (mExpandedBubble.iconView.getWidth() / 2f); mExpandedBubble.expandedView.setPointerPosition(pointerPosition); mExpandedBubble.expandedView.setPointerPosition((int) pointerPosition); } } Loading @@ -772,7 +770,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places. */ public float getNormalizedXPosition() { return new BigDecimal(getPosition().x / mDisplaySize.x) return new BigDecimal(getStackPosition().x / mDisplaySize.x) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); } Loading @@ -781,7 +779,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places. */ public float getNormalizedYPosition() { return new BigDecimal(getPosition().y / mDisplaySize.y) return new BigDecimal(getStackPosition().y / mDisplaySize.y) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); } Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java +65 −103 Original line number Diff line number Diff line Loading @@ -34,17 +34,16 @@ import com.android.systemui.pip.phone.PipDismissViewController; * dismissing, and flings. */ class BubbleTouchHandler implements View.OnTouchListener { /** Velocity required to dismiss a bubble without dragging it into the dismiss target. */ private static final float DISMISS_MIN_VELOCITY = 4000f; private final PointF mTouchDown = new PointF(); private final PointF mViewPositionOnTouchDown = new PointF(); private final BubbleStackView mStack; private BubbleController mController = Dependency.get(BubbleController.class); private PipDismissViewController mDismissViewController; // The position of the bubble on down event private float mBubbleDownPosX; private float mBubbleDownPosY; // The touch position on down event private float mDownX = -1; private float mDownY = -1; private boolean mMovedEnough; private int mTouchSlopSquared; private VelocityTracker mVelocityTracker; Loading @@ -58,65 +57,42 @@ class BubbleTouchHandler implements View.OnTouchListener { } }; // Bubble being dragged from the row of bubbles when the stack is expanded private BubbleView mBubbleDraggingOut; /** * Views movable by this touch handler should implement this interface. */ public interface FloatingView { /** * Sets the position of the view. */ void setPosition(float x, float y); /** * Sets the x position of the view. */ void setPositionX(float x); /** * Sets the y position of the view. */ void setPositionY(float y); /** * @return the position of the view. */ PointF getPosition(); } /** View that was initially touched, when we received the first ACTION_DOWN event. */ private View mTouchedView; public BubbleTouchHandler(Context context) { BubbleTouchHandler(Context context, BubbleStackView stackView) { final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mTouchSlopSquared = touchSlop * touchSlop; mDismissViewController = new PipDismissViewController(context); mStack = stackView; } @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getActionMasked(); BubbleStackView stack = (BubbleStackView) v; View targetView = mBubbleDraggingOut != null ? mBubbleDraggingOut : stack.getTargetView(event); boolean isFloating = targetView instanceof FloatingView; if (!isFloating || targetView == null || action == MotionEvent.ACTION_OUTSIDE) { stack.collapseStack(); final int action = event.getActionMasked(); // If we aren't currently in the process of touching a view, figure out what we're touching. // It'll be the stack, an individual bubble, or nothing. if (mTouchedView == null) { mTouchedView = mStack.getTargetView(event); } // If this is an ACTION_OUTSIDE event, or the stack reported that we aren't touching // anything, collapse the stack. if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) { mStack.collapseStack(); cleanUpDismissTarget(); resetTouches(); mTouchedView = null; return false; } FloatingView floatingView = (FloatingView) targetView; boolean isBubbleStack = floatingView instanceof BubbleStackView; final boolean isStack = mStack.equals(mTouchedView); final float rawX = event.getRawX(); final float rawY = event.getRawY(); PointF startPos = floatingView.getPosition(); float rawX = event.getRawX(); float rawY = event.getRawY(); float x = mBubbleDownPosX + rawX - mDownX; float y = mBubbleDownPosY + rawY - mDownY; // The coordinates of the touch event, in terms of the touched view's position. final float viewX = mViewPositionOnTouchDown.x + rawX - mTouchDown.x; final float viewY = mViewPositionOnTouchDown.y + rawY - mTouchDown.y; switch (action) { case MotionEvent.ACTION_DOWN: trackMovement(event); Loading @@ -124,87 +100,83 @@ class BubbleTouchHandler implements View.OnTouchListener { mDismissViewController.createDismissTarget(); mHandler.postDelayed(mShowDismissAffordance, SHOW_TARGET_DELAY); mBubbleDownPosX = startPos.x; mBubbleDownPosY = startPos.y; mDownX = rawX; mDownY = rawY; mMovedEnough = false; mTouchDown.set(rawX, rawY); if (isBubbleStack) { stack.onDragStart(); if (isStack) { mViewPositionOnTouchDown.set(mStack.getStackPosition()); mStack.onDragStart(); } else { stack.onBubbleDragStart((BubbleView) floatingView); mViewPositionOnTouchDown.set( mTouchedView.getTranslationX(), mTouchedView.getTranslationY()); mStack.onBubbleDragStart(mTouchedView); } break; case MotionEvent.ACTION_MOVE: trackMovement(event); final float deltaX = rawX - mTouchDown.x; final float deltaY = rawY - mTouchDown.y; if (mBubbleDownPosX == -1 || mDownX == -1) { mBubbleDownPosX = startPos.x; mBubbleDownPosY = startPos.y; mDownX = rawX; mDownY = rawY; } final float deltaX = rawX - mDownX; final float deltaY = rawY - mDownY; if ((deltaX * deltaX) + (deltaY * deltaY) > mTouchSlopSquared && !mMovedEnough) { mMovedEnough = true; } if (mMovedEnough) { if (floatingView instanceof BubbleView) { mBubbleDraggingOut = ((BubbleView) floatingView); stack.onBubbleDragged(mBubbleDraggingOut, x, y); if (isStack) { mStack.onDragged(viewX, viewY); } else { stack.onDragged(x, y); mStack.onBubbleDragged(mTouchedView, viewX, viewY); } } // TODO - when we're in the target stick to it / animate in some way? mInDismissTarget = mDismissViewController.updateTarget( isBubbleStack ? stack.getBubbleAt(0) : (View) floatingView); isStack ? mStack.getBubbleAt(0) : mTouchedView); break; case MotionEvent.ACTION_CANCEL: resetTouches(); mTouchedView = null; cleanUpDismissTarget(); break; case MotionEvent.ACTION_UP: trackMovement(event); if (mInDismissTarget) { if (isBubbleStack) { if (mInDismissTarget && isStack) { mController.dismissStack(); } else { mController.removeBubble(((BubbleView) floatingView).getKey()); } } else if (mMovedEnough) { mVelocityTracker.computeCurrentVelocity(1000); final float velX = mVelocityTracker.getXVelocity(); final float velY = mVelocityTracker.getYVelocity(); if (isBubbleStack) { stack.onDragFinish(x, y, velX, velY); if (isStack) { mStack.onDragFinish(viewX, viewY, velX, velY); } else { stack.onBubbleDragFinish(mBubbleDraggingOut, x, y, velX, velY); } } else if (floatingView.equals(stack.getExpandedBubbleView())) { stack.collapseStack(); } else if (isBubbleStack) { if (stack.isExpanded()) { stack.collapseStack(); final boolean dismissed = mInDismissTarget || velY > DISMISS_MIN_VELOCITY; mStack.onBubbleDragFinish( mTouchedView, viewX, viewY, velX, velY, /* dismissed */ dismissed); if (dismissed) { mController.removeBubble(((BubbleView) mTouchedView).getKey()); } } } else if (mTouchedView.equals(mStack.getExpandedBubbleView())) { mStack.collapseStack(); } else if (isStack) { if (mStack.isExpanded()) { mStack.collapseStack(); } else { stack.expandStack(); mStack.expandStack(); } } else { stack.setExpandedBubble(((BubbleView) floatingView).getKey()); mStack.setExpandedBubble(((BubbleView) mTouchedView).getKey()); } cleanUpDismissTarget(); mVelocityTracker.recycle(); mVelocityTracker = null; resetTouches(); mTouchedView = null; mMovedEnough = false; break; } return true; } Loading @@ -216,16 +188,6 @@ class BubbleTouchHandler implements View.OnTouchListener { mDismissViewController.destroyDismissTarget(); } /** * Resets anything we care about after a gesture is complete. */ private void resetTouches() { mDownX = -1; mDownY = -1; mBubbleDownPosX = -1; mBubbleDownPosY = -1; mBubbleDraggingOut = null; } private void trackMovement(MotionEvent event) { if (mVelocityTracker == null) { Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java +1 −23 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.graphics.Color; import android.graphics.PointF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; Loading @@ -39,7 +38,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow /** * A floating object on the screen that can post message updates. */ public class BubbleView extends FrameLayout implements BubbleTouchHandler.FloatingView { public class BubbleView extends FrameLayout { private static final String TAG = "BubbleView"; // Same value as Launcher3 badge code Loading Loading @@ -217,25 +216,4 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati // XXX: should we pull from the drawable, app icon, notif tint? return ColorUtils.blendARGB(defaultTint, Color.WHITE, WHITE_SCRIM_ALPHA); } @Override public void setPosition(float x, float y) { setPositionX(x); setPositionY(y); } @Override public void setPositionX(float x) { setTranslationX(x); } @Override public void setPositionY(float y) { setTranslationY(y); } @Override public PointF getPosition() { return new PointF(getTranslationX(), getTranslationY()); } }
packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +146 −14 Original line number Diff line number Diff line Loading @@ -44,6 +44,9 @@ public class ExpandedAnimationController */ private static final int ANIMATE_TRANSLATION_FACTOR = 4; /** How much to scale down bubbles when they're animating in/out. */ private static final float ANIMATE_SCALE_PERCENT = 0.5f; /** * The stack position from which the bubbles were expanded. Saved in {@link #expandFromStack} * and used to return to stack form in {@link #collapseBackToStack}. Loading @@ -59,6 +62,22 @@ public class ExpandedAnimationController /** Height of the status bar. */ private float mStatusBarHeight; /** * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause * the rest of the bubbles to animate to fill the gap. */ private boolean mBubbleDraggedOutEnough = false; /** The bubble currently being dragged out of the row (to potentially be dismissed). */ private View mBubbleDraggingOut; /** * Drag velocities for the dragging-out bubble when the drag finished. These are used by * {@link #onChildRemoved} to animate out the bubble while respecting touch velocity. */ private float mBubbleDraggingOutVelX; private float mBubbleDraggingOutVelY; @Override protected void setLayout(PhysicsAnimationLayout layout) { super.setLayout(layout); Loading Loading @@ -102,6 +121,87 @@ public class ExpandedAnimationController runAfterTranslationsEnd(after); } /** Prepares the given bubble to be dragged out. */ public void prepareForBubbleDrag(View bubble) { mLayout.cancelAnimationsOnView(bubble); mBubbleDraggingOut = bubble; mBubbleDraggingOut.setTranslationZ(Short.MAX_VALUE); } /** * Drags an individual bubble to the given coordinates. Bubbles to the right will animate to * take its place once it's dragged out of the row of bubbles, and animate out of the way if the * bubble is dragged back into the row. */ public void dragBubbleOut(View bubbleView, float x, float y) { bubbleView.setTranslationX(x); bubbleView.setTranslationY(y); final boolean draggedOutEnough = y > getExpandedY() + mBubbleSizePx || y < getExpandedY() - mBubbleSizePx; if (draggedOutEnough != mBubbleDraggedOutEnough) { animateStackByBubbleWidthsStartingFrom( /* numBubbleWidths */ draggedOutEnough ? -1 : 0, /* startIndex */ mLayout.indexOfChild(bubbleView) + 1); mBubbleDraggedOutEnough = draggedOutEnough; } } /** * Snaps a bubble back to its position within the bubble row, and animates the rest of the * bubbles to accommodate it if it was previously dragged out past the threshold. */ public void snapBubbleBack(View bubbleView, float velX, float velY) { final int index = mLayout.indexOfChild(bubbleView); // Snap the bubble back, respecting its current velocity. mLayout.animateValueForChildAtIndex( DynamicAnimation.TRANSLATION_X, index, getXForChildAtIndex(index), velX); mLayout.animateValueForChildAtIndex( DynamicAnimation.TRANSLATION_Y, index, getExpandedY(), velY); mLayout.setEndListenerForProperties( mLayout.new OneTimeMultiplePropertyEndListener() { @Override void onAllAnimationsForPropertiesEnd() { // Reset Z translation once the bubble is done snapping back. bubbleView.setTranslationZ(0f); } }, DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); animateStackByBubbleWidthsStartingFrom( /* numBubbleWidths */ 0, /* startIndex */ index + 1); mBubbleDraggingOut = null; mBubbleDraggedOutEnough = false; } /** * Sets configuration variables so that when the given bubble is removed, the animations are * started with the given velocities. */ public void prepareForDismissalWithVelocity(View bubbleView, float velX, float velY) { mBubbleDraggingOut = bubbleView; mBubbleDraggingOutVelX = velX; mBubbleDraggingOutVelY = velY; mBubbleDraggedOutEnough = false; } /** * Animates the bubbles, starting at the given index, to the left or right by the given number * of bubble widths. Passing zero for numBubbleWidths will animate the bubbles to their normal * positions. */ private void animateStackByBubbleWidthsStartingFrom(int numBubbleWidths, int startIndex) { for (int i = startIndex; i < mLayout.getChildCount(); i++) { mLayout.animateValueForChildAtIndex( DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i + numBubbleWidths)); } } /** The Y value of the row of expanded bubbles. */ private float getExpandedY() { final WindowInsets insets = mLayout.getRootWindowInsets(); Loading Loading @@ -165,12 +265,7 @@ public class ExpandedAnimationController child.setTranslationX(getXForChildAtIndex(index)); child.setTranslationY(getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR); mLayout.animateValueForChild(DynamicAnimation.TRANSLATION_Y, child, getExpandedY()); // Animate the remaining bubbles to the correct X position. for (int i = index + 1; i < mLayout.getChildCount(); i++) { mLayout.animateValueForChildAtIndex( DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i)); } animateBubblesAfterIndexToCorrectX(index); } @Override Loading @@ -179,16 +274,36 @@ public class ExpandedAnimationController // TODO: Reverse this when bubbles are at the bottom. mLayout.animateValueForChild( DynamicAnimation.ALPHA, child, 0f, finishRemoval); // If we're removing the dragged-out bubble, that means it got dismissed. if (child.equals(mBubbleDraggingOut)) { // Throw it to the bottom of the screen, towards the center horizontally. mLayout.animateValueForChild( DynamicAnimation.TRANSLATION_X, child, mLayout.getWidth() / 2f - mBubbleSizePx / 2f, mBubbleDraggingOutVelX); mLayout.animateValueForChild( DynamicAnimation.TRANSLATION_Y, child, getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR); mLayout.getHeight() + mBubbleSizePx, mBubbleDraggingOutVelY); // Animate the remaining bubbles to the correct X position. for (int i = index; i < mLayout.getChildCount(); i++) { mLayout.animateValueForChildAtIndex( DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i)); // Scale it down a bit so it looks like it's disappearing. mLayout.animateValueForChild(DynamicAnimation.SCALE_X, child, ANIMATE_SCALE_PERCENT); mLayout.animateValueForChild(DynamicAnimation.SCALE_Y, child, ANIMATE_SCALE_PERCENT); mBubbleDraggingOut = null; } else { // If we're removing some random bubble just throw it off the top. mLayout.animateValueForChild( DynamicAnimation.TRANSLATION_Y, child, getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR); } // Animate all the other bubbles to their new positions sans this bubble. animateBubblesAfterIndexToCorrectX(index); } @Override Loading @@ -207,6 +322,23 @@ public class ExpandedAnimationController () -> super.setChildVisibility(child, index, visibility)); } /** * Animates the bubbles after the given index to the X position they should be in according to * {@link #getXForChildAtIndex}. */ private void animateBubblesAfterIndexToCorrectX(int start) { for (int i = start; i < mLayout.getChildCount(); i++) { final View bubble = mLayout.getChildAt(i); // Don't animate the dragging out bubble, or it'll jump around while being dragged. It // will be snapped to the correct X value after the drag (if it's not dismissed). if (!bubble.equals(mBubbleDraggingOut)) { mLayout.animateValueForChild( DynamicAnimation.TRANSLATION_X, bubble, getXForChildAtIndex(i)); } } } /** Returns the appropriate X translation value for a bubble at the given index. */ private float getXForChildAtIndex(int index) { return mBubblePaddingPx + (mBubbleSizePx + mBubblePaddingPx) * index; Loading
packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java +72 −2 File changed.Preview size limit exceeded, changes collapsed. Show changes