diff options
| author | 2020-03-27 14:45:29 +0000 | |
|---|---|---|
| committer | 2020-03-27 14:45:29 +0000 | |
| commit | eb7da42d2765c0693d375f62a369a4bfc57c9cd9 (patch) | |
| tree | ba0b290d6a307296707963a85666fda24d549100 | |
| parent | 210deda3ef927937d57d3a85f296a53c4190275c (diff) | |
| parent | 2ed260ef275f3ee68c05894615a5498b73e29dbb (diff) | |
Merge "Assorted dot-wrangling." into rvc-dev
8 files changed, 116 insertions, 67 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java index a1cb7f61ad04..0b59ebcd57e9 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java @@ -28,6 +28,8 @@ import com.android.launcher3.icons.DotRenderer; import com.android.systemui.Interpolators; import com.android.systemui.R; +import java.util.EnumSet; + /** * View that displays an adaptive icon with an app-badge and a dot. * @@ -42,12 +44,27 @@ public class BadgedImageView extends ImageView { /** Same as value in Launcher3 IconShape */ public static final int DEFAULT_PATH_SIZE = 100; - static final int DOT_STATE_DEFAULT = 0; - static final int DOT_STATE_SUPPRESSED_FOR_FLYOUT = 1; - static final int DOT_STATE_ANIMATING = 2; + /** + * Flags that suppress the visibility of the 'new' dot, for one reason or another. If any of + * these flags are set, the dot will not be shown even if {@link Bubble#showDot()} returns true. + */ + enum SuppressionFlag { + // Suppressed because the flyout is visible - it will morph into the dot via animation. + FLYOUT_VISIBLE, + // Suppressed because this bubble is behind others in the collapsed stack. + BEHIND_STACK, + } - // Flyout gets shown before the dot - private int mCurrentDotState = DOT_STATE_SUPPRESSED_FOR_FLYOUT; + /** + * Start by suppressing the dot because the flyout is visible - most bubbles are added with a + * flyout, so this is a reasonable default. + */ + private final EnumSet<SuppressionFlag> mDotSuppressionFlags = + EnumSet.of(SuppressionFlag.FLYOUT_VISIBLE); + + private float mDotScale = 0f; + private float mAnimatingToDotScale = 0f; + private boolean mDotIsAnimating = false; private BubbleViewProvider mBubble; @@ -57,8 +74,6 @@ public class BadgedImageView extends ImageView { private boolean mOnLeft; private int mDotColor; - private float mDotScale = 0f; - private boolean mDotDrawn; private Rect mTempBounds = new Rect(); @@ -88,23 +103,21 @@ public class BadgedImageView extends ImageView { /** * Updates the view with provided info. */ - public void update(BubbleViewProvider bubble) { + public void setRenderedBubble(BubbleViewProvider bubble) { mBubble = bubble; setImageBitmap(bubble.getBadgedImage()); - setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT); mDotColor = bubble.getDotColor(); drawDot(bubble.getDotPath()); - animateDot(); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); - if (isDotHidden()) { - mDotDrawn = false; + + if (!shouldDrawDot()) { return; } - mDotDrawn = mDotScale > 0.1f; + getDrawingRect(mTempBounds); mDrawParams.color = mDotColor; @@ -115,23 +128,33 @@ public class BadgedImageView extends ImageView { mDotRenderer.draw(canvas, mDrawParams); } - /** - * Sets the dot state, does not animate changes. - */ - void setDotState(int state) { - mCurrentDotState = state; - if (state == DOT_STATE_SUPPRESSED_FOR_FLYOUT || state == DOT_STATE_DEFAULT) { - mDotScale = mBubble.showDot() ? 1f : 0f; - invalidate(); + /** Adds a dot suppression flag, updating dot visibility if needed. */ + void addDotSuppressionFlag(SuppressionFlag flag) { + if (mDotSuppressionFlags.add(flag)) { + // Update dot visibility, and animate out if we're now behind the stack. + updateDotVisibility(flag == SuppressionFlag.BEHIND_STACK /* animate */); } } - /** - * Whether the dot should be hidden based on current dot state. - */ - private boolean isDotHidden() { - return (mCurrentDotState == DOT_STATE_DEFAULT && !mBubble.showDot()) - || mCurrentDotState == DOT_STATE_SUPPRESSED_FOR_FLYOUT; + /** Removes a dot suppression flag, updating dot visibility if needed. */ + void removeDotSuppressionFlag(SuppressionFlag flag) { + if (mDotSuppressionFlags.remove(flag)) { + // Update dot visibility, animating if we're no longer behind the stack. + updateDotVisibility(flag == SuppressionFlag.BEHIND_STACK); + } + } + + /** Updates the visibility of the dot, animating if requested. */ + void updateDotVisibility(boolean animate) { + final float targetScale = shouldDrawDot() ? 1f : 0f; + + if (animate) { + animateDotScale(targetScale, null /* after */); + } else { + mDotScale = targetScale; + mAnimatingToDotScale = targetScale; + invalidate(); + } } /** @@ -194,11 +217,11 @@ public class BadgedImageView extends ImageView { } /** Sets the position of the 'new' dot, animating it out and back in if requested. */ - void setDotPosition(boolean onLeft, boolean animate) { - if (animate && onLeft != getDotOnLeft() && !isDotHidden()) { - animateDot(false /* showDot */, () -> { + void setDotPositionOnLeft(boolean onLeft, boolean animate) { + if (animate && onLeft != getDotOnLeft() && shouldDrawDot()) { + animateDotScale(0f /* showDot */, () -> { setDotOnLeft(onLeft); - animateDot(true /* showDot */, null); + animateDotScale(1.0f, null /* after */); }); } else { setDotOnLeft(onLeft); @@ -209,28 +232,34 @@ public class BadgedImageView extends ImageView { return getDotOnLeft(); } - /** Changes the dot's visibility to match the bubble view's state. */ - void animateDot() { - if (mCurrentDotState == DOT_STATE_DEFAULT) { - animateDot(mBubble.showDot(), null); - } + /** Whether to draw the dot in onDraw(). */ + private boolean shouldDrawDot() { + // Always render the dot if it's animating, since it could be animating out. Otherwise, show + // it if the bubble wants to show it, and we aren't suppressing it. + return mDotIsAnimating || (mBubble.showDot() && mDotSuppressionFlags.isEmpty()); } /** - * Animates the dot to show or hide. + * Animates the dot to the given scale, running the optional callback when the animation ends. */ - private void animateDot(boolean showDot, Runnable after) { - if (mDotDrawn == showDot) { - // State is consistent, do nothing. + private void animateDotScale(float toScale, @Nullable Runnable after) { + mDotIsAnimating = true; + + // Don't restart the animation if we're already animating to the given value. + if (mAnimatingToDotScale == toScale || !shouldDrawDot()) { + mDotIsAnimating = false; return; } - setDotState(DOT_STATE_ANIMATING); + mAnimatingToDotScale = toScale; + + final boolean showDot = toScale > 0f; // Do NOT wait until after animation ends to setShowDot // to avoid overriding more recent showDot states. clearAnimation(); - animate().setDuration(200) + animate() + .setDuration(200) .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .setUpdateListener((valueAnimator) -> { float fraction = valueAnimator.getAnimatedFraction(); @@ -238,7 +267,7 @@ public class BadgedImageView extends ImageView { setDotScale(fraction); }).withEndAction(() -> { setDotScale(showDot ? 1f : 0f); - setDotState(DOT_STATE_DEFAULT); + mDotIsAnimating = false; if (after != null) { after.run(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 726a7dd111d7..afa3164cbd38 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -247,7 +247,7 @@ class Bubble implements BubbleViewProvider { mExpandedView.update(/* bubble */ this); } if (mIconView != null) { - mIconView.update(/* bubble */ this); + mIconView.setRenderedBubble(/* bubble */ this); } } @@ -306,7 +306,7 @@ class Bubble implements BubbleViewProvider { void markAsAccessedAt(long lastAccessedMillis) { mLastAccessed = lastAccessedMillis; setSuppressNotification(true); - setShowDot(false /* show */, true /* animate */); + setShowDot(false /* show */); } /** @@ -346,12 +346,11 @@ class Bubble implements BubbleViewProvider { /** * Sets whether the bubble for this notification should show a dot indicating updated content. */ - void setShowDot(boolean showDot, boolean animate) { + void setShowDot(boolean showDot) { mShowBubbleUpdateDot = showDot; - if (animate && mIconView != null) { - mIconView.animateDot(); - } else if (mIconView != null) { - mIconView.invalidate(); + + if (mIconView != null) { + mIconView.updateDotVisibility(true /* animate */); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 01c2faa62403..9d885fd3c207 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -331,14 +331,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @Override public void onZenChanged(int zen) { for (Bubble b : mBubbleData.getBubbles()) { - b.setShowDot(b.showInShade(), true /* animate */); + b.setShowDot(b.showInShade()); } } @Override public void onConfigChanged(ZenModeConfig config) { for (Bubble b : mBubbleData.getBubbles()) { - b.setShowDot(b.showInShade(), true /* animate */); + b.setShowDot(b.showInShade()); } } }); @@ -1101,7 +1101,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } else if (interceptBubbleDismissal) { Bubble bubble = mBubbleData.getBubbleWithKey(entry.getKey()); bubble.setSuppressNotification(true); - bubble.setShowDot(false /* show */, true /* animate */); + bubble.setShowDot(false /* show */); } else { return false; } @@ -1141,7 +1141,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi Bubble bubbleChild = mBubbleData.getBubbleWithKey(child.getKey()); mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry()); bubbleChild.setSuppressNotification(true); - bubbleChild.setShowDot(false /* show */, true /* animate */); + bubbleChild.setShowDot(false /* show */); } else { // non-bubbled children can be removed for (NotifCallback cb : mCallbacks) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index 2bd15188b7d3..1c69594469c1 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -288,7 +288,7 @@ public class BubbleData { boolean isBubbleExpandedAndSelected = mExpanded && mSelectedBubble == bubble; boolean suppress = isBubbleExpandedAndSelected || !showInShade || !bubble.showInShade(); bubble.setSuppressNotification(suppress); - bubble.setShowDot(!isBubbleExpandedAndSelected /* show */, true /* animate */); + bubble.setShowDot(!isBubbleExpandedAndSelected /* show */); dispatchPendingChanges(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java index 4fb2d0881ede..13669a68defa 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java @@ -112,7 +112,7 @@ public class BubbleOverflow implements BubbleViewProvider { mPath.transform(matrix); mOverflowBtn.setVisibility(GONE); - mOverflowBtn.update(this); + mOverflowBtn.setRenderedBubble(this); } ImageView getBtn() { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java index b65198595095..2231d11b7bc2 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java @@ -183,7 +183,7 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V public void onBindViewHolder(ViewHolder vh, int index) { Bubble b = mBubbles.get(index); - vh.iconView.update(b); + vh.iconView.setRenderedBubble(b); vh.iconView.setOnClickListener(view -> { mBubbles.remove(b); notifyDataSetChanged(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 7191a203ea8c..4b036812dbed 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -22,8 +22,6 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION; import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION; -import static com.android.systemui.bubbles.BadgedImageView.DOT_STATE_DEFAULT; -import static com.android.systemui.bubbles.BadgedImageView.DOT_STATE_SUPPRESSED_FOR_FLYOUT; import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW; import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; @@ -226,7 +224,7 @@ public class BubbleStackView extends FrameLayout { private boolean mIsExpanded; /** Whether the stack is currently on the left side of the screen, or animating there. */ - private boolean mStackOnLeftOrWillBe = false; + private boolean mStackOnLeftOrWillBe = true; /** Whether a touch gesture, such as a stack/bubble drag or flyout drag, is in progress. */ private boolean mIsGestureInProgress = false; @@ -936,9 +934,13 @@ public class BubbleStackView extends FrameLayout { mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide(); } + if (bubble.getIconView() == null) { + return; + } + // Set the dot position to the opposite of the side the stack is resting on, since the stack // resting slightly off-screen would result in the dot also being off-screen. - bubble.getIconView().setDotPosition( + bubble.getIconView().setDotPositionOnLeft( !mStackOnLeftOrWillBe /* onLeft */, false /* animate */); mBubbleContainer.addView(bubble.getIconView(), 0, @@ -1698,7 +1700,7 @@ public class BubbleStackView extends FrameLayout { || mBubbleToExpandAfterFlyoutCollapse != null || bubbleView == null) { if (bubbleView != null) { - bubbleView.setDotState(DOT_STATE_DEFAULT); + bubbleView.removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE); } // Skip the message if none exists, we're expanded or animating expansion, or we're // about to expand a bubble from the previous tapped flyout, or if bubble view is null. @@ -1717,12 +1719,16 @@ public class BubbleStackView extends FrameLayout { mBubbleData.setExpanded(true); mBubbleToExpandAfterFlyoutCollapse = null; } - bubbleView.setDotState(DOT_STATE_DEFAULT); + + // Stop suppressing the dot now that the flyout has morphed into the dot. + bubbleView.removeDotSuppressionFlag( + BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE); }; mFlyout.setVisibility(INVISIBLE); - // Don't show the dot when we're animating the flyout - bubbleView.setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT); + // Suppress the dot when we are animating the flyout. + bubbleView.addDotSuppressionFlag( + BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE); // Start flyout expansion. Post in case layout isn't complete and getWidth returns 0. post(() -> { @@ -1743,6 +1749,11 @@ public class BubbleStackView extends FrameLayout { }; mFlyout.postDelayed(mAnimateInFlyout, 200); }; + + if (bubble.getIconView() == null) { + return; + } + mFlyout.setupFlyoutStartingAsDot(flyoutMessage, mStackAnimationController.getStackPosition(), getWidth(), mStackAnimationController.isStackOnLeftSide(), @@ -1877,9 +1888,19 @@ public class BubbleStackView extends FrameLayout { for (int i = 0; i < bubbleCount; i++) { BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i); bv.setZ((mMaxBubbles * mBubbleElevation) - i); + // If the dot is on the left, and so is the stack, we need to change the dot position. if (bv.getDotPositionOnLeft() == mStackOnLeftOrWillBe) { - bv.setDotPosition(!mStackOnLeftOrWillBe, animate); + bv.setDotPositionOnLeft(!mStackOnLeftOrWillBe, animate); + } + + if (!mIsExpanded && i > 0) { + // If we're collapsed and this bubble is behind other bubbles, suppress its dot. + bv.addDotSuppressionFlag( + BadgedImageView.SuppressionFlag.BEHIND_STACK); + } else { + bv.removeDotSuppressionFlag( + BadgedImageView.SuppressionFlag.BEHIND_STACK); } } } 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 7ee162e03dbc..00de8b4a51b8 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -287,7 +287,7 @@ public class StackAnimationController extends /** Whether the stack is on the left side of the screen. */ public boolean isStackOnLeftSide() { if (mLayout == null || !isStackPositionSet()) { - return false; + return true; // Default to left, which is where it starts by default. } float stackCenter = mStackPosition.x + mBubbleBitmapSize / 2; |