diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java index 32c6e368583d9..1971ca97d4262 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java @@ -131,7 +131,11 @@ public class BadgedImageView extends ImageView { */ public void setRenderedBubble(BubbleViewProvider bubble) { mBubble = bubble; - showBadge(); + if (mDotSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK)) { + hideBadge(); + } else { + showBadge(); + } mDotColor = bubble.getDotColor(); drawDot(bubble.getDotPath()); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 0f81d7ec6ae6d..fac368677d12d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -647,10 +647,12 @@ public class BubbleStackView extends FrameLayout } else { // Fling the stack to the edge, and save whether or not it's going to end up on // the left side of the screen. + final boolean oldOnLeft = mStackOnLeftOrWillBe; mStackOnLeftOrWillBe = mStackAnimationController.flingStackThenSpringToEdge( viewInitialX + dx, velX, velY) <= 0; - updateBubbleIcons(); + final boolean updateForCollapsedStack = oldOnLeft != mStackOnLeftOrWillBe; + updateBadgesAndZOrder(updateForCollapsedStack); logBubbleEvent(null /* no bubble associated with bubble stack move */, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED); } @@ -1481,7 +1483,7 @@ public class BubbleStackView extends FrameLayout }; if (mIsExpanded) { reorder.run(); - updateBubbleIcons(); + updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */); } else { List bubbleViews = bubbles.stream() .map(b -> b.getIconView()).collect(Collectors.toList()); @@ -2616,27 +2618,28 @@ public class BubbleStackView extends FrameLayout } mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide(); - updateBubbleIcons(); + updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */); } /** * Sets the appropriate Z-order, badge, and dot position for each bubble in the stack. * Animate dot and badge changes. */ - private void updateBubbleIcons() { + private void updateBadgesAndZOrder(boolean setBadgeForCollapsedStack) { int bubbleCount = getBubbleCount(); for (int i = 0; i < bubbleCount; i++) { BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i); bv.setZ((mMaxBubbles * mBubbleElevation) - i); - if (mIsExpanded) { // If we're not displaying vertically, we always show the badge on the left. boolean onLeft = mPositioner.showBubblesVertically() && !mStackOnLeftOrWillBe; bv.showDotAndBadge(onLeft); - } else if (i == 0) { - bv.showDotAndBadge(!mStackOnLeftOrWillBe); - } else { - bv.hideDotAndBadge(!mStackOnLeftOrWillBe); + } else if (setBadgeForCollapsedStack) { + if (i == 0) { + bv.showDotAndBadge(!mStackOnLeftOrWillBe); + } else { + bv.hideDotAndBadge(!mStackOnLeftOrWillBe); + } } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java index 96398be42fb2c..e1f831eae6990 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java @@ -64,10 +64,10 @@ public class StackAnimationController extends /** Values to use for animating bubbles in. */ private static final float ANIMATE_IN_STIFFNESS = 1000f; - private static final int ANIMATE_IN_START_DELAY = 25; /** Values to use for animating updated bubble to top of stack. */ - private static final float BUBBLE_SWAP_SCALE = 0.8f; + private static final float NEW_BUBBLE_START_SCALE = 0.5f; + private static final float NEW_BUBBLE_START_Y = 100f; private static final long BUBBLE_SWAP_DURATION = 300L; /** @@ -779,48 +779,58 @@ public class StackAnimationController extends } public void animateReorder(List bubbleViews, Runnable after) { + // After the bubble going to index 0 springs above stack, update all icons + // at the same time, to avoid visibly changing bubble order before the animation completes. + Runnable updateAllIcons = () -> { + for (int newIndex = 0; newIndex < bubbleViews.size(); newIndex++) { + View view = bubbleViews.get(newIndex); + updateBadgesAndZOrder(view, newIndex); + } + }; + for (int newIndex = 0; newIndex < bubbleViews.size(); newIndex++) { View view = bubbleViews.get(newIndex); final int oldIndex = mLayout.indexOfChild(view); - animateSwap(view, oldIndex, newIndex, after); + animateSwap(view, oldIndex, newIndex, updateAllIcons, after); } } - private void animateSwap(View view, int oldIndex, int newIndex, Runnable finishReorder) { - final float newY = getStackPosition().y + newIndex * mSwapAnimationOffset; - final float swapY = newIndex == 0 - ? newY - mSwapAnimationOffset // Above top of stack - : newY + mSwapAnimationOffset; // Below where bubble will be - final ViewPropertyAnimator animator = view.animate() - .scaleX(BUBBLE_SWAP_SCALE) - .scaleY(BUBBLE_SWAP_SCALE) - .translationY(swapY) + private void animateSwap(View view, int oldIndex, int newIndex, + Runnable updateAllIcons, Runnable finishReorder) { + if (newIndex == oldIndex) { + // Add new bubble to index 0; move existing bubbles down + updateBadgesAndZOrder(view, newIndex); + if (newIndex == 0) { + animateInBubble(view, newIndex); + } else { + moveToFinalIndex(view, newIndex, finishReorder); + } + } else { + // Reorder existing bubbles + if (newIndex == 0) { + animateToFrontThenUpdateIcons(view, updateAllIcons, finishReorder); + } else { + moveToFinalIndex(view, newIndex, finishReorder); + } + } + } + + private void animateToFrontThenUpdateIcons(View v, Runnable updateAllIcons, + Runnable finishReorder) { + final ViewPropertyAnimator animator = v.animate() + .translationY(getStackPosition().y - mSwapAnimationOffset) .setDuration(BUBBLE_SWAP_DURATION) .withEndAction(() -> { - finishSwapAnimation(view, oldIndex, newIndex, finishReorder); + updateAllIcons.run(); + moveToFinalIndex(v, 0 /* index */, finishReorder); }); - view.setTag(R.id.reorder_animator_tag, animator); + v.setTag(R.id.reorder_animator_tag, animator); } - private void finishSwapAnimation(View view, int oldIndex, int newIndex, + private void moveToFinalIndex(View view, int newIndex, Runnable finishReorder) { - - // At this point, swapping bubbles have the least overlap. - // Update z-index and badge visibility here for least jarring transition. - view.setZ((mMaxBubbles * mElevation) - newIndex); - BadgedImageView bv = (BadgedImageView) view; - if (oldIndex == 0 && newIndex > 0) { - bv.hideDotAndBadge(!isStackOnLeftSide()); - } else if (oldIndex > 0 && newIndex == 0) { - bv.showDotAndBadge(!isStackOnLeftSide()); - } - - // Animate bubble back into stack, at new index and original size. - final float newY = getStackPosition().y + newIndex * mStackOffset; final ViewPropertyAnimator animator = view.animate() - .scaleX(1f) - .scaleY(1f) - .translationY(newY) + .translationY(getStackPosition().y + newIndex * mStackOffset) .setDuration(BUBBLE_SWAP_DURATION) .withEndAction(() -> { view.setTag(R.id.reorder_animator_tag, null); @@ -829,13 +839,19 @@ public class StackAnimationController extends view.setTag(R.id.reorder_animator_tag, animator); } - @Override - void onChildReordered(View child, int oldIndex, int newIndex) { - if (isStackPositionSet()) { - setStackPosition(mStackPosition); + private void updateBadgesAndZOrder(View v, int index) { + v.setZ((mMaxBubbles * mElevation) - index); + BadgedImageView bv = (BadgedImageView) v; + if (index == 0) { + bv.showDotAndBadge(!isStackOnLeftSide()); + } else { + bv.hideDotAndBadge(!isStackOnLeftSide()); } } + @Override + void onChildReordered(View child, int oldIndex, int newIndex) {} + @Override void onActiveControllerForLayout(PhysicsAnimationLayout layout) { Resources res = layout.getResources(); @@ -943,35 +959,29 @@ public class StackAnimationController extends } /** Animates in the given bubble. */ - private void animateInBubble(View child, int index) { + private void animateInBubble(View v, int index) { if (!isActiveController()) { return; } - + v.setTranslationX(mStackPosition.x); final float yOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_Y); - - // Position the new bubble in the correct position, scaled down completely. - child.setTranslationX(mStackPosition.x); - child.setTranslationY(mStackPosition.y + yOffset * index); - child.setScaleX(0f); - child.setScaleY(0f); - - // Push the subsequent views out of the way, if there are subsequent views. - if (index + 1 < mLayout.getChildCount()) { - animationForChildAtIndex(index + 1) - .translationY(mStackPosition.y + yOffset * (index + 1)) - .withStiffness(SpringForce.STIFFNESS_LOW) - .start(); - } - - // Scale in the new bubble, slightly delayed. - animationForChild(child) + final float endY = mStackPosition.y + yOffset * index; + final float startY = endY + NEW_BUBBLE_START_Y; + v.setTranslationY(startY); + v.setScaleX(NEW_BUBBLE_START_SCALE); + v.setScaleY(NEW_BUBBLE_START_SCALE); + v.setAlpha(0f); + final ViewPropertyAnimator animator = v.animate() + .translationY(endY) .scaleX(1f) .scaleY(1f) - .withStiffness(ANIMATE_IN_STIFFNESS) - .withStartDelay(mLayout.getChildCount() > 1 ? ANIMATE_IN_START_DELAY : 0) - .start(); + .alpha(1f) + .setDuration(BUBBLE_SWAP_DURATION) + .withEndAction(() -> { + v.setTag(R.id.reorder_animator_tag, null); + }); + v.setTag(R.id.reorder_animator_tag, animator); } /**