Merge "decouple Bubble from NotificationEntry" into rvc-dev

This commit is contained in:
TreeHugger Robot
2020-06-17 19:25:25 +00:00
committed by Android (Google) Code Review
19 changed files with 325 additions and 289 deletions

View File

@@ -15,7 +15,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;
@@ -29,21 +28,19 @@ 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.ApplicationInfo;
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;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.graphics.drawable.Icon;
import android.os.UserHandle; import android.os.UserHandle;
import android.provider.Settings; import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.Log; import android.util.Log;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -57,17 +54,12 @@ 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 final String mKey; private final String mKey;
private long mLastUpdated; private long mLastUpdated;
private long mLastAccessed; private long mLastAccessed;
@Nullable
private BubbleController.NotificationSuppressionChangedListener mSuppressionListener; private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
/** Whether the bubble should show a dot for the notification indicating updated content. */ /** Whether the bubble should show a dot for the notification indicating updated content. */
@@ -75,8 +67,6 @@ class Bubble implements BubbleViewProvider {
/** Whether flyout text should be suppressed, regardless of any other flags or state. */ /** Whether flyout text should be suppressed, regardless of any other flags or state. */
private boolean mSuppressFlyout; private boolean mSuppressFlyout;
/** Whether this bubble should auto expand regardless of the normal flag, used for overflow. */
private boolean mShouldAutoExpand;
// Items that are typically loaded later // Items that are typically loaded later
private String mAppName; private String mAppName;
@@ -92,6 +82,7 @@ class Bubble implements BubbleViewProvider {
* Presentational info about the flyout. * Presentational info about the flyout.
*/ */
public static class FlyoutMessage { public static class FlyoutMessage {
@Nullable public Icon senderIcon;
@Nullable public Drawable senderAvatar; @Nullable public Drawable senderAvatar;
@Nullable public CharSequence senderName; @Nullable public CharSequence senderName;
@Nullable public CharSequence message; @Nullable public CharSequence message;
@@ -109,16 +100,39 @@ class Bubble implements BubbleViewProvider {
private UserHandle mUser; private UserHandle mUser;
@NonNull @NonNull
private String mPackageName; private String mPackageName;
@Nullable
private String mTitle;
@Nullable
private Icon mIcon;
private boolean mIsBubble;
private boolean mIsVisuallyInterruptive;
private boolean mIsClearable;
private boolean mShouldSuppressNotificationDot;
private boolean mShouldSuppressNotificationList;
private boolean mShouldSuppressPeek;
private int mDesiredHeight; private int mDesiredHeight;
@DimenRes @DimenRes
private int mDesiredHeightResId; private int mDesiredHeightResId;
/** for logging **/
@Nullable
private InstanceId mInstanceId;
@Nullable
private String mChannelId;
private int mNotificationId;
private int mAppUid = -1;
@Nullable
private PendingIntent mIntent;
@Nullable
private PendingIntent mDeleteIntent;
/** /**
* Create a bubble with limited information based on given {@link ShortcutInfo}. * 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. * Note: Currently this is only being used when the bubble is persisted to disk.
*/ */
Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo, Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
final int desiredHeight, final int desiredHeightResId) { final int desiredHeight, final int desiredHeightResId, @Nullable final String title) {
Objects.requireNonNull(key); Objects.requireNonNull(key);
Objects.requireNonNull(shortcutInfo); Objects.requireNonNull(shortcutInfo);
mShortcutInfo = shortcutInfo; mShortcutInfo = shortcutInfo;
@@ -126,8 +140,10 @@ class Bubble implements BubbleViewProvider {
mFlags = 0; mFlags = 0;
mUser = shortcutInfo.getUserHandle(); mUser = shortcutInfo.getUserHandle();
mPackageName = shortcutInfo.getPackage(); mPackageName = shortcutInfo.getPackage();
mIcon = shortcutInfo.getIcon();
mDesiredHeight = desiredHeight; mDesiredHeight = desiredHeight;
mDesiredHeightResId = desiredHeightResId; mDesiredHeightResId = desiredHeightResId;
mTitle = title;
} }
/** Used in tests when no UI is required. */ /** Used in tests when no UI is required. */
@@ -145,12 +161,6 @@ class Bubble implements BubbleViewProvider {
return mKey; return mKey;
} }
@Nullable
public NotificationEntry getEntry() {
return mEntry;
}
@NonNull
public UserHandle getUser() { public UserHandle getUser() {
return mUser; return mUser;
} }
@@ -203,14 +213,7 @@ class Bubble implements BubbleViewProvider {
@Nullable @Nullable
public String getTitle() { public String getTitle() {
final CharSequence titleCharSeq; return mTitle;
if (mEntry == null) {
titleCharSeq = null;
} else {
titleCharSeq = mEntry.getSbn().getNotification().extras.getCharSequence(
Notification.EXTRA_TITLE);
}
return titleCharSeq != null ? titleCharSeq.toString() : null;
} }
/** /**
@@ -331,17 +334,45 @@ class Bubble implements BubbleViewProvider {
void setEntry(@NonNull final NotificationEntry entry) { void setEntry(@NonNull final NotificationEntry entry) {
Objects.requireNonNull(entry); Objects.requireNonNull(entry);
Objects.requireNonNull(entry.getSbn()); Objects.requireNonNull(entry.getSbn());
mEntry = entry;
mLastUpdated = entry.getSbn().getPostTime(); mLastUpdated = entry.getSbn().getPostTime();
mFlags = entry.getSbn().getNotification().flags; mIsBubble = entry.getSbn().getNotification().isBubbleNotification();
mPackageName = entry.getSbn().getPackageName(); mPackageName = entry.getSbn().getPackageName();
mUser = entry.getSbn().getUser(); mUser = entry.getSbn().getUser();
mTitle = getTitle(entry);
mIsClearable = entry.isClearable();
mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
mShouldSuppressPeek = entry.shouldSuppressPeek();
mChannelId = entry.getSbn().getNotification().getChannelId();
mNotificationId = entry.getSbn().getId();
mAppUid = entry.getSbn().getUid();
mInstanceId = entry.getSbn().getInstanceId();
mFlyoutMessage = BubbleViewInfoTask.extractFlyoutMessage(entry);
mShortcutInfo = (entry.getBubbleMetadata() != null
&& entry.getBubbleMetadata().getShortcutId() != null
&& entry.getRanking() != null) ? entry.getRanking().getShortcutInfo() : null;
if (entry.getRanking() != null) {
mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive();
}
if (entry.getBubbleMetadata() != null) { if (entry.getBubbleMetadata() != null) {
mFlags = entry.getBubbleMetadata().getFlags();
mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight(); mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight();
mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId(); mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId();
mIcon = entry.getBubbleMetadata().getIcon();
mIntent = entry.getBubbleMetadata().getIntent();
mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent();
} }
} }
@Nullable
Icon getIcon() {
return mIcon;
}
boolean isVisuallyInterruptive() {
return mIsVisuallyInterruptive;
}
/** /**
* @return the last time this bubble was updated or accessed, whichever is most recent. * @return the last time this bubble was updated or accessed, whichever is most recent.
*/ */
@@ -364,6 +395,19 @@ class Bubble implements BubbleViewProvider {
return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY; return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
} }
public InstanceId getInstanceId() {
return mInstanceId;
}
@Nullable
public String getChannelId() {
return mChannelId;
}
public int getNotificationId() {
return mNotificationId;
}
/** /**
* Should be invoked whenever a Bubble is accessed (selected while expanded). * Should be invoked whenever a Bubble is accessed (selected while expanded).
*/ */
@@ -384,24 +428,19 @@ 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() || !mIsClearable;
return !shouldSuppressNotification() || !mEntry.isClearable();
} }
/** /**
* 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();
int flags = data.getFlags();
if (suppressNotification) { if (suppressNotification) {
flags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; mFlags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
} else { } else {
flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; mFlags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
} }
data.setFlags(flags);
if (showInShade() != prevShowInShade && mSuppressionListener != null) { if (showInShade() != prevShowInShade && mSuppressionListener != null) {
mSuppressionListener.onBubbleNotificationSuppressionChange(this); mSuppressionListener.onBubbleNotificationSuppressionChange(this);
@@ -424,9 +463,8 @@ class Bubble implements BubbleViewProvider {
*/ */
@Override @Override
public boolean showDot() { public boolean showDot() {
if (mEntry == null) return false;
return mShowBubbleUpdateDot return mShowBubbleUpdateDot
&& !mEntry.shouldSuppressNotificationDot() && !mShouldSuppressNotificationDot
&& !shouldSuppressNotification(); && !shouldSuppressNotification();
} }
@@ -434,10 +472,9 @@ 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 && !mShouldSuppressPeek
return !mSuppressFlyout && !mEntry.shouldSuppressPeek()
&& !shouldSuppressNotification() && !shouldSuppressNotification()
&& !mEntry.shouldSuppressNotificationList(); && !mShouldSuppressNotificationList;
} }
/** /**
@@ -480,25 +517,14 @@ class Bubble implements BubbleViewProvider {
} }
} }
/** @Nullable
* Whether shortcut information should be used to populate the bubble. PendingIntent getBubbleIntent() {
* <p> return mIntent;
* To populate the activity use {@link LauncherApps#startShortcut(ShortcutInfo, Rect, Bundle)}.
* To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}.
*/
boolean usingShortcutInfo() {
return mEntry != null && mEntry.getBubbleMetadata().getShortcutId() != null
|| mShortcutInfo != null;
} }
@Nullable @Nullable
PendingIntent getBubbleIntent() { PendingIntent getDeleteIntent() {
if (mEntry == null) return null; return mDeleteIntent;
Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
if (data != null) {
return data.getIntent();
}
return null;
} }
Intent getSettingsIntent(final Context context) { Intent getSettingsIntent(final Context context) {
@@ -514,8 +540,12 @@ class Bubble implements BubbleViewProvider {
return intent; return intent;
} }
public int getAppUid() {
return mAppUid;
}
private int getUid(final Context context) { private int getUid(final Context context) {
if (mEntry != null) return mEntry.getSbn().getUid(); if (mAppUid != -1) return mAppUid;
final PackageManager pm = context.getPackageManager(); final PackageManager pm = context.getPackageManager();
if (pm == null) return -1; if (pm == null) return -1;
try { try {
@@ -548,24 +578,27 @@ class Bubble implements BubbleViewProvider {
} }
private boolean shouldSuppressNotification() { private boolean shouldSuppressNotification() {
if (mEntry == null) return true; return isEnabled(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
return mEntry.getBubbleMetadata() != null
&& mEntry.getBubbleMetadata().isNotificationSuppressed();
} }
boolean shouldAutoExpand() { public boolean shouldAutoExpand() {
if (mEntry == null) return mShouldAutoExpand; return isEnabled(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
Notification.BubbleMetadata metadata = mEntry.getBubbleMetadata();
return (metadata != null && metadata.getAutoExpandBubble()) || mShouldAutoExpand;
} }
void setShouldAutoExpand(boolean shouldAutoExpand) { void setShouldAutoExpand(boolean shouldAutoExpand) {
mShouldAutoExpand = shouldAutoExpand; if (shouldAutoExpand) {
enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
} else {
disable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
}
}
public void setIsBubble(final boolean isBubble) {
mIsBubble = isBubble;
} }
public boolean isBubble() { public boolean isBubble() {
if (mEntry == null) return (mFlags & FLAG_BUBBLE) != 0; return mIsBubble;
return (mEntry.getSbn().getNotification().flags & FLAG_BUBBLE) != 0;
} }
public void enable(int option) { public void enable(int option) {
@@ -576,6 +609,10 @@ class Bubble implements BubbleViewProvider {
mFlags &= ~option; mFlags &= ~option;
} }
public boolean isEnabled(int option) {
return (mFlags & option) != 0;
}
@Override @Override
public String toString() { public String toString() {
return "Bubble{" + mKey + '}'; return "Bubble{" + mKey + '}';
@@ -610,34 +647,24 @@ class Bubble implements BubbleViewProvider {
@Override @Override
public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) { public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) {
if (this.getEntry() == null SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
|| this.getEntry().getSbn() == null) { mPackageName,
SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED, mChannelId,
null /* package name */, mNotificationId,
null /* notification channel */, index,
0 /* notification ID */, bubbleCount,
0 /* bubble position */, action,
bubbleCount, normalX,
action, normalY,
normalX, showInShade(),
normalY, false /* isOngoing (unused) */,
false /* unread bubble */, false /* isAppForeground (unused) */);
false /* on-going bubble */, }
false /* isAppForeground (unused) */);
} else { @Nullable
StatusBarNotification notification = this.getEntry().getSbn(); private static String getTitle(@NonNull final NotificationEntry e) {
SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED, final CharSequence titleCharSeq = e.getSbn().getNotification().extras.getCharSequence(
notification.getPackageName(), Notification.EXTRA_TITLE);
notification.getNotification().getChannelId(), return titleCharSeq == null ? null : titleCharSeq.toString();
notification.getId(),
index,
bubbleCount,
action,
normalX,
normalY,
this.showInShade(),
false /* isOngoing (unused) */,
false /* isAppForeground (unused) */);
}
} }
} }

View File

@@ -507,8 +507,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
addNotifCallback(new NotifCallback() { addNotifCallback(new NotifCallback() {
@Override @Override
public void removeNotification(NotificationEntry entry, int reason) { public void removeNotification(NotificationEntry entry, int reason) {
mNotificationEntryManager.performRemoveNotification(entry.getSbn(), mNotificationEntryManager.performRemoveNotification(entry.getSbn(), reason);
reason);
} }
@Override @Override
@@ -639,8 +638,13 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mStackView.setExpandListener(mExpandListener); mStackView.setExpandListener(mExpandListener);
} }
mStackView.setUnbubbleConversationCallback(notificationEntry -> mStackView.setUnbubbleConversationCallback(key -> {
onUserChangedBubble(notificationEntry, false /* shouldBubble */)); final NotificationEntry entry =
mNotificationEntryManager.getPendingOrActiveNotif(key);
if (entry != null) {
onUserChangedBubble(entry, false /* shouldBubble */);
}
});
} }
addToWindowManagerMaybe(); addToWindowManagerMaybe();
@@ -1026,10 +1030,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(@NonNull final 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();
@@ -1105,7 +1106,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mBubbleData.removeSuppressedSummary(groupKey); mBubbleData.removeSuppressedSummary(groupKey);
// 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, mNotificationEntryManager);
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).getKey(), DISMISS_GROUP_CANCELLED);
} }
@@ -1163,21 +1165,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private void setIsBubble(@NonNull final Bubble b, final boolean isBubble) { private void setIsBubble(@NonNull final Bubble b, final boolean isBubble) {
Objects.requireNonNull(b); Objects.requireNonNull(b);
if (isBubble) { b.setIsBubble(isBubble);
b.enable(FLAG_BUBBLE); final NotificationEntry entry = mNotificationEntryManager
} else { .getPendingOrActiveNotif(b.getKey());
b.disable(FLAG_BUBBLE); if (entry != null) {
}
if (b.getEntry() != null) {
// Updating the entry to be a bubble will trigger our normal update flow // Updating the entry to be a bubble will trigger our normal update flow
setIsBubble(b.getEntry(), isBubble, b.shouldAutoExpand()); setIsBubble(entry, isBubble, b.shouldAutoExpand());
} else if (isBubble) { } else if (isBubble) {
// If we have no entry to update, it's a persisted bubble so // If bubble doesn't exist, it's a persisted bubble so we need to add it to the
// we need to add it to the stack ourselves // stack ourselves
Bubble bubble = mBubbleData.getOrCreateBubble(null, b /* persistedBubble */); Bubble bubble = mBubbleData.getOrCreateBubble(null, b /* persistedBubble */);
inflateAndAdd(bubble, bubble.shouldAutoExpand() /* suppressFlyout */, inflateAndAdd(bubble, bubble.shouldAutoExpand() /* suppressFlyout */,
!bubble.shouldAutoExpand() /* showInShade */); !bubble.shouldAutoExpand() /* showInShade */);
} }
} }
@@ -1216,6 +1215,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (reason == DISMISS_NOTIF_CANCEL) { if (reason == DISMISS_NOTIF_CANCEL) {
bubblesToBeRemovedFromRepository.add(bubble); bubblesToBeRemovedFromRepository.add(bubble);
} }
final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
bubble.getKey());
if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
if (!mBubbleData.hasOverflowBubbleWithKey(bubble.getKey()) if (!mBubbleData.hasOverflowBubbleWithKey(bubble.getKey())
&& (!bubble.showInShade() && (!bubble.showInShade()
@@ -1224,26 +1225,27 @@ 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) { if (entry != null) {
cb.removeNotification(bubble.getEntry(), REASON_CANCEL); cb.removeNotification(entry, REASON_CANCEL);
} }
} }
} else { } else {
if (bubble.isBubble()) { if (bubble.isBubble()) {
setIsBubble(bubble, false /* isBubble */); setIsBubble(bubble, false /* isBubble */);
} }
if (bubble.getEntry() != null && bubble.getEntry().getRow() != null) { if (entry != null && entry.getRow() != null) {
bubble.getEntry().getRow().updateBubbleButton(); entry.getRow().updateBubbleButton();
} }
} }
} }
if (bubble.getEntry() != null) { if (entry != null) {
final String groupKey = bubble.getEntry().getSbn().getGroupKey(); final String groupKey = entry.getSbn().getGroupKey();
if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) { if (mBubbleData.getBubblesInGroup(
groupKey, mNotificationEntryManager).isEmpty()) {
// Time to potentially remove the summary // Time to potentially remove the summary
for (NotifCallback cb : mCallbacks) { for (NotifCallback cb : mCallbacks) {
cb.maybeCancelSummary(bubble.getEntry()); cb.maybeCancelSummary(entry);
} }
} }
} }
@@ -1268,9 +1270,12 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (update.selectionChanged && mStackView != null) { if (update.selectionChanged && mStackView != null) {
mStackView.setSelectedBubble(update.selectedBubble); mStackView.setSelectedBubble(update.selectedBubble);
if (update.selectedBubble != null && update.selectedBubble.getEntry() != null) { if (update.selectedBubble != null) {
mNotificationGroupManager.updateSuppression( final NotificationEntry entry = mNotificationEntryManager
update.selectedBubble.getEntry()); .getPendingOrActiveNotif(update.selectedBubble.getKey());
if (entry != null) {
mNotificationGroupManager.updateSuppression(entry);
}
} }
} }
@@ -1343,7 +1348,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
} }
String groupKey = entry.getSbn().getGroupKey(); String groupKey = entry.getSbn().getGroupKey();
ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey); ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(
groupKey, mNotificationEntryManager);
boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey) boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
&& mBubbleData.getSummaryKey(groupKey).equals(entry.getKey())); && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey()));
boolean isSummary = entry.getSbn().getNotification().isGroupSummary(); boolean isSummary = entry.getSbn().getNotification().isGroupSummary();
@@ -1363,9 +1369,15 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// As far as group manager is concerned, once a child is no longer shown // As far as group manager is concerned, once a child is no longer shown
// in the shade, it is essentially removed. // in the shade, it is essentially removed.
Bubble bubbleChild = mBubbleData.getAnyBubbleWithkey(child.getKey()); Bubble bubbleChild = mBubbleData.getAnyBubbleWithkey(child.getKey());
mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry()); if (bubbleChild != null) {
bubbleChild.setSuppressNotification(true); final NotificationEntry entry = mNotificationEntryManager
bubbleChild.setShowDot(false /* show */); .getPendingOrActiveNotif(bubbleChild.getKey());
if (entry != null) {
mNotificationGroupManager.onEntryRemoved(entry);
}
bubbleChild.setSuppressNotification(true);
bubbleChild.setShowDot(false /* show */);
}
} else { } else {
// non-bubbled children can be removed // non-bubbled children can be removed
for (NotifCallback cb : mCallbacks) { for (NotifCallback cb : mCallbacks) {

View File

@@ -22,7 +22,6 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.Notification;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.util.Log; import android.util.Log;
@@ -34,6 +33,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController.DismissReason; import com.android.systemui.bubbles.BubbleController.DismissReason;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.io.FileDescriptor; import java.io.FileDescriptor;
@@ -256,8 +256,7 @@ public class BubbleData {
} }
mPendingBubbles.remove(bubble.getKey()); // No longer pending once we're here mPendingBubbles.remove(bubble.getKey()); // No longer pending once we're here
Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey()); Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey());
suppressFlyout |= bubble.getEntry() == null suppressFlyout |= !bubble.isVisuallyInterruptive();
|| !bubble.getEntry().getRanking().visuallyInterruptive();
if (prevBubble == null) { if (prevBubble == null) {
// Create a new bubble // Create a new bubble
@@ -335,13 +334,15 @@ public class BubbleData {
* Retrieves any bubbles that are part of the notification group represented by the provided * Retrieves any bubbles that are part of the notification group represented by the provided
* group key. * group key.
*/ */
ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey) { ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey, @NonNull
NotificationEntryManager nem) {
ArrayList<Bubble> bubbleChildren = new ArrayList<>(); ArrayList<Bubble> bubbleChildren = new ArrayList<>();
if (groupKey == null) { if (groupKey == null) {
return bubbleChildren; return bubbleChildren;
} }
for (Bubble b : mBubbles) { for (Bubble b : mBubbles) {
if (b.getEntry() != null && groupKey.equals(b.getEntry().getSbn().getGroupKey())) { final NotificationEntry entry = nem.getPendingOrActiveNotif(b.getKey());
if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) {
bubbleChildren.add(b); bubbleChildren.add(b);
} }
} }
@@ -439,9 +440,7 @@ public class BubbleData {
Bubble newSelected = mBubbles.get(newIndex); Bubble newSelected = mBubbles.get(newIndex);
setSelectedBubbleInternal(newSelected); setSelectedBubbleInternal(newSelected);
} }
if (bubbleToRemove.getEntry() != null) { maybeSendDeleteIntent(reason, bubbleToRemove);
maybeSendDeleteIntent(reason, bubbleToRemove.getEntry());
}
} }
void overflowBubble(@DismissReason int reason, Bubble bubble) { void overflowBubble(@DismissReason int reason, Bubble bubble) {
@@ -611,21 +610,14 @@ public class BubbleData {
return true; return true;
} }
private void maybeSendDeleteIntent(@DismissReason int reason, private void maybeSendDeleteIntent(@DismissReason int reason, @NonNull final Bubble bubble) {
@NonNull final NotificationEntry entry) { if (reason != BubbleController.DISMISS_USER_GESTURE) return;
if (reason == BubbleController.DISMISS_USER_GESTURE) { PendingIntent deleteIntent = bubble.getDeleteIntent();
Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata(); if (deleteIntent == null) return;
PendingIntent deleteIntent = bubbleMetadata != null try {
? bubbleMetadata.getDeleteIntent() deleteIntent.send();
: null; } catch (PendingIntent.CanceledException e) {
if (deleteIntent != null) { Log.w(TAG, "Failed to send delete intent for bubble with key: " + bubble.getKey());
try {
deleteIntent.send();
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Failed to send delete intent for bubble with key: "
+ entry.getKey());
}
}
} }
} }

