From bb53cce0dbaea0f3a6018b64db4152332fac9c5e Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Fri, 14 May 2021 13:35:25 -0700 Subject: [PATCH 1/3] Update sizes for the bubble avatar * Update sizes of badge & bubble; also simplify some of the sizing code, we can do it with just the overall size * Simplified things, instead of the inner / outer sizes just use the overall size Test: visual - create a bubble and look at the size of the bubble and badge visual - have some bubbles, change display size, create a new bubble & verify that all of the sizes are the same Bug: 183657577 Change-Id: Ibd8609a8737bb0a57519fa70fba17138bb69b379 --- .../Shell/res/layout/bubble_overflow_view.xml | 4 ++-- libs/WindowManager/Shell/res/values/dimen.xml | 16 ++++++++------ .../wm/shell/bubbles/BadgedImageView.java | 4 ++-- .../wm/shell/bubbles/BubbleExpandedView.java | 7 +++++- .../wm/shell/bubbles/BubbleFlyoutView.java | 4 +--- .../wm/shell/bubbles/BubbleIconFactory.java | 5 ++--- .../wm/shell/bubbles/BubbleOverflow.kt | 22 +++++-------------- .../wm/shell/bubbles/BubblePositioner.java | 19 +++++----------- .../wm/shell/bubbles/BubbleStackView.java | 2 +- .../wm/shell/bubbles/StackEducationView.kt | 2 +- .../ExpandedAnimationController.java | 2 +- .../animation/StackAnimationController.java | 9 +++----- .../shell/bubbles/BubbleFlyoutViewTest.java | 1 - 13 files changed, 39 insertions(+), 58 deletions(-) diff --git a/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml b/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml index d07107c8f0a01..544b731bb550c 100644 --- a/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml +++ b/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml @@ -25,8 +25,8 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/bubble_view" android:layout_gravity="center" - android:layout_width="@dimen/individual_bubble_size" - android:layout_height="@dimen/individual_bubble_size"/> + android:layout_width="@dimen/bubble_size" + android:layout_height="@dimen/bubble_size"/> 6dp 16dp - - 8dp - - 60dp - - 52dp + + 8dp + + 60dp + + 24dp 12dp @@ -120,6 +120,8 @@ 180dp 412dp + + 30dp 480dp @@ -154,7 +156,7 @@ 4dp - 10dp + 12dp 15dp 16dp - 8dp + 3dp 60dp diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 7dcedfe50b37f..a91bf046965de 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -710,6 +710,7 @@ public class BubbleController { || !newConfig.windowConfiguration.getBounds().equals(mScreenBounds)) { mDensityDpi = newConfig.densityDpi; mScreenBounds.set(newConfig.windowConfiguration.getBounds()); + mBubbleData.onMaxBubblesChanged(); mBubbleIconFactory = new BubbleIconFactory(mContext); mStackView.onDisplaySizeChanged(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 6f526ecf2b62e..69a741c674dbe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -141,9 +141,11 @@ public class BubbleData { private final BubbleOverflow mOverflow; private boolean mShowingOverflow; private boolean mExpanded; - private final int mMaxBubbles; + private int mMaxBubbles; private int mMaxOverflowBubbles; + private boolean mNeedsTrimming; + // State tracked during an operation -- keeps track of what listener events to dispatch. private Update mStateChange; @@ -180,7 +182,7 @@ public class BubbleData { mOverflowBubbles = new ArrayList<>(); mPendingBubbles = new HashMap<>(); mStateChange = new Update(mBubbles, mOverflowBubbles); - mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered); + mMaxBubbles = mPositioner.getMaxBubbles(); mMaxOverflowBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_overflow); } @@ -194,6 +196,16 @@ public class BubbleData { mCancelledListener = listener; } + public void onMaxBubblesChanged() { + mMaxBubbles = mPositioner.getMaxBubbles(); + if (!mExpanded) { + trim(); + dispatchPendingChanges(); + } else { + mNeedsTrimming = true; + } + } + public boolean hasBubbles() { return !mBubbles.isEmpty(); } @@ -455,13 +467,19 @@ public class BubbleData { private void trim() { if (mBubbles.size() > mMaxBubbles) { + int numtoRemove = mBubbles.size() - mMaxBubbles; + ArrayList toRemove = new ArrayList<>(); mBubbles.stream() // sort oldest first (ascending lastActivity) .sorted(Comparator.comparingLong(Bubble::getLastActivity)) // skip the selected bubble .filter((b) -> !b.equals(mSelectedBubble)) - .findFirst() - .ifPresent((b) -> doRemove(b.getKey(), Bubbles.DISMISS_AGED)); + .forEachOrdered((b) -> { + if (toRemove.size() < numtoRemove) { + toRemove.add(b); + } + }); + toRemove.forEach((b) -> doRemove(b.getKey(), Bubbles.DISMISS_AGED)); } } @@ -770,6 +788,10 @@ public class BubbleData { } } } + if (mNeedsTrimming) { + mNeedsTrimming = false; + trim(); + } mExpanded = shouldExpand; mStateChange.expanded = shouldExpand; mStateChange.expandedChanged = true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 62df2cb69da99..c600f56ba0c5a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -64,9 +64,12 @@ public class BubblePositioner { private Rect mPositionRect; private @Surface.Rotation int mRotation = Surface.ROTATION_0; private Insets mInsets; + private int mDefaultMaxBubbles; + private int mMaxBubbles; private int mBubbleSize; private int mBubbleBadgeSize; + private int mSpacingBetweenBubbles; private int mExpandedViewLargeScreenWidth; private int mExpandedViewPadding; private int mPointerMargin; @@ -149,17 +152,45 @@ public class BubblePositioner { Resources res = mContext.getResources(); mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size); mBubbleBadgeSize = res.getDimensionPixelSize(R.dimen.bubble_badge_size); + mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing); + mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered); + mExpandedViewLargeScreenWidth = res.getDimensionPixelSize( R.dimen.bubble_expanded_view_tablet_width); mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width); mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height); mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); + + mMaxBubbles = calculateMaxBubbles(); + if (mShowingInTaskbar) { adjustForTaskbar(); } } + /** + * @return the maximum number of bubbles that can fit on the screen when expanded. If the + * screen size / screen density is too small to support the default maximum number, then + * the number will be adjust to something lower to ensure everything is presented nicely. + */ + private int calculateMaxBubbles() { + // Use the shortest edge. + // In portrait the bubbles should align with the expanded view so subtract its padding. + // We always show the overflow so subtract one bubble size. + int padding = showBubblesVertically() ? 0 : (mExpandedViewPadding * 2); + int availableSpace = Math.min(mPositionRect.width(), mPositionRect.height()) + - padding + - mBubbleSize; + // Each of the bubbles have spacing because the overflow is at the end. + int howManyFit = availableSpace / (mBubbleSize + mSpacingBetweenBubbles); + if (howManyFit < mDefaultMaxBubbles) { + // Not enough space for the default. + return howManyFit; + } + return mDefaultMaxBubbles; + } + /** * Taskbar insets appear as navigationBar insets, however, unlike navigationBar this should * not inset bubbles UI as bubbles floats above the taskbar. This adjust the available space @@ -229,6 +260,11 @@ public class BubblePositioner { : mBubbleSize; } + /** The maximum number of bubbles that can be displayed comfortably on screen. */ + public int getMaxBubbles() { + return mMaxBubbles; + } + /** * Calculates the left & right padding for the bubble expanded view. * 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 ee822109f40ca..3680071f6b320 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 @@ -243,7 +243,6 @@ public class BubbleStackView extends FrameLayout @Nullable private RelativeStackPosition mRelativeStackPositionBeforeRotation; - private int mMaxBubbles; private int mBubbleSize; private int mBubbleElevation; private int mBubbleTouchPadding; @@ -764,7 +763,6 @@ public class BubbleStackView extends FrameLayout mBubbleData = data; Resources res = getResources(); - mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered); mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size); mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding); @@ -2725,7 +2723,7 @@ public class BubbleStackView extends FrameLayout private void updateBubbleShadows(boolean showForAllBubbles) { int bubbleCount = getBubbleCount(); for (int i = 0; i < bubbleCount; i++) { - final float z = (mMaxBubbles * mBubbleElevation) - i; + final float z = (mPositioner.getMaxBubbles() * mBubbleElevation) - i; BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i); boolean isDraggedOut = mMagnetizedObject != null && mMagnetizedObject.getUnderlyingObject().equals(bv); @@ -2759,7 +2757,7 @@ public class BubbleStackView extends FrameLayout for (int i = 0; i < bubbleCount; i++) { BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i); bv.setZ(i < NUM_VISIBLE_WHEN_RESTING - ? (mMaxBubbles * mBubbleElevation) - i + ? (mPositioner.getMaxBubbles() * mBubbleElevation) - i : 0f); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java index 9dfd8c591db55..df2b440c19df4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java @@ -211,16 +211,8 @@ public class ExpandedAnimationController mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mBubbleSizePx = mPositioner.getBubbleSize(); - mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered); - mBubblesMaxSpace = res.getDimensionPixelSize(R.dimen.bubble_spacing); - final float availableSpace = mPositioner.isLandscape() - ? mPositioner.getAvailableRect().height() - : mPositioner.getAvailableRect().width(); - final float spaceForMaxBubbles = (mExpandedViewPadding * 2) - + (mBubblesMaxRendered + 1) * mBubbleSizePx; - float spaceBetweenBubbles = - (availableSpace - spaceForMaxBubbles) / mBubblesMaxRendered; - mSpaceBetweenBubbles = Math.min(spaceBetweenBubbles, mBubblesMaxSpace); + mBubblesMaxRendered = mPositioner.getMaxBubbles(); + mSpaceBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing); } /** diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java index 2bb7204c79411..3e3195fe8dc55 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java @@ -19,9 +19,12 @@ package com.android.wm.shell.bubbles; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static junit.framework.Assert.assertNotNull; import static junit.framework.TestCase.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -90,6 +93,7 @@ public class BubbleDataTest extends ShellTestCase { private Bubble mBubbleDismissed; private BubbleData mBubbleData; + private TestableBubblePositioner mPositioner; @Mock private TimeSource mTimeSource; @@ -150,9 +154,9 @@ public class BubbleDataTest extends ShellTestCase { mMainExecutor); mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener, mPendingIntentCanceledListener, mMainExecutor); - TestableBubblePositioner positioner = new TestableBubblePositioner(mContext, + mPositioner = new TestableBubblePositioner(mContext, mock(WindowManager.class)); - mBubbleData = new BubbleData(getContext(), mBubbleLogger, positioner, + mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner, mMainExecutor); // Used by BubbleData to set lastAccessedTime @@ -810,6 +814,61 @@ public class BubbleDataTest extends ShellTestCase { assertEquals(1, mBubbleData.getOverflowBubbles().size()); } + @Test + public void test_onMaxBubblesChanged_notExpanded() { + mBubbleData.setListener(mListener); + mPositioner.setMaxBubbles(5); + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryA2, 2000); + sendUpdatedEntryAtTime(mEntryA3, 3000); + sendUpdatedEntryAtTime(mEntryB1, 4000); + sendUpdatedEntryAtTime(mEntryB2, 5000); + mBubbleData.setExpanded(false); + reset(mListener); + + mPositioner.setMaxBubbles(3); + mBubbleData.onMaxBubblesChanged(); + verifyUpdateReceived(); + + BubbleData.Update update = mUpdateCaptor.getValue(); + assertThat(update.removedBubbles.get(0)).isEqualTo( + Pair.create(mBubbleA1, Bubbles.DISMISS_AGED)); + assertThat(update.removedBubbles.get(1)).isEqualTo( + Pair.create(mBubbleA2, Bubbles.DISMISS_AGED)); + + assertNotNull(mBubbleData.getOverflowBubbleWithKey(mBubbleA1.getKey())); + assertNotNull(mBubbleData.getOverflowBubbleWithKey(mBubbleA2.getKey())); + } + + @Test + public void test_onMaxBubblesChanged_expanded() { + mBubbleData.setListener(mListener); + mPositioner.setMaxBubbles(5); + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryA2, 2000); + sendUpdatedEntryAtTime(mEntryA3, 3000); + sendUpdatedEntryAtTime(mEntryB1, 4000); + sendUpdatedEntryAtTime(mEntryB2, 5000); + mBubbleData.setExpanded(true); + reset(mListener); + + mPositioner.setMaxBubbles(3); + mBubbleData.onMaxBubblesChanged(); + verify(mListener, never()).applyUpdate(any()); + + mBubbleData.setExpanded(false); + verifyUpdateReceived(); + + BubbleData.Update update = mUpdateCaptor.getValue(); + assertThat(update.removedBubbles.get(0)).isEqualTo( + Pair.create(mBubbleA1, Bubbles.DISMISS_AGED)); + assertThat(update.removedBubbles.get(1)).isEqualTo( + Pair.create(mBubbleA2, Bubbles.DISMISS_AGED)); + + assertNotNull(mBubbleData.getOverflowBubbleWithKey(mBubbleA1.getKey())); + assertNotNull(mBubbleData.getOverflowBubbleWithKey(mBubbleA2.getKey())); + } + private void verifyUpdateReceived() { verify(mListener).applyUpdate(mUpdateCaptor.capture()); reset(mListener); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/TestableBubblePositioner.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/TestableBubblePositioner.java index 96bc5335a32cb..9027ca442e2c1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/TestableBubblePositioner.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/TestableBubblePositioner.java @@ -22,7 +22,10 @@ import android.graphics.Insets; import android.graphics.Rect; import android.view.WindowManager; +import com.android.wm.shell.R; + public class TestableBubblePositioner extends BubblePositioner { + private int mMaxBubbles; public TestableBubblePositioner(Context context, WindowManager windowManager) { @@ -31,5 +34,15 @@ public class TestableBubblePositioner extends BubblePositioner { updateInternal(Configuration.ORIENTATION_PORTRAIT, Insets.of(0, 0, 0, 0), new Rect(0, 0, 500, 1000)); + mMaxBubbles = context.getResources().getInteger(R.integer.bubbles_max_rendered); + } + + public void setMaxBubbles(int max) { + mMaxBubbles = max; + } + + @Override + public int getMaxBubbles() { + return mMaxBubbles; } }