Loading packages/SystemUI/res/values/dimens.xml +2 −0 Original line number Diff line number Diff line Loading @@ -1218,6 +1218,8 @@ <dimen name="bubble_message_padding">4dp</dimen> <!-- Offset between bubbles in their stacked position. --> <dimen name="bubble_stack_offset">10dp</dimen> <!-- Offset between stack y and animation y for bubble swap. --> <dimen name="bubble_swap_animation_offset">15dp</dimen> <!-- How far offscreen the bubble stack rests. Cuts off padding and part of icon bitmap. --> <dimen name="bubble_stack_offscreen">9dp</dimen> <!-- How far down the screen the stack starts. --> Loading packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java +12 −0 Original line number Diff line number Diff line Loading @@ -113,6 +113,18 @@ public class BadgedImageView extends ImageView { setClickable(true); } public void showDotAndBadge(boolean onLeft) { removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.BEHIND_STACK); animateDotBadgePositions(onLeft); } public void hideDotAndBadge(boolean onLeft) { addDotSuppressionFlag(BadgedImageView.SuppressionFlag.BEHIND_STACK); mOnLeft = onLeft; hideBadge(); } /** * Updates the view with provided info. */ Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +20 −13 Original line number Diff line number Diff line Loading @@ -90,6 +90,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Consumer; import java.util.stream.Collectors; /** * Renders bubbles in a stack and handles animating expanded and collapsed states. Loading Loading @@ -1479,12 +1480,24 @@ public class BubbleStackView extends FrameLayout logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__UPDATED); } /** * Update bubble order and pointer position. */ public void updateBubbleOrder(List<Bubble> bubbles) { final Runnable reorder = () -> { for (int i = 0; i < bubbles.size(); i++) { Bubble bubble = bubbles.get(i); mBubbleContainer.reorderView(bubble.getIconView(), i); } }; if (mIsExpanded) { reorder.run(); updateBubbleIcons(); } else { List<View> bubbleViews = bubbles.stream() .map(b -> b.getIconView()).collect(Collectors.toList()); mStackAnimationController.animateReorder(bubbleViews, reorder); } updatePointerPosition(); } Loading Loading @@ -2595,17 +2608,11 @@ public class BubbleStackView extends FrameLayout bv.setZ((mMaxBubbles * mBubbleElevation) - i); if (mIsExpanded) { bv.removeDotSuppressionFlag( BadgedImageView.SuppressionFlag.BEHIND_STACK); bv.animateDotBadgePositions(false /* onLeft */); bv.showDotAndBadge(false /* onLeft */); } else if (i == 0) { bv.removeDotSuppressionFlag( BadgedImageView.SuppressionFlag.BEHIND_STACK); bv.animateDotBadgePositions(!mStackOnLeftOrWillBe); bv.showDotAndBadge(!mStackOnLeftOrWillBe); } else { bv.addDotSuppressionFlag( BadgedImageView.SuppressionFlag.BEHIND_STACK); bv.hideBadge(); bv.hideDotAndBadge(!mStackOnLeftOrWillBe); } } } Loading packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +59 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.systemui.R; import com.android.systemui.bubbles.BadgedImageView; import com.android.systemui.bubbles.BubblePositioner; import com.android.systemui.bubbles.BubbleStackView; import com.android.wm.shell.animation.PhysicsAnimator; Loading @@ -45,6 +46,7 @@ import com.google.android.collect.Sets; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.function.IntSupplier; Loading @@ -63,6 +65,10 @@ public class StackAnimationController extends 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 long BUBBLE_SWAP_DURATION = 300L; /** * Values to use for the default {@link SpringForce} provided to the physics animation layout. */ Loading Loading @@ -180,6 +186,12 @@ public class StackAnimationController extends /** Horizontal offset of bubbles in the stack. */ private float mStackOffset; /** Offset between stack y and animation y for bubble swap. */ private float mSwapAnimationOffset; /** Max number of bubbles to show in the expanded bubble row. */ private int mMaxBubbles; /** Default bubble elevation. */ private int mElevation; /** Diameter of the bubble icon. */ private int mBubbleBitmapSize; /** Width of the bubble (icon and padding). */ Loading Loading @@ -770,6 +782,50 @@ public class StackAnimationController extends } } public void animateReorder(List<View> bubbleViews, Runnable after) { for (int newIndex = 0; newIndex < bubbleViews.size(); newIndex++) { View view = bubbleViews.get(newIndex); final int oldIndex= mLayout.indexOfChild(view); animateSwap(view, oldIndex, newIndex, 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 view.animate() .scaleX(BUBBLE_SWAP_SCALE) .scaleY(BUBBLE_SWAP_SCALE) .translationY(swapY) .setDuration(BUBBLE_SWAP_DURATION) .withEndAction(() -> finishSwapAnimation(view, oldIndex, newIndex, finishReorder)); } private void finishSwapAnimation(View view, int oldIndex, 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; view.animate() .scaleX(1f) .scaleY(1f) .translationY(newY) .setDuration(BUBBLE_SWAP_DURATION) .withEndAction(() -> finishReorder.run()); } @Override void onChildReordered(View child, int oldIndex, int newIndex) { if (isStackPositionSet()) { Loading @@ -781,6 +837,9 @@ public class StackAnimationController extends void onActiveControllerForLayout(PhysicsAnimationLayout layout) { Resources res = layout.getResources(); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mSwapAnimationOffset = res.getDimensionPixelSize(R.dimen.bubble_swap_animation_offset); mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered); mElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); Loading Loading
packages/SystemUI/res/values/dimens.xml +2 −0 Original line number Diff line number Diff line Loading @@ -1218,6 +1218,8 @@ <dimen name="bubble_message_padding">4dp</dimen> <!-- Offset between bubbles in their stacked position. --> <dimen name="bubble_stack_offset">10dp</dimen> <!-- Offset between stack y and animation y for bubble swap. --> <dimen name="bubble_swap_animation_offset">15dp</dimen> <!-- How far offscreen the bubble stack rests. Cuts off padding and part of icon bitmap. --> <dimen name="bubble_stack_offscreen">9dp</dimen> <!-- How far down the screen the stack starts. --> Loading
packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java +12 −0 Original line number Diff line number Diff line Loading @@ -113,6 +113,18 @@ public class BadgedImageView extends ImageView { setClickable(true); } public void showDotAndBadge(boolean onLeft) { removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.BEHIND_STACK); animateDotBadgePositions(onLeft); } public void hideDotAndBadge(boolean onLeft) { addDotSuppressionFlag(BadgedImageView.SuppressionFlag.BEHIND_STACK); mOnLeft = onLeft; hideBadge(); } /** * Updates the view with provided info. */ Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +20 −13 Original line number Diff line number Diff line Loading @@ -90,6 +90,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Consumer; import java.util.stream.Collectors; /** * Renders bubbles in a stack and handles animating expanded and collapsed states. Loading Loading @@ -1479,12 +1480,24 @@ public class BubbleStackView extends FrameLayout logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__UPDATED); } /** * Update bubble order and pointer position. */ public void updateBubbleOrder(List<Bubble> bubbles) { final Runnable reorder = () -> { for (int i = 0; i < bubbles.size(); i++) { Bubble bubble = bubbles.get(i); mBubbleContainer.reorderView(bubble.getIconView(), i); } }; if (mIsExpanded) { reorder.run(); updateBubbleIcons(); } else { List<View> bubbleViews = bubbles.stream() .map(b -> b.getIconView()).collect(Collectors.toList()); mStackAnimationController.animateReorder(bubbleViews, reorder); } updatePointerPosition(); } Loading Loading @@ -2595,17 +2608,11 @@ public class BubbleStackView extends FrameLayout bv.setZ((mMaxBubbles * mBubbleElevation) - i); if (mIsExpanded) { bv.removeDotSuppressionFlag( BadgedImageView.SuppressionFlag.BEHIND_STACK); bv.animateDotBadgePositions(false /* onLeft */); bv.showDotAndBadge(false /* onLeft */); } else if (i == 0) { bv.removeDotSuppressionFlag( BadgedImageView.SuppressionFlag.BEHIND_STACK); bv.animateDotBadgePositions(!mStackOnLeftOrWillBe); bv.showDotAndBadge(!mStackOnLeftOrWillBe); } else { bv.addDotSuppressionFlag( BadgedImageView.SuppressionFlag.BEHIND_STACK); bv.hideBadge(); bv.hideDotAndBadge(!mStackOnLeftOrWillBe); } } } Loading
packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +59 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.systemui.R; import com.android.systemui.bubbles.BadgedImageView; import com.android.systemui.bubbles.BubblePositioner; import com.android.systemui.bubbles.BubbleStackView; import com.android.wm.shell.animation.PhysicsAnimator; Loading @@ -45,6 +46,7 @@ import com.google.android.collect.Sets; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.function.IntSupplier; Loading @@ -63,6 +65,10 @@ public class StackAnimationController extends 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 long BUBBLE_SWAP_DURATION = 300L; /** * Values to use for the default {@link SpringForce} provided to the physics animation layout. */ Loading Loading @@ -180,6 +186,12 @@ public class StackAnimationController extends /** Horizontal offset of bubbles in the stack. */ private float mStackOffset; /** Offset between stack y and animation y for bubble swap. */ private float mSwapAnimationOffset; /** Max number of bubbles to show in the expanded bubble row. */ private int mMaxBubbles; /** Default bubble elevation. */ private int mElevation; /** Diameter of the bubble icon. */ private int mBubbleBitmapSize; /** Width of the bubble (icon and padding). */ Loading Loading @@ -770,6 +782,50 @@ public class StackAnimationController extends } } public void animateReorder(List<View> bubbleViews, Runnable after) { for (int newIndex = 0; newIndex < bubbleViews.size(); newIndex++) { View view = bubbleViews.get(newIndex); final int oldIndex= mLayout.indexOfChild(view); animateSwap(view, oldIndex, newIndex, 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 view.animate() .scaleX(BUBBLE_SWAP_SCALE) .scaleY(BUBBLE_SWAP_SCALE) .translationY(swapY) .setDuration(BUBBLE_SWAP_DURATION) .withEndAction(() -> finishSwapAnimation(view, oldIndex, newIndex, finishReorder)); } private void finishSwapAnimation(View view, int oldIndex, 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; view.animate() .scaleX(1f) .scaleY(1f) .translationY(newY) .setDuration(BUBBLE_SWAP_DURATION) .withEndAction(() -> finishReorder.run()); } @Override void onChildReordered(View child, int oldIndex, int newIndex) { if (isStackPositionSet()) { Loading @@ -781,6 +837,9 @@ public class StackAnimationController extends void onActiveControllerForLayout(PhysicsAnimationLayout layout) { Resources res = layout.getResources(); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mSwapAnimationOffset = res.getDimensionPixelSize(R.dimen.bubble_swap_animation_offset); mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered); mElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); Loading