View File

@@ -74,11 +74,15 @@ 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 BubbleEntity(
if (shortcutId == null) shortcutId = b.entry?.bubbleMetadata?.shortcutId userId,
if (shortcutId == null) return@mapNotNull null b.packageName,
BubbleEntity(userId, b.packageName, shortcutId, b.key, b.rawDesiredHeight, b.shortcutInfo?.id ?: return@mapNotNull null,
b.rawDesiredHeightResId) b.key,
b.rawDesiredHeight,
b.rawDesiredHeightResId,
b.title
)
} }
} }
@@ -159,8 +163,13 @@ internal class BubbleDataRepository @Inject constructor(
val bubbles = entities.mapNotNull { entity -> val bubbles = entities.mapNotNull { entity ->
shortcutMap[ShortcutKey(entity.userId, entity.packageName)] shortcutMap[ShortcutKey(entity.userId, entity.packageName)]
?.first { shortcutInfo -> entity.shortcutId == shortcutInfo.id } ?.first { shortcutInfo -> entity.shortcutId == shortcutInfo.id }
?.let { shortcutInfo -> Bubble(entity.key, shortcutInfo, entity.desiredHeight, ?.let { shortcutInfo -> Bubble(
entity.desiredHeightResId) } entity.key,
shortcutInfo,
entity.desiredHeight,
entity.desiredHeightResId,
entity.title
) }
} }
uiScope.launch { cb(bubbles) } uiScope.launch { cb(bubbles) }
} }

