Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java +5 −1 Original line number Diff line number Diff line Loading @@ -131,7 +131,11 @@ public class BadgedImageView extends ImageView { */ public void setRenderedBubble(BubbleViewProvider bubble) { mBubble = bubble; if (mDotSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK)) { hideBadge(); } else { showBadge(); } mDotColor = bubble.getDotColor(); drawDot(bubble.getDotPath()); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +12 −9 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -1481,7 +1483,7 @@ public class BubbleStackView extends FrameLayout }; if (mIsExpanded) { reorder.run(); updateBubbleIcons(); updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */); } else { List<View> bubbleViews = bubbles.stream() .map(b -> b.getIconView()).collect(Collectors.toList()); Loading Loading @@ -2616,30 +2618,31 @@ 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) { } else if (setBadgeForCollapsedStack) { if (i == 0) { bv.showDotAndBadge(!mStackOnLeftOrWillBe); } else { bv.hideDotAndBadge(!mStackOnLeftOrWillBe); } } } } private void updatePointerPosition() { if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java +67 −57 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -779,48 +779,58 @@ public class StackAnimationController extends } public void animateReorder(List<View> 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); Loading @@ -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(); Loading Loading @@ -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); } /** Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java +5 −1 Original line number Diff line number Diff line Loading @@ -131,7 +131,11 @@ public class BadgedImageView extends ImageView { */ public void setRenderedBubble(BubbleViewProvider bubble) { mBubble = bubble; if (mDotSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK)) { hideBadge(); } else { showBadge(); } mDotColor = bubble.getDotColor(); drawDot(bubble.getDotPath()); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +12 −9 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -1481,7 +1483,7 @@ public class BubbleStackView extends FrameLayout }; if (mIsExpanded) { reorder.run(); updateBubbleIcons(); updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */); } else { List<View> bubbleViews = bubbles.stream() .map(b -> b.getIconView()).collect(Collectors.toList()); Loading Loading @@ -2616,30 +2618,31 @@ 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) { } else if (setBadgeForCollapsedStack) { if (i == 0) { bv.showDotAndBadge(!mStackOnLeftOrWillBe); } else { bv.hideDotAndBadge(!mStackOnLeftOrWillBe); } } } } private void updatePointerPosition() { if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java +67 −57 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -779,48 +779,58 @@ public class StackAnimationController extends } public void animateReorder(List<View> 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); Loading @@ -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(); Loading Loading @@ -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); } /** Loading