Loading packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java +72 −43 Original line number Diff line number Diff line Loading @@ -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. * Loading @@ -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; Loading @@ -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(); Loading Loading @@ -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; Loading @@ -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(); } } /** Loading Loading @@ -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); Loading @@ -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(); Loading @@ -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(); } Loading packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +6 −7 Original line number Diff line number Diff line Loading @@ -247,7 +247,7 @@ class Bubble implements BubbleViewProvider { mExpandedView.update(/* bubble */ this); } if (mIconView != null) { mIconView.update(/* bubble */ this); mIconView.setRenderedBubble(/* bubble */ this); } } Loading Loading @@ -306,7 +306,7 @@ class Bubble implements BubbleViewProvider { void markAsAccessedAt(long lastAccessedMillis) { mLastAccessed = lastAccessedMillis; setSuppressNotification(true); setShowDot(false /* show */, true /* animate */); setShowDot(false /* show */); } /** Loading Loading @@ -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 */); } } Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +4 −4 Original line number Diff line number Diff line Loading @@ -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()); } } }); Loading Loading @@ -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; } Loading Loading @@ -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) { Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +1 −1 Original line number Diff line number Diff line Loading @@ -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(); } Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java +1 −1 Original line number Diff line number Diff line Loading @@ -112,7 +112,7 @@ public class BubbleOverflow implements BubbleViewProvider { mPath.transform(matrix); mOverflowBtn.setVisibility(GONE); mOverflowBtn.update(this); mOverflowBtn.setRenderedBubble(this); } ImageView getBtn() { Loading Loading
packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java +72 −43 Original line number Diff line number Diff line Loading @@ -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. * Loading @@ -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; Loading @@ -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(); Loading Loading @@ -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; Loading @@ -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(); } } /** Loading Loading @@ -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); Loading @@ -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(); Loading @@ -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(); } Loading
packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +6 −7 Original line number Diff line number Diff line Loading @@ -247,7 +247,7 @@ class Bubble implements BubbleViewProvider { mExpandedView.update(/* bubble */ this); } if (mIconView != null) { mIconView.update(/* bubble */ this); mIconView.setRenderedBubble(/* bubble */ this); } } Loading Loading @@ -306,7 +306,7 @@ class Bubble implements BubbleViewProvider { void markAsAccessedAt(long lastAccessedMillis) { mLastAccessed = lastAccessedMillis; setSuppressNotification(true); setShowDot(false /* show */, true /* animate */); setShowDot(false /* show */); } /** Loading Loading @@ -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 */); } } Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +4 −4 Original line number Diff line number Diff line Loading @@ -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()); } } }); Loading Loading @@ -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; } Loading Loading @@ -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) { Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +1 −1 Original line number Diff line number Diff line Loading @@ -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(); } Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java +1 −1 Original line number Diff line number Diff line Loading @@ -112,7 +112,7 @@ public class BubbleOverflow implements BubbleViewProvider { mPath.transform(matrix); mOverflowBtn.setVisibility(GONE); mOverflowBtn.update(this); mOverflowBtn.setRenderedBubble(this); } ImageView getBtn() { Loading