View File

@@ -171,7 +171,7 @@ public class BubbleExpandedView extends LinearLayout {
return; return;
} }
try { try {
if (!mIsOverflow && mBubble.usingShortcutInfo()) { if (!mIsOverflow && mBubble.getShortcutInfo() != null) {
options.setApplyActivityFlagsForBubbles(true); options.setApplyActivityFlagsForBubbles(true);
mActivityView.startShortcutActivity(mBubble.getShortcutInfo(), mActivityView.startShortcutActivity(mBubble.getShortcutInfo(),
options, null /* sourceBounds */); options, null /* sourceBounds */);

View File

@@ -31,6 +31,7 @@ import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.PointF; import android.graphics.PointF;
import android.graphics.RectF; import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.ShapeDrawable;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@@ -223,9 +224,10 @@ public class BubbleFlyoutView extends FrameLayout {
float[] dotCenter, float[] dotCenter,
boolean hideDot) { boolean hideDot) {
if (flyoutMessage.senderAvatar != null && flyoutMessage.isGroupChat) { final Drawable senderAvatar = flyoutMessage.senderAvatar;
if (senderAvatar != null && flyoutMessage.isGroupChat) {
mSenderAvatar.setVisibility(VISIBLE); mSenderAvatar.setVisibility(VISIBLE);
mSenderAvatar.setImageDrawable(flyoutMessage.senderAvatar); mSenderAvatar.setImageDrawable(senderAvatar);
} else { } else {
mSenderAvatar.setVisibility(GONE); mSenderAvatar.setVisibility(GONE);
mSenderAvatar.setTranslationX(0); mSenderAvatar.setTranslationX(0);

View File

@@ -15,7 +15,8 @@
*/ */
package com.android.systemui.bubbles; package com.android.systemui.bubbles;
import android.app.Notification; import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.LauncherApps; import android.content.pm.LauncherApps;
@@ -50,15 +51,14 @@ public class BubbleIconFactory extends BaseIconFactory {
/** /**
* Returns the drawable that the developer has provided to display in the bubble. * Returns the drawable that the developer has provided to display in the bubble.
*/ */
Drawable getBubbleDrawable(Context context, ShortcutInfo shortcutInfo, Drawable getBubbleDrawable(@NonNull final Context context,
Notification.BubbleMetadata metadata) { @Nullable final ShortcutInfo shortcutInfo, @Nullable final Icon ic) {
if (shortcutInfo != null) { if (shortcutInfo != null) {
LauncherApps launcherApps = LauncherApps launcherApps =
(LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE); (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
int density = context.getResources().getConfiguration().densityDpi; int density = context.getResources().getConfiguration().densityDpi;
return launcherApps.getShortcutIconDrawable(shortcutInfo, density); return launcherApps.getShortcutIconDrawable(shortcutInfo, density);
} else { } else {
Icon ic = metadata.getIcon();
if (ic != null) { if (ic != null) {
if (ic.getType() == Icon.TYPE_URI if (ic.getType() == Icon.TYPE_URI
|| ic.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) { || ic.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {

View File

@@ -16,8 +16,6 @@
package com.android.systemui.bubbles; package com.android.systemui.bubbles;
import android.service.notification.StatusBarNotification;
import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.logging.UiEventLoggerImpl;
/** /**
@@ -32,12 +30,11 @@ public class BubbleLoggerImpl extends UiEventLoggerImpl implements BubbleLogger
* @param e UI event * @param e UI event
*/ */
public void log(Bubble b, UiEventEnum e) { public void log(Bubble b, UiEventEnum e) {
if (b.getEntry() == null) { if (b.getInstanceId() == null) {
// Added from persistence -- TODO log this with specific event? // Added from persistence -- TODO log this with specific event?
return; return;
} }
StatusBarNotification sbn = b.getEntry().getSbn(); logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
logWithInstanceId(e, sbn.getUid(), sbn.getPackageName(), sbn.getInstanceId());
} }
/** /**

View File

@@ -27,7 +27,6 @@ import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
@@ -291,9 +290,7 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V
}); });
// If the bubble was persisted, the entry is null but it should have shortcut info // If the bubble was persisted, the entry is null but it should have shortcut info
ShortcutInfo info = b.getEntry() == null ShortcutInfo info = b.getShortcutInfo();
? b.getShortcutInfo()
: b.getEntry().getRanking().getShortcutInfo();
if (info == null) { if (info == null) {
Log.d(TAG, "ShortcutInfo required to bubble but none found for " + b); Log.d(TAG, "ShortcutInfo required to bubble but none found for " + b);
} else { } else {

View File

@@ -92,7 +92,6 @@ import com.android.systemui.bubbles.animation.StackAnimationController;
import com.android.systemui.model.SysUiState; import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.util.DismissCircleView; import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator; import com.android.systemui.util.FloatingContentCoordinator;
@@ -303,7 +302,7 @@ public class BubbleStackView extends FrameLayout
private BubbleController.BubbleExpandListener mExpandListener; private BubbleController.BubbleExpandListener mExpandListener;
/** Callback to run when we want to unbubble the given notification's conversation. */ /** Callback to run when we want to unbubble the given notification's conversation. */
private Consumer<NotificationEntry> mUnbubbleConversationCallback; private Consumer<String> mUnbubbleConversationCallback;
private SysUiState mSysUiState; private SysUiState mSysUiState;
@@ -1057,10 +1056,7 @@ public class BubbleStackView extends FrameLayout
mManageMenu.findViewById(R.id.bubble_manage_menu_dont_bubble_container).setOnClickListener( mManageMenu.findViewById(R.id.bubble_manage_menu_dont_bubble_container).setOnClickListener(
view -> { view -> {
showManageMenu(false /* show */); showManageMenu(false /* show */);
final Bubble bubble = mBubbleData.getSelectedBubble(); mUnbubbleConversationCallback.accept(mBubbleData.getSelectedBubble().getKey());
if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
mUnbubbleConversationCallback.accept(bubble.getEntry());
}
}); });
mManageMenu.findViewById(R.id.bubble_manage_menu_settings_container).setOnClickListener( mManageMenu.findViewById(R.id.bubble_manage_menu_settings_container).setOnClickListener(
@@ -1412,7 +1408,7 @@ public class BubbleStackView extends FrameLayout
/** Sets the function to call to un-bubble the given conversation. */ /** Sets the function to call to un-bubble the given conversation. */
public void setUnbubbleConversationCallback( public void setUnbubbleConversationCallback(
Consumer<NotificationEntry> unbubbleConversationCallback) { Consumer<String> unbubbleConversationCallback) {
mUnbubbleConversationCallback = unbubbleConversationCallback; mUnbubbleConversationCallback = unbubbleConversationCallback;
} }

View File

@@ -37,8 +37,6 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Parcelable; import android.os.Parcelable;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.util.PathParser; import android.util.PathParser;
@@ -53,6 +51,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
* Simple task to inflate views & load necessary info to display a bubble. * Simple task to inflate views & load necessary info to display a bubble.
@@ -129,35 +128,10 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
@Nullable @Nullable
static BubbleViewInfo populate(Context c, BubbleStackView stackView, static BubbleViewInfo populate(Context c, BubbleStackView stackView,
BubbleIconFactory iconFactory, Bubble b, boolean skipInflation) { BubbleIconFactory iconFactory, Bubble b, boolean skipInflation) {
final NotificationEntry entry = b.getEntry();
if (entry == null) {
// populate from ShortcutInfo when NotificationEntry is not available
final ShortcutInfo s = b.getShortcutInfo();
return populate(c, stackView, iconFactory, skipInflation || b.isInflated(),
s.getPackage(), s.getUserHandle(), s, null);
}
final StatusBarNotification sbn = entry.getSbn();
final String bubbleShortcutId = entry.getBubbleMetadata().getShortcutId();
final ShortcutInfo si = bubbleShortcutId == null
? null : entry.getRanking().getShortcutInfo();
return populate(
c, stackView, iconFactory, skipInflation || b.isInflated(),
sbn.getPackageName(), sbn.getUser(), si, entry);
}
private static BubbleViewInfo populate(
@NonNull final Context c,
@NonNull final BubbleStackView stackView,
@NonNull final BubbleIconFactory iconFactory,
final boolean isInflated,
@NonNull final String packageName,
@NonNull final UserHandle user,
@Nullable final ShortcutInfo shortcutInfo,
@Nullable final NotificationEntry entry) {
BubbleViewInfo info = new BubbleViewInfo(); BubbleViewInfo info = new BubbleViewInfo();
// View inflation: only should do this once per bubble // View inflation: only should do this once per bubble
if (!isInflated) { if (!skipInflation && !b.isInflated()) {
LayoutInflater inflater = LayoutInflater.from(c); LayoutInflater inflater = LayoutInflater.from(c);
info.imageView = (BadgedImageView) inflater.inflate( info.imageView = (BadgedImageView) inflater.inflate(
R.layout.bubble_view, stackView, false /* attachToRoot */); R.layout.bubble_view, stackView, false /* attachToRoot */);
@@ -167,8 +141,8 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
info.expandedView.setStackView(stackView); info.expandedView.setStackView(stackView);
} }
if (shortcutInfo != null) { if (b.getShortcutInfo() != null) {
info.shortcutInfo = shortcutInfo; info.shortcutInfo = b.getShortcutInfo();
} }
// App name & app icon // App name & app icon
@@ -178,7 +152,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
Drawable appIcon; Drawable appIcon;
try { try {
appInfo = pm.getApplicationInfo( appInfo = pm.getApplicationInfo(
packageName, b.getPackageName(),
PackageManager.MATCH_UNINSTALLED_PACKAGES PackageManager.MATCH_UNINSTALLED_PACKAGES
| PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
@@ -186,17 +160,17 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
if (appInfo != null) { if (appInfo != null) {
info.appName = String.valueOf(pm.getApplicationLabel(appInfo)); info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
} }
appIcon = pm.getApplicationIcon(packageName); appIcon = pm.getApplicationIcon(b.getPackageName());
badgedIcon = pm.getUserBadgedIcon(appIcon, user); badgedIcon = pm.getUserBadgedIcon(appIcon, b.getUser());
} catch (PackageManager.NameNotFoundException exception) { } catch (PackageManager.NameNotFoundException exception) {
// If we can't find package... don't think we should show the bubble. // If we can't find package... don't think we should show the bubble.
Log.w(TAG, "Unable to find package: " + packageName); Log.w(TAG, "Unable to find package: " + b.getPackageName());
return null; return null;
} }
// Badged bubble image // Badged bubble image
Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo, Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
entry == null ? null : entry.getBubbleMetadata()); b.getIcon());
if (bubbleDrawable == null) { if (bubbleDrawable == null) {
// Default to app icon // Default to app icon
bubbleDrawable = appIcon; bubbleDrawable = appIcon;
@@ -222,8 +196,10 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
Color.WHITE, WHITE_SCRIM_ALPHA); Color.WHITE, WHITE_SCRIM_ALPHA);
// Flyout // Flyout
if (entry != null) { info.flyoutMessage = b.getFlyoutMessage();
info.flyoutMessage = extractFlyoutMessage(c, entry); if (info.flyoutMessage != null) {
info.flyoutMessage.senderAvatar =
loadSenderAvatar(c, info.flyoutMessage.senderIcon);
} }
return info; return info;
} }
@@ -235,8 +211,8 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
* notification, based on its type. Returns null if there should not be an update message. * notification, based on its type. Returns null if there should not be an update message.
*/ */
@NonNull @NonNull
static Bubble.FlyoutMessage extractFlyoutMessage(Context context, static Bubble.FlyoutMessage extractFlyoutMessage(NotificationEntry entry) {
NotificationEntry entry) { Objects.requireNonNull(entry);
final Notification underlyingNotif = entry.getSbn().getNotification(); final Notification underlyingNotif = entry.getSbn().getNotification();
final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle(); final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle();
@@ -264,20 +240,9 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
if (latestMessage != null) { if (latestMessage != null) {
bubbleMessage.message = latestMessage.getText(); bubbleMessage.message = latestMessage.getText();
Person sender = latestMessage.getSenderPerson(); Person sender = latestMessage.getSenderPerson();
bubbleMessage.senderName = sender != null bubbleMessage.senderName = sender != null ? sender.getName() : null;
? sender.getName()
: null;
bubbleMessage.senderAvatar = null; bubbleMessage.senderAvatar = null;
if (sender != null && sender.getIcon() != null) { bubbleMessage.senderIcon = sender != null ? sender.getIcon() : null;
if (sender.getIcon().getType() == Icon.TYPE_URI
|| sender.getIcon().getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
context.grantUriPermission(context.getPackageName(),
sender.getIcon().getUri(),
Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
bubbleMessage.senderAvatar = sender.getIcon().loadDrawable(context);
}
return bubbleMessage; return bubbleMessage;
} }
} else if (Notification.InboxStyle.class.equals(style)) { } else if (Notification.InboxStyle.class.equals(style)) {
@@ -306,4 +271,15 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
return bubbleMessage; return bubbleMessage;
} }
@Nullable
static Drawable loadSenderAvatar(@NonNull final Context context, @Nullable final Icon icon) {
Objects.requireNonNull(context);
if (icon == null) return null;
if (icon.getType() == Icon.TYPE_URI || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
context.grantUriPermission(context.getPackageName(),
icon.getUri(), Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
return icon.loadDrawable(context);
}
} }

View File

@@ -24,5 +24,6 @@ data class BubbleEntity(
val shortcutId: String, val shortcutId: String,
val key: String, val key: String,
val desiredHeight: Int, val desiredHeight: Int,
@DimenRes val desiredHeightResId: Int @DimenRes val desiredHeightResId: Int,
val title: String? = null
) )

View File

@@ -33,6 +33,7 @@ private const val ATTR_SHORTCUT_ID = "sid"
private const val ATTR_KEY = "key" private const val ATTR_KEY = "key"
private const val ATTR_DESIRED_HEIGHT = "h" private const val ATTR_DESIRED_HEIGHT = "h"
private const val ATTR_DESIRED_HEIGHT_RES_ID = "hid" private const val ATTR_DESIRED_HEIGHT_RES_ID = "hid"
private const val ATTR_TITLE = "t"
/** /**
* Writes the bubbles in xml format into given output stream. * Writes the bubbles in xml format into given output stream.
@@ -63,6 +64,7 @@ private fun writeXmlEntry(serializer: XmlSerializer, bubble: BubbleEntity) {
serializer.attribute(null, ATTR_KEY, bubble.key) serializer.attribute(null, ATTR_KEY, bubble.key)
serializer.attribute(null, ATTR_DESIRED_HEIGHT, bubble.desiredHeight.toString()) serializer.attribute(null, ATTR_DESIRED_HEIGHT, bubble.desiredHeight.toString())
serializer.attribute(null, ATTR_DESIRED_HEIGHT_RES_ID, bubble.desiredHeightResId.toString()) serializer.attribute(null, ATTR_DESIRED_HEIGHT_RES_ID, bubble.desiredHeightResId.toString())
bubble.title?.let { serializer.attribute(null, ATTR_TITLE, it) }
serializer.endTag(null, TAG_BUBBLE) serializer.endTag(null, TAG_BUBBLE)
} catch (e: IOException) { } catch (e: IOException) {
throw RuntimeException(e) throw RuntimeException(e)
@@ -92,7 +94,8 @@ private fun readXmlEntry(parser: XmlPullParser): BubbleEntity? {
parser.getAttributeWithName(ATTR_SHORTCUT_ID) ?: return null, parser.getAttributeWithName(ATTR_SHORTCUT_ID) ?: return null,
parser.getAttributeWithName(ATTR_KEY) ?: return null, parser.getAttributeWithName(ATTR_KEY) ?: return null,
parser.getAttributeWithName(ATTR_DESIRED_HEIGHT)?.toInt() ?: return null, parser.getAttributeWithName(ATTR_DESIRED_HEIGHT)?.toInt() ?: return null,
parser.getAttributeWithName(ATTR_DESIRED_HEIGHT_RES_ID)?.toInt() ?: return null parser.getAttributeWithName(ATTR_DESIRED_HEIGHT_RES_ID)?.toInt() ?: return null,
parser.getAttributeWithName(ATTR_TITLE)
) )
} }

View File

@@ -304,6 +304,10 @@ public class BubbleControllerTest extends SysuiTestCase {
public void testPromoteBubble_autoExpand() throws Exception { public void testPromoteBubble_autoExpand() throws Exception {
mBubbleController.updateBubble(mRow2.getEntry()); mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry()); mBubbleController.updateBubble(mRow.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
.thenReturn(mRow.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
.thenReturn(mRow2.getEntry());
mBubbleController.removeBubble( mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
@@ -331,6 +335,10 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.updateBubble(mRow2.getEntry()); mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry(), /* suppressFlyout */ mBubbleController.updateBubble(mRow.getEntry(), /* suppressFlyout */
false, /* showInShade */ true); false, /* showInShade */ true);
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
.thenReturn(mRow.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
.thenReturn(mRow2.getEntry());
mBubbleController.removeBubble( mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE); mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
@@ -433,15 +441,16 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mSysUiStateBubblesExpanded); assertTrue(mSysUiStateBubblesExpanded);
// Last added is the one that is expanded // Last added is the one that is expanded
assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry()); assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry())); mRow2.getEntry()));
// Switch which bubble is expanded // Switch which bubble is expanded
mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(
mRow.getEntry().getKey()));
mBubbleData.setExpanded(true); mBubbleData.setExpanded(true);
assertEquals(mRow.getEntry(), assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry()); stackView.getExpandedBubble().getKey()).getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry())); mRow.getEntry()));
@@ -543,27 +552,27 @@ public class BubbleControllerTest extends SysuiTestCase {
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
// Last added is the one that is expanded // Last added is the one that is expanded
assertEquals(mRow2.getEntry(), assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry()); stackView.getExpandedBubble().getKey()).getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry())); mRow2.getEntry()));
// Dismiss currently expanded // Dismiss currently expanded
mBubbleController.removeBubble( mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()) mBubbleData.getBubbleInStackWithKey(
.getEntry().getKey(), stackView.getExpandedBubble().getKey()).getKey(),
BubbleController.DISMISS_USER_GESTURE); BubbleController.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey()); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
// Make sure first bubble is selected // Make sure first bubble is selected
assertEquals(mRow.getEntry(), assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry()); stackView.getExpandedBubble().getKey()).getKey());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
// Dismiss that one // Dismiss that one
mBubbleController.removeBubble( mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()) mBubbleData.getBubbleInStackWithKey(
.getEntry().getKey(), stackView.getExpandedBubble().getKey()).getKey(),
BubbleController.DISMISS_USER_GESTURE); BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens // Make sure state changes and collapse happens
@@ -839,6 +848,12 @@ public class BubbleControllerTest extends SysuiTestCase {
mRow2.getEntry(), /* suppressFlyout */ false, /* showInShade */ false); mRow2.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
mBubbleController.updateBubble( mBubbleController.updateBubble(
mRow3.getEntry(), /* suppressFlyout */ false, /* showInShade */ false); mRow3.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
.thenReturn(mRow.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
.thenReturn(mRow2.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow3.getEntry().getKey()))
.thenReturn(mRow3.getEntry());
assertEquals(mBubbleData.getBubbles().size(), 3); assertEquals(mBubbleData.getBubbles().size(), 3);
mBubbleData.setMaxOverflowBubbles(1); mBubbleData.setMaxOverflowBubbles(1);
@@ -908,6 +923,8 @@ public class BubbleControllerTest extends SysuiTestCase {
// GIVEN a group summary with a bubble child // GIVEN a group summary with a bubble child
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
.thenReturn(groupedBubble.getEntry());
mEntryListener.onPendingEntryAdded(groupedBubble.getEntry()); mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble); groupSummary.addChildNotification(groupedBubble);
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -927,6 +944,8 @@ public class BubbleControllerTest extends SysuiTestCase {
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onPendingEntryAdded(groupedBubble.getEntry()); mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
.thenReturn(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble); groupSummary.addChildNotification(groupedBubble);
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -948,6 +967,8 @@ public class BubbleControllerTest extends SysuiTestCase {
// GIVEN a group summary with two (non-bubble) children and one bubble child // GIVEN a group summary with two (non-bubble) children and one bubble child
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2); ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
.thenReturn(groupedBubble.getEntry());
mEntryListener.onPendingEntryAdded(groupedBubble.getEntry()); mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble); groupSummary.addChildNotification(groupedBubble);

View File

@@ -86,8 +86,7 @@ public class BubbleTest extends SysuiTestCase {
final String msg = "Hello there!"; final String msg = "Hello there!";
doReturn(Notification.Style.class).when(mNotif).getNotificationStyle(); doReturn(Notification.Style.class).when(mNotif).getNotificationStyle();
mExtras.putCharSequence(Notification.EXTRA_TEXT, msg); mExtras.putCharSequence(Notification.EXTRA_TEXT, msg);
assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mContext, assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
mEntry).message);
} }
@Test @Test
@@ -98,8 +97,7 @@ public class BubbleTest extends SysuiTestCase {
mExtras.putCharSequence(Notification.EXTRA_BIG_TEXT, msg); mExtras.putCharSequence(Notification.EXTRA_BIG_TEXT, msg);
// Should be big text, not the small text. // Should be big text, not the small text.
assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mContext, assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
mEntry).message);
} }
@Test @Test
@@ -107,8 +105,7 @@ public class BubbleTest extends SysuiTestCase {
doReturn(Notification.MediaStyle.class).when(mNotif).getNotificationStyle(); doReturn(Notification.MediaStyle.class).when(mNotif).getNotificationStyle();
// Media notifs don't get update messages. // Media notifs don't get update messages.
assertNull(BubbleViewInfoTask.extractFlyoutMessage(mContext, assertNull(BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
mEntry).message);
} }
@Test @Test
@@ -124,7 +121,7 @@ public class BubbleTest extends SysuiTestCase {
// Should be the last one only. // Should be the last one only.
assertEquals("Really? I prefer them that way.", assertEquals("Really? I prefer them that way.",
BubbleViewInfoTask.extractFlyoutMessage(mContext, mEntry).message); BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
} }
@Test @Test
@@ -139,11 +136,8 @@ public class BubbleTest extends SysuiTestCase {
"Oh, hello!", 0, "Mady").toBundle()}); "Oh, hello!", 0, "Mady").toBundle()});
// Should be the last one only. // Should be the last one only.
assertEquals("Oh, hello!", assertEquals("Oh, hello!", BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
BubbleViewInfoTask.extractFlyoutMessage(mContext, mEntry).message); assertEquals("Mady", BubbleViewInfoTask.extractFlyoutMessage(mEntry).senderName);
assertEquals("Mady",
BubbleViewInfoTask.extractFlyoutMessage(mContext,
mEntry).senderName);
} }
@Test @Test

