Loading packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +11 −108 Original line number Original line Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.systemui.bubbles; package com.android.systemui.bubbles; import static android.app.Notification.FLAG_BUBBLE; import static android.os.AsyncTask.Status.FINISHED; import static android.os.AsyncTask.Status.FINISHED; import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.INVALID_DISPLAY; Loading @@ -28,7 +27,6 @@ import android.app.Notification; import android.app.PendingIntent; import android.app.PendingIntent; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherApps; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo; Loading Loading @@ -57,11 +55,6 @@ import java.util.Objects; class Bubble implements BubbleViewProvider { class Bubble implements BubbleViewProvider { private static final String TAG = "Bubble"; 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 NotificationEntry mEntry; private final String mKey; private final String mKey; private final String mGroupId; private final String mGroupId; Loading Loading @@ -102,56 +95,18 @@ class Bubble implements BubbleViewProvider { private Bitmap mBadgedImage; private Bitmap mBadgedImage; private int mDotColor; private int mDotColor; private Path mDotPath; private Path mDotPath; private int mFlags; /** * Extract GroupId from {@link NotificationEntry}. See also {@link #groupId(ShortcutInfo)}. */ public static String groupId(NotificationEntry entry) { public static String groupId(NotificationEntry entry) { UserHandle user = entry.getSbn().getUser(); UserHandle user = entry.getSbn().getUser(); return user.getIdentifier() + "|" + entry.getSbn().getPackageName(); return user.getIdentifier() + "|" + entry.getSbn().getPackageName(); } } /** // TODO: Decouple Bubble from NotificationEntry and transform ShortcutInfo into Bubble * Extract GroupId from {@link ShortcutInfo}. This should match the one generated from * {@link NotificationEntry}. See also {@link #groupId(NotificationEntry)}. */ @NonNull public static String groupId(@NonNull final ShortcutInfo shortcutInfo) { return shortcutInfo.getUserId() + "|" + shortcutInfo.getPackage(); } /** * Generate a unique identifier for this bubble based on given {@link NotificationEntry}. If * {@link ShortcutInfo} was found in the notification entry, the identifier would be generated * from {@link ShortcutInfo} instead. See also {@link #key(ShortcutInfo)}. */ @NonNull public static String key(@NonNull final NotificationEntry entry) { final ShortcutInfo shortcutInfo = entry.getRanking().getShortcutInfo(); if (shortcutInfo != null) return key(shortcutInfo); return entry.getKey(); } /** * Generate a unique identifier for this bubble based on given {@link ShortcutInfo}. * See also {@link #key(NotificationEntry)}. */ @NonNull public static String key(@NonNull final ShortcutInfo shortcutInfo) { return shortcutInfo.getUserId() + "|" + shortcutInfo.getPackage() + "|" + shortcutInfo.getId(); } /** * 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(ShortcutInfo shortcutInfo) { Bubble(ShortcutInfo shortcutInfo) { mShortcutInfo = shortcutInfo; mShortcutInfo = shortcutInfo; mKey = key(shortcutInfo); mKey = shortcutInfo.getId(); mGroupId = groupId(shortcutInfo); mGroupId = shortcutInfo.getId(); mFlags = 0; } } /** Used in tests when no UI is required. */ /** Used in tests when no UI is required. */ Loading @@ -159,11 +114,10 @@ class Bubble implements BubbleViewProvider { Bubble(NotificationEntry e, Bubble(NotificationEntry e, BubbleController.NotificationSuppressionChangedListener listener) { BubbleController.NotificationSuppressionChangedListener listener) { mEntry = e; mEntry = e; mKey = key(e); mKey = e.getKey(); mLastUpdated = e.getSbn().getPostTime(); mLastUpdated = e.getSbn().getPostTime(); mGroupId = groupId(e); mGroupId = groupId(e); mSuppressionListener = listener; mSuppressionListener = listener; mFlags = e.getSbn().getNotification().flags; } } @Override @Override Loading @@ -171,26 +125,16 @@ class Bubble implements BubbleViewProvider { return mKey; return mKey; } } @Nullable public NotificationEntry getEntry() { public NotificationEntry getEntry() { return mEntry; return mEntry; } } @Nullable public UserHandle getUser() { if (mEntry != null) return mEntry.getSbn().getUser(); if (mShortcutInfo != null) return mShortcutInfo.getUserHandle(); return null; } public String getGroupId() { public String getGroupId() { return mGroupId; return mGroupId; } } public String getPackageName() { public String getPackageName() { return mEntry == null return mEntry.getSbn().getPackageName(); ? mShortcutInfo == null ? null : mShortcutInfo.getPackage() : mEntry.getSbn().getPackageName(); } } @Override @Override Loading Loading @@ -274,8 +218,7 @@ class Bubble implements BubbleViewProvider { void inflate(BubbleViewInfoTask.Callback callback, void inflate(BubbleViewInfoTask.Callback callback, Context context, Context context, BubbleStackView stackView, BubbleStackView stackView, BubbleIconFactory iconFactory, BubbleIconFactory iconFactory) { boolean skipInflation) { if (isBubbleLoading()) { if (isBubbleLoading()) { mInflationTask.cancel(true /* mayInterruptIfRunning */); mInflationTask.cancel(true /* mayInterruptIfRunning */); } } Loading @@ -283,7 +226,6 @@ class Bubble implements BubbleViewProvider { context, context, stackView, stackView, iconFactory, iconFactory, skipInflation, callback); callback); if (mInflateSynchronously) { if (mInflateSynchronously) { mInflationTask.onPostExecute(mInflationTask.doInBackground()); mInflationTask.onPostExecute(mInflationTask.doInBackground()); Loading Loading @@ -403,7 +345,6 @@ class Bubble implements BubbleViewProvider { * Whether this notification should be shown in the shade. * Whether this notification should be shown in the shade. */ */ boolean showInShade() { boolean showInShade() { if (mEntry == null) return false; return !shouldSuppressNotification() || !mEntry.isClearable(); return !shouldSuppressNotification() || !mEntry.isClearable(); } } Loading @@ -411,8 +352,8 @@ class Bubble implements BubbleViewProvider { * Sets whether this notification should be suppressed in the shade. * Sets whether this notification should be suppressed in the shade. */ */ void setSuppressNotification(boolean suppressNotification) { void setSuppressNotification(boolean suppressNotification) { if (mEntry == null) return; boolean prevShowInShade = showInShade(); boolean prevShowInShade = showInShade(); Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); int flags = data.getFlags(); int flags = data.getFlags(); if (suppressNotification) { if (suppressNotification) { Loading Loading @@ -443,7 +384,6 @@ class Bubble implements BubbleViewProvider { */ */ @Override @Override public boolean showDot() { public boolean showDot() { if (mEntry == null) return false; return mShowBubbleUpdateDot return mShowBubbleUpdateDot && !mEntry.shouldSuppressNotificationDot() && !mEntry.shouldSuppressNotificationDot() && !shouldSuppressNotification(); && !shouldSuppressNotification(); Loading @@ -453,7 +393,6 @@ class Bubble implements BubbleViewProvider { * Whether the flyout for the bubble should be shown. * Whether the flyout for the bubble should be shown. */ */ boolean showFlyout() { boolean showFlyout() { if (mEntry == null) return false; return !mSuppressFlyout && !mEntry.shouldSuppressPeek() return !mSuppressFlyout && !mEntry.shouldSuppressPeek() && !shouldSuppressNotification() && !shouldSuppressNotification() && !mEntry.shouldSuppressNotificationList(); && !mEntry.shouldSuppressNotificationList(); Loading @@ -477,13 +416,11 @@ class Bubble implements BubbleViewProvider { * is an ongoing bubble. * is an ongoing bubble. */ */ boolean isOngoing() { boolean isOngoing() { if (mEntry == null) return false; int flags = mEntry.getSbn().getNotification().flags; int flags = mEntry.getSbn().getNotification().flags; return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; } } float getDesiredHeight(Context context) { float getDesiredHeight(Context context) { if (mEntry == null) return 0; Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); boolean useRes = data.getDesiredHeightResId() != 0; boolean useRes = data.getDesiredHeightResId() != 0; if (useRes) { if (useRes) { Loading @@ -497,7 +434,6 @@ class Bubble implements BubbleViewProvider { } } String getDesiredHeightString() { String getDesiredHeightString() { if (mEntry == null) return String.valueOf(0); Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); boolean useRes = data.getDesiredHeightResId() != 0; boolean useRes = data.getDesiredHeightResId() != 0; if (useRes) { if (useRes) { Loading @@ -514,13 +450,11 @@ class Bubble implements BubbleViewProvider { * To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}. * To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}. */ */ boolean usingShortcutInfo() { boolean usingShortcutInfo() { return mEntry != null && mEntry.getBubbleMetadata().getShortcutId() != null return mEntry.getBubbleMetadata().getShortcutId() != null; || mShortcutInfo != null; } } @Nullable @Nullable PendingIntent getBubbleIntent() { PendingIntent getBubbleIntent() { if (mEntry == null) return null; Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); if (data != null) { if (data != null) { return data.getIntent(); return data.getIntent(); Loading @@ -528,32 +462,16 @@ class Bubble implements BubbleViewProvider { return null; return null; } } Intent getSettingsIntent(final Context context) { Intent getSettingsIntent() { final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS); final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); final int uid = getUid(context); intent.putExtra(Settings.EXTRA_APP_UID, mEntry.getSbn().getUid()); if (uid != -1) { intent.putExtra(Settings.EXTRA_APP_UID, uid); } intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); return intent; return intent; } } private int getUid(final Context context) { if (mEntry != null) return mEntry.getSbn().getUid(); final PackageManager pm = context.getPackageManager(); if (pm == null) return -1; try { final ApplicationInfo info = pm.getApplicationInfo(mShortcutInfo.getPackage(), 0); return info.uid; } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "cannot find uid", e); } return -1; } private int getDimenForPackageUser(Context context, int resId, String pkg, int userId) { private int getDimenForPackageUser(Context context, int resId, String pkg, int userId) { PackageManager pm = context.getPackageManager(); PackageManager pm = context.getPackageManager(); Resources r; Resources r; Loading @@ -575,30 +493,15 @@ class Bubble implements BubbleViewProvider { } } private boolean shouldSuppressNotification() { private boolean shouldSuppressNotification() { if (mEntry == null) return false; return mEntry.getBubbleMetadata() != null return mEntry.getBubbleMetadata() != null && mEntry.getBubbleMetadata().isNotificationSuppressed(); && mEntry.getBubbleMetadata().isNotificationSuppressed(); } } boolean shouldAutoExpand() { boolean shouldAutoExpand() { if (mEntry == null) return false; Notification.BubbleMetadata metadata = mEntry.getBubbleMetadata(); Notification.BubbleMetadata metadata = mEntry.getBubbleMetadata(); return metadata != null && metadata.getAutoExpandBubble(); return metadata != null && metadata.getAutoExpandBubble(); } } public boolean isBubble() { if (mEntry == null) return (mFlags & FLAG_BUBBLE) != 0; return (mEntry.getSbn().getNotification().flags & FLAG_BUBBLE) != 0; } public void enable(int option) { mFlags |= option; } public void disable(int option) { mFlags &= ~option; } @Override @Override public String toString() { public String toString() { return "Bubble{" + mKey + '}'; return "Bubble{" + mKey + '}'; Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +29 −69 Original line number Original line Diff line number Diff line Loading @@ -42,7 +42,6 @@ import static java.lang.annotation.ElementType.LOCAL_VARIABLE; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.SOURCE; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.annotation.UserIdInt; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.RunningTaskInfo; import android.app.INotificationManager; import android.app.INotificationManager; Loading Loading @@ -243,7 +242,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * This can happen when an app cancels a bubbled notification or when the user dismisses a * This can happen when an app cancels a bubbled notification or when the user dismisses a * bubble. * bubble. */ */ void removeNotification(@NonNull NotificationEntry entry, int reason); void removeNotification(NotificationEntry entry, int reason); /** /** * Called when a bubbled notification has changed whether it should be * Called when a bubbled notification has changed whether it should be Loading @@ -259,7 +258,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * removes all remnants of the group's summary from the notification pipeline. * removes all remnants of the group's summary from the notification pipeline. * TODO: (b/145659174) Only old pipeline needs this - delete post-migration. * TODO: (b/145659174) Only old pipeline needs this - delete post-migration. */ */ void maybeCancelSummary(@NonNull NotificationEntry entry); void maybeCancelSummary(NotificationEntry entry); } } /** /** Loading Loading @@ -482,7 +481,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi addNotifCallback(new NotifCallback() { addNotifCallback(new NotifCallback() { @Override @Override public void removeNotification(@NonNull final NotificationEntry entry, int reason) { public void removeNotification(NotificationEntry entry, int reason) { mNotificationEntryManager.performRemoveNotification(entry.getSbn(), mNotificationEntryManager.performRemoveNotification(entry.getSbn(), reason); reason); } } Loading @@ -493,7 +492,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } @Override @Override public void maybeCancelSummary(@NonNull final NotificationEntry entry) { public void maybeCancelSummary(NotificationEntry entry) { // Check if removed bubble has an associated suppressed group summary that needs // Check if removed bubble has an associated suppressed group summary that needs // to be removed now. // to be removed now. final String groupKey = entry.getSbn().getGroupKey(); final String groupKey = entry.getSbn().getGroupKey(); Loading Loading @@ -702,12 +701,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mBubbleIconFactory = new BubbleIconFactory(mContext); mBubbleIconFactory = new BubbleIconFactory(mContext); // Reload each bubble // Reload each bubble for (Bubble b: mBubbleData.getBubbles()) { for (Bubble b: mBubbleData.getBubbles()) { b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory, b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory); false /* skipInflation */); } } for (Bubble b: mBubbleData.getOverflowBubbles()) { for (Bubble b: mBubbleData.getOverflowBubbles()) { b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory, b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory); false /* skipInflation */); } } } } Loading Loading @@ -806,7 +803,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (bubble != null) { if (bubble != null) { mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory); mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory); } } } else if (bubble.isBubble()) { } else if (bubble.getEntry().isBubble()){ mBubbleData.setSelectedBubble(bubble); mBubbleData.setSelectedBubble(bubble); } } mBubbleData.setExpanded(true); mBubbleData.setExpanded(true); Loading Loading @@ -835,33 +832,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi updateBubble(notif, suppressFlyout, true /* showInShade */); updateBubble(notif, suppressFlyout, true /* showInShade */); } } /** * Fills the overflow bubbles by loading them from disk. */ void loadOverflowBubblesFromDisk() { if (!mBubbleData.getOverflowBubbles().isEmpty()) { // we don't need to load overflow bubbles from disk if it is already in memory return; } mDataRepository.loadBubbles((bubbles) -> { bubbles.forEach(bubble -> { if (mBubbleData.getBubbles().contains(bubble)) { // if the bubble is already active, there's no need to push it to overflow return; } bubble.inflate((b) -> mBubbleData.overflowBubble(DISMISS_AGED, bubble), mContext, mStackView, mBubbleIconFactory, true /* skipInflation */); }); return null; }); } void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) { void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) { if (mStackView == null) { if (mStackView == null) { // Lazy init stack view when a bubble is created // Lazy init stack view when a bubble is created ensureStackViewCreated(); ensureStackViewCreated(); // Lazy load overflow bubbles from disk loadOverflowBubblesFromDisk(); } } // If this is an interruptive notif, mark that it's interrupted // If this is an interruptive notif, mark that it's interrupted if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) { if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) { Loading @@ -881,11 +855,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi return; return; } } mHandler.post( mHandler.post( () -> removeBubble(bubble.getKey(), () -> removeBubble(bubble.getEntry(), BubbleController.DISMISS_INVALID_INTENT)); BubbleController.DISMISS_INVALID_INTENT)); }); }); }, }, mContext, mStackView, mBubbleIconFactory, false /* skipInflation */); mContext, mStackView, mBubbleIconFactory); } } /** /** Loading @@ -897,10 +871,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * @param entry the notification to change bubble state for. * @param entry the notification to change bubble state for. * @param shouldBubble whether the notification should show as a bubble or not. * @param shouldBubble whether the notification should show as a bubble or not. */ */ public void onUserChangedBubble(@Nullable final NotificationEntry entry, boolean shouldBubble) { public void onUserChangedBubble(NotificationEntry entry, boolean shouldBubble) { if (entry == null) { return; } NotificationChannel channel = entry.getChannel(); NotificationChannel channel = entry.getChannel(); final String appPkg = entry.getSbn().getPackageName(); final String appPkg = entry.getSbn().getPackageName(); final int appUid = entry.getSbn().getUid(); final int appUid = entry.getSbn().getUid(); Loading Loading @@ -939,14 +910,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } /** /** * Removes the bubble with the given key. * Removes the bubble with the given NotificationEntry. * <p> * <p> * Must be called from the main thread. * Must be called from the main thread. */ */ @MainThread @MainThread void removeBubble(String key, int reason) { void removeBubble(NotificationEntry entry, int reason) { if (mBubbleData.hasAnyBubbleWithKey(key)) { if (mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { mBubbleData.notificationEntryRemoved(key, reason); mBubbleData.notificationEntryRemoved(entry, reason); } } } } Loading @@ -962,7 +933,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi && canLaunchInActivityView(mContext, entry); && canLaunchInActivityView(mContext, entry); if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { // It was previously a bubble but no longer a bubble -- lets remove it // It was previously a bubble but no longer a bubble -- lets remove it removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE); removeBubble(entry, DISMISS_NO_LONGER_BUBBLE); } else if (shouldBubble) { } else if (shouldBubble) { updateBubble(entry); updateBubble(entry); } } Loading @@ -976,10 +947,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // Remove any associated bubble children with the summary // Remove any associated bubble children with the summary final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey); final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey); for (int i = 0; i < bubbleChildren.size(); i++) { for (int i = 0; i < bubbleChildren.size(); i++) { removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED); removeBubble(bubbleChildren.get(i).getEntry(), DISMISS_GROUP_CANCELLED); } } } else { } else { removeBubble(entry.getKey(), DISMISS_NOTIF_CANCEL); removeBubble(entry, DISMISS_NOTIF_CANCEL); } } } } Loading @@ -1001,8 +972,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi rankingMap.getRanking(key, mTmpRanking); rankingMap.getRanking(key, mTmpRanking); boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key); boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key); if (isActiveBubble && !mTmpRanking.canBubble()) { if (isActiveBubble && !mTmpRanking.canBubble()) { mBubbleData.notificationEntryRemoved(entry.getKey(), mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED); BubbleController.DISMISS_BLOCKED); } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) { } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) { entry.setFlagBubble(true); entry.setFlagBubble(true); onEntryUpdated(entry); onEntryUpdated(entry); Loading @@ -1012,16 +982,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi private void setIsBubble(Bubble b, boolean isBubble) { private void setIsBubble(Bubble b, boolean isBubble) { if (isBubble) { if (isBubble) { if (b.getEntry() != null) { b.getEntry().getSbn().getNotification().flags |= FLAG_BUBBLE; b.getEntry().getSbn().getNotification().flags |= FLAG_BUBBLE; } b.enable(FLAG_BUBBLE); } else { } else { if (b.getEntry() != null) { b.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE; b.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE; } } b.disable(FLAG_BUBBLE); } try { try { mBarService.onNotificationBubbleChanged(b.getKey(), isBubble, 0); mBarService.onNotificationBubbleChanged(b.getKey(), isBubble, 0); } catch (RemoteException e) { } catch (RemoteException e) { Loading Loading @@ -1068,21 +1032,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // The bubble is now gone & the notification is hidden from the shade, so // The bubble is now gone & the notification is hidden from the shade, so // time to actually remove it // time to actually remove it for (NotifCallback cb : mCallbacks) { for (NotifCallback cb : mCallbacks) { if (bubble.getEntry() != null) { cb.removeNotification(bubble.getEntry(), REASON_CANCEL); cb.removeNotification(bubble.getEntry(), REASON_CANCEL); } } } } else { } else { if (bubble.isBubble() && bubble.showInShade()) { if (bubble.getEntry().isBubble() && bubble.showInShade()) { setIsBubble(bubble, /* isBubble */ false); setIsBubble(bubble, /* isBubble */ false); } } if (bubble.getEntry() != null && bubble.getEntry().getRow() != null) { if (bubble.getEntry().getRow() != null) { bubble.getEntry().getRow().updateBubbleButton(); bubble.getEntry().getRow().updateBubbleButton(); } } } } } } if (bubble.getEntry() != null) { final String groupKey = bubble.getEntry().getSbn().getGroupKey(); final String groupKey = bubble.getEntry().getSbn().getGroupKey(); if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) { if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) { // Time to potentially remove the summary // Time to potentially remove the summary Loading @@ -1091,7 +1052,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } } } } } } mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository); mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository); if (update.addedBubble != null) { if (update.addedBubble != null) { Loading @@ -1113,7 +1073,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (update.selectionChanged) { if (update.selectionChanged) { mStackView.setSelectedBubble(update.selectedBubble); mStackView.setSelectedBubble(update.selectedBubble); if (update.selectedBubble != null && update.selectedBubble.getEntry() != null) { if (update.selectedBubble != null) { mNotificationGroupManager.updateSuppression( mNotificationGroupManager.updateSuppression( update.selectedBubble.getEntry()); update.selectedBubble.getEntry()); } } Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +8 −16 Original line number Original line Diff line number Diff line Loading @@ -23,7 +23,6 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList; import android.annotation.NonNull; import android.app.Notification; import android.app.Notification; import android.app.PendingIntent; import android.app.PendingIntent; import android.content.Context; import android.content.Context; Loading Loading @@ -224,7 +223,7 @@ public class BubbleData { false, /* showInShade */ true); false, /* showInShade */ true); setSelectedBubble(bubble); setSelectedBubble(bubble); }, }, mContext, stack, factory, false /* skipInflation */); mContext, stack, factory); dispatchPendingChanges(); dispatchPendingChanges(); } } Loading Loading @@ -279,8 +278,7 @@ public class BubbleData { } } mPendingBubbles.remove(bubble); // No longer pending once we're here mPendingBubbles.remove(bubble); // No longer pending once we're here Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey()); Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey()); suppressFlyout |= bubble.getEntry() == null suppressFlyout |= !bubble.getEntry().getRanking().visuallyInterruptive(); || !bubble.getEntry().getRanking().visuallyInterruptive(); if (prevBubble == null) { if (prevBubble == null) { // Create a new bubble // Create a new bubble Loading Loading @@ -309,14 +307,11 @@ public class BubbleData { dispatchPendingChanges(); dispatchPendingChanges(); } } /** public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) { * Called when a notification associated with a bubble is removed. */ public void notificationEntryRemoved(String key, @DismissReason int reason) { if (DEBUG_BUBBLE_DATA) { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "notificationEntryRemoved: key=" + key + " reason=" + reason); Log.d(TAG, "notificationEntryRemoved: entry=" + entry + " reason=" + reason); } } doRemove(key, reason); doRemove(entry.getKey(), reason); dispatchPendingChanges(); dispatchPendingChanges(); } } Loading Loading @@ -364,7 +359,7 @@ public class BubbleData { return bubbleChildren; return bubbleChildren; } } for (Bubble b : mBubbles) { for (Bubble b : mBubbles) { if (b.getEntry() != null && groupKey.equals(b.getEntry().getSbn().getGroupKey())) { if (groupKey.equals(b.getEntry().getSbn().getGroupKey())) { bubbleChildren.add(b); bubbleChildren.add(b); } } } } Loading Loading @@ -475,10 +470,8 @@ public class BubbleData { Bubble newSelected = mBubbles.get(newIndex); Bubble newSelected = mBubbles.get(newIndex); setSelectedBubbleInternal(newSelected); setSelectedBubbleInternal(newSelected); } } if (bubbleToRemove.getEntry() != null) { maybeSendDeleteIntent(reason, bubbleToRemove.getEntry()); maybeSendDeleteIntent(reason, bubbleToRemove.getEntry()); } } } void overflowBubble(@DismissReason int reason, Bubble bubble) { void overflowBubble(@DismissReason int reason, Bubble bubble) { if (bubble.getPendingIntentCanceled() if (bubble.getPendingIntentCanceled() Loading Loading @@ -751,8 +744,7 @@ public class BubbleData { return true; return true; } } private void maybeSendDeleteIntent(@DismissReason int reason, private void maybeSendDeleteIntent(@DismissReason int reason, NotificationEntry entry) { @NonNull final NotificationEntry entry) { if (reason == BubbleController.DISMISS_USER_GESTURE) { if (reason == BubbleController.DISMISS_USER_GESTURE) { Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata(); Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata(); PendingIntent deleteIntent = bubbleMetadata != null PendingIntent deleteIntent = bubbleMetadata != null Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt +2 −4 Original line number Original line Diff line number Diff line Loading @@ -74,10 +74,7 @@ internal class BubbleDataRepository @Inject constructor( private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleEntity> { private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleEntity> { return bubbles.mapNotNull { b -> return bubbles.mapNotNull { b -> var shortcutId = b.shortcutInfo?.id val shortcutId = b.shortcutInfo?.id ?: return@mapNotNull null if (shortcutId == null) shortcutId = b.entry?.bubbleMetadata?.shortcutId if (shortcutId == null) shortcutId = b.entry?.ranking?.shortcutInfo?.id if (shortcutId == null) return@mapNotNull null BubbleEntity(userId, b.packageName, shortcutId) BubbleEntity(userId, b.packageName, shortcutId) } } } } Loading Loading @@ -111,6 +108,7 @@ internal class BubbleDataRepository @Inject constructor( /** /** * Load bubbles from disk. * Load bubbles from disk. */ */ // TODO: call this method from BubbleController and update UI @SuppressLint("WrongConstant") @SuppressLint("WrongConstant") fun loadBubbles(cb: (List<Bubble>) -> Unit) = ioScope.launch { fun loadBubbles(cb: (List<Bubble>) -> Unit) = ioScope.launch { /** /** Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +7 −2 Original line number Original line Diff line number Diff line Loading @@ -65,6 +65,7 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.R; import com.android.systemui.recents.TriangleShape; import com.android.systemui.recents.TriangleShape; import com.android.systemui.statusbar.AlphaOptimizedButton; import com.android.systemui.statusbar.AlphaOptimizedButton; import com.android.systemui.statusbar.notification.collection.NotificationEntry; /** /** * Container for the expanded bubble view, handles rendering the caret and settings icon. * Container for the expanded bubble view, handles rendering the caret and settings icon. Loading Loading @@ -160,7 +161,7 @@ public class BubbleExpandedView extends LinearLayout { // the bubble again so we'll just remove it. // the bubble again so we'll just remove it. Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey() Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey() + ", " + e.getMessage() + "; removing bubble"); + ", " + e.getMessage() + "; removing bubble"); mBubbleController.removeBubble(getBubbleKey(), mBubbleController.removeBubble(getBubbleEntry(), BubbleController.DISMISS_INVALID_INTENT); BubbleController.DISMISS_INVALID_INTENT); } } }); }); Loading Loading @@ -204,7 +205,7 @@ public class BubbleExpandedView extends LinearLayout { } } if (mBubble != null) { if (mBubble != null) { // Must post because this is called from a binder thread. // Must post because this is called from a binder thread. post(() -> mBubbleController.removeBubble(mBubble.getKey(), post(() -> mBubbleController.removeBubble(mBubble.getEntry(), BubbleController.DISMISS_TASK_FINISHED)); BubbleController.DISMISS_TASK_FINISHED)); } } } } Loading Loading @@ -291,6 +292,10 @@ public class BubbleExpandedView extends LinearLayout { return mBubble != null ? mBubble.getKey() : "null"; return mBubble != null ? mBubble.getKey() : "null"; } } private NotificationEntry getBubbleEntry() { return mBubble != null ? mBubble.getEntry() : null; } void setManageClickListener(OnClickListener manageClickListener) { void setManageClickListener(OnClickListener manageClickListener) { findViewById(R.id.settings_button).setOnClickListener(manageClickListener); findViewById(R.id.settings_button).setOnClickListener(manageClickListener); } } Loading Loading
packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +11 −108 Original line number Original line Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.systemui.bubbles; package com.android.systemui.bubbles; import static android.app.Notification.FLAG_BUBBLE; import static android.os.AsyncTask.Status.FINISHED; import static android.os.AsyncTask.Status.FINISHED; import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.INVALID_DISPLAY; Loading @@ -28,7 +27,6 @@ import android.app.Notification; import android.app.PendingIntent; import android.app.PendingIntent; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherApps; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo; Loading Loading @@ -57,11 +55,6 @@ import java.util.Objects; class Bubble implements BubbleViewProvider { class Bubble implements BubbleViewProvider { private static final String TAG = "Bubble"; 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 NotificationEntry mEntry; private final String mKey; private final String mKey; private final String mGroupId; private final String mGroupId; Loading Loading @@ -102,56 +95,18 @@ class Bubble implements BubbleViewProvider { private Bitmap mBadgedImage; private Bitmap mBadgedImage; private int mDotColor; private int mDotColor; private Path mDotPath; private Path mDotPath; private int mFlags; /** * Extract GroupId from {@link NotificationEntry}. See also {@link #groupId(ShortcutInfo)}. */ public static String groupId(NotificationEntry entry) { public static String groupId(NotificationEntry entry) { UserHandle user = entry.getSbn().getUser(); UserHandle user = entry.getSbn().getUser(); return user.getIdentifier() + "|" + entry.getSbn().getPackageName(); return user.getIdentifier() + "|" + entry.getSbn().getPackageName(); } } /** // TODO: Decouple Bubble from NotificationEntry and transform ShortcutInfo into Bubble * Extract GroupId from {@link ShortcutInfo}. This should match the one generated from * {@link NotificationEntry}. See also {@link #groupId(NotificationEntry)}. */ @NonNull public static String groupId(@NonNull final ShortcutInfo shortcutInfo) { return shortcutInfo.getUserId() + "|" + shortcutInfo.getPackage(); } /** * Generate a unique identifier for this bubble based on given {@link NotificationEntry}. If * {@link ShortcutInfo} was found in the notification entry, the identifier would be generated * from {@link ShortcutInfo} instead. See also {@link #key(ShortcutInfo)}. */ @NonNull public static String key(@NonNull final NotificationEntry entry) { final ShortcutInfo shortcutInfo = entry.getRanking().getShortcutInfo(); if (shortcutInfo != null) return key(shortcutInfo); return entry.getKey(); } /** * Generate a unique identifier for this bubble based on given {@link ShortcutInfo}. * See also {@link #key(NotificationEntry)}. */ @NonNull public static String key(@NonNull final ShortcutInfo shortcutInfo) { return shortcutInfo.getUserId() + "|" + shortcutInfo.getPackage() + "|" + shortcutInfo.getId(); } /** * 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(ShortcutInfo shortcutInfo) { Bubble(ShortcutInfo shortcutInfo) { mShortcutInfo = shortcutInfo; mShortcutInfo = shortcutInfo; mKey = key(shortcutInfo); mKey = shortcutInfo.getId(); mGroupId = groupId(shortcutInfo); mGroupId = shortcutInfo.getId(); mFlags = 0; } } /** Used in tests when no UI is required. */ /** Used in tests when no UI is required. */ Loading @@ -159,11 +114,10 @@ class Bubble implements BubbleViewProvider { Bubble(NotificationEntry e, Bubble(NotificationEntry e, BubbleController.NotificationSuppressionChangedListener listener) { BubbleController.NotificationSuppressionChangedListener listener) { mEntry = e; mEntry = e; mKey = key(e); mKey = e.getKey(); mLastUpdated = e.getSbn().getPostTime(); mLastUpdated = e.getSbn().getPostTime(); mGroupId = groupId(e); mGroupId = groupId(e); mSuppressionListener = listener; mSuppressionListener = listener; mFlags = e.getSbn().getNotification().flags; } } @Override @Override Loading @@ -171,26 +125,16 @@ class Bubble implements BubbleViewProvider { return mKey; return mKey; } } @Nullable public NotificationEntry getEntry() { public NotificationEntry getEntry() { return mEntry; return mEntry; } } @Nullable public UserHandle getUser() { if (mEntry != null) return mEntry.getSbn().getUser(); if (mShortcutInfo != null) return mShortcutInfo.getUserHandle(); return null; } public String getGroupId() { public String getGroupId() { return mGroupId; return mGroupId; } } public String getPackageName() { public String getPackageName() { return mEntry == null return mEntry.getSbn().getPackageName(); ? mShortcutInfo == null ? null : mShortcutInfo.getPackage() : mEntry.getSbn().getPackageName(); } } @Override @Override Loading Loading @@ -274,8 +218,7 @@ class Bubble implements BubbleViewProvider { void inflate(BubbleViewInfoTask.Callback callback, void inflate(BubbleViewInfoTask.Callback callback, Context context, Context context, BubbleStackView stackView, BubbleStackView stackView, BubbleIconFactory iconFactory, BubbleIconFactory iconFactory) { boolean skipInflation) { if (isBubbleLoading()) { if (isBubbleLoading()) { mInflationTask.cancel(true /* mayInterruptIfRunning */); mInflationTask.cancel(true /* mayInterruptIfRunning */); } } Loading @@ -283,7 +226,6 @@ class Bubble implements BubbleViewProvider { context, context, stackView, stackView, iconFactory, iconFactory, skipInflation, callback); callback); if (mInflateSynchronously) { if (mInflateSynchronously) { mInflationTask.onPostExecute(mInflationTask.doInBackground()); mInflationTask.onPostExecute(mInflationTask.doInBackground()); Loading Loading @@ -403,7 +345,6 @@ class Bubble implements BubbleViewProvider { * Whether this notification should be shown in the shade. * Whether this notification should be shown in the shade. */ */ boolean showInShade() { boolean showInShade() { if (mEntry == null) return false; return !shouldSuppressNotification() || !mEntry.isClearable(); return !shouldSuppressNotification() || !mEntry.isClearable(); } } Loading @@ -411,8 +352,8 @@ class Bubble implements BubbleViewProvider { * Sets whether this notification should be suppressed in the shade. * Sets whether this notification should be suppressed in the shade. */ */ void setSuppressNotification(boolean suppressNotification) { void setSuppressNotification(boolean suppressNotification) { if (mEntry == null) return; boolean prevShowInShade = showInShade(); boolean prevShowInShade = showInShade(); Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); int flags = data.getFlags(); int flags = data.getFlags(); if (suppressNotification) { if (suppressNotification) { Loading Loading @@ -443,7 +384,6 @@ class Bubble implements BubbleViewProvider { */ */ @Override @Override public boolean showDot() { public boolean showDot() { if (mEntry == null) return false; return mShowBubbleUpdateDot return mShowBubbleUpdateDot && !mEntry.shouldSuppressNotificationDot() && !mEntry.shouldSuppressNotificationDot() && !shouldSuppressNotification(); && !shouldSuppressNotification(); Loading @@ -453,7 +393,6 @@ class Bubble implements BubbleViewProvider { * Whether the flyout for the bubble should be shown. * Whether the flyout for the bubble should be shown. */ */ boolean showFlyout() { boolean showFlyout() { if (mEntry == null) return false; return !mSuppressFlyout && !mEntry.shouldSuppressPeek() return !mSuppressFlyout && !mEntry.shouldSuppressPeek() && !shouldSuppressNotification() && !shouldSuppressNotification() && !mEntry.shouldSuppressNotificationList(); && !mEntry.shouldSuppressNotificationList(); Loading @@ -477,13 +416,11 @@ class Bubble implements BubbleViewProvider { * is an ongoing bubble. * is an ongoing bubble. */ */ boolean isOngoing() { boolean isOngoing() { if (mEntry == null) return false; int flags = mEntry.getSbn().getNotification().flags; int flags = mEntry.getSbn().getNotification().flags; return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; } } float getDesiredHeight(Context context) { float getDesiredHeight(Context context) { if (mEntry == null) return 0; Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); boolean useRes = data.getDesiredHeightResId() != 0; boolean useRes = data.getDesiredHeightResId() != 0; if (useRes) { if (useRes) { Loading @@ -497,7 +434,6 @@ class Bubble implements BubbleViewProvider { } } String getDesiredHeightString() { String getDesiredHeightString() { if (mEntry == null) return String.valueOf(0); Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); boolean useRes = data.getDesiredHeightResId() != 0; boolean useRes = data.getDesiredHeightResId() != 0; if (useRes) { if (useRes) { Loading @@ -514,13 +450,11 @@ class Bubble implements BubbleViewProvider { * To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}. * To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}. */ */ boolean usingShortcutInfo() { boolean usingShortcutInfo() { return mEntry != null && mEntry.getBubbleMetadata().getShortcutId() != null return mEntry.getBubbleMetadata().getShortcutId() != null; || mShortcutInfo != null; } } @Nullable @Nullable PendingIntent getBubbleIntent() { PendingIntent getBubbleIntent() { if (mEntry == null) return null; Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); if (data != null) { if (data != null) { return data.getIntent(); return data.getIntent(); Loading @@ -528,32 +462,16 @@ class Bubble implements BubbleViewProvider { return null; return null; } } Intent getSettingsIntent(final Context context) { Intent getSettingsIntent() { final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS); final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); final int uid = getUid(context); intent.putExtra(Settings.EXTRA_APP_UID, mEntry.getSbn().getUid()); if (uid != -1) { intent.putExtra(Settings.EXTRA_APP_UID, uid); } intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); return intent; return intent; } } private int getUid(final Context context) { if (mEntry != null) return mEntry.getSbn().getUid(); final PackageManager pm = context.getPackageManager(); if (pm == null) return -1; try { final ApplicationInfo info = pm.getApplicationInfo(mShortcutInfo.getPackage(), 0); return info.uid; } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "cannot find uid", e); } return -1; } private int getDimenForPackageUser(Context context, int resId, String pkg, int userId) { private int getDimenForPackageUser(Context context, int resId, String pkg, int userId) { PackageManager pm = context.getPackageManager(); PackageManager pm = context.getPackageManager(); Resources r; Resources r; Loading @@ -575,30 +493,15 @@ class Bubble implements BubbleViewProvider { } } private boolean shouldSuppressNotification() { private boolean shouldSuppressNotification() { if (mEntry == null) return false; return mEntry.getBubbleMetadata() != null return mEntry.getBubbleMetadata() != null && mEntry.getBubbleMetadata().isNotificationSuppressed(); && mEntry.getBubbleMetadata().isNotificationSuppressed(); } } boolean shouldAutoExpand() { boolean shouldAutoExpand() { if (mEntry == null) return false; Notification.BubbleMetadata metadata = mEntry.getBubbleMetadata(); Notification.BubbleMetadata metadata = mEntry.getBubbleMetadata(); return metadata != null && metadata.getAutoExpandBubble(); return metadata != null && metadata.getAutoExpandBubble(); } } public boolean isBubble() { if (mEntry == null) return (mFlags & FLAG_BUBBLE) != 0; return (mEntry.getSbn().getNotification().flags & FLAG_BUBBLE) != 0; } public void enable(int option) { mFlags |= option; } public void disable(int option) { mFlags &= ~option; } @Override @Override public String toString() { public String toString() { return "Bubble{" + mKey + '}'; return "Bubble{" + mKey + '}'; Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +29 −69 Original line number Original line Diff line number Diff line Loading @@ -42,7 +42,6 @@ import static java.lang.annotation.ElementType.LOCAL_VARIABLE; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.SOURCE; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.annotation.UserIdInt; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.RunningTaskInfo; import android.app.INotificationManager; import android.app.INotificationManager; Loading Loading @@ -243,7 +242,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * This can happen when an app cancels a bubbled notification or when the user dismisses a * This can happen when an app cancels a bubbled notification or when the user dismisses a * bubble. * bubble. */ */ void removeNotification(@NonNull NotificationEntry entry, int reason); void removeNotification(NotificationEntry entry, int reason); /** /** * Called when a bubbled notification has changed whether it should be * Called when a bubbled notification has changed whether it should be Loading @@ -259,7 +258,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * removes all remnants of the group's summary from the notification pipeline. * removes all remnants of the group's summary from the notification pipeline. * TODO: (b/145659174) Only old pipeline needs this - delete post-migration. * TODO: (b/145659174) Only old pipeline needs this - delete post-migration. */ */ void maybeCancelSummary(@NonNull NotificationEntry entry); void maybeCancelSummary(NotificationEntry entry); } } /** /** Loading Loading @@ -482,7 +481,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi addNotifCallback(new NotifCallback() { addNotifCallback(new NotifCallback() { @Override @Override public void removeNotification(@NonNull final NotificationEntry entry, int reason) { public void removeNotification(NotificationEntry entry, int reason) { mNotificationEntryManager.performRemoveNotification(entry.getSbn(), mNotificationEntryManager.performRemoveNotification(entry.getSbn(), reason); reason); } } Loading @@ -493,7 +492,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } @Override @Override public void maybeCancelSummary(@NonNull final NotificationEntry entry) { public void maybeCancelSummary(NotificationEntry entry) { // Check if removed bubble has an associated suppressed group summary that needs // Check if removed bubble has an associated suppressed group summary that needs // to be removed now. // to be removed now. final String groupKey = entry.getSbn().getGroupKey(); final String groupKey = entry.getSbn().getGroupKey(); Loading Loading @@ -702,12 +701,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mBubbleIconFactory = new BubbleIconFactory(mContext); mBubbleIconFactory = new BubbleIconFactory(mContext); // Reload each bubble // Reload each bubble for (Bubble b: mBubbleData.getBubbles()) { for (Bubble b: mBubbleData.getBubbles()) { b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory, b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory); false /* skipInflation */); } } for (Bubble b: mBubbleData.getOverflowBubbles()) { for (Bubble b: mBubbleData.getOverflowBubbles()) { b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory, b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory); false /* skipInflation */); } } } } Loading Loading @@ -806,7 +803,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (bubble != null) { if (bubble != null) { mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory); mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory); } } } else if (bubble.isBubble()) { } else if (bubble.getEntry().isBubble()){ mBubbleData.setSelectedBubble(bubble); mBubbleData.setSelectedBubble(bubble); } } mBubbleData.setExpanded(true); mBubbleData.setExpanded(true); Loading Loading @@ -835,33 +832,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi updateBubble(notif, suppressFlyout, true /* showInShade */); updateBubble(notif, suppressFlyout, true /* showInShade */); } } /** * Fills the overflow bubbles by loading them from disk. */ void loadOverflowBubblesFromDisk() { if (!mBubbleData.getOverflowBubbles().isEmpty()) { // we don't need to load overflow bubbles from disk if it is already in memory return; } mDataRepository.loadBubbles((bubbles) -> { bubbles.forEach(bubble -> { if (mBubbleData.getBubbles().contains(bubble)) { // if the bubble is already active, there's no need to push it to overflow return; } bubble.inflate((b) -> mBubbleData.overflowBubble(DISMISS_AGED, bubble), mContext, mStackView, mBubbleIconFactory, true /* skipInflation */); }); return null; }); } void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) { void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) { if (mStackView == null) { if (mStackView == null) { // Lazy init stack view when a bubble is created // Lazy init stack view when a bubble is created ensureStackViewCreated(); ensureStackViewCreated(); // Lazy load overflow bubbles from disk loadOverflowBubblesFromDisk(); } } // If this is an interruptive notif, mark that it's interrupted // If this is an interruptive notif, mark that it's interrupted if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) { if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) { Loading @@ -881,11 +855,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi return; return; } } mHandler.post( mHandler.post( () -> removeBubble(bubble.getKey(), () -> removeBubble(bubble.getEntry(), BubbleController.DISMISS_INVALID_INTENT)); BubbleController.DISMISS_INVALID_INTENT)); }); }); }, }, mContext, mStackView, mBubbleIconFactory, false /* skipInflation */); mContext, mStackView, mBubbleIconFactory); } } /** /** Loading @@ -897,10 +871,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * @param entry the notification to change bubble state for. * @param entry the notification to change bubble state for. * @param shouldBubble whether the notification should show as a bubble or not. * @param shouldBubble whether the notification should show as a bubble or not. */ */ public void onUserChangedBubble(@Nullable final NotificationEntry entry, boolean shouldBubble) { public void onUserChangedBubble(NotificationEntry entry, boolean shouldBubble) { if (entry == null) { return; } NotificationChannel channel = entry.getChannel(); NotificationChannel channel = entry.getChannel(); final String appPkg = entry.getSbn().getPackageName(); final String appPkg = entry.getSbn().getPackageName(); final int appUid = entry.getSbn().getUid(); final int appUid = entry.getSbn().getUid(); Loading Loading @@ -939,14 +910,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } /** /** * Removes the bubble with the given key. * Removes the bubble with the given NotificationEntry. * <p> * <p> * Must be called from the main thread. * Must be called from the main thread. */ */ @MainThread @MainThread void removeBubble(String key, int reason) { void removeBubble(NotificationEntry entry, int reason) { if (mBubbleData.hasAnyBubbleWithKey(key)) { if (mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { mBubbleData.notificationEntryRemoved(key, reason); mBubbleData.notificationEntryRemoved(entry, reason); } } } } Loading @@ -962,7 +933,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi && canLaunchInActivityView(mContext, entry); && canLaunchInActivityView(mContext, entry); if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { // It was previously a bubble but no longer a bubble -- lets remove it // It was previously a bubble but no longer a bubble -- lets remove it removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE); removeBubble(entry, DISMISS_NO_LONGER_BUBBLE); } else if (shouldBubble) { } else if (shouldBubble) { updateBubble(entry); updateBubble(entry); } } Loading @@ -976,10 +947,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // Remove any associated bubble children with the summary // Remove any associated bubble children with the summary final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey); final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey); for (int i = 0; i < bubbleChildren.size(); i++) { for (int i = 0; i < bubbleChildren.size(); i++) { removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED); removeBubble(bubbleChildren.get(i).getEntry(), DISMISS_GROUP_CANCELLED); } } } else { } else { removeBubble(entry.getKey(), DISMISS_NOTIF_CANCEL); removeBubble(entry, DISMISS_NOTIF_CANCEL); } } } } Loading @@ -1001,8 +972,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi rankingMap.getRanking(key, mTmpRanking); rankingMap.getRanking(key, mTmpRanking); boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key); boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key); if (isActiveBubble && !mTmpRanking.canBubble()) { if (isActiveBubble && !mTmpRanking.canBubble()) { mBubbleData.notificationEntryRemoved(entry.getKey(), mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED); BubbleController.DISMISS_BLOCKED); } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) { } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) { entry.setFlagBubble(true); entry.setFlagBubble(true); onEntryUpdated(entry); onEntryUpdated(entry); Loading @@ -1012,16 +982,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi private void setIsBubble(Bubble b, boolean isBubble) { private void setIsBubble(Bubble b, boolean isBubble) { if (isBubble) { if (isBubble) { if (b.getEntry() != null) { b.getEntry().getSbn().getNotification().flags |= FLAG_BUBBLE; b.getEntry().getSbn().getNotification().flags |= FLAG_BUBBLE; } b.enable(FLAG_BUBBLE); } else { } else { if (b.getEntry() != null) { b.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE; b.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE; } } b.disable(FLAG_BUBBLE); } try { try { mBarService.onNotificationBubbleChanged(b.getKey(), isBubble, 0); mBarService.onNotificationBubbleChanged(b.getKey(), isBubble, 0); } catch (RemoteException e) { } catch (RemoteException e) { Loading Loading @@ -1068,21 +1032,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // The bubble is now gone & the notification is hidden from the shade, so // The bubble is now gone & the notification is hidden from the shade, so // time to actually remove it // time to actually remove it for (NotifCallback cb : mCallbacks) { for (NotifCallback cb : mCallbacks) { if (bubble.getEntry() != null) { cb.removeNotification(bubble.getEntry(), REASON_CANCEL); cb.removeNotification(bubble.getEntry(), REASON_CANCEL); } } } } else { } else { if (bubble.isBubble() && bubble.showInShade()) { if (bubble.getEntry().isBubble() && bubble.showInShade()) { setIsBubble(bubble, /* isBubble */ false); setIsBubble(bubble, /* isBubble */ false); } } if (bubble.getEntry() != null && bubble.getEntry().getRow() != null) { if (bubble.getEntry().getRow() != null) { bubble.getEntry().getRow().updateBubbleButton(); bubble.getEntry().getRow().updateBubbleButton(); } } } } } } if (bubble.getEntry() != null) { final String groupKey = bubble.getEntry().getSbn().getGroupKey(); final String groupKey = bubble.getEntry().getSbn().getGroupKey(); if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) { if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) { // Time to potentially remove the summary // Time to potentially remove the summary Loading @@ -1091,7 +1052,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } } } } } } mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository); mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository); if (update.addedBubble != null) { if (update.addedBubble != null) { Loading @@ -1113,7 +1073,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (update.selectionChanged) { if (update.selectionChanged) { mStackView.setSelectedBubble(update.selectedBubble); mStackView.setSelectedBubble(update.selectedBubble); if (update.selectedBubble != null && update.selectedBubble.getEntry() != null) { if (update.selectedBubble != null) { mNotificationGroupManager.updateSuppression( mNotificationGroupManager.updateSuppression( update.selectedBubble.getEntry()); update.selectedBubble.getEntry()); } } Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +8 −16 Original line number Original line Diff line number Diff line Loading @@ -23,7 +23,6 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList; import android.annotation.NonNull; import android.app.Notification; import android.app.Notification; import android.app.PendingIntent; import android.app.PendingIntent; import android.content.Context; import android.content.Context; Loading Loading @@ -224,7 +223,7 @@ public class BubbleData { false, /* showInShade */ true); false, /* showInShade */ true); setSelectedBubble(bubble); setSelectedBubble(bubble); }, }, mContext, stack, factory, false /* skipInflation */); mContext, stack, factory); dispatchPendingChanges(); dispatchPendingChanges(); } } Loading Loading @@ -279,8 +278,7 @@ public class BubbleData { } } mPendingBubbles.remove(bubble); // No longer pending once we're here mPendingBubbles.remove(bubble); // No longer pending once we're here Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey()); Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey()); suppressFlyout |= bubble.getEntry() == null suppressFlyout |= !bubble.getEntry().getRanking().visuallyInterruptive(); || !bubble.getEntry().getRanking().visuallyInterruptive(); if (prevBubble == null) { if (prevBubble == null) { // Create a new bubble // Create a new bubble Loading Loading @@ -309,14 +307,11 @@ public class BubbleData { dispatchPendingChanges(); dispatchPendingChanges(); } } /** public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) { * Called when a notification associated with a bubble is removed. */ public void notificationEntryRemoved(String key, @DismissReason int reason) { if (DEBUG_BUBBLE_DATA) { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "notificationEntryRemoved: key=" + key + " reason=" + reason); Log.d(TAG, "notificationEntryRemoved: entry=" + entry + " reason=" + reason); } } doRemove(key, reason); doRemove(entry.getKey(), reason); dispatchPendingChanges(); dispatchPendingChanges(); } } Loading Loading @@ -364,7 +359,7 @@ public class BubbleData { return bubbleChildren; return bubbleChildren; } } for (Bubble b : mBubbles) { for (Bubble b : mBubbles) { if (b.getEntry() != null && groupKey.equals(b.getEntry().getSbn().getGroupKey())) { if (groupKey.equals(b.getEntry().getSbn().getGroupKey())) { bubbleChildren.add(b); bubbleChildren.add(b); } } } } Loading Loading @@ -475,10 +470,8 @@ public class BubbleData { Bubble newSelected = mBubbles.get(newIndex); Bubble newSelected = mBubbles.get(newIndex); setSelectedBubbleInternal(newSelected); setSelectedBubbleInternal(newSelected); } } if (bubbleToRemove.getEntry() != null) { maybeSendDeleteIntent(reason, bubbleToRemove.getEntry()); maybeSendDeleteIntent(reason, bubbleToRemove.getEntry()); } } } void overflowBubble(@DismissReason int reason, Bubble bubble) { void overflowBubble(@DismissReason int reason, Bubble bubble) { if (bubble.getPendingIntentCanceled() if (bubble.getPendingIntentCanceled() Loading Loading @@ -751,8 +744,7 @@ public class BubbleData { return true; return true; } } private void maybeSendDeleteIntent(@DismissReason int reason, private void maybeSendDeleteIntent(@DismissReason int reason, NotificationEntry entry) { @NonNull final NotificationEntry entry) { if (reason == BubbleController.DISMISS_USER_GESTURE) { if (reason == BubbleController.DISMISS_USER_GESTURE) { Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata(); Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata(); PendingIntent deleteIntent = bubbleMetadata != null PendingIntent deleteIntent = bubbleMetadata != null Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt +2 −4 Original line number Original line Diff line number Diff line Loading @@ -74,10 +74,7 @@ internal class BubbleDataRepository @Inject constructor( private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleEntity> { private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleEntity> { return bubbles.mapNotNull { b -> return bubbles.mapNotNull { b -> var shortcutId = b.shortcutInfo?.id val shortcutId = b.shortcutInfo?.id ?: return@mapNotNull null if (shortcutId == null) shortcutId = b.entry?.bubbleMetadata?.shortcutId if (shortcutId == null) shortcutId = b.entry?.ranking?.shortcutInfo?.id if (shortcutId == null) return@mapNotNull null BubbleEntity(userId, b.packageName, shortcutId) BubbleEntity(userId, b.packageName, shortcutId) } } } } Loading Loading @@ -111,6 +108,7 @@ internal class BubbleDataRepository @Inject constructor( /** /** * Load bubbles from disk. * Load bubbles from disk. */ */ // TODO: call this method from BubbleController and update UI @SuppressLint("WrongConstant") @SuppressLint("WrongConstant") fun loadBubbles(cb: (List<Bubble>) -> Unit) = ioScope.launch { fun loadBubbles(cb: (List<Bubble>) -> Unit) = ioScope.launch { /** /** Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +7 −2 Original line number Original line Diff line number Diff line Loading @@ -65,6 +65,7 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.R; import com.android.systemui.recents.TriangleShape; import com.android.systemui.recents.TriangleShape; import com.android.systemui.statusbar.AlphaOptimizedButton; import com.android.systemui.statusbar.AlphaOptimizedButton; import com.android.systemui.statusbar.notification.collection.NotificationEntry; /** /** * Container for the expanded bubble view, handles rendering the caret and settings icon. * Container for the expanded bubble view, handles rendering the caret and settings icon. Loading Loading @@ -160,7 +161,7 @@ public class BubbleExpandedView extends LinearLayout { // the bubble again so we'll just remove it. // the bubble again so we'll just remove it. Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey() Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey() + ", " + e.getMessage() + "; removing bubble"); + ", " + e.getMessage() + "; removing bubble"); mBubbleController.removeBubble(getBubbleKey(), mBubbleController.removeBubble(getBubbleEntry(), BubbleController.DISMISS_INVALID_INTENT); BubbleController.DISMISS_INVALID_INTENT); } } }); }); Loading Loading @@ -204,7 +205,7 @@ public class BubbleExpandedView extends LinearLayout { } } if (mBubble != null) { if (mBubble != null) { // Must post because this is called from a binder thread. // Must post because this is called from a binder thread. post(() -> mBubbleController.removeBubble(mBubble.getKey(), post(() -> mBubbleController.removeBubble(mBubble.getEntry(), BubbleController.DISMISS_TASK_FINISHED)); BubbleController.DISMISS_TASK_FINISHED)); } } } } Loading Loading @@ -291,6 +292,10 @@ public class BubbleExpandedView extends LinearLayout { return mBubble != null ? mBubble.getKey() : "null"; return mBubble != null ? mBubble.getKey() : "null"; } } private NotificationEntry getBubbleEntry() { return mBubble != null ? mBubble.getEntry() : null; } void setManageClickListener(OnClickListener manageClickListener) { void setManageClickListener(OnClickListener manageClickListener) { findViewById(R.id.settings_button).setOnClickListener(manageClickListener); findViewById(R.id.settings_button).setOnClickListener(manageClickListener); } } Loading