From 2e73117c0e71554de126c75ee4135c89852bdb90 Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Mon, 15 Jun 2020 20:45:49 +0000 Subject: [PATCH] Revert "decouple Bubble from NotificationEntry" This reverts commit d07c2deaf035e8aae86fa5f0b11ef36d237d653a. Reason for revert: debug test failure in b/158986168 Bug: 158986168 Change-Id: I10cb613e4f160561c06944aebccaf16c090bcd9e --- .../com/android/systemui/bubbles/Bubble.java | 224 ++++++++---------- .../systemui/bubbles/BubbleController.java | 80 +++---- .../android/systemui/bubbles/BubbleData.java | 38 +-- .../systemui/bubbles/BubbleDataRepository.kt | 23 +- .../systemui/bubbles/BubbleExpandedView.java | 2 +- .../systemui/bubbles/BubbleFlyoutView.java | 6 +- .../systemui/bubbles/BubbleIconFactory.java | 8 +- .../systemui/bubbles/BubbleLoggerImpl.java | 7 +- .../bubbles/BubbleOverflowActivity.java | 4 +- .../systemui/bubbles/BubbleStackView.java | 10 +- .../systemui/bubbles/BubbleViewInfoTask.java | 80 ++++--- .../systemui/bubbles/storage/BubbleEntity.kt | 3 +- .../bubbles/storage/BubbleXmlHelper.kt | 5 +- .../bubbles/BubbleControllerTest.java | 45 +--- .../android/systemui/bubbles/BubbleTest.java | 18 +- .../NewNotifPipelineBubbleControllerTest.java | 26 +- .../storage/BubblePersistentRepositoryTest.kt | 2 +- .../storage/BubbleVolatileRepositoryTest.kt | 7 +- .../bubbles/storage/BubbleXmlHelperTest.kt | 24 +- 19 files changed, 288 insertions(+), 324 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 6da7bc8a2ade0..7f78ddf2cf1c5 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -15,6 +15,7 @@ */ package com.android.systemui.bubbles; +import static android.app.Notification.FLAG_BUBBLE; import static android.os.AsyncTask.Status.FINISHED; import static android.view.Display.INVALID_DISPLAY; @@ -28,19 +29,21 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Path; +import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.graphics.drawable.Icon; +import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings; +import android.service.notification.StatusBarNotification; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.logging.InstanceId; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -54,12 +57,17 @@ import java.util.Objects; class Bubble implements BubbleViewProvider { private static final String TAG = "Bubble"; + /** + * NotificationEntry associated with the bubble. A null value implies this bubble is loaded + * from disk. + */ + @Nullable + private NotificationEntry mEntry; private final String mKey; private long mLastUpdated; private long mLastAccessed; - @Nullable private BubbleController.NotificationSuppressionChangedListener mSuppressionListener; /** Whether the bubble should show a dot for the notification indicating updated content. */ @@ -67,6 +75,8 @@ class Bubble implements BubbleViewProvider { /** Whether flyout text should be suppressed, regardless of any other flags or state. */ private boolean mSuppressFlyout; + /** Whether this bubble should auto expand regardless of the normal flag, used for overflow. */ + private boolean mShouldAutoExpand; // Items that are typically loaded later private String mAppName; @@ -82,7 +92,6 @@ class Bubble implements BubbleViewProvider { * Presentational info about the flyout. */ public static class FlyoutMessage { - @Nullable public Icon senderIcon; @Nullable public Drawable senderAvatar; @Nullable public CharSequence senderName; @Nullable public CharSequence message; @@ -100,39 +109,16 @@ class Bubble implements BubbleViewProvider { private UserHandle mUser; @NonNull private String mPackageName; - @Nullable - private String mTitle; - @Nullable - private Icon mIcon; - private boolean mIsBubble; - private boolean mIsVisuallyInterruptive; - private boolean mIsClearable; - private boolean mShouldSuppressNotificationDot; - private boolean mShouldSuppressNotificationList; - private boolean mShouldSuppressPeek; private int mDesiredHeight; @DimenRes private int mDesiredHeightResId; - /** for logging **/ - @Nullable - private InstanceId mInstanceId; - @Nullable - private String mChannelId; - private int mNotificationId; - private int mAppUid = -1; - - @Nullable - private PendingIntent mIntent; - @Nullable - private PendingIntent mDeleteIntent; - /** * Create a bubble with limited information based on given {@link ShortcutInfo}. * Note: Currently this is only being used when the bubble is persisted to disk. */ Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo, - final int desiredHeight, final int desiredHeightResId, @Nullable final String title) { + final int desiredHeight, final int desiredHeightResId) { Objects.requireNonNull(key); Objects.requireNonNull(shortcutInfo); mShortcutInfo = shortcutInfo; @@ -140,10 +126,8 @@ class Bubble implements BubbleViewProvider { mFlags = 0; mUser = shortcutInfo.getUserHandle(); mPackageName = shortcutInfo.getPackage(); - mIcon = shortcutInfo.getIcon(); mDesiredHeight = desiredHeight; mDesiredHeightResId = desiredHeightResId; - mTitle = title; } /** Used in tests when no UI is required. */ @@ -161,6 +145,12 @@ class Bubble implements BubbleViewProvider { return mKey; } + @Nullable + public NotificationEntry getEntry() { + return mEntry; + } + + @NonNull public UserHandle getUser() { return mUser; } @@ -213,7 +203,14 @@ class Bubble implements BubbleViewProvider { @Nullable public String getTitle() { - return mTitle; + final CharSequence titleCharSeq; + if (mEntry == null) { + titleCharSeq = null; + } else { + titleCharSeq = mEntry.getSbn().getNotification().extras.getCharSequence( + Notification.EXTRA_TITLE); + } + return titleCharSeq != null ? titleCharSeq.toString() : null; } /** @@ -334,44 +331,17 @@ class Bubble implements BubbleViewProvider { void setEntry(@NonNull final NotificationEntry entry) { Objects.requireNonNull(entry); Objects.requireNonNull(entry.getSbn()); + mEntry = entry; mLastUpdated = entry.getSbn().getPostTime(); - mIsBubble = entry.getSbn().getNotification().isBubbleNotification(); + mFlags = entry.getSbn().getNotification().flags; mPackageName = entry.getSbn().getPackageName(); mUser = entry.getSbn().getUser(); - mTitle = getTitle(entry); - mIsClearable = entry.isClearable(); - mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot(); - mShouldSuppressNotificationList = entry.shouldSuppressNotificationList(); - mShouldSuppressPeek = entry.shouldSuppressPeek(); - mChannelId = entry.getSbn().getNotification().getChannelId(); - mNotificationId = entry.getSbn().getId(); - mAppUid = entry.getSbn().getUid(); - mInstanceId = entry.getSbn().getInstanceId(); - mFlyoutMessage = BubbleViewInfoTask.extractFlyoutMessage(entry); - if (entry.getRanking() != null) { - mShortcutInfo = entry.getRanking().getShortcutInfo() != null - ? entry.getRanking().getShortcutInfo() : mShortcutInfo; - mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive(); - } if (entry.getBubbleMetadata() != null) { - mFlags = entry.getBubbleMetadata().getFlags(); mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight(); mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId(); - mIcon = entry.getBubbleMetadata().getIcon(); - mIntent = entry.getBubbleMetadata().getIntent(); - mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent(); } } - @Nullable - Icon getIcon() { - return mIcon; - } - - boolean isVisuallyInterruptive() { - return mIsVisuallyInterruptive; - } - /** * @return the last time this bubble was updated or accessed, whichever is most recent. */ @@ -394,19 +364,6 @@ class Bubble implements BubbleViewProvider { return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY; } - public InstanceId getInstanceId() { - return mInstanceId; - } - - @Nullable - public String getChannelId() { - return mChannelId; - } - - public int getNotificationId() { - return mNotificationId; - } - /** * Should be invoked whenever a Bubble is accessed (selected while expanded). */ @@ -427,19 +384,24 @@ class Bubble implements BubbleViewProvider { * Whether this notification should be shown in the shade. */ boolean showInShade() { - return !shouldSuppressNotification() || !mIsClearable; + if (mEntry == null) return false; + return !shouldSuppressNotification() || !mEntry.isClearable(); } /** * Sets whether this notification should be suppressed in the shade. */ void setSuppressNotification(boolean suppressNotification) { + if (mEntry == null) return; boolean prevShowInShade = showInShade(); + Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); + int flags = data.getFlags(); if (suppressNotification) { - mFlags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; + flags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; } else { - mFlags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; + flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; } + data.setFlags(flags); if (showInShade() != prevShowInShade && mSuppressionListener != null) { mSuppressionListener.onBubbleNotificationSuppressionChange(this); @@ -462,8 +424,9 @@ class Bubble implements BubbleViewProvider { */ @Override public boolean showDot() { + if (mEntry == null) return false; return mShowBubbleUpdateDot - && !mShouldSuppressNotificationDot + && !mEntry.shouldSuppressNotificationDot() && !shouldSuppressNotification(); } @@ -471,9 +434,10 @@ class Bubble implements BubbleViewProvider { * Whether the flyout for the bubble should be shown. */ boolean showFlyout() { - return !mSuppressFlyout && !mShouldSuppressPeek + if (mEntry == null) return false; + return !mSuppressFlyout && !mEntry.shouldSuppressPeek() && !shouldSuppressNotification() - && !mShouldSuppressNotificationList; + && !mEntry.shouldSuppressNotificationList(); } /** @@ -516,14 +480,25 @@ class Bubble implements BubbleViewProvider { } } - @Nullable - PendingIntent getBubbleIntent() { - return mIntent; + /** + * Whether shortcut information should be used to populate the bubble. + *

+ * To populate the activity use {@link LauncherApps#startShortcut(ShortcutInfo, Rect, Bundle)}. + * To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}. + */ + boolean usingShortcutInfo() { + return mEntry != null && mEntry.getBubbleMetadata().getShortcutId() != null + || mShortcutInfo != null; } @Nullable - PendingIntent getDeleteIntent() { - return mDeleteIntent; + PendingIntent getBubbleIntent() { + if (mEntry == null) return null; + Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); + if (data != null) { + return data.getIntent(); + } + return null; } Intent getSettingsIntent(final Context context) { @@ -539,12 +514,8 @@ class Bubble implements BubbleViewProvider { return intent; } - public int getAppUid() { - return mAppUid; - } - private int getUid(final Context context) { - if (mAppUid != -1) return mAppUid; + if (mEntry != null) return mEntry.getSbn().getUid(); final PackageManager pm = context.getPackageManager(); if (pm == null) return -1; try { @@ -577,27 +548,24 @@ class Bubble implements BubbleViewProvider { } private boolean shouldSuppressNotification() { - return isEnabled(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION); + if (mEntry == null) return true; + return mEntry.getBubbleMetadata() != null + && mEntry.getBubbleMetadata().isNotificationSuppressed(); } - public boolean shouldAutoExpand() { - return isEnabled(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE); + boolean shouldAutoExpand() { + if (mEntry == null) return mShouldAutoExpand; + Notification.BubbleMetadata metadata = mEntry.getBubbleMetadata(); + return (metadata != null && metadata.getAutoExpandBubble()) || mShouldAutoExpand; } void setShouldAutoExpand(boolean shouldAutoExpand) { - if (shouldAutoExpand) { - enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE); - } else { - disable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE); - } - } - - public void setIsBubble(final boolean isBubble) { - mIsBubble = isBubble; + mShouldAutoExpand = shouldAutoExpand; } public boolean isBubble() { - return mIsBubble; + if (mEntry == null) return (mFlags & FLAG_BUBBLE) != 0; + return (mEntry.getSbn().getNotification().flags & FLAG_BUBBLE) != 0; } public void enable(int option) { @@ -608,10 +576,6 @@ class Bubble implements BubbleViewProvider { mFlags &= ~option; } - public boolean isEnabled(int option) { - return (mFlags & option) != 0; - } - @Override public String toString() { return "Bubble{" + mKey + '}'; @@ -646,24 +610,34 @@ class Bubble implements BubbleViewProvider { @Override public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) { - SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED, - mPackageName, - mChannelId, - mNotificationId, - index, - bubbleCount, - action, - normalX, - normalY, - showInShade(), - false /* isOngoing (unused) */, - false /* isAppForeground (unused) */); - } - - @Nullable - private static String getTitle(@NonNull final NotificationEntry e) { - final CharSequence titleCharSeq = e.getSbn().getNotification().extras.getCharSequence( - Notification.EXTRA_TITLE); - return titleCharSeq == null ? null : titleCharSeq.toString(); + if (this.getEntry() == null + || this.getEntry().getSbn() == null) { + SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED, + null /* package name */, + null /* notification channel */, + 0 /* notification ID */, + 0 /* bubble position */, + bubbleCount, + action, + normalX, + normalY, + false /* unread bubble */, + false /* on-going bubble */, + false /* isAppForeground (unused) */); + } else { + StatusBarNotification notification = this.getEntry().getSbn(); + SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED, + notification.getPackageName(), + notification.getNotification().getChannelId(), + notification.getId(), + index, + bubbleCount, + action, + normalX, + normalY, + this.showInShade(), + false /* isOngoing (unused) */, + false /* isAppForeground (unused) */); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index b2c5402c7cd35..c4c5da42ec06d 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -505,7 +505,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi addNotifCallback(new NotifCallback() { @Override public void removeNotification(NotificationEntry entry, int reason) { - mNotificationEntryManager.performRemoveNotification(entry.getSbn(), reason); + mNotificationEntryManager.performRemoveNotification(entry.getSbn(), + reason); } @Override @@ -636,13 +637,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mStackView.setExpandListener(mExpandListener); } - mStackView.setUnbubbleConversationCallback(key -> { - final NotificationEntry entry = - mNotificationEntryManager.getPendingOrActiveNotif(key); - if (entry != null) { - onUserChangedBubble(entry, false /* shouldBubble */); - } - }); + mStackView.setUnbubbleConversationCallback(notificationEntry -> + onUserChangedBubble(notificationEntry, false /* shouldBubble */)); } addToWindowManagerMaybe(); @@ -1028,7 +1024,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * @param entry the notification to change bubble state for. * @param shouldBubble whether the notification should show as a bubble or not. */ - public void onUserChangedBubble(@NonNull final NotificationEntry entry, boolean shouldBubble) { + public void onUserChangedBubble(@Nullable final NotificationEntry entry, boolean shouldBubble) { + if (entry == null) { + return; + } NotificationChannel channel = entry.getChannel(); final String appPkg = entry.getSbn().getPackageName(); final int appUid = entry.getSbn().getUid(); @@ -1104,8 +1103,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mBubbleData.removeSuppressedSummary(groupKey); // Remove any associated bubble children with the summary - final List bubbleChildren = mBubbleData.getBubblesInGroup( - groupKey, mNotificationEntryManager); + final List bubbleChildren = mBubbleData.getBubblesInGroup(groupKey); for (int i = 0; i < bubbleChildren.size(); i++) { removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED); } @@ -1163,18 +1161,21 @@ public class BubbleController implements ConfigurationController.ConfigurationLi private void setIsBubble(@NonNull final Bubble b, final boolean isBubble) { Objects.requireNonNull(b); - b.setIsBubble(isBubble); - final NotificationEntry entry = mNotificationEntryManager - .getPendingOrActiveNotif(b.getKey()); - if (entry != null) { + if (isBubble) { + b.enable(FLAG_BUBBLE); + } else { + b.disable(FLAG_BUBBLE); + } + if (b.getEntry() != null) { // Updating the entry to be a bubble will trigger our normal update flow - setIsBubble(entry, isBubble, b.shouldAutoExpand()); + setIsBubble(b.getEntry(), isBubble, b.shouldAutoExpand()); } else if (isBubble) { - // If bubble doesn't exist, it's a persisted bubble so we need to add it to the - // stack ourselves + // If we have no entry to update, it's a persisted bubble so + // we need to add it to the stack ourselves Bubble bubble = mBubbleData.getOrCreateBubble(null, b /* persistedBubble */); inflateAndAdd(bubble, bubble.shouldAutoExpand() /* suppressFlyout */, !bubble.shouldAutoExpand() /* showInShade */); + } } @@ -1213,8 +1214,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (reason == DISMISS_NOTIF_CANCEL) { bubblesToBeRemovedFromRepository.add(bubble); } - final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( - bubble.getKey()); if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { if (!mBubbleData.hasOverflowBubbleWithKey(bubble.getKey()) && (!bubble.showInShade() @@ -1223,27 +1222,26 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // The bubble is now gone & the notification is hidden from the shade, so // time to actually remove it for (NotifCallback cb : mCallbacks) { - if (entry != null) { - cb.removeNotification(entry, REASON_CANCEL); + if (bubble.getEntry() != null) { + cb.removeNotification(bubble.getEntry(), REASON_CANCEL); } } } else { if (bubble.isBubble()) { setIsBubble(bubble, false /* isBubble */); } - if (entry != null && entry.getRow() != null) { - entry.getRow().updateBubbleButton(); + if (bubble.getEntry() != null && bubble.getEntry().getRow() != null) { + bubble.getEntry().getRow().updateBubbleButton(); } } } - if (entry != null) { - final String groupKey = entry.getSbn().getGroupKey(); - if (mBubbleData.getBubblesInGroup( - groupKey, mNotificationEntryManager).isEmpty()) { + if (bubble.getEntry() != null) { + final String groupKey = bubble.getEntry().getSbn().getGroupKey(); + if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) { // Time to potentially remove the summary for (NotifCallback cb : mCallbacks) { - cb.maybeCancelSummary(entry); + cb.maybeCancelSummary(bubble.getEntry()); } } } @@ -1268,12 +1266,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (update.selectionChanged && mStackView != null) { mStackView.setSelectedBubble(update.selectedBubble); - if (update.selectedBubble != null) { - final NotificationEntry entry = mNotificationEntryManager - .getPendingOrActiveNotif(update.selectedBubble.getKey()); - if (entry != null) { - mNotificationGroupManager.updateSuppression(entry); - } + if (update.selectedBubble != null && update.selectedBubble.getEntry() != null) { + mNotificationGroupManager.updateSuppression( + update.selectedBubble.getEntry()); } } @@ -1346,8 +1341,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } String groupKey = entry.getSbn().getGroupKey(); - ArrayList bubbleChildren = mBubbleData.getBubblesInGroup( - groupKey, mNotificationEntryManager); + ArrayList bubbleChildren = mBubbleData.getBubblesInGroup(groupKey); boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey) && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey())); boolean isSummary = entry.getSbn().getNotification().isGroupSummary(); @@ -1367,15 +1361,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // As far as group manager is concerned, once a child is no longer shown // in the shade, it is essentially removed. Bubble bubbleChild = mBubbleData.getAnyBubbleWithkey(child.getKey()); - if (bubbleChild != null) { - final NotificationEntry entry = mNotificationEntryManager - .getPendingOrActiveNotif(bubbleChild.getKey()); - if (entry != null) { - mNotificationGroupManager.onEntryRemoved(entry); - } - bubbleChild.setSuppressNotification(true); - bubbleChild.setShowDot(false /* show */); - } + mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry()); + bubbleChild.setSuppressNotification(true); + 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 c8706126c1ad5..20a9a8cf324c4 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -22,6 +22,7 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; import android.annotation.NonNull; +import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.util.Log; @@ -33,7 +34,6 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController.DismissReason; -import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.io.FileDescriptor; @@ -256,7 +256,8 @@ public class BubbleData { } mPendingBubbles.remove(bubble.getKey()); // No longer pending once we're here Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey()); - suppressFlyout |= !bubble.isVisuallyInterruptive(); + suppressFlyout |= bubble.getEntry() == null + || !bubble.getEntry().getRanking().visuallyInterruptive(); if (prevBubble == null) { // Create a new bubble @@ -334,15 +335,13 @@ public class BubbleData { * Retrieves any bubbles that are part of the notification group represented by the provided * group key. */ - ArrayList getBubblesInGroup(@Nullable String groupKey, @NonNull - NotificationEntryManager nem) { + ArrayList getBubblesInGroup(@Nullable String groupKey) { ArrayList bubbleChildren = new ArrayList<>(); if (groupKey == null) { return bubbleChildren; } for (Bubble b : mBubbles) { - final NotificationEntry entry = nem.getPendingOrActiveNotif(b.getKey()); - if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) { + if (b.getEntry() != null && groupKey.equals(b.getEntry().getSbn().getGroupKey())) { bubbleChildren.add(b); } } @@ -440,7 +439,9 @@ public class BubbleData { Bubble newSelected = mBubbles.get(newIndex); setSelectedBubbleInternal(newSelected); } - maybeSendDeleteIntent(reason, bubbleToRemove); + if (bubbleToRemove.getEntry() != null) { + maybeSendDeleteIntent(reason, bubbleToRemove.getEntry()); + } } void overflowBubble(@DismissReason int reason, Bubble bubble) { @@ -610,14 +611,21 @@ public class BubbleData { return true; } - private void maybeSendDeleteIntent(@DismissReason int reason, @NonNull final Bubble bubble) { - if (reason != BubbleController.DISMISS_USER_GESTURE) return; - PendingIntent deleteIntent = bubble.getDeleteIntent(); - if (deleteIntent == null) return; - try { - deleteIntent.send(); - } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "Failed to send delete intent for bubble with key: " + bubble.getKey()); + private void maybeSendDeleteIntent(@DismissReason int reason, + @NonNull final NotificationEntry entry) { + if (reason == BubbleController.DISMISS_USER_GESTURE) { + Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata(); + PendingIntent deleteIntent = bubbleMetadata != null + ? bubbleMetadata.getDeleteIntent() + : null; + if (deleteIntent != null) { + try { + deleteIntent.send(); + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "Failed to send delete intent for bubble with key: " + + entry.getKey()); + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt index 0c25d144938c2..d20f40559b5d9 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt @@ -74,15 +74,11 @@ internal class BubbleDataRepository @Inject constructor( private fun transform(userId: Int, bubbles: List): List { return bubbles.mapNotNull { b -> - BubbleEntity( - userId, - b.packageName, - b.shortcutInfo?.id ?: return@mapNotNull null, - b.key, - b.rawDesiredHeight, - b.rawDesiredHeightResId, - b.title - ) + var shortcutId = b.shortcutInfo?.id + if (shortcutId == null) shortcutId = b.entry?.bubbleMetadata?.shortcutId + if (shortcutId == null) return@mapNotNull null + BubbleEntity(userId, b.packageName, shortcutId, b.key, b.rawDesiredHeight, + b.rawDesiredHeightResId) } } @@ -163,13 +159,8 @@ internal class BubbleDataRepository @Inject constructor( val bubbles = entities.mapNotNull { entity -> shortcutMap[ShortcutKey(entity.userId, entity.packageName)] ?.first { shortcutInfo -> entity.shortcutId == shortcutInfo.id } - ?.let { shortcutInfo -> Bubble( - entity.key, - shortcutInfo, - entity.desiredHeight, - entity.desiredHeightResId, - entity.title - ) } + ?.let { shortcutInfo -> Bubble(entity.key, shortcutInfo, entity.desiredHeight, + entity.desiredHeightResId) } } uiScope.launch { cb(bubbles) } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 471a769c82045..6dcc9dcdc63ce 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -169,7 +169,7 @@ public class BubbleExpandedView extends LinearLayout { return; } try { - if (!mIsOverflow && mBubble.getShortcutInfo() != null) { + if (!mIsOverflow && mBubble.usingShortcutInfo()) { options.setApplyActivityFlagsForBubbles(true); mActivityView.startShortcutActivity(mBubble.getShortcutInfo(), options, null /* sourceBounds */); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java index 1fa3aaae5e617..8c76cda3290f9 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java @@ -31,7 +31,6 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.PointF; import android.graphics.RectF; -import android.graphics.drawable.Drawable; import android.graphics.drawable.ShapeDrawable; import android.text.TextUtils; import android.view.LayoutInflater; @@ -224,10 +223,9 @@ public class BubbleFlyoutView extends FrameLayout { float[] dotCenter, boolean hideDot) { - final Drawable senderAvatar = flyoutMessage.senderAvatar; - if (senderAvatar != null && flyoutMessage.isGroupChat) { + if (flyoutMessage.senderAvatar != null && flyoutMessage.isGroupChat) { mSenderAvatar.setVisibility(VISIBLE); - mSenderAvatar.setImageDrawable(senderAvatar); + mSenderAvatar.setImageDrawable(flyoutMessage.senderAvatar); } else { mSenderAvatar.setVisibility(GONE); mSenderAvatar.setTranslationX(0); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java index a799f2d739e5d..74231c648f006 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java @@ -15,8 +15,7 @@ */ package com.android.systemui.bubbles; -import android.annotation.NonNull; -import android.annotation.Nullable; +import android.app.Notification; import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; @@ -51,14 +50,15 @@ public class BubbleIconFactory extends BaseIconFactory { /** * Returns the drawable that the developer has provided to display in the bubble. */ - Drawable getBubbleDrawable(@NonNull final Context context, - @Nullable final ShortcutInfo shortcutInfo, @Nullable final Icon ic) { + Drawable getBubbleDrawable(Context context, ShortcutInfo shortcutInfo, + Notification.BubbleMetadata metadata) { if (shortcutInfo != null) { LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE); int density = context.getResources().getConfiguration().densityDpi; return launcherApps.getShortcutIconDrawable(shortcutInfo, density); } else { + Icon ic = metadata.getIcon(); if (ic != null) { if (ic.getType() == Icon.TYPE_URI || ic.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java index c1dd8c36ff6f7..c5faae0d703e1 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.bubbles; +import android.service.notification.StatusBarNotification; + import com.android.internal.logging.UiEventLoggerImpl; /** @@ -30,11 +32,12 @@ public class BubbleLoggerImpl extends UiEventLoggerImpl implements BubbleLogger * @param e UI event */ public void log(Bubble b, UiEventEnum e) { - if (b.getInstanceId() == null) { + if (b.getEntry() == null) { // Added from persistence -- TODO log this with specific event? return; } - logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId()); + StatusBarNotification sbn = b.getEntry().getSbn(); + logWithInstanceId(e, sbn.getUid(), sbn.getPackageName(), sbn.getInstanceId()); } /** diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java index ea324af3bd1a2..b4672c14b49a3 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java @@ -300,7 +300,9 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter mUnbubbleConversationCallback; + private Consumer mUnbubbleConversationCallback; private SysUiState mSysUiState; @@ -996,7 +997,10 @@ public class BubbleStackView extends FrameLayout mManageMenu.findViewById(R.id.bubble_manage_menu_dont_bubble_container).setOnClickListener( view -> { showManageMenu(false /* show */); - mUnbubbleConversationCallback.accept(mBubbleData.getSelectedBubble().getKey()); + final Bubble bubble = mBubbleData.getSelectedBubble(); + if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { + mUnbubbleConversationCallback.accept(bubble.getEntry()); + } }); mManageMenu.findViewById(R.id.bubble_manage_menu_settings_container).setOnClickListener( @@ -1344,7 +1348,7 @@ public class BubbleStackView extends FrameLayout /** Sets the function to call to un-bubble the given conversation. */ public void setUnbubbleConversationCallback( - Consumer unbubbleConversationCallback) { + Consumer unbubbleConversationCallback) { mUnbubbleConversationCallback = unbubbleConversationCallback; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java index 3e4ff5262bbdf..525d5b56cc8ec 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java @@ -37,6 +37,8 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.AsyncTask; import android.os.Parcelable; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.Log; import android.util.PathParser; @@ -51,7 +53,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.lang.ref.WeakReference; import java.util.List; -import java.util.Objects; /** * Simple task to inflate views & load necessary info to display a bubble. @@ -128,10 +129,35 @@ public class BubbleViewInfoTask extends AsyncTask style = underlyingNotif.getNotificationStyle(); @@ -240,9 +264,20 @@ public class BubbleViewInfoTask extends AsyncTask - - + + + """.trimIndent() ByteArrayOutputStream().use { writeXml(it, bubbles) @@ -54,12 +54,12 @@ class BubbleXmlHelperTest : SysuiTestCase() { @Test fun testReadXml() { val src = """ - - - - - - + + + + + + """.trimIndent() val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8))) assertEquals("failed parsing bubbles from xml\n$src", bubbles, actual)