diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index c63389a47d2da..771df2d86110b 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -330,9 +330,7 @@ public class BubbleStackView extends FrameLayout { mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER; mBubbleContainer = new PhysicsAnimationLayout(context); - mBubbleContainer.setMaxRenderedChildren( - getResources().getInteger(R.integer.bubbles_max_rendered)); - mBubbleContainer.setController(mStackAnimationController); + mBubbleContainer.setActiveController(mStackAnimationController); mBubbleContainer.setElevation(elevation); mBubbleContainer.setClipChildren(false); addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); @@ -728,7 +726,7 @@ public class BubbleStackView extends FrameLayout { public void updateBubbleOrder(List bubbles) { for (int i = 0; i < bubbles.size(); i++) { Bubble bubble = bubbles.get(i); - mBubbleContainer.moveViewTo(bubble.iconView, i); + mBubbleContainer.reorderView(bubble.iconView, i); } } @@ -908,19 +906,17 @@ public class BubbleStackView extends FrameLayout { }; if (shouldExpand) { - mBubbleContainer.setController(mExpandedAnimationController); - mExpandedAnimationController.expandFromStack( - mStackAnimationController.getStackPositionAlongNearestHorizontalEdge() - /* collapseTo */, - () -> { - updatePointerPosition(); - updateAfter.run(); - } /* after */); + mBubbleContainer.setActiveController(mExpandedAnimationController); + mExpandedAnimationController.expandFromStack(() -> { + updatePointerPosition(); + updateAfter.run(); + } /* after */); } else { mBubbleContainer.cancelAllAnimations(); mExpandedAnimationController.collapseBackToStack( + mStackAnimationController.getStackPositionAlongNearestHorizontalEdge(), () -> { - mBubbleContainer.setController(mStackAnimationController); + mBubbleContainer.setActiveController(mStackAnimationController); updateAfter.run(); }); } @@ -1013,7 +1009,7 @@ public class BubbleStackView extends FrameLayout { } mStackAnimationController.cancelStackPositionAnimations(); - mBubbleContainer.setController(mStackAnimationController); + mBubbleContainer.setActiveController(mStackAnimationController); hideFlyoutImmediate(); mDraggingInDismissTarget = false; @@ -1111,6 +1107,10 @@ public class BubbleStackView extends FrameLayout { /** Called when a gesture is completed or cancelled. */ void onGestureFinished() { mIsGestureInProgress = false; + + if (mIsExpanded) { + mExpandedAnimationController.onGestureFinished(); + } } /** Prepares and starts the desaturate/darken animation on the bubble stack. */ @@ -1201,6 +1201,7 @@ public class BubbleStackView extends FrameLayout { */ void magnetToStackIfNeededThenAnimateDismissal( View touchedView, float velX, float velY, Runnable after) { + final View draggedOutBubble = mExpandedAnimationController.getDraggedOutBubble(); final Runnable animateDismissal = () -> { mAfterMagnet = null; @@ -1218,7 +1219,7 @@ public class BubbleStackView extends FrameLayout { resetDesaturationAndDarken(); }); } else { - mExpandedAnimationController.dismissDraggedOutBubble(() -> { + mExpandedAnimationController.dismissDraggedOutBubble(draggedOutBubble, () -> { mAnimatingMagnet = false; mShowingDismiss = false; mDraggingInDismissTarget = false; @@ -1385,10 +1386,18 @@ public class BubbleStackView extends FrameLayout { }; // Post in case layout isn't complete and getWidth returns 0. - post(() -> mFlyout.showFlyout( - updateMessage, mStackAnimationController.getStackPosition(), getWidth(), - mStackAnimationController.isStackOnLeftSide(), - bubble.iconView.getBadgeColor(), mAfterFlyoutHides)); + post(() -> { + // An auto-expanding bubble could have been posted during the time it takes to + // layout. + if (isExpanded()) { + return; + } + + mFlyout.showFlyout( + updateMessage, mStackAnimationController.getStackPosition(), getWidth(), + mStackAnimationController.isStackOnLeftSide(), + bubble.iconView.getBadgeColor(), mAfterFlyoutHides); + }); } mFlyout.removeCallbacks(mHideFlyout); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java index 24337a3c33125..1fa0e12452e12 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -22,6 +22,7 @@ import android.graphics.PointF; import android.view.View; import android.view.WindowInsets; +import androidx.annotation.Nullable; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringForce; @@ -63,12 +64,16 @@ public class ExpandedAnimationController private Point mDisplaySize; /** Size of dismiss target at bottom of screen. */ private float mPipDismissHeight; - /** Max number of bubbles shown in row above expanded view.*/ - private int mBubblesMaxRendered; /** Whether the dragged-out bubble is in the dismiss target. */ private boolean mIndividualBubbleWithinDismissTarget = false; + private boolean mAnimatingExpand = false; + private boolean mAnimatingCollapse = false; + private Runnable mAfterExpand; + private Runnable mAfterCollapse; + private PointF mCollapsePoint; + /** * Whether the dragged out bubble is springing towards the touch point, rather than using the * default behavior of moving directly to the touch point. @@ -96,57 +101,61 @@ public class ExpandedAnimationController /** 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); - - final Resources res = layout.getResources(); - mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); - mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding); - mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size); - mStatusBarHeight = - res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); - mPipDismissHeight = res.getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height); - mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered); - } - /** * Animates expanding the bubbles into a row along the top of the screen. */ - public void expandFromStack(PointF collapseTo, Runnable after) { - animationsForChildrenFromIndex( - 0, /* startIndex */ - new ChildAnimationConfigurator() { - @Override - public void configureAnimationForChildAtIndex( - int index, PhysicsAnimationLayout.PhysicsPropertyAnimator animation) { - animation.position(getBubbleLeft(index), getExpandedY()); - } - }) - .startAll(after); + public void expandFromStack(Runnable after) { + mAnimatingCollapse = false; + mAnimatingExpand = true; + mAfterExpand = after; - mCollapseToPoint = collapseTo; + startOrUpdateExpandAnimation(); } /** Animate collapsing the bubbles back to their stacked position. */ - public void collapseBackToStack(Runnable after) { - // Stack to the left if we're going to the left, or right if not. - final float sideMultiplier = mLayout.isFirstChildXLeftOfCenter(mCollapseToPoint.x) ? -1 : 1; + public void collapseBackToStack(PointF collapsePoint, Runnable after) { + mAnimatingExpand = false; + mAnimatingCollapse = true; + mAfterCollapse = after; + mCollapsePoint = collapsePoint; + startOrUpdateCollapseAnimation(); + } + + private void startOrUpdateExpandAnimation() { animationsForChildrenFromIndex( 0, /* startIndex */ - (index, animation) -> + (index, animation) -> animation.position(getBubbleLeft(index), getExpandedY())) + .startAll(() -> { + mAnimatingExpand = false; + + if (mAfterExpand != null) { + mAfterExpand.run(); + } + + mAfterExpand = null; + }); + } + + private void startOrUpdateCollapseAnimation() { + // Stack to the left if we're going to the left, or right if not. + final float sideMultiplier = mLayout.isFirstChildXLeftOfCenter(mCollapsePoint.x) ? -1 : 1; + animationsForChildrenFromIndex( + 0, /* startIndex */ + (index, animation) -> { animation.position( - mCollapseToPoint.x + (sideMultiplier * index * mStackOffsetPx), - mCollapseToPoint.y)) - .startAll(after /* endAction */); + mCollapsePoint.x + (sideMultiplier * index * mStackOffsetPx), + mCollapsePoint.y); + }) + .startAll(() -> { + mAnimatingCollapse = false; + + if (mAfterCollapse != null) { + mAfterCollapse.run(); + } + + mAfterCollapse = null; + }); } /** Prepares the given bubble to be dragged out. */ @@ -190,10 +199,10 @@ public class ExpandedAnimationController } /** Plays a dismiss animation on the dragged out bubble. */ - public void dismissDraggedOutBubble(Runnable after) { + public void dismissDraggedOutBubble(View bubble, Runnable after) { mIndividualBubbleWithinDismissTarget = false; - animationForChild(mBubbleDraggingOut) + animationForChild(bubble) .withStiffness(SpringForce.STIFFNESS_HIGH) .scaleX(1.1f) .scaleY(1.1f) @@ -203,6 +212,10 @@ public class ExpandedAnimationController updateBubblePositions(); } + @Nullable public View getDraggedOutBubble() { + return mBubbleDraggingOut; + } + /** Magnets the given bubble to the dismiss target. */ public void magnetBubbleToDismiss( View bubbleView, float velX, float velY, float destY, Runnable after) { @@ -241,24 +254,17 @@ public class ExpandedAnimationController final int index = mLayout.indexOfChild(bubbleView); animationForChildAtIndex(index) - .position(getBubbleLeft(index), getExpandedY()) - .withPositionStartVelocities(velX, velY) - .start(() -> bubbleView.setTranslationZ(0f) /* after */); + .position(getBubbleLeft(index), getExpandedY()) + .withPositionStartVelocities(velX, velY) + .start(() -> bubbleView.setTranslationZ(0f) /* after */); - mBubbleDraggingOut = null; - mBubbleDraggedOutEnough = false; updateBubblePositions(); } - /** - * 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; + /** Resets bubble drag out gesture flags. */ + public void onGestureFinished() { mBubbleDraggedOutEnough = false; + mBubbleDraggingOut = null; } /** @@ -296,6 +302,23 @@ public class ExpandedAnimationController : 0); } + @Override + void onActiveControllerForLayout(PhysicsAnimationLayout layout) { + final Resources res = layout.getResources(); + mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); + mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding); + mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size); + mStatusBarHeight = + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + mPipDismissHeight = res.getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height); + + // Ensure that all child views are at 1x scale, and visible, in case they were animating + // in. + mLayout.setVisibility(View.VISIBLE); + animationsForChildrenFromIndex(0 /* startIndex */, (index, animation) -> + animation.scaleX(1f).scaleY(1f).alpha(1f)).startAll(); + } + @Override Set getAnimatedProperties() { return Sets.newHashSet( @@ -325,14 +348,21 @@ public class ExpandedAnimationController @Override void onChildAdded(View child, int index) { - child.setTranslationX(getXForChildAtIndex(index)); - - animationForChild(child) - .translationY( - getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR, /* from */ - getExpandedY() /* to */) - .start(); - updateBubblePositions(); + // If a bubble is added while the expand/collapse animations are playing, update the + // animation to include the new bubble. + if (mAnimatingExpand) { + startOrUpdateExpandAnimation(); + } else if (mAnimatingCollapse) { + startOrUpdateCollapseAnimation(); + } else { + child.setTranslationX(getXForChildAtIndex(index)); + animationForChild(child) + .translationY( + getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR, /* from */ + getExpandedY() /* to */) + .start(); + updateBubblePositions(); + } } @Override @@ -357,19 +387,15 @@ public class ExpandedAnimationController } @Override - protected void setChildVisibility(View child, int index, int visibility) { - if (visibility == View.VISIBLE) { - // Set alpha to 0 but then become visible immediately so the animation is visible. - child.setAlpha(0f); - child.setVisibility(View.VISIBLE); - } - - animationForChild(child) - .alpha(visibility == View.GONE ? 0f : 1f) - .start(() -> super.setChildVisibility(child, index, visibility) /* after */); + void onChildReordered(View child, int oldIndex, int newIndex) { + updateBubblePositions(); } private void updateBubblePositions() { + if (mAnimatingExpand || mAnimatingCollapse) { + return; + } + for (int i = 0; i < mLayout.getChildCount(); i++) { final View bubble = mLayout.getChildAt(i); @@ -378,6 +404,7 @@ public class ExpandedAnimationController if (bubble.equals(mBubbleDraggingOut)) { return; } + animationForChild(bubble) .translationX(getBubbleLeft(i)) .start(); @@ -403,10 +430,7 @@ public class ExpandedAnimationController return 0; } int bubbleCount = mLayout.getChildCount(); - if (bubbleCount > mBubblesMaxRendered) { - // Only shown bubbles are relevant for calculating position. - bubbleCount = mBubblesMaxRendered; - } + // Width calculations. double bubble = bubbleCount * mBubbleSizePx; float gap = (bubbleCount - 1) * mBubblePaddingPx; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java index 997d2c4627d86..3a3339249d5b0 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java @@ -17,10 +17,12 @@ package com.android.systemui.bubbles.animation; import android.content.Context; +import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import androidx.annotation.Nullable; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; @@ -137,27 +139,39 @@ public class PhysicsAnimationLayout extends FrameLayout { */ abstract void onChildRemoved(View child, int index, Runnable finishRemoval); + /** Called when a child view has been reordered in the view hierachy. */ + abstract void onChildReordered(View child, int oldIndex, int newIndex); + + /** + * Called when the controller is set as the active animation controller for the given + * layout. Once active, the controller can start animations using the animator instances + * returned by {@link #animationForChild}. + * + * While all animations started by the previous controller will be cancelled, the new + * controller should not make any assumptions about the state of the layout or its children. + * Their translation, alpha, scale, etc. values may have been changed by the previous + * controller and should be reset here if relevant. + */ + abstract void onActiveControllerForLayout(PhysicsAnimationLayout layout); + protected PhysicsAnimationLayout mLayout; PhysicsAnimationController() { } + /** Whether this controller is the currently active controller for its associated layout. */ + protected boolean isActiveController() { + return this == mLayout.mController; + } + protected void setLayout(PhysicsAnimationLayout layout) { this.mLayout = layout; + onActiveControllerForLayout(layout); } protected PhysicsAnimationLayout getLayout() { return mLayout; } - /** - * Sets the child's visibility when it moves beyond or within the limits set by a call to - * {@link PhysicsAnimationLayout#setMaxRenderedChildren}. This can be overridden to animate - * this transition. - */ - protected void setChildVisibility(View child, int index, int visibility) { - child.setVisibility(visibility); - } - /** * Returns a {@link PhysicsPropertyAnimator} instance for the given child view. */ @@ -170,6 +184,9 @@ public class PhysicsAnimationLayout extends FrameLayout { child.setTag(R.id.physics_animator_tag, animator); } + animator.clearAnimator(); + animator.setAssociatedController(this); + return animator; } @@ -235,32 +252,17 @@ public class PhysicsAnimationLayout extends FrameLayout { new HashMap<>(); /** The currently active animation controller. */ - private PhysicsAnimationController mController; - - /** - * The maximum number of children to render and animate at a time. See - * {@link #setMaxRenderedChildren}. - */ - private int mMaxRenderedChildren = 5; + @Nullable protected PhysicsAnimationController mController; public PhysicsAnimationLayout(Context context) { super(context); } - /** - * The maximum number of children to render and animate at a time. Any child views added beyond - * this limit will be set to {@link View#GONE}. If any animations attempt to run on the view, - * the corresponding property will be set with no animation. - */ - public void setMaxRenderedChildren(int max) { - this.mMaxRenderedChildren = max; - } - /** * Sets the animation controller and constructs or reconfigures the layout's physics animations * to meet the controller's specifications. */ - public void setController(PhysicsAnimationController controller) { + public void setActiveController(PhysicsAnimationController controller) { cancelAllAnimations(); mEndActionForProperty.clear(); @@ -312,42 +314,11 @@ public class PhysicsAnimationLayout extends FrameLayout { @Override public void addView(View child, int index, ViewGroup.LayoutParams params) { - super.addView(child, index, params); - - // Set up animations for the new view, if the controller is set. If it isn't set, we'll be - // setting up animations for all children when setController is called. - if (mController != null) { - for (DynamicAnimation.ViewProperty property : mController.getAnimatedProperties()) { - setUpAnimationForChild(property, child, index); - } - - mController.onChildAdded(child, index); - } - - setChildrenVisibility(); + addViewInternal(child, index, params, false /* isReorder */); } @Override public void removeView(View view) { - removeViewAndThen(view, /* callback */ null); - } - - @Override - public void removeViewAt(int index) { - removeView(getChildAt(index)); - } - - /** Immediately moves the view from wherever it currently is, to the given index. */ - public void moveViewTo(View view, int index) { - super.removeView(view); - addView(view, index); - } - - /** - * Let the controller know that this view should be removed, and then call the callback once the - * controller has finished any removal animations and the view has actually been removed. - */ - public void removeViewAndThen(View view, Runnable callback) { if (mController != null) { final int index = indexOfChild(view); @@ -355,8 +326,6 @@ public class PhysicsAnimationLayout extends FrameLayout { super.removeView(view); addTransientView(view, index); - setChildrenVisibility(); - // Tell the controller to animate this view out, and call the callback when it's // finished. mController.onChildRemoved(view, index, () -> { @@ -364,19 +333,28 @@ public class PhysicsAnimationLayout extends FrameLayout { // any are still running and then remove it. cancelAnimationsOnView(view); removeTransientView(view); - - if (callback != null) { - callback.run(); - } }); } else { // Without a controller, nobody will animate this view out, so it gets an unceremonious // departure. super.removeView(view); + } + } - if (callback != null) { - callback.run(); - } + @Override + public void removeViewAt(int index) { + removeView(getChildAt(index)); + } + + /** Immediately re-orders the view to the given index. */ + public void reorderView(View view, int index) { + final int oldIndex = indexOfChild(view); + + super.removeView(view); + addViewInternal(view, index, view.getLayoutParams(), true /* isReorder */); + + if (mController != null) { + mController.onChildReordered(view, oldIndex, index); } } @@ -427,6 +405,10 @@ public class PhysicsAnimationLayout extends FrameLayout { } } + protected boolean isActiveController(PhysicsAnimationController controller) { + return mController == controller; + } + /** Whether the first child would be left of center if translated to the given x value. */ protected boolean isFirstChildXLeftOfCenter(float x) { if (getChildCount() > 0) { @@ -453,6 +435,26 @@ public class PhysicsAnimationLayout extends FrameLayout { } } + /** + * Adds a view to the layout. If this addition is not the result of a call to + * {@link #reorderView}, this will also notify the controller via + * {@link PhysicsAnimationController#onChildAdded} and set up animations for the view. + */ + private void addViewInternal( + View child, int index, ViewGroup.LayoutParams params, boolean isReorder) { + super.addView(child, index, params); + + // Set up animations for the new view, if the controller is set. If it isn't set, we'll be + // setting up animations for all children when setActiveController is called. + if (mController != null && !isReorder) { + for (DynamicAnimation.ViewProperty property : mController.getAnimatedProperties()) { + setUpAnimationForChild(property, child, index); + } + + mController.onChildAdded(child, index); + } + } + /** * Retrieves the animation of the given property from the view at the given index via the view * tag system. @@ -481,33 +483,16 @@ public class PhysicsAnimationLayout extends FrameLayout { SpringAnimation newAnim = new SpringAnimation(child, property); newAnim.addUpdateListener((animation, value, velocity) -> { final int indexOfChild = indexOfChild(child); - final int nextAnimInChain = - mController.getNextAnimationInChain(property, indexOfChild); + final int nextAnimInChain = mController.getNextAnimationInChain(property, indexOfChild); if (nextAnimInChain == PhysicsAnimationController.NONE || indexOfChild < 0) { return; } - final int animIndex = indexOfChild(child); - final float offset = - mController.getOffsetForChainedPropertyAnimation(property); - - // If this property's animations should be chained, then check to see if there is a - // subsequent animation within the rendering limit, and if so, tell it to animate to - // this animation's new value (plus the offset). - if (nextAnimInChain < Math.min(getChildCount(), mMaxRenderedChildren)) { - getAnimationAtIndex(property, animIndex + 1) + final float offset = mController.getOffsetForChainedPropertyAnimation(property); + if (nextAnimInChain < getChildCount()) { + getAnimationAtIndex(property, nextAnimInChain) .animateToFinalPosition(value + offset); - } else if (nextAnimInChain < getChildCount()) { - // If the next child view is not rendered, update the property directly without - // animating it, so that the view is still in the correct state if it later - // becomes visible. - for (int i = nextAnimInChain; i < getChildCount(); i++) { - // 'value' here is the value of the last child within the rendering limit, - // not the first child's value - so we want to subtract the last child's - // index when calculating the offset. - property.setValue(getChildAt(i), value + offset * (i - animIndex)); - } } }); @@ -516,22 +501,6 @@ public class PhysicsAnimationLayout extends FrameLayout { child.setTag(getTagIdForProperty(property), newAnim); } - /** Hides children beyond the max rendering count. */ - private void setChildrenVisibility() { - for (int i = 0; i < getChildCount(); i++) { - final int targetVisibility = i < mMaxRenderedChildren ? View.VISIBLE : View.GONE; - final View targetView = getChildAt(i); - - if (targetView.getVisibility() != targetVisibility) { - if (mController != null) { - mController.setChildVisibility(targetView, i, targetVisibility); - } else { - targetView.setVisibility(targetVisibility); - } - } - } - } - /** Return a stable ID to use as a tag key for the given property's animations. */ private int getTagIdForProperty(DynamicAnimation.ViewProperty property) { if (property.equals(DynamicAnimation.TRANSLATION_X)) { @@ -592,7 +561,7 @@ public class PhysicsAnimationLayout extends FrameLayout { private View mView; /** Start velocity to use for all property animations. */ - private float mDefaultStartVelocity = 0f; + private float mDefaultStartVelocity = -Float.MAX_VALUE; /** Start delay to use when start is called. */ private long mStartDelay = 0; @@ -625,6 +594,15 @@ public class PhysicsAnimationLayout extends FrameLayout { */ private Map mAnimatedProperties = new HashMap<>(); + /** + * All of the initial property values that have been set. These values will be instantly set + * when {@link #start} is called, just before the animation begins. + */ + private Map mInitialPropertyValues = new HashMap<>(); + + /** The animation controller that last retrieved this animator instance. */ + private PhysicsAnimationController mAssociatedController; + protected PhysicsPropertyAnimator(View view) { this.mView = view; } @@ -644,7 +622,7 @@ public class PhysicsAnimationLayout extends FrameLayout { /** Set the view's alpha value to 'from', then animate it to the given value. */ public PhysicsPropertyAnimator alpha(float from, float to, Runnable... endActions) { - mView.setAlpha(from); + mInitialPropertyValues.put(DynamicAnimation.ALPHA, from); return alpha(to, endActions); } @@ -656,7 +634,7 @@ public class PhysicsAnimationLayout extends FrameLayout { /** Set the view's translationX value to 'from', then animate it to the given value. */ public PhysicsPropertyAnimator translationX( float from, float to, Runnable... endActions) { - mView.setTranslationX(from); + mInitialPropertyValues.put(DynamicAnimation.TRANSLATION_X, from); return translationX(to, endActions); } @@ -668,7 +646,7 @@ public class PhysicsAnimationLayout extends FrameLayout { /** Set the view's translationY value to 'from', then animate it to the given value. */ public PhysicsPropertyAnimator translationY( float from, float to, Runnable... endActions) { - mView.setTranslationY(from); + mInitialPropertyValues.put(DynamicAnimation.TRANSLATION_Y, from); return translationY(to, endActions); } @@ -690,7 +668,7 @@ public class PhysicsAnimationLayout extends FrameLayout { /** Set the view's scaleX value to 'from', then animate it to the given value. */ public PhysicsPropertyAnimator scaleX(float from, float to, Runnable... endActions) { - mView.setScaleX(from); + mInitialPropertyValues.put(DynamicAnimation.SCALE_X, from); return scaleX(to, endActions); } @@ -701,7 +679,7 @@ public class PhysicsAnimationLayout extends FrameLayout { /** Set the view's scaleY value to 'from', then animate it to the given value. */ public PhysicsPropertyAnimator scaleY(float from, float to, Runnable... endActions) { - mView.setScaleY(from); + mInitialPropertyValues.put(DynamicAnimation.SCALE_Y, from); return scaleY(to, endActions); } @@ -750,6 +728,13 @@ public class PhysicsAnimationLayout extends FrameLayout { * animated property on every child (including chained animations) have ended. */ public void start(Runnable... after) { + if (!isActiveController(mAssociatedController)) { + Log.w(TAG, "Only the active animation controller is allowed to start animations. " + + "Use PhysicsAnimationLayout#setActiveController to set the active " + + "animation controller."); + return; + } + final Set properties = getAnimatedProperties(); // If there are end actions, set an end listener on the layout for all the properties @@ -791,6 +776,10 @@ public class PhysicsAnimationLayout extends FrameLayout { // Actually start the animations. for (DynamicAnimation.ViewProperty property : properties) { + if (mInitialPropertyValues.containsKey(property)) { + property.setValue(mView, mInitialPropertyValues.get(property)); + } + final SpringForce defaultSpringForce = mController.getSpringForce(property, mView); animateValueForChild( property, @@ -803,14 +792,7 @@ public class PhysicsAnimationLayout extends FrameLayout { mEndActionsForProperty.get(property)); } - // Clear out the animator. - mAnimatedProperties.clear(); - mPositionStartVelocities.clear(); - mDefaultStartVelocity = 0; - mStartDelay = 0; - mStiffness = -1; - mDampingRatio = -1; - mEndActionsForProperty.clear(); + clearAnimator(); } /** Returns the set of properties that will animate once {@link #start} is called. */ @@ -847,20 +829,50 @@ public class PhysicsAnimationLayout extends FrameLayout { }); } - animation.getSpring().setStiffness(stiffness); - animation.getSpring().setDampingRatio(dampingRatio); + final SpringForce animationSpring = animation.getSpring(); - if (startVel > 0) { - animation.setStartVelocity(startVel); + if (animationSpring == null) { + return; } + final Runnable configureAndStartAnimation = () -> { + animationSpring.setStiffness(stiffness); + animationSpring.setDampingRatio(dampingRatio); + + if (startVel > -Float.MAX_VALUE) { + animation.setStartVelocity(startVel); + } + + animationSpring.setFinalPosition(value); + animation.start(); + }; + if (startDelay > 0) { - postDelayed(() -> animation.animateToFinalPosition(value), startDelay); + postDelayed(configureAndStartAnimation, startDelay); } else { - animation.animateToFinalPosition(value); + configureAndStartAnimation.run(); } } } + + private void clearAnimator() { + mInitialPropertyValues.clear(); + mAnimatedProperties.clear(); + mPositionStartVelocities.clear(); + mDefaultStartVelocity = -Float.MAX_VALUE; + mStartDelay = 0; + mStiffness = -1; + mDampingRatio = -1; + mEndActionsForProperty.clear(); + } + + /** + * Sets the controller that last retrieved this animator instance, so that we can prevent + * {@link #start} from actually starting animations if called by a non-active controller. + */ + private void setAssociatedController(PhysicsAnimationController controller) { + mAssociatedController = controller; + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index b9cdc844eef94..ab8752e4195fa 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -154,21 +154,6 @@ public class StackAnimationController extends /** Height of the status bar. */ private float mStatusBarHeight; - @Override - protected void setLayout(PhysicsAnimationLayout layout) { - super.setLayout(layout); - - Resources res = layout.getResources(); - mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); - mIndividualBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); - mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding); - mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen); - mStackStartingVerticalOffset = - res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y); - mStatusBarHeight = - res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); - } - /** * Instantly move the first bubble to the given point, and animate the rest of the stack behind * it with the 'following' effect. @@ -286,6 +271,8 @@ public class StackAnimationController extends }, DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); + // If we're flinging now, there's no more touch event to catch up to. + mFirstBubbleSpringingToTouch = false; mIsMovingFromFlinging = true; return destinationRelativeX; } @@ -656,19 +643,38 @@ public class StackAnimationController extends if (mLayout.getChildCount() > 0) { animationForChildAtIndex(0).translationX(mStackPosition.x).start(); + } else { + // Set the start position back to the default since we're out of bubbles. New bubbles + // will then animate in from the start position. + mStackPosition = getDefaultStartPosition(); } } + @Override + void onChildReordered(View child, int oldIndex, int newIndex) {} + + @Override + void onActiveControllerForLayout(PhysicsAnimationLayout layout) { + Resources res = layout.getResources(); + mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); + mIndividualBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); + mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding); + mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen); + mStackStartingVerticalOffset = + res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y); + mStatusBarHeight = + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + } + /** Moves the stack, without any animation, to the starting position. */ private void moveStackToStartPosition() { // Post to ensure that the layout's width and height have been calculated. mLayout.setVisibility(View.INVISIBLE); mLayout.post(() -> { + setStackPosition(mRestingStackPosition == null + ? getDefaultStartPosition() + : mRestingStackPosition); mStackMovedToStartPosition = true; - setStackPosition( - mRestingStackPosition == null - ? getDefaultStartPosition() - : mRestingStackPosition); mLayout.setVisibility(View.VISIBLE); // Animate in the top bubble now that we're visible. @@ -707,15 +713,20 @@ public class StackAnimationController extends Log.d(TAG, String.format("Setting position to (%f, %f).", pos.x, pos.y)); mStackPosition.set(pos.x, pos.y); - mLayout.cancelAllAnimations(); - cancelStackPositionAnimations(); + // If we're not the active controller, we don't want to physically move the bubble views. + if (isActiveController()) { + mLayout.cancelAllAnimations(); + cancelStackPositionAnimations(); - // Since we're not using the chained animations, apply the offsets manually. - final float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); - final float yOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_Y); - for (int i = 0; i < mLayout.getChildCount(); i++) { - mLayout.getChildAt(i).setTranslationX(pos.x + (i * xOffset)); - mLayout.getChildAt(i).setTranslationY(pos.y + (i * yOffset)); + // Since we're not using the chained animations, apply the offsets manually. + final float xOffset = getOffsetForChainedPropertyAnimation( + DynamicAnimation.TRANSLATION_X); + final float yOffset = getOffsetForChainedPropertyAnimation( + DynamicAnimation.TRANSLATION_Y); + for (int i = 0; i < mLayout.getChildCount(); i++) { + mLayout.getChildAt(i).setTranslationX(pos.x + (i * xOffset)); + mLayout.getChildAt(i).setTranslationY(pos.y + (i * yOffset)); + } } } @@ -732,6 +743,10 @@ public class StackAnimationController extends /** Animates in the given bubble. */ private void animateInBubble(View child) { + if (!isActiveController()) { + return; + } + child.setTranslationY(mStackPosition.y); float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java index 756cf3e138f68..b324235106c21 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java @@ -60,8 +60,8 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC @Before public void setUp() throws Exception { super.setUp(); - addOneMoreThanRenderLimitBubbles(); - mLayout.setController(mExpandedController); + addOneMoreThanBubbleLimitBubbles(); + mLayout.setActiveController(mExpandedController); Resources res = mLayout.getResources(); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); @@ -73,14 +73,14 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC @Test public void testExpansionAndCollapse() throws InterruptedException { Runnable afterExpand = Mockito.mock(Runnable.class); - mExpandedController.expandFromStack(mExpansionPoint, afterExpand); + mExpandedController.expandFromStack(afterExpand); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); testBubblesInCorrectExpandedPositions(); verify(afterExpand).run(); Runnable afterCollapse = Mockito.mock(Runnable.class); - mExpandedController.collapseBackToStack(afterCollapse); + mExpandedController.collapseBackToStack(mExpansionPoint, afterCollapse); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y, -1); @@ -139,7 +139,6 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC assertEquals(500f, draggedBubble.getTranslationY(), 1f); // Snap it back and make sure it made it back correctly. - mExpandedController.prepareForDismissalWithVelocity(draggedBubble, 0f, 0f); mLayout.removeView(draggedBubble); waitForLayoutMessageQueue(); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); @@ -169,7 +168,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC // Dismiss the now-magneted bubble, verify that the callback was called. final Runnable afterDismiss = Mockito.mock(Runnable.class); - mExpandedController.dismissDraggedOutBubble(afterDismiss); + mExpandedController.dismissDraggedOutBubble(draggedOutView, afterDismiss); waitForPropertyAnimations(DynamicAnimation.ALPHA); verify(after).run(); @@ -224,7 +223,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC /** Expand the stack and wait for animations to finish. */ private void expand() throws InterruptedException { - mExpandedController.expandFromStack(mExpansionPoint, Mockito.mock(Runnable.class)); + mExpandedController.expandFromStack(Mockito.mock(Runnable.class)); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); } @@ -236,26 +235,19 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC assertEquals(x + i * offsetMultiplier * mStackOffset, mLayout.getChildAt(i).getTranslationX(), 2f); assertEquals(y, mLayout.getChildAt(i).getTranslationY(), 2f); - - if (i < mMaxRenderedBubbles) { - assertEquals(1f, mLayout.getChildAt(i).getAlpha(), .01f); - } + assertEquals(1f, mLayout.getChildAt(i).getAlpha(), .01f); } } /** Check that children are in the correct positions for being expanded. */ private void testBubblesInCorrectExpandedPositions() { // Check all the visible bubbles to see if they're in the right place. - for (int i = 0; i < Math.min(mLayout.getChildCount(), mMaxRenderedBubbles); i++) { + for (int i = 0; i < mLayout.getChildCount(); i++) { assertEquals(getBubbleLeft(i), mLayout.getChildAt(i).getTranslationX(), 2f); assertEquals(mExpandedController.getExpandedY(), mLayout.getChildAt(i).getTranslationY(), 2f); - - if (i < mMaxRenderedBubbles) { - assertEquals(1f, mLayout.getChildAt(i).getAlpha(), .01f); - } } } @@ -273,9 +265,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC return 0; } int bubbleCount = mLayout.getChildCount(); - if (bubbleCount > mMaxRenderedBubbles) { - bubbleCount = mMaxRenderedBubbles; - } + // Width calculations. double bubble = bubbleCount * mBubbleSize; float gap = (bubbleCount - 1) * mBubblePadding; diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java index eef6ddcf6d6b3..f8b32c2131098 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java @@ -23,7 +23,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -76,22 +75,10 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { mTestableController.setRemoveImmediately(false); } - @Test - public void testRenderVisibility() throws InterruptedException { - mLayout.setController(mTestableController); - addOneMoreThanRenderLimitBubbles(); - - // The last child should be GONE, the rest VISIBLE. - for (int i = 0; i < mMaxRenderedBubbles + 1; i++) { - assertEquals(i == mMaxRenderedBubbles ? View.GONE : View.VISIBLE, - mLayout.getChildAt(i).getVisibility()); - } - } - @Test public void testHierarchyChanges() throws InterruptedException { - mLayout.setController(mTestableController); - addOneMoreThanRenderLimitBubbles(); + mLayout.setActiveController(mTestableController); + addOneMoreThanBubbleLimitBubbles(); // Make sure the controller was notified of all the views we added. for (View mView : mViews) { @@ -115,8 +102,8 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { @Test public void testUpdateValueNotChained() throws InterruptedException { - mLayout.setController(mTestableController); - addOneMoreThanRenderLimitBubbles(); + mLayout.setActiveController(mTestableController); + addOneMoreThanBubbleLimitBubbles(); // Don't chain any values. mTestableController.setChainedProperties(Sets.newHashSet()); @@ -146,8 +133,8 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { @Test public void testSetEndActions() throws InterruptedException { - mLayout.setController(mTestableController); - addOneMoreThanRenderLimitBubbles(); + mLayout.setActiveController(mTestableController); + addOneMoreThanBubbleLimitBubbles(); mTestableController.setChainedProperties(Sets.newHashSet()); final CountDownLatch xLatch = new CountDownLatch(1); @@ -189,8 +176,8 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { @Test public void testRemoveEndListeners() throws InterruptedException { - mLayout.setController(mTestableController); - addOneMoreThanRenderLimitBubbles(); + mLayout.setActiveController(mTestableController); + addOneMoreThanBubbleLimitBubbles(); mTestableController.setChainedProperties(Sets.newHashSet()); final CountDownLatch xLatch = new CountDownLatch(1); @@ -229,8 +216,8 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { public void testSetController() throws InterruptedException { // Add the bubbles, then set the controller, to make sure that a controller added to an // already-initialized view works correctly. - addOneMoreThanRenderLimitBubbles(); - mLayout.setController(mTestableController); + addOneMoreThanBubbleLimitBubbles(); + mLayout.setActiveController(mTestableController); testChainedTranslationAnimations(); TestableAnimationController secondController = @@ -243,7 +230,7 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { DynamicAnimation.SCALE_X, 10f); secondController.setRemoveImmediately(true); - mLayout.setController(secondController); + mLayout.setActiveController(secondController); mTestableController.animationForChildAtIndex(0) .scaleX(1.5f) .start(); @@ -266,7 +253,7 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { Mockito.verify(secondController, Mockito.atLeastOnce()) .getOffsetForChainedPropertyAnimation(eq(DynamicAnimation.SCALE_X)); - mLayout.setController(mTestableController); + mLayout.setActiveController(mTestableController); mTestableController.animationForChildAtIndex(0) .translationX(100f) .start(); @@ -283,8 +270,8 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { @Test public void testArePropertiesAnimating() throws InterruptedException { - mLayout.setController(mTestableController); - addOneMoreThanRenderLimitBubbles(); + mLayout.setActiveController(mTestableController); + addOneMoreThanBubbleLimitBubbles(); assertFalse(mLayout.arePropertiesAnimating( DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y)); @@ -307,8 +294,8 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { @Test public void testCancelAllAnimations() throws InterruptedException { - mLayout.setController(mTestableController); - addOneMoreThanRenderLimitBubbles(); + mLayout.setActiveController(mTestableController); + addOneMoreThanBubbleLimitBubbles(); mTestableController.animationForChildAtIndex(0) .position(1000, 1000) @@ -321,29 +308,10 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { assertTrue(mViews.get(0).getTranslationY() < 1000); } - @Test - public void testSetChildVisibility() throws InterruptedException { - mLayout.setController(mTestableController); - addOneMoreThanRenderLimitBubbles(); - - // The last view should have been set to GONE by the controller, since we added one more - // than the limit and it got pushed off. None of the first children should have been set - // VISIBLE, since they would have been animated in by onChildAdded. - Mockito.verify(mTestableController).setChildVisibility( - mViews.get(mViews.size() - 1), 5, View.GONE); - Mockito.verify(mTestableController, never()).setChildVisibility( - any(View.class), anyInt(), eq(View.VISIBLE)); - - // Remove the first view, which should cause the last view to become visible again. - mLayout.removeView(mViews.get(0)); - Mockito.verify(mTestableController).setChildVisibility( - mViews.get(mViews.size() - 1), 4, View.VISIBLE); - } - /** Standard test of chained translation animations. */ private void testChainedTranslationAnimations() throws InterruptedException { - mLayout.setController(mTestableController); - addOneMoreThanRenderLimitBubbles(); + mLayout.setActiveController(mTestableController); + addOneMoreThanBubbleLimitBubbles(); assertEquals(0, mLayout.getChildAt(0).getTranslationX(), .1f); assertEquals(0, mLayout.getChildAt(1).getTranslationX(), .1f); @@ -354,11 +322,7 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X); - // Since we enabled chaining, animating the first view to 100 should animate the second to - // 115 (since we set the offset to 15) and the third to 130, etc. Despite the sixth bubble - // not being visible, or animated, make sure that it has the appropriate chained - // translation. - for (int i = 0; i < mMaxRenderedBubbles + 1; i++) { + for (int i = 0; i < mLayout.getChildCount(); i++) { assertEquals( 100 + i * TEST_TRANSLATION_X_OFFSET, mLayout.getChildAt(i).getTranslationX(), .1f); @@ -383,8 +347,8 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { @Test public void testPhysicsAnimator() throws InterruptedException { - mLayout.setController(mTestableController); - addOneMoreThanRenderLimitBubbles(); + mLayout.setActiveController(mTestableController); + addOneMoreThanBubbleLimitBubbles(); Runnable afterAll = Mockito.mock(Runnable.class); Runnable after = Mockito.spy(new Runnable() { @@ -430,9 +394,9 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { // Don't chain since we're going to invoke each animation independently. mTestableController.setChainedProperties(new HashSet<>()); - mLayout.setController(mTestableController); + mLayout.setActiveController(mTestableController); - addOneMoreThanRenderLimitBubbles(); + addOneMoreThanBubbleLimitBubbles(); Runnable allEnd = Mockito.mock(Runnable.class); @@ -452,7 +416,7 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { @Test public void testAnimationsForChildrenFromIndex_noChildren() { - mLayout.setController(mTestableController); + mLayout.setActiveController(mTestableController); final Runnable after = Mockito.mock(Runnable.class); mTestableController @@ -523,8 +487,9 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase { } @Override - protected void setChildVisibility(View child, int index, int visibility) { - super.setChildVisibility(child, index, visibility); - } + void onChildReordered(View child, int oldIndex, int newIndex) {} + + @Override + void onActiveControllerForLayout(PhysicsAnimationLayout layout) {} } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java index c6acef5d49070..f633f3996d13e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java @@ -56,7 +56,6 @@ public class PhysicsAnimationLayoutTestCase extends SysuiTestCase { Handler mMainThreadHandler; - int mMaxRenderedBubbles; int mSystemWindowInsetSize = 50; int mCutoutInsetSize = 100; @@ -69,6 +68,8 @@ public class PhysicsAnimationLayoutTestCase extends SysuiTestCase { @Mock private DisplayCutout mCutout; + private int mMaxBubbles; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -79,7 +80,7 @@ public class PhysicsAnimationLayoutTestCase extends SysuiTestCase { mLayout.setTop(0); mLayout.setBottom(mHeight); - mMaxRenderedBubbles = + mMaxBubbles = getContext().getResources().getInteger(R.integer.bubbles_max_rendered); mMainThreadHandler = new Handler(Looper.getMainLooper()); @@ -96,8 +97,8 @@ public class PhysicsAnimationLayoutTestCase extends SysuiTestCase { } /** Add one extra bubble over the limit, so we can make sure it's gone/chains appropriately. */ - void addOneMoreThanRenderLimitBubbles() throws InterruptedException { - for (int i = 0; i < mMaxRenderedBubbles + 1; i++) { + void addOneMoreThanBubbleLimitBubbles() throws InterruptedException { + for (int i = 0; i < mMaxBubbles + 1; i++) { final View newView = new FrameLayout(mContext); mLayout.addView(newView, 0); mViews.add(0, newView); @@ -137,6 +138,13 @@ public class PhysicsAnimationLayoutTestCase extends SysuiTestCase { super(context); } + @Override + protected boolean isActiveController(PhysicsAnimationController controller) { + // Return true since otherwise all test controllers will be seen as inactive since they + // are wrapped by MainThreadAnimationControllerWrapper. + return true; + } + @Override public boolean post(Runnable action) { return mMainThreadHandler.post(action); @@ -148,9 +156,9 @@ public class PhysicsAnimationLayoutTestCase extends SysuiTestCase { } @Override - public void setController(PhysicsAnimationController controller) { + public void setActiveController(PhysicsAnimationController controller) { runOnMainThreadAndBlock( - () -> super.setController( + () -> super.setActiveController( new MainThreadAnimationControllerWrapper(controller))); } @@ -267,8 +275,15 @@ public class PhysicsAnimationLayoutTestCase extends SysuiTestCase { } @Override - protected void setChildVisibility(View child, int index, int visibility) { - mWrappedController.setChildVisibility(child, index, visibility); + void onChildReordered(View child, int oldIndex, int newIndex) { + runOnMainThreadAndBlock( + () -> mWrappedController.onChildReordered(child, oldIndex, newIndex)); + } + + @Override + void onActiveControllerForLayout(PhysicsAnimationLayout layout) { + runOnMainThreadAndBlock( + () -> mWrappedController.onActiveControllerForLayout(layout)); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java index 9218a8b93f664..31a7d5a45b685 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java @@ -54,8 +54,8 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase @Before public void setUp() throws Exception { super.setUp(); - mLayout.setController(mStackController); - addOneMoreThanRenderLimitBubbles(); + mLayout.setActiveController(mStackController); + addOneMoreThanBubbleLimitBubbles(); mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset); }