Loading packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +99 −30 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; import android.app.Person; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; Loading @@ -35,16 +36,18 @@ import android.graphics.Bitmap; import android.graphics.Path; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Parcelable; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; import java.util.Objects; /** Loading Loading @@ -169,11 +172,10 @@ class Bubble implements BubbleViewProvider { } @VisibleForTesting(visibility = PRIVATE) Bubble(@NonNull final NotificationEntry e, Bubble(@NonNull final BubbleEntry entry, @Nullable final BubbleController.NotificationSuppressionChangedListener listener, final BubbleController.PendingIntentCanceledListener intentCancelListener) { Objects.requireNonNull(e); mKey = e.getKey(); mKey = entry.getKey(); mSuppressionListener = listener; mIntentCancelListener = intent -> { if (mIntent != null) { Loading @@ -181,7 +183,7 @@ class Bubble implements BubbleViewProvider { } intentCancelListener.onPendingIntentCanceled(this); }; setEntry(e); setEntry(entry); } @Override Loading Loading @@ -294,8 +296,8 @@ class Bubble implements BubbleViewProvider { } /** * Sets whether this bubble is considered visually interruptive. Normally pulled from the * {@link NotificationEntry}, this method is purely for testing. * Sets whether this bubble is considered visually interruptive. This method is purely for * testing. */ @VisibleForTesting void setVisuallyInterruptiveForTest(boolean visuallyInterruptive) { Loading Loading @@ -388,30 +390,28 @@ class Bubble implements BubbleViewProvider { /** * Sets the entry associated with this bubble. */ void setEntry(@NonNull final NotificationEntry entry) { void setEntry(@NonNull final BubbleEntry entry) { Objects.requireNonNull(entry); Objects.requireNonNull(entry.getSbn()); mLastUpdated = entry.getSbn().getPostTime(); mIsBubble = entry.getSbn().getNotification().isBubbleNotification(); mPackageName = entry.getSbn().getPackageName(); mUser = entry.getSbn().getUser(); mLastUpdated = entry.getStatusBarNotification().getPostTime(); mIsBubble = entry.getStatusBarNotification().getNotification().isBubbleNotification(); mPackageName = entry.getStatusBarNotification().getPackageName(); mUser = entry.getStatusBarNotification().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); mShortcutInfo = (entry.getRanking() != null ? entry.getRanking().getShortcutInfo() : null); mMetadataShortcutId = (entry.getBubbleMetadata() != null ? entry.getBubbleMetadata().getShortcutId() : null); mChannelId = entry.getStatusBarNotification().getNotification().getChannelId(); mNotificationId = entry.getStatusBarNotification().getId(); mAppUid = entry.getStatusBarNotification().getUid(); mInstanceId = entry.getStatusBarNotification().getInstanceId(); mFlyoutMessage = extractFlyoutMessage(entry); if (entry.getRanking() != null) { mShortcutInfo = entry.getRanking().getShortcutInfo(); mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive(); if (entry.getRanking().getChannel() != null) { mIsImportantConversation = entry.getRanking().getChannel().isImportantConversation(); } } if (entry.getBubbleMetadata() != null) { mMetadataShortcutId = entry.getBubbleMetadata().getShortcutId(); mFlags = entry.getBubbleMetadata().getFlags(); mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight(); mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId(); Loading @@ -433,8 +433,11 @@ class Bubble implements BubbleViewProvider { } mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent(); } mIsImportantConversation = entry.getChannel() != null && entry.getChannel().isImportantConversation(); mIsClearable = entry.isClearable(); mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot(); mShouldSuppressNotificationList = entry.shouldSuppressNotificationList(); mShouldSuppressPeek = entry.shouldSuppressPeek(); } @Nullable Loading Loading @@ -735,9 +738,75 @@ class Bubble implements BubbleViewProvider { } @Nullable private static String getTitle(@NonNull final NotificationEntry e) { final CharSequence titleCharSeq = e.getSbn().getNotification().extras.getCharSequence( Notification.EXTRA_TITLE); private static String getTitle(@NonNull final BubbleEntry e) { final CharSequence titleCharSeq = e.getStatusBarNotification() .getNotification().extras.getCharSequence(Notification.EXTRA_TITLE); return titleCharSeq == null ? null : titleCharSeq.toString(); } /** * Returns our best guess for the most relevant text summary of the latest update to this * notification, based on its type. Returns null if there should not be an update message. */ @NonNull static Bubble.FlyoutMessage extractFlyoutMessage(BubbleEntry entry) { Objects.requireNonNull(entry); final Notification underlyingNotif = entry.getStatusBarNotification().getNotification(); final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle(); Bubble.FlyoutMessage bubbleMessage = new Bubble.FlyoutMessage(); bubbleMessage.isGroupChat = underlyingNotif.extras.getBoolean( Notification.EXTRA_IS_GROUP_CONVERSATION); try { if (Notification.BigTextStyle.class.equals(style)) { // Return the big text, it is big so probably important. If it's not there use the // normal text. CharSequence bigText = underlyingNotif.extras.getCharSequence(Notification.EXTRA_BIG_TEXT); bubbleMessage.message = !TextUtils.isEmpty(bigText) ? bigText : underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT); return bubbleMessage; } else if (Notification.MessagingStyle.class.equals(style)) { final List<Notification.MessagingStyle.Message> messages = Notification.MessagingStyle.Message.getMessagesFromBundleArray( (Parcelable[]) underlyingNotif.extras.get( Notification.EXTRA_MESSAGES)); final Notification.MessagingStyle.Message latestMessage = Notification.MessagingStyle.findLatestIncomingMessage(messages); if (latestMessage != null) { bubbleMessage.message = latestMessage.getText(); Person sender = latestMessage.getSenderPerson(); bubbleMessage.senderName = sender != null ? sender.getName() : null; bubbleMessage.senderAvatar = null; bubbleMessage.senderIcon = sender != null ? sender.getIcon() : null; return bubbleMessage; } } else if (Notification.InboxStyle.class.equals(style)) { CharSequence[] lines = underlyingNotif.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES); // Return the last line since it should be the most recent. if (lines != null && lines.length > 0) { bubbleMessage.message = lines[lines.length - 1]; return bubbleMessage; } } else if (Notification.MediaStyle.class.equals(style)) { // Return nothing, media updates aren't typically useful as a text update. return bubbleMessage; } else { // Default to text extra. bubbleMessage.message = underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT); return bubbleMessage; } } catch (ClassCastException | NullPointerException | ArrayIndexOutOfBoundsException e) { // No use crashing, we'll just return null and the caller will assume there's no update // message. e.printStackTrace(); } return bubbleMessage; } } packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +67 −9 Original line number Diff line number Diff line Loading @@ -345,7 +345,42 @@ public class BubbleController implements Bubbles, ConfigurationController.Config /** * Injected constructor. See {@link BubbleModule}. */ public BubbleController(Context context, public static BubbleController create(Context context, NotificationShadeWindowController notificationShadeWindowController, StatusBarStateController statusBarStateController, ShadeController shadeController, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer, ConfigurationController configurationController, NotificationInterruptStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager, NotifPipeline notifPipeline, FeatureFlags featureFlags, DumpManager dumpManager, FloatingContentCoordinator floatingContentCoordinator, BubbleDataRepository dataRepository, SysUiState sysUiState, INotificationManager notificationManager, @Nullable IStatusBarService statusBarService, WindowManager windowManager, WindowManagerShellWrapper windowManagerShellWrapper, LauncherApps launcherApps) { return new BubbleController(context, notificationShadeWindowController, statusBarStateController, shadeController, new BubbleData(context), synchronizer, configurationController, interruptionStateProvider, zenModeController, notifUserManager, groupManager, entryManager, notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, dataRepository, sysUiState, notificationManager, statusBarService, windowManager, windowManagerShellWrapper, launcherApps); } /** * Testing constructor. */ @VisibleForTesting BubbleController(Context context, NotificationShadeWindowController notificationShadeWindowController, StatusBarStateController statusBarStateController, ShadeController shadeController, Loading Loading @@ -1120,9 +1155,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) { // Update the bubble but don't promote it out of overflow Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey()); b.setEntry(notif); b.setEntry(notifToBubbleEntry(notif)); } else { Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */); Bubble bubble = mBubbleData.getOrCreateBubble( notifToBubbleEntry(notif), null /* persistedBubble */); inflateAndAdd(bubble, suppressFlyout, showInShade); } } Loading Loading @@ -1208,8 +1244,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config mBubbleData.removeSuppressedSummary(groupKey); // Remove any associated bubble children with the summary final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup( groupKey, mNotificationEntryManager); final List<Bubble> bubbleChildren = getBubblesInGroup(groupKey); for (int i = 0; i < bubbleChildren.size(); i++) { removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED); } Loading Loading @@ -1255,6 +1290,25 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } /** * Retrieves any bubbles that are part of the notification group represented by the provided * group key. */ private ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey) { ArrayList<Bubble> bubbleChildren = new ArrayList<>(); if (groupKey == null) { return bubbleChildren; } for (Bubble bubble : mBubbleData.getActiveBubbles()) { final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(bubble.getKey()); if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) { bubbleChildren.add(bubble); } } return bubbleChildren; } private void setIsBubble(@NonNull final NotificationEntry entry, final boolean isBubble, final boolean autoExpand) { Objects.requireNonNull(entry); Loading Loading @@ -1365,8 +1419,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } if (entry != null) { final String groupKey = entry.getSbn().getGroupKey(); if (mBubbleData.getBubblesInGroup( groupKey, mNotificationEntryManager).isEmpty()) { if (getBubblesInGroup(groupKey).isEmpty()) { // Time to potentially remove the summary for (NotifCallback cb : mCallbacks) { cb.maybeCancelSummary(entry); Loading Loading @@ -1449,8 +1502,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } String groupKey = entry.getSbn().getGroupKey(); ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup( groupKey, mNotificationEntryManager); ArrayList<Bubble> bubbleChildren = getBubblesInGroup(groupKey); boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey) && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey())); boolean isSummary = entry.getSbn().getNotification().isGroupSummary(); Loading Loading @@ -1694,4 +1746,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } } static BubbleEntry notifToBubbleEntry(NotificationEntry e) { return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(), e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(), e.shouldSuppressPeek()); } } packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +11 −30 Original line number Diff line number Diff line Loading @@ -36,8 +36,6 @@ import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController.DismissReason; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.io.FileDescriptor; import java.io.PrintWriter; Loading @@ -52,8 +50,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; import javax.inject.Inject; /** * Keeps track of active bubbles. */ Loading Loading @@ -154,12 +150,11 @@ public class BubbleData { * associated with it). This list is used to check if the summary should be hidden from the * shade. * * Key: group key of the NotificationEntry * Value: key of the NotificationEntry * Key: group key of the notification * Value: key of the notification */ private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>(); @Inject public BubbleData(Context context) { mContext = context; mBubbles = new ArrayList<>(); Loading Loading @@ -205,6 +200,11 @@ public class BubbleData { return mSelectedBubble; } /** Return a read-only current active bubble lists. */ public List<Bubble> getActiveBubbles() { return Collections.unmodifiableList(mBubbles); } public void setExpanded(boolean expanded) { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "setExpanded: " + expanded); Loading Loading @@ -235,8 +235,8 @@ public class BubbleData { * @param persistedBubble The bubble to use, only non-null if it's a bubble being promoted from * the overflow that was persisted over reboot. */ Bubble getOrCreateBubble(NotificationEntry entry, Bubble persistedBubble) { String key = entry != null ? entry.getKey() : persistedBubble.getKey(); public Bubble getOrCreateBubble(BubbleEntry entry, Bubble persistedBubble) { String key = persistedBubble != null ? persistedBubble.getKey() : entry.getKey(); Bubble bubbleToReturn = getBubbleInStackWithKey(key); if (bubbleToReturn == null) { Loading Loading @@ -266,7 +266,7 @@ public class BubbleData { /** * When this method is called it is expected that all info in the bubble has completed loading. * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, * BubbleStackView, BubbleIconFactory). * BubbleStackView, BubbleIconFactory, boolean). */ void notificationEntryUpdated(Bubble bubble, boolean suppressFlyout, boolean showInShade) { if (DEBUG_BUBBLE_DATA) { Loading Loading @@ -329,7 +329,7 @@ public class BubbleData { * Retrieves the notif entry key of the summary associated with the provided group key. * * @param groupKey the group to look up * @return the key for the {@link NotificationEntry} that is the summary of this group. * @return the key for the notification that is the summary of this group. */ String getSummaryKey(String groupKey) { return mSuppressedGroupKeys.get(groupKey); Loading @@ -349,25 +349,6 @@ public class BubbleData { return mSuppressedGroupKeys.containsKey(groupKey); } /** * Retrieves any bubbles that are part of the notification group represented by the provided * group key. */ ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey, @NonNull NotificationEntryManager nem) { ArrayList<Bubble> 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())) { bubbleChildren.add(b); } } return bubbleChildren; } /** * Removes bubbles from the given package whose shortcut are not in the provided list of valid * shortcuts. Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.bubbles; import android.app.Notification.BubbleMetadata; import android.app.NotificationManager.Policy; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.StatusBarNotification; import androidx.annotation.NonNull; import androidx.annotation.Nullable; /** * Represents a notification with needed data and flag for bubbles. * * @see Bubble */ public class BubbleEntry { private StatusBarNotification mSbn; private Ranking mRanking; private boolean mIsClearable; private boolean mShouldSuppressNotificationDot; private boolean mShouldSuppressNotificationList; private boolean mShouldSuppressPeek; public BubbleEntry(@NonNull StatusBarNotification sbn, Ranking ranking, boolean isClearable, boolean shouldSuppressNotificationDot, boolean shouldSuppressNotificationList, boolean shouldSuppressPeek) { mSbn = sbn; mRanking = ranking; mIsClearable = isClearable; mShouldSuppressNotificationDot = shouldSuppressNotificationDot; mShouldSuppressNotificationList = shouldSuppressNotificationList; mShouldSuppressPeek = shouldSuppressPeek; } /** @return the {@link StatusBarNotification} for this entry. */ @NonNull public StatusBarNotification getStatusBarNotification() { return mSbn; } /** @return the {@link Ranking} for this entry. */ public Ranking getRanking() { return mRanking; } /** @return the key in the {@link StatusBarNotification}. */ public String getKey() { return mSbn.getKey(); } /** @return the {@link BubbleMetadata} in the {@link StatusBarNotification}. */ @Nullable public BubbleMetadata getBubbleMetadata() { return getStatusBarNotification().getNotification().getBubbleMetadata(); } /** @return true if this notification is clearable. */ public boolean isClearable() { return mIsClearable; } /** @return true if {@link Policy#SUPPRESSED_EFFECT_BADGE} set for this notification. */ public boolean shouldSuppressNotificationDot() { return mShouldSuppressNotificationDot; } /** * @return true if {@link Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST} * set for this notification. */ public boolean shouldSuppressNotificationList() { return mShouldSuppressNotificationList; } /** @return true if {@link Policy#SUPPRESSED_EFFECT_PEEK} set for this notification. */ public boolean shouldSuppressPeek() { return mShouldSuppressPeek; } } packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java +0 −73 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +99 −30 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; import android.app.Person; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; Loading @@ -35,16 +36,18 @@ import android.graphics.Bitmap; import android.graphics.Path; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Parcelable; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; import java.util.Objects; /** Loading Loading @@ -169,11 +172,10 @@ class Bubble implements BubbleViewProvider { } @VisibleForTesting(visibility = PRIVATE) Bubble(@NonNull final NotificationEntry e, Bubble(@NonNull final BubbleEntry entry, @Nullable final BubbleController.NotificationSuppressionChangedListener listener, final BubbleController.PendingIntentCanceledListener intentCancelListener) { Objects.requireNonNull(e); mKey = e.getKey(); mKey = entry.getKey(); mSuppressionListener = listener; mIntentCancelListener = intent -> { if (mIntent != null) { Loading @@ -181,7 +183,7 @@ class Bubble implements BubbleViewProvider { } intentCancelListener.onPendingIntentCanceled(this); }; setEntry(e); setEntry(entry); } @Override Loading Loading @@ -294,8 +296,8 @@ class Bubble implements BubbleViewProvider { } /** * Sets whether this bubble is considered visually interruptive. Normally pulled from the * {@link NotificationEntry}, this method is purely for testing. * Sets whether this bubble is considered visually interruptive. This method is purely for * testing. */ @VisibleForTesting void setVisuallyInterruptiveForTest(boolean visuallyInterruptive) { Loading Loading @@ -388,30 +390,28 @@ class Bubble implements BubbleViewProvider { /** * Sets the entry associated with this bubble. */ void setEntry(@NonNull final NotificationEntry entry) { void setEntry(@NonNull final BubbleEntry entry) { Objects.requireNonNull(entry); Objects.requireNonNull(entry.getSbn()); mLastUpdated = entry.getSbn().getPostTime(); mIsBubble = entry.getSbn().getNotification().isBubbleNotification(); mPackageName = entry.getSbn().getPackageName(); mUser = entry.getSbn().getUser(); mLastUpdated = entry.getStatusBarNotification().getPostTime(); mIsBubble = entry.getStatusBarNotification().getNotification().isBubbleNotification(); mPackageName = entry.getStatusBarNotification().getPackageName(); mUser = entry.getStatusBarNotification().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); mShortcutInfo = (entry.getRanking() != null ? entry.getRanking().getShortcutInfo() : null); mMetadataShortcutId = (entry.getBubbleMetadata() != null ? entry.getBubbleMetadata().getShortcutId() : null); mChannelId = entry.getStatusBarNotification().getNotification().getChannelId(); mNotificationId = entry.getStatusBarNotification().getId(); mAppUid = entry.getStatusBarNotification().getUid(); mInstanceId = entry.getStatusBarNotification().getInstanceId(); mFlyoutMessage = extractFlyoutMessage(entry); if (entry.getRanking() != null) { mShortcutInfo = entry.getRanking().getShortcutInfo(); mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive(); if (entry.getRanking().getChannel() != null) { mIsImportantConversation = entry.getRanking().getChannel().isImportantConversation(); } } if (entry.getBubbleMetadata() != null) { mMetadataShortcutId = entry.getBubbleMetadata().getShortcutId(); mFlags = entry.getBubbleMetadata().getFlags(); mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight(); mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId(); Loading @@ -433,8 +433,11 @@ class Bubble implements BubbleViewProvider { } mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent(); } mIsImportantConversation = entry.getChannel() != null && entry.getChannel().isImportantConversation(); mIsClearable = entry.isClearable(); mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot(); mShouldSuppressNotificationList = entry.shouldSuppressNotificationList(); mShouldSuppressPeek = entry.shouldSuppressPeek(); } @Nullable Loading Loading @@ -735,9 +738,75 @@ class Bubble implements BubbleViewProvider { } @Nullable private static String getTitle(@NonNull final NotificationEntry e) { final CharSequence titleCharSeq = e.getSbn().getNotification().extras.getCharSequence( Notification.EXTRA_TITLE); private static String getTitle(@NonNull final BubbleEntry e) { final CharSequence titleCharSeq = e.getStatusBarNotification() .getNotification().extras.getCharSequence(Notification.EXTRA_TITLE); return titleCharSeq == null ? null : titleCharSeq.toString(); } /** * Returns our best guess for the most relevant text summary of the latest update to this * notification, based on its type. Returns null if there should not be an update message. */ @NonNull static Bubble.FlyoutMessage extractFlyoutMessage(BubbleEntry entry) { Objects.requireNonNull(entry); final Notification underlyingNotif = entry.getStatusBarNotification().getNotification(); final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle(); Bubble.FlyoutMessage bubbleMessage = new Bubble.FlyoutMessage(); bubbleMessage.isGroupChat = underlyingNotif.extras.getBoolean( Notification.EXTRA_IS_GROUP_CONVERSATION); try { if (Notification.BigTextStyle.class.equals(style)) { // Return the big text, it is big so probably important. If it's not there use the // normal text. CharSequence bigText = underlyingNotif.extras.getCharSequence(Notification.EXTRA_BIG_TEXT); bubbleMessage.message = !TextUtils.isEmpty(bigText) ? bigText : underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT); return bubbleMessage; } else if (Notification.MessagingStyle.class.equals(style)) { final List<Notification.MessagingStyle.Message> messages = Notification.MessagingStyle.Message.getMessagesFromBundleArray( (Parcelable[]) underlyingNotif.extras.get( Notification.EXTRA_MESSAGES)); final Notification.MessagingStyle.Message latestMessage = Notification.MessagingStyle.findLatestIncomingMessage(messages); if (latestMessage != null) { bubbleMessage.message = latestMessage.getText(); Person sender = latestMessage.getSenderPerson(); bubbleMessage.senderName = sender != null ? sender.getName() : null; bubbleMessage.senderAvatar = null; bubbleMessage.senderIcon = sender != null ? sender.getIcon() : null; return bubbleMessage; } } else if (Notification.InboxStyle.class.equals(style)) { CharSequence[] lines = underlyingNotif.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES); // Return the last line since it should be the most recent. if (lines != null && lines.length > 0) { bubbleMessage.message = lines[lines.length - 1]; return bubbleMessage; } } else if (Notification.MediaStyle.class.equals(style)) { // Return nothing, media updates aren't typically useful as a text update. return bubbleMessage; } else { // Default to text extra. bubbleMessage.message = underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT); return bubbleMessage; } } catch (ClassCastException | NullPointerException | ArrayIndexOutOfBoundsException e) { // No use crashing, we'll just return null and the caller will assume there's no update // message. e.printStackTrace(); } return bubbleMessage; } }
packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +67 −9 Original line number Diff line number Diff line Loading @@ -345,7 +345,42 @@ public class BubbleController implements Bubbles, ConfigurationController.Config /** * Injected constructor. See {@link BubbleModule}. */ public BubbleController(Context context, public static BubbleController create(Context context, NotificationShadeWindowController notificationShadeWindowController, StatusBarStateController statusBarStateController, ShadeController shadeController, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer, ConfigurationController configurationController, NotificationInterruptStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager, NotifPipeline notifPipeline, FeatureFlags featureFlags, DumpManager dumpManager, FloatingContentCoordinator floatingContentCoordinator, BubbleDataRepository dataRepository, SysUiState sysUiState, INotificationManager notificationManager, @Nullable IStatusBarService statusBarService, WindowManager windowManager, WindowManagerShellWrapper windowManagerShellWrapper, LauncherApps launcherApps) { return new BubbleController(context, notificationShadeWindowController, statusBarStateController, shadeController, new BubbleData(context), synchronizer, configurationController, interruptionStateProvider, zenModeController, notifUserManager, groupManager, entryManager, notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, dataRepository, sysUiState, notificationManager, statusBarService, windowManager, windowManagerShellWrapper, launcherApps); } /** * Testing constructor. */ @VisibleForTesting BubbleController(Context context, NotificationShadeWindowController notificationShadeWindowController, StatusBarStateController statusBarStateController, ShadeController shadeController, Loading Loading @@ -1120,9 +1155,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) { // Update the bubble but don't promote it out of overflow Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey()); b.setEntry(notif); b.setEntry(notifToBubbleEntry(notif)); } else { Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */); Bubble bubble = mBubbleData.getOrCreateBubble( notifToBubbleEntry(notif), null /* persistedBubble */); inflateAndAdd(bubble, suppressFlyout, showInShade); } } Loading Loading @@ -1208,8 +1244,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config mBubbleData.removeSuppressedSummary(groupKey); // Remove any associated bubble children with the summary final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup( groupKey, mNotificationEntryManager); final List<Bubble> bubbleChildren = getBubblesInGroup(groupKey); for (int i = 0; i < bubbleChildren.size(); i++) { removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED); } Loading Loading @@ -1255,6 +1290,25 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } /** * Retrieves any bubbles that are part of the notification group represented by the provided * group key. */ private ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey) { ArrayList<Bubble> bubbleChildren = new ArrayList<>(); if (groupKey == null) { return bubbleChildren; } for (Bubble bubble : mBubbleData.getActiveBubbles()) { final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(bubble.getKey()); if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) { bubbleChildren.add(bubble); } } return bubbleChildren; } private void setIsBubble(@NonNull final NotificationEntry entry, final boolean isBubble, final boolean autoExpand) { Objects.requireNonNull(entry); Loading Loading @@ -1365,8 +1419,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } if (entry != null) { final String groupKey = entry.getSbn().getGroupKey(); if (mBubbleData.getBubblesInGroup( groupKey, mNotificationEntryManager).isEmpty()) { if (getBubblesInGroup(groupKey).isEmpty()) { // Time to potentially remove the summary for (NotifCallback cb : mCallbacks) { cb.maybeCancelSummary(entry); Loading Loading @@ -1449,8 +1502,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } String groupKey = entry.getSbn().getGroupKey(); ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup( groupKey, mNotificationEntryManager); ArrayList<Bubble> bubbleChildren = getBubblesInGroup(groupKey); boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey) && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey())); boolean isSummary = entry.getSbn().getNotification().isGroupSummary(); Loading Loading @@ -1694,4 +1746,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } } static BubbleEntry notifToBubbleEntry(NotificationEntry e) { return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(), e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(), e.shouldSuppressPeek()); } }
packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +11 −30 Original line number Diff line number Diff line Loading @@ -36,8 +36,6 @@ import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController.DismissReason; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.io.FileDescriptor; import java.io.PrintWriter; Loading @@ -52,8 +50,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; import javax.inject.Inject; /** * Keeps track of active bubbles. */ Loading Loading @@ -154,12 +150,11 @@ public class BubbleData { * associated with it). This list is used to check if the summary should be hidden from the * shade. * * Key: group key of the NotificationEntry * Value: key of the NotificationEntry * Key: group key of the notification * Value: key of the notification */ private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>(); @Inject public BubbleData(Context context) { mContext = context; mBubbles = new ArrayList<>(); Loading Loading @@ -205,6 +200,11 @@ public class BubbleData { return mSelectedBubble; } /** Return a read-only current active bubble lists. */ public List<Bubble> getActiveBubbles() { return Collections.unmodifiableList(mBubbles); } public void setExpanded(boolean expanded) { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "setExpanded: " + expanded); Loading Loading @@ -235,8 +235,8 @@ public class BubbleData { * @param persistedBubble The bubble to use, only non-null if it's a bubble being promoted from * the overflow that was persisted over reboot. */ Bubble getOrCreateBubble(NotificationEntry entry, Bubble persistedBubble) { String key = entry != null ? entry.getKey() : persistedBubble.getKey(); public Bubble getOrCreateBubble(BubbleEntry entry, Bubble persistedBubble) { String key = persistedBubble != null ? persistedBubble.getKey() : entry.getKey(); Bubble bubbleToReturn = getBubbleInStackWithKey(key); if (bubbleToReturn == null) { Loading Loading @@ -266,7 +266,7 @@ public class BubbleData { /** * When this method is called it is expected that all info in the bubble has completed loading. * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, * BubbleStackView, BubbleIconFactory). * BubbleStackView, BubbleIconFactory, boolean). */ void notificationEntryUpdated(Bubble bubble, boolean suppressFlyout, boolean showInShade) { if (DEBUG_BUBBLE_DATA) { Loading Loading @@ -329,7 +329,7 @@ public class BubbleData { * Retrieves the notif entry key of the summary associated with the provided group key. * * @param groupKey the group to look up * @return the key for the {@link NotificationEntry} that is the summary of this group. * @return the key for the notification that is the summary of this group. */ String getSummaryKey(String groupKey) { return mSuppressedGroupKeys.get(groupKey); Loading @@ -349,25 +349,6 @@ public class BubbleData { return mSuppressedGroupKeys.containsKey(groupKey); } /** * Retrieves any bubbles that are part of the notification group represented by the provided * group key. */ ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey, @NonNull NotificationEntryManager nem) { ArrayList<Bubble> 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())) { bubbleChildren.add(b); } } return bubbleChildren; } /** * Removes bubbles from the given package whose shortcut are not in the provided list of valid * shortcuts. Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.bubbles; import android.app.Notification.BubbleMetadata; import android.app.NotificationManager.Policy; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.StatusBarNotification; import androidx.annotation.NonNull; import androidx.annotation.Nullable; /** * Represents a notification with needed data and flag for bubbles. * * @see Bubble */ public class BubbleEntry { private StatusBarNotification mSbn; private Ranking mRanking; private boolean mIsClearable; private boolean mShouldSuppressNotificationDot; private boolean mShouldSuppressNotificationList; private boolean mShouldSuppressPeek; public BubbleEntry(@NonNull StatusBarNotification sbn, Ranking ranking, boolean isClearable, boolean shouldSuppressNotificationDot, boolean shouldSuppressNotificationList, boolean shouldSuppressPeek) { mSbn = sbn; mRanking = ranking; mIsClearable = isClearable; mShouldSuppressNotificationDot = shouldSuppressNotificationDot; mShouldSuppressNotificationList = shouldSuppressNotificationList; mShouldSuppressPeek = shouldSuppressPeek; } /** @return the {@link StatusBarNotification} for this entry. */ @NonNull public StatusBarNotification getStatusBarNotification() { return mSbn; } /** @return the {@link Ranking} for this entry. */ public Ranking getRanking() { return mRanking; } /** @return the key in the {@link StatusBarNotification}. */ public String getKey() { return mSbn.getKey(); } /** @return the {@link BubbleMetadata} in the {@link StatusBarNotification}. */ @Nullable public BubbleMetadata getBubbleMetadata() { return getStatusBarNotification().getNotification().getBubbleMetadata(); } /** @return true if this notification is clearable. */ public boolean isClearable() { return mIsClearable; } /** @return true if {@link Policy#SUPPRESSED_EFFECT_BADGE} set for this notification. */ public boolean shouldSuppressNotificationDot() { return mShouldSuppressNotificationDot; } /** * @return true if {@link Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST} * set for this notification. */ public boolean shouldSuppressNotificationList() { return mShouldSuppressNotificationList; } /** @return true if {@link Policy#SUPPRESSED_EFFECT_PEEK} set for this notification. */ public boolean shouldSuppressPeek() { return mShouldSuppressPeek; } }
packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java +0 −73 File changed.Preview size limit exceeded, changes collapsed. Show changes