Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +61 −44 Original line number Diff line number Diff line Loading @@ -124,7 +124,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @Retention(SOURCE) @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED, DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE, DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT}) DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT, DISMISS_OVERFLOW_MAX_REACHED}) @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) @interface DismissReason {} Loading @@ -138,6 +139,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi static final int DISMISS_USER_CHANGED = 8; static final int DISMISS_GROUP_CANCELLED = 9; static final int DISMISS_INVALID_INTENT = 10; static final int DISMISS_OVERFLOW_MAX_REACHED = 11; private final Context mContext; private final NotificationEntryManager mNotificationEntryManager; Loading Loading @@ -469,7 +471,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (userRemovedNotif) { return handleDismissalInterception(entry); } return false; } }); Loading Loading @@ -740,18 +741,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi */ public boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry) { String key = entry.getKey(); boolean isBubbleAndSuppressed = mBubbleData.hasBubbleWithKey(key) && !mBubbleData.getBubbleWithKey(key).showInShade(); boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key) && !mBubbleData.getAnyBubbleWithkey(key).showInShade()); String groupKey = entry.getSbn().getGroupKey(); boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey); boolean isSummary = key.equals(mBubbleData.getSummaryKey(groupKey)); return (isSummary && isSuppressedSummary) || isBubbleAndSuppressed; return (isSummary && isSuppressedSummary) || isSuppressedBubble; } void promoteBubbleFromOverflow(Bubble bubble) { bubble.setInflateSynchronously(mInflateSynchronously); setIsBubble(bubble, /* isBubble */ true); mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory); } Loading @@ -761,11 +762,16 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * @param notificationKey the notification key for the bubble to be selected */ public void expandStackAndSelectBubble(String notificationKey) { Bubble bubble = mBubbleData.getBubbleWithKey(notificationKey); Bubble bubble = mBubbleData.getBubbleInStackWithKey(notificationKey); if (bubble == null) { bubble = mBubbleData.getOverflowBubbleWithKey(notificationKey); if (bubble != null) { mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory); } } else if (bubble.getEntry().isBubble()){ mBubbleData.setSelectedBubble(bubble); mBubbleData.setExpanded(true); } mBubbleData.setExpanded(true); } /** Loading Loading @@ -870,7 +876,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi */ @MainThread void removeBubble(NotificationEntry entry, int reason) { if (mBubbleData.hasBubbleWithKey(entry.getKey())) { if (mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { mBubbleData.notificationEntryRemoved(entry, reason); } } Loading @@ -885,7 +891,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi private void onEntryUpdated(NotificationEntry entry) { boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry) && canLaunchInActivityView(mContext, entry); if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) { if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { // It was previously a bubble but no longer a bubble -- lets remove it removeBubble(entry, DISMISS_NO_LONGER_BUBBLE); } else if (shouldBubble) { Loading Loading @@ -924,7 +930,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi String key = orderedKeys[i]; NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key); rankingMap.getRanking(key, mTmpRanking); boolean isActiveBubble = mBubbleData.hasBubbleWithKey(key); boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key); if (isActiveBubble && !mTmpRanking.canBubble()) { mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED); } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) { Loading @@ -934,6 +940,19 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } private void setIsBubble(Bubble b, boolean isBubble) { if (isBubble) { b.getEntry().getSbn().getNotification().flags |= FLAG_BUBBLE; } else { b.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE; } try { mBarService.onNotificationBubbleChanged(b.getKey(), isBubble, 0); } catch (RemoteException e) { // Bad things have happened } } @SuppressWarnings("FieldCanBeLocal") private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() { Loading @@ -956,30 +975,31 @@ public class BubbleController implements ConfigurationController.ConfigurationLi final Bubble bubble = removed.first; @DismissReason final int reason = removed.second; mStackView.removeBubble(bubble); // If the bubble is removed for user switching, leave the notification in place. if (reason != DISMISS_USER_CHANGED) { if (!mBubbleData.hasBubbleWithKey(bubble.getKey()) && !bubble.showInShade()) { if (reason == DISMISS_USER_CHANGED) { continue; } if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { if (!mBubbleData.hasOverflowBubbleWithKey(bubble.getKey()) && (!bubble.showInShade() || reason == DISMISS_NOTIF_CANCEL || reason == DISMISS_GROUP_CANCELLED)) { // The bubble is now gone & the notification is hidden from the shade, so // time to actually remove it for (NotifCallback cb : mCallbacks) { cb.removeNotification(bubble.getEntry(), REASON_CANCEL); } } else { // Update the flag for SysUI bubble.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE; if (bubble.getEntry().isBubble() && bubble.showInShade()) { setIsBubble(bubble, /* isBubble */ false); } if (bubble.getEntry().getRow() != null) { bubble.getEntry().getRow().updateBubbleButton(); } // Update the state in NotificationManagerService try { mBarService.onNotificationBubbleChanged(bubble.getKey(), false /* isBubble */, 0 /* flags */); } catch (RemoteException e) { } } } final String groupKey = bubble.getEntry().getSbn().getGroupKey(); if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) { // Time to potentially remove the summary Loading @@ -988,7 +1008,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } } } if (update.addedBubble != null) { mStackView.addBubble(update.addedBubble); Loading Loading @@ -1034,7 +1053,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } Log.d(TAG, "\n[BubbleData] overflow:"); Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getOverflowBubbles(), null)); null) + "\n"); } } }; Loading @@ -1053,21 +1072,19 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (entry == null) { return false; } final boolean interceptBubbleDismissal = mBubbleData.hasBubbleWithKey(entry.getKey()) && entry.isBubble(); final boolean interceptSummaryDismissal = isSummaryOfBubbles(entry); if (interceptSummaryDismissal) { if (isSummaryOfBubbles(entry)) { handleSummaryDismissalInterception(entry); } else if (interceptBubbleDismissal) { Bubble bubble = mBubbleData.getBubbleWithKey(entry.getKey()); bubble.setSuppressNotification(true); bubble.setShowDot(false /* show */); } else { Bubble bubble = mBubbleData.getBubbleInStackWithKey(entry.getKey()); if (bubble == null || !entry.isBubble()) { bubble = mBubbleData.getOverflowBubbleWithKey(entry.getKey()); } if (bubble == null) { return false; } bubble.setSuppressNotification(true); bubble.setShowDot(false /* show */); } // Update the shade for (NotifCallback cb : mCallbacks) { cb.invalidateNotifications("BubbleController.handleDismissalInterception"); Loading Loading @@ -1096,11 +1113,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (children != null) { for (int i = 0; i < children.size(); i++) { NotificationEntry child = children.get(i); if (mBubbleData.hasBubbleWithKey(child.getKey())) { if (mBubbleData.hasAnyBubbleWithKey(child.getKey())) { // Suppress the bubbled child // As far as group manager is concerned, once a child is no longer shown // in the shade, it is essentially removed. Bubble bubbleChild = mBubbleData.getBubbleWithKey(child.getKey()); Bubble bubbleChild = mBubbleData.getAnyBubbleWithkey(child.getKey()); mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry()); bubbleChild.setSuppressNotification(true); bubbleChild.setShowDot(false /* show */); Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +72 −31 Original line number Diff line number Diff line Loading @@ -123,7 +123,7 @@ public class BubbleData { private boolean mShowingOverflow; private boolean mExpanded; private final int mMaxBubbles; private final int mMaxOverflowBubbles; private int mMaxOverflowBubbles; // State tracked during an operation -- keeps track of what listener events to dispatch. private Update mStateChange; Loading Loading @@ -175,8 +175,16 @@ public class BubbleData { return mExpanded; } public boolean hasBubbleWithKey(String key) { return getBubbleWithKey(key) != null; public boolean hasAnyBubbleWithKey(String key) { return hasBubbleInStackWithKey(key) || hasOverflowBubbleWithKey(key); } public boolean hasBubbleInStackWithKey(String key) { return getBubbleInStackWithKey(key) != null; } public boolean hasOverflowBubbleWithKey(String key) { return getOverflowBubbleWithKey(key) != null; } @Nullable Loading Loading @@ -206,6 +214,8 @@ public class BubbleData { Log.d(TAG, "promoteBubbleFromOverflow: " + bubble); } moveOverflowBubbleToPending(bubble); // Preserve new order for next repack, which sorts by last updated time. bubble.markUpdatedAt(mTimeSource.currentTimeMillis()); bubble.inflate( b -> { notificationEntryUpdated(bubble, /* suppressFlyout */ Loading @@ -221,8 +231,6 @@ public class BubbleData { } private void moveOverflowBubbleToPending(Bubble b) { // Preserve new order for next repack, which sorts by last updated time. b.markUpdatedAt(mTimeSource.currentTimeMillis()); mOverflowBubbles.remove(b); mPendingBubbles.add(b); } Loading @@ -233,15 +241,16 @@ public class BubbleData { * for that. */ Bubble getOrCreateBubble(NotificationEntry entry) { Bubble bubble = getBubbleWithKey(entry.getKey()); if (bubble == null) { for (int i = 0; i < mOverflowBubbles.size(); i++) { Bubble b = mOverflowBubbles.get(i); if (b.getKey().equals(entry.getKey())) { moveOverflowBubbleToPending(b); b.setEntry(entry); return b; } String key = entry.getKey(); Bubble bubble = getBubbleInStackWithKey(entry.getKey()); if (bubble != null) { bubble.setEntry(entry); } else { bubble = getOverflowBubbleWithKey(key); if (bubble != null) { moveOverflowBubbleToPending(bubble); bubble.setEntry(entry); return bubble; } // Check for it in pending for (int i = 0; i < mPendingBubbles.size(); i++) { Loading @@ -253,8 +262,6 @@ public class BubbleData { } bubble = new Bubble(entry, mSuppressionListener); mPendingBubbles.add(bubble); } else { bubble.setEntry(entry); } return bubble; } Loading @@ -269,7 +276,7 @@ public class BubbleData { Log.d(TAG, "notificationEntryUpdated: " + bubble); } mPendingBubbles.remove(bubble); // No longer pending once we're here Bubble prevBubble = getBubbleWithKey(bubble.getKey()); Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey()); suppressFlyout |= !bubble.getEntry().getRanking().visuallyInterruptive(); if (prevBubble == null) { Loading Loading @@ -422,6 +429,19 @@ public class BubbleData { } int indexToRemove = indexForKey(key); if (indexToRemove == -1) { if (hasOverflowBubbleWithKey(key) && (reason == BubbleController.DISMISS_NOTIF_CANCEL || reason == BubbleController.DISMISS_GROUP_CANCELLED || reason == BubbleController.DISMISS_NO_LONGER_BUBBLE || reason == BubbleController.DISMISS_BLOCKED)) { Bubble b = getOverflowBubbleWithKey(key); if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "Cancel overflow bubble: " + b); } mStateChange.bubbleRemoved(b, reason); mOverflowBubbles.remove(b); } return; } Bubble bubbleToRemove = mBubbles.get(indexToRemove); Loading Loading @@ -453,8 +473,10 @@ public class BubbleData { } void overflowBubble(@DismissReason int reason, Bubble bubble) { if (reason == BubbleController.DISMISS_AGED || reason == BubbleController.DISMISS_USER_GESTURE) { if (!(reason == BubbleController.DISMISS_AGED || reason == BubbleController.DISMISS_USER_GESTURE)) { return; } if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "Overflowing: " + bubble); } Loading @@ -462,12 +484,12 @@ public class BubbleData { bubble.stopInflation(); if (mOverflowBubbles.size() == mMaxOverflowBubbles + 1) { // Remove oldest bubble. Bubble oldest = mOverflowBubbles.get(mOverflowBubbles.size() - 1); if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "Overflow full. Remove: " + mOverflowBubbles.get( mOverflowBubbles.size() - 1)); } mOverflowBubbles.remove(mOverflowBubbles.size() - 1); Log.d(TAG, "Overflow full. Remove: " + oldest); } mStateChange.bubbleRemoved(oldest, BubbleController.DISMISS_OVERFLOW_MAX_REACHED); mOverflowBubbles.remove(oldest); } } Loading Loading @@ -764,7 +786,17 @@ public class BubbleData { @VisibleForTesting(visibility = PRIVATE) @Nullable Bubble getBubbleWithKey(String key) { Bubble getAnyBubbleWithkey(String key) { Bubble b = getBubbleInStackWithKey(key); if (b == null) { b = getOverflowBubbleWithKey(key); } return b; } @VisibleForTesting(visibility = PRIVATE) @Nullable Bubble getBubbleInStackWithKey(String key) { for (int i = 0; i < mBubbles.size(); i++) { Bubble bubble = mBubbles.get(i); if (bubble.getKey().equals(key)) { Loading Loading @@ -805,6 +837,15 @@ public class BubbleData { mListener = listener; } /** * Set maximum number of bubbles allowed in overflow. * This method should only be used in tests, not in production. */ @VisibleForTesting void setMaxOverflowBubbles(int maxOverflowBubbles) { mMaxOverflowBubbles = maxOverflowBubbles; } /** * Description of current bubble data state. */ Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java +1 −1 Original line number Diff line number Diff line Loading @@ -71,7 +71,7 @@ public class BubbleExperimentConfig { private static final String WHITELISTED_AUTO_BUBBLE_APPS = "whitelisted_auto_bubble_apps"; private static final String ALLOW_BUBBLE_OVERFLOW = "allow_bubble_overflow"; private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = false; private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = true; /** * When true, if a notification has the information necessary to bubble (i.e. valid Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +6 −7 Original line number Diff line number Diff line Loading @@ -906,7 +906,7 @@ public class BubbleStackView extends FrameLayout { view -> { showManageMenu(false /* show */); final Bubble bubble = mBubbleData.getSelectedBubble(); if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) { if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { mUnbubbleConversationCallback.accept(bubble.getEntry()); } }); Loading @@ -915,7 +915,7 @@ public class BubbleStackView extends FrameLayout { view -> { showManageMenu(false /* show */); final Bubble bubble = mBubbleData.getSelectedBubble(); if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) { if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { final Intent intent = bubble.getSettingsIntent(); collapseStack(() -> { mContext.startActivityAsUser( Loading Loading @@ -1756,14 +1756,13 @@ public class BubbleStackView extends FrameLayout { if (mIsExpanded) { final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject(); dismissBubbleIfExists(mBubbleData.getBubbleWithView(draggedOutBubbleView)); } else { mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); } } private void dismissBubbleIfExists(@Nullable Bubble bubble) { if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) { if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { mBubbleData.notificationEntryRemoved( bubble.getEntry(), BubbleController.DISMISS_USER_GESTURE); } Loading Loading @@ -2024,8 +2023,8 @@ public class BubbleStackView extends FrameLayout { // If available, update the manage menu's settings option with the expanded bubble's app // name and icon. if (show && mBubbleData.hasBubbleWithKey(mExpandedBubble.getKey())) { final Bubble bubble = mBubbleData.getBubbleWithKey(mExpandedBubble.getKey()); if (show && mBubbleData.hasBubbleInStackWithKey(mExpandedBubble.getKey())) { final Bubble bubble = mBubbleData.getBubbleInStackWithKey(mExpandedBubble.getKey()); mManageSettingsIcon.setImageDrawable(bubble.getBadgedAppIcon()); mManageSettingsText.setText(getResources().getString( R.string.bubbles_app_settings, bubble.getAppName())); Loading Loading @@ -2241,7 +2240,7 @@ public class BubbleStackView extends FrameLayout { View child = mBubbleContainer.getChildAt(i); if (child instanceof BadgedImageView) { String key = ((BadgedImageView) child).getKey(); Bubble bubble = mBubbleData.getBubbleWithKey(key); Bubble bubble = mBubbleData.getBubbleInStackWithKey(key); bubbles.add(bubble); } } Loading packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +103 −49 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +61 −44 Original line number Diff line number Diff line Loading @@ -124,7 +124,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @Retention(SOURCE) @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED, DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE, DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT}) DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT, DISMISS_OVERFLOW_MAX_REACHED}) @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) @interface DismissReason {} Loading @@ -138,6 +139,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi static final int DISMISS_USER_CHANGED = 8; static final int DISMISS_GROUP_CANCELLED = 9; static final int DISMISS_INVALID_INTENT = 10; static final int DISMISS_OVERFLOW_MAX_REACHED = 11; private final Context mContext; private final NotificationEntryManager mNotificationEntryManager; Loading Loading @@ -469,7 +471,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (userRemovedNotif) { return handleDismissalInterception(entry); } return false; } }); Loading Loading @@ -740,18 +741,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi */ public boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry) { String key = entry.getKey(); boolean isBubbleAndSuppressed = mBubbleData.hasBubbleWithKey(key) && !mBubbleData.getBubbleWithKey(key).showInShade(); boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key) && !mBubbleData.getAnyBubbleWithkey(key).showInShade()); String groupKey = entry.getSbn().getGroupKey(); boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey); boolean isSummary = key.equals(mBubbleData.getSummaryKey(groupKey)); return (isSummary && isSuppressedSummary) || isBubbleAndSuppressed; return (isSummary && isSuppressedSummary) || isSuppressedBubble; } void promoteBubbleFromOverflow(Bubble bubble) { bubble.setInflateSynchronously(mInflateSynchronously); setIsBubble(bubble, /* isBubble */ true); mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory); } Loading @@ -761,11 +762,16 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * @param notificationKey the notification key for the bubble to be selected */ public void expandStackAndSelectBubble(String notificationKey) { Bubble bubble = mBubbleData.getBubbleWithKey(notificationKey); Bubble bubble = mBubbleData.getBubbleInStackWithKey(notificationKey); if (bubble == null) { bubble = mBubbleData.getOverflowBubbleWithKey(notificationKey); if (bubble != null) { mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory); } } else if (bubble.getEntry().isBubble()){ mBubbleData.setSelectedBubble(bubble); mBubbleData.setExpanded(true); } mBubbleData.setExpanded(true); } /** Loading Loading @@ -870,7 +876,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi */ @MainThread void removeBubble(NotificationEntry entry, int reason) { if (mBubbleData.hasBubbleWithKey(entry.getKey())) { if (mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { mBubbleData.notificationEntryRemoved(entry, reason); } } Loading @@ -885,7 +891,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi private void onEntryUpdated(NotificationEntry entry) { boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry) && canLaunchInActivityView(mContext, entry); if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) { if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { // It was previously a bubble but no longer a bubble -- lets remove it removeBubble(entry, DISMISS_NO_LONGER_BUBBLE); } else if (shouldBubble) { Loading Loading @@ -924,7 +930,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi String key = orderedKeys[i]; NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key); rankingMap.getRanking(key, mTmpRanking); boolean isActiveBubble = mBubbleData.hasBubbleWithKey(key); boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key); if (isActiveBubble && !mTmpRanking.canBubble()) { mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED); } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) { Loading @@ -934,6 +940,19 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } private void setIsBubble(Bubble b, boolean isBubble) { if (isBubble) { b.getEntry().getSbn().getNotification().flags |= FLAG_BUBBLE; } else { b.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE; } try { mBarService.onNotificationBubbleChanged(b.getKey(), isBubble, 0); } catch (RemoteException e) { // Bad things have happened } } @SuppressWarnings("FieldCanBeLocal") private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() { Loading @@ -956,30 +975,31 @@ public class BubbleController implements ConfigurationController.ConfigurationLi final Bubble bubble = removed.first; @DismissReason final int reason = removed.second; mStackView.removeBubble(bubble); // If the bubble is removed for user switching, leave the notification in place. if (reason != DISMISS_USER_CHANGED) { if (!mBubbleData.hasBubbleWithKey(bubble.getKey()) && !bubble.showInShade()) { if (reason == DISMISS_USER_CHANGED) { continue; } if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { if (!mBubbleData.hasOverflowBubbleWithKey(bubble.getKey()) && (!bubble.showInShade() || reason == DISMISS_NOTIF_CANCEL || reason == DISMISS_GROUP_CANCELLED)) { // The bubble is now gone & the notification is hidden from the shade, so // time to actually remove it for (NotifCallback cb : mCallbacks) { cb.removeNotification(bubble.getEntry(), REASON_CANCEL); } } else { // Update the flag for SysUI bubble.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE; if (bubble.getEntry().isBubble() && bubble.showInShade()) { setIsBubble(bubble, /* isBubble */ false); } if (bubble.getEntry().getRow() != null) { bubble.getEntry().getRow().updateBubbleButton(); } // Update the state in NotificationManagerService try { mBarService.onNotificationBubbleChanged(bubble.getKey(), false /* isBubble */, 0 /* flags */); } catch (RemoteException e) { } } } final String groupKey = bubble.getEntry().getSbn().getGroupKey(); if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) { // Time to potentially remove the summary Loading @@ -988,7 +1008,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } } } if (update.addedBubble != null) { mStackView.addBubble(update.addedBubble); Loading Loading @@ -1034,7 +1053,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } Log.d(TAG, "\n[BubbleData] overflow:"); Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getOverflowBubbles(), null)); null) + "\n"); } } }; Loading @@ -1053,21 +1072,19 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (entry == null) { return false; } final boolean interceptBubbleDismissal = mBubbleData.hasBubbleWithKey(entry.getKey()) && entry.isBubble(); final boolean interceptSummaryDismissal = isSummaryOfBubbles(entry); if (interceptSummaryDismissal) { if (isSummaryOfBubbles(entry)) { handleSummaryDismissalInterception(entry); } else if (interceptBubbleDismissal) { Bubble bubble = mBubbleData.getBubbleWithKey(entry.getKey()); bubble.setSuppressNotification(true); bubble.setShowDot(false /* show */); } else { Bubble bubble = mBubbleData.getBubbleInStackWithKey(entry.getKey()); if (bubble == null || !entry.isBubble()) { bubble = mBubbleData.getOverflowBubbleWithKey(entry.getKey()); } if (bubble == null) { return false; } bubble.setSuppressNotification(true); bubble.setShowDot(false /* show */); } // Update the shade for (NotifCallback cb : mCallbacks) { cb.invalidateNotifications("BubbleController.handleDismissalInterception"); Loading Loading @@ -1096,11 +1113,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (children != null) { for (int i = 0; i < children.size(); i++) { NotificationEntry child = children.get(i); if (mBubbleData.hasBubbleWithKey(child.getKey())) { if (mBubbleData.hasAnyBubbleWithKey(child.getKey())) { // Suppress the bubbled child // As far as group manager is concerned, once a child is no longer shown // in the shade, it is essentially removed. Bubble bubbleChild = mBubbleData.getBubbleWithKey(child.getKey()); Bubble bubbleChild = mBubbleData.getAnyBubbleWithkey(child.getKey()); mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry()); bubbleChild.setSuppressNotification(true); bubbleChild.setShowDot(false /* show */); Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +72 −31 Original line number Diff line number Diff line Loading @@ -123,7 +123,7 @@ public class BubbleData { private boolean mShowingOverflow; private boolean mExpanded; private final int mMaxBubbles; private final int mMaxOverflowBubbles; private int mMaxOverflowBubbles; // State tracked during an operation -- keeps track of what listener events to dispatch. private Update mStateChange; Loading Loading @@ -175,8 +175,16 @@ public class BubbleData { return mExpanded; } public boolean hasBubbleWithKey(String key) { return getBubbleWithKey(key) != null; public boolean hasAnyBubbleWithKey(String key) { return hasBubbleInStackWithKey(key) || hasOverflowBubbleWithKey(key); } public boolean hasBubbleInStackWithKey(String key) { return getBubbleInStackWithKey(key) != null; } public boolean hasOverflowBubbleWithKey(String key) { return getOverflowBubbleWithKey(key) != null; } @Nullable Loading Loading @@ -206,6 +214,8 @@ public class BubbleData { Log.d(TAG, "promoteBubbleFromOverflow: " + bubble); } moveOverflowBubbleToPending(bubble); // Preserve new order for next repack, which sorts by last updated time. bubble.markUpdatedAt(mTimeSource.currentTimeMillis()); bubble.inflate( b -> { notificationEntryUpdated(bubble, /* suppressFlyout */ Loading @@ -221,8 +231,6 @@ public class BubbleData { } private void moveOverflowBubbleToPending(Bubble b) { // Preserve new order for next repack, which sorts by last updated time. b.markUpdatedAt(mTimeSource.currentTimeMillis()); mOverflowBubbles.remove(b); mPendingBubbles.add(b); } Loading @@ -233,15 +241,16 @@ public class BubbleData { * for that. */ Bubble getOrCreateBubble(NotificationEntry entry) { Bubble bubble = getBubbleWithKey(entry.getKey()); if (bubble == null) { for (int i = 0; i < mOverflowBubbles.size(); i++) { Bubble b = mOverflowBubbles.get(i); if (b.getKey().equals(entry.getKey())) { moveOverflowBubbleToPending(b); b.setEntry(entry); return b; } String key = entry.getKey(); Bubble bubble = getBubbleInStackWithKey(entry.getKey()); if (bubble != null) { bubble.setEntry(entry); } else { bubble = getOverflowBubbleWithKey(key); if (bubble != null) { moveOverflowBubbleToPending(bubble); bubble.setEntry(entry); return bubble; } // Check for it in pending for (int i = 0; i < mPendingBubbles.size(); i++) { Loading @@ -253,8 +262,6 @@ public class BubbleData { } bubble = new Bubble(entry, mSuppressionListener); mPendingBubbles.add(bubble); } else { bubble.setEntry(entry); } return bubble; } Loading @@ -269,7 +276,7 @@ public class BubbleData { Log.d(TAG, "notificationEntryUpdated: " + bubble); } mPendingBubbles.remove(bubble); // No longer pending once we're here Bubble prevBubble = getBubbleWithKey(bubble.getKey()); Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey()); suppressFlyout |= !bubble.getEntry().getRanking().visuallyInterruptive(); if (prevBubble == null) { Loading Loading @@ -422,6 +429,19 @@ public class BubbleData { } int indexToRemove = indexForKey(key); if (indexToRemove == -1) { if (hasOverflowBubbleWithKey(key) && (reason == BubbleController.DISMISS_NOTIF_CANCEL || reason == BubbleController.DISMISS_GROUP_CANCELLED || reason == BubbleController.DISMISS_NO_LONGER_BUBBLE || reason == BubbleController.DISMISS_BLOCKED)) { Bubble b = getOverflowBubbleWithKey(key); if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "Cancel overflow bubble: " + b); } mStateChange.bubbleRemoved(b, reason); mOverflowBubbles.remove(b); } return; } Bubble bubbleToRemove = mBubbles.get(indexToRemove); Loading Loading @@ -453,8 +473,10 @@ public class BubbleData { } void overflowBubble(@DismissReason int reason, Bubble bubble) { if (reason == BubbleController.DISMISS_AGED || reason == BubbleController.DISMISS_USER_GESTURE) { if (!(reason == BubbleController.DISMISS_AGED || reason == BubbleController.DISMISS_USER_GESTURE)) { return; } if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "Overflowing: " + bubble); } Loading @@ -462,12 +484,12 @@ public class BubbleData { bubble.stopInflation(); if (mOverflowBubbles.size() == mMaxOverflowBubbles + 1) { // Remove oldest bubble. Bubble oldest = mOverflowBubbles.get(mOverflowBubbles.size() - 1); if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "Overflow full. Remove: " + mOverflowBubbles.get( mOverflowBubbles.size() - 1)); } mOverflowBubbles.remove(mOverflowBubbles.size() - 1); Log.d(TAG, "Overflow full. Remove: " + oldest); } mStateChange.bubbleRemoved(oldest, BubbleController.DISMISS_OVERFLOW_MAX_REACHED); mOverflowBubbles.remove(oldest); } } Loading Loading @@ -764,7 +786,17 @@ public class BubbleData { @VisibleForTesting(visibility = PRIVATE) @Nullable Bubble getBubbleWithKey(String key) { Bubble getAnyBubbleWithkey(String key) { Bubble b = getBubbleInStackWithKey(key); if (b == null) { b = getOverflowBubbleWithKey(key); } return b; } @VisibleForTesting(visibility = PRIVATE) @Nullable Bubble getBubbleInStackWithKey(String key) { for (int i = 0; i < mBubbles.size(); i++) { Bubble bubble = mBubbles.get(i); if (bubble.getKey().equals(key)) { Loading Loading @@ -805,6 +837,15 @@ public class BubbleData { mListener = listener; } /** * Set maximum number of bubbles allowed in overflow. * This method should only be used in tests, not in production. */ @VisibleForTesting void setMaxOverflowBubbles(int maxOverflowBubbles) { mMaxOverflowBubbles = maxOverflowBubbles; } /** * Description of current bubble data state. */ Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java +1 −1 Original line number Diff line number Diff line Loading @@ -71,7 +71,7 @@ public class BubbleExperimentConfig { private static final String WHITELISTED_AUTO_BUBBLE_APPS = "whitelisted_auto_bubble_apps"; private static final String ALLOW_BUBBLE_OVERFLOW = "allow_bubble_overflow"; private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = false; private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = true; /** * When true, if a notification has the information necessary to bubble (i.e. valid Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +6 −7 Original line number Diff line number Diff line Loading @@ -906,7 +906,7 @@ public class BubbleStackView extends FrameLayout { view -> { showManageMenu(false /* show */); final Bubble bubble = mBubbleData.getSelectedBubble(); if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) { if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { mUnbubbleConversationCallback.accept(bubble.getEntry()); } }); Loading @@ -915,7 +915,7 @@ public class BubbleStackView extends FrameLayout { view -> { showManageMenu(false /* show */); final Bubble bubble = mBubbleData.getSelectedBubble(); if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) { if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { final Intent intent = bubble.getSettingsIntent(); collapseStack(() -> { mContext.startActivityAsUser( Loading Loading @@ -1756,14 +1756,13 @@ public class BubbleStackView extends FrameLayout { if (mIsExpanded) { final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject(); dismissBubbleIfExists(mBubbleData.getBubbleWithView(draggedOutBubbleView)); } else { mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); } } private void dismissBubbleIfExists(@Nullable Bubble bubble) { if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) { if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { mBubbleData.notificationEntryRemoved( bubble.getEntry(), BubbleController.DISMISS_USER_GESTURE); } Loading Loading @@ -2024,8 +2023,8 @@ public class BubbleStackView extends FrameLayout { // If available, update the manage menu's settings option with the expanded bubble's app // name and icon. if (show && mBubbleData.hasBubbleWithKey(mExpandedBubble.getKey())) { final Bubble bubble = mBubbleData.getBubbleWithKey(mExpandedBubble.getKey()); if (show && mBubbleData.hasBubbleInStackWithKey(mExpandedBubble.getKey())) { final Bubble bubble = mBubbleData.getBubbleInStackWithKey(mExpandedBubble.getKey()); mManageSettingsIcon.setImageDrawable(bubble.getBadgedAppIcon()); mManageSettingsText.setText(getResources().getString( R.string.bubbles_app_settings, bubble.getAppName())); Loading Loading @@ -2241,7 +2240,7 @@ public class BubbleStackView extends FrameLayout { View child = mBubbleContainer.getChildAt(i); if (child instanceof BadgedImageView) { String key = ((BadgedImageView) child).getKey(); Bubble bubble = mBubbleData.getBubbleWithKey(key); Bubble bubble = mBubbleData.getBubbleInStackWithKey(key); bubbles.add(bubble); } } Loading
packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +103 −49 File changed.Preview size limit exceeded, changes collapsed. Show changes