View File

@@ -302,6 +302,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
public void testRemoveBubble_withDismissedNotif_notInOverflow() { public void testRemoveBubble_withDismissedNotif_notInOverflow() {
mEntryListener.onEntryAdded(mRow.getEntry()); mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry()); mBubbleController.updateBubble(mRow.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
.thenReturn(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles()); assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry())); assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
@@ -388,14 +390,14 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
true, mRow.getEntry().getKey()); true, mRow.getEntry().getKey());
// Last added is the one that is expanded // Last added is the one that is expanded
assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry()); assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry())); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry()));
// Switch which bubble is expanded // Switch which bubble is expanded
mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey())); mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
mBubbleData.setExpanded(true); mBubbleData.setExpanded(true);
assertEquals(mRow.getEntry(), assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry()); stackView.getExpandedBubble().getKey()).getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry())); mRow.getEntry()));
@@ -488,27 +490,27 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
// Last added is the one that is expanded // Last added is the one that is expanded
assertEquals(mRow2.getEntry(), assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry()); stackView.getExpandedBubble().getKey()).getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry())); mRow2.getEntry()));
// Dismiss currently expanded // Dismiss currently expanded
mBubbleController.removeBubble( mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey( mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getEntry().getKey(), stackView.getExpandedBubble().getKey()).getKey(),
BubbleController.DISMISS_USER_GESTURE); BubbleController.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey()); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
// Make sure first bubble is selected // Make sure first bubble is selected
assertEquals(mRow.getEntry(), assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry()); stackView.getExpandedBubble().getKey()).getKey());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
// Dismiss that one // Dismiss that one
mBubbleController.removeBubble( mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey( mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getEntry().getKey(), stackView.getExpandedBubble().getKey()).getKey(),
BubbleController.DISMISS_USER_GESTURE); BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens // Make sure state changes and collapse happens
@@ -767,6 +769,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onEntryAdded(groupedBubble.getEntry()); mEntryListener.onEntryAdded(groupedBubble.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
.thenReturn(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble); groupSummary.addChildNotification(groupedBubble);
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -785,6 +789,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onEntryAdded(groupedBubble.getEntry()); mEntryListener.onEntryAdded(groupedBubble.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
.thenReturn(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble); groupSummary.addChildNotification(groupedBubble);
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -807,6 +813,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2); ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onEntryAdded(groupedBubble.getEntry()); mEntryListener.onEntryAdded(groupedBubble.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
.thenReturn(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble); groupSummary.addChildNotification(groupedBubble);
// WHEN the summary is dismissed // WHEN the summary is dismissed

View File

@@ -32,7 +32,7 @@ class BubblePersistentRepositoryTest : SysuiTestCase() {
private val bubbles = listOf( private val bubbles = listOf(
BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0), BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0),
BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428), BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428, "title"),
BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0) BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0)
) )
private lateinit var repository: BubblePersistentRepository private lateinit var repository: BubblePersistentRepository

View File

@@ -37,9 +37,10 @@ class BubbleVolatileRepositoryTest : SysuiTestCase() {
private val user0 = UserHandle.of(0) private val user0 = UserHandle.of(0)
private val user10 = UserHandle.of(10) private val user10 = UserHandle.of(10)
private val bubble1 = BubbleEntity(0, PKG_MESSENGER, "shortcut-1", "k1", 120, 0) private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0)
private val bubble2 = BubbleEntity(10, PKG_CHAT, "alice and bob", "k2", 0, 16537428) private val bubble2 = BubbleEntity(10, "com.example.chat", "alice and bob",
private val bubble3 = BubbleEntity(0, PKG_MESSENGER, "shortcut-2", "k3", 120, 0) "key-2", 0, 16537428, "title")
private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0)
private val bubbles = listOf(bubble1, bubble2, bubble3) private val bubbles = listOf(bubble1, bubble2, bubble3)

View File

@@ -31,17 +31,17 @@ import java.io.ByteArrayOutputStream
class BubbleXmlHelperTest : SysuiTestCase() { class BubbleXmlHelperTest : SysuiTestCase() {
private val bubbles = listOf( private val bubbles = listOf(
BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0), BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0),
BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428), BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title"),
BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0) BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0)
) )
@Test @Test
fun testWriteXml() { fun testWriteXml() {
val expectedEntries = """ val expectedEntries = """
<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" /> <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" /> <bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" />
<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" /> <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
""".trimIndent() """.trimIndent()
ByteArrayOutputStream().use { ByteArrayOutputStream().use {
writeXml(it, bubbles) writeXml(it, bubbles)
@@ -54,12 +54,12 @@ class BubbleXmlHelperTest : SysuiTestCase() {
@Test @Test
fun testReadXml() { fun testReadXml() {
val src = """ val src = """
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<bs> <bs>
<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" /> <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" /> <bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" />
<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" /> <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
</bs> </bs>
""".trimIndent() """.trimIndent()
val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8))) val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
assertEquals("failed parsing bubbles from xml\n$src", bubbles, actual) assertEquals("failed parsing bubbles from xml\n$src", bubbles, actual)