Merge "Make sure PendingIntent cancel listener is unregistered" into rvc-dev am: 0e3d9d31a2
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/12037679
Change-Id: I5887b805f035a9986f9835924d50ea5ef73ef264
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 6dc8322..c6d1286 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -124,8 +124,26 @@
private int mNotificationId;
private int mAppUid = -1;
+ /**
+ * A bubble is created and can be updated. This intent is updated until the user first
+ * expands the bubble. Once the user has expanded the contents, we ignore the intent updates
+ * to prevent restarting the intent & possibly altering UI state in the activity in front of
+ * the user.
+ *
+ * Once the bubble is overflowed, the activity is finished and updates to the
+ * notification are respected. Typically an update to an overflowed bubble would result in
+ * that bubble being added back to the stack anyways.
+ */
@Nullable
private PendingIntent mIntent;
+ private boolean mIntentActive;
+ @Nullable
+ private PendingIntent.CancelListener mIntentCancelListener;
+
+ /**
+ * Sent when the bubble & notification are no longer visible to the user (i.e. no
+ * notification in the shade, no bubble in the stack or overflow).
+ */
@Nullable
private PendingIntent mDeleteIntent;
@@ -150,13 +168,19 @@
mShowBubbleUpdateDot = false;
}
- /** Used in tests when no UI is required. */
@VisibleForTesting(visibility = PRIVATE)
Bubble(@NonNull final NotificationEntry e,
- @Nullable final BubbleController.NotificationSuppressionChangedListener listener) {
+ @Nullable final BubbleController.NotificationSuppressionChangedListener listener,
+ final BubbleController.PendingIntentCanceledListener intentCancelListener) {
Objects.requireNonNull(e);
mKey = e.getKey();
mSuppressionListener = listener;
+ mIntentCancelListener = intent -> {
+ if (mIntent != null) {
+ mIntent.unregisterCancelListener(mIntentCancelListener);
+ }
+ intentCancelListener.onPendingIntentCanceled(this);
+ };
setEntry(e);
}
@@ -238,6 +262,10 @@
mExpandedView = null;
}
mIconView = null;
+ if (mIntent != null) {
+ mIntent.unregisterCancelListener(mIntentCancelListener);
+ }
+ mIntentActive = false;
}
void setPendingIntentCanceled() {
@@ -371,11 +399,24 @@
mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight();
mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId();
mIcon = entry.getBubbleMetadata().getIcon();
- mIntent = entry.getBubbleMetadata().getIntent();
+
+ if (!mIntentActive || mIntent == null) {
+ if (mIntent != null) {
+ mIntent.unregisterCancelListener(mIntentCancelListener);
+ }
+ mIntent = entry.getBubbleMetadata().getIntent();
+ if (mIntent != null) {
+ mIntent.registerCancelListener(mIntentCancelListener);
+ }
+ } else if (mIntent != null && entry.getBubbleMetadata().getIntent() == null) {
+ // Was an intent bubble now it's a shortcut bubble... still unregister the listener
+ mIntent.unregisterCancelListener(mIntentCancelListener);
+ mIntent = null;
+ }
mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent();
}
mIsImportantConversation =
- entry.getChannel() == null ? false : entry.getChannel().isImportantConversation();
+ entry.getChannel() != null && entry.getChannel().isImportantConversation();
}
@Nullable
@@ -395,10 +436,15 @@
}
/**
- * @return if the bubble was ever expanded
+ * Sets if the intent used for this bubble is currently active (i.e. populating an
+ * expanded view, expanded or not).
*/
- boolean getWasAccessed() {
- return mLastAccessed != 0L;
+ void setIntentActive() {
+ mIntentActive = true;
+ }
+
+ boolean isIntentActive() {
+ return mIntentActive;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 6ea0cde..6f103b0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -263,6 +263,16 @@
}
/**
+ * Listener to be notified when a pending intent has been canceled for a bubble.
+ */
+ public interface PendingIntentCanceledListener {
+ /**
+ * Called when the pending intent for a bubble has been canceled.
+ */
+ void onPendingIntentCanceled(Bubble bubble);
+ }
+
+ /**
* Callback for when the BubbleController wants to interact with the notification pipeline to:
* - Remove a previously bubbled notification
* - Update the notification shade since bubbled notification should/shouldn't be showing
@@ -390,6 +400,18 @@
}
}
});
+ mBubbleData.setPendingIntentCancelledListener(bubble -> {
+ if (bubble.getBubbleIntent() == null) {
+ return;
+ }
+ if (bubble.isIntentActive()) {
+ bubble.setPendingIntentCanceled();
+ return;
+ }
+ mHandler.post(
+ () -> removeBubble(bubble.getKey(),
+ BubbleController.DISMISS_INVALID_INTENT));
+ });
mNotificationEntryManager = entryManager;
mNotificationGroupManager = groupManager;
@@ -1101,23 +1123,7 @@
// Lazy init stack view when a bubble is created
ensureStackViewCreated();
bubble.setInflateSynchronously(mInflateSynchronously);
- bubble.inflate(
- b -> {
- mBubbleData.notificationEntryUpdated(b, suppressFlyout,
- showInShade);
- if (bubble.getBubbleIntent() == null) {
- return;
- }
- bubble.getBubbleIntent().registerCancelListener(pendingIntent -> {
- if (bubble.getWasAccessed()) {
- bubble.setPendingIntentCanceled();
- return;
- }
- mHandler.post(
- () -> removeBubble(bubble.getKey(),
- BubbleController.DISMISS_INVALID_INTENT));
- });
- },
+ bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade),
mContext, mStackView, mBubbleIconFactory, false /* skipInflation */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index ec4304f..d2dc506 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -59,7 +59,7 @@
@Singleton
public class BubbleData {
- BubbleLogger mLogger = new BubbleLoggerImpl();
+ private BubbleLogger mLogger = new BubbleLoggerImpl();
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleData" : TAG_BUBBLES;
@@ -137,6 +137,7 @@
@Nullable
private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+ private BubbleController.PendingIntentCanceledListener mCancelledListener;
/**
* We track groups with summaries that aren't visibly displayed but still kept around because
@@ -167,6 +168,11 @@
mSuppressionListener = listener;
}
+ public void setPendingIntentCancelledListener(
+ BubbleController.PendingIntentCanceledListener listener) {
+ mCancelledListener = listener;
+ }
+
public boolean hasBubbles() {
return !mBubbles.isEmpty();
}
@@ -236,7 +242,7 @@
bubbleToReturn = mPendingBubbles.get(key);
} else if (entry != null) {
// New bubble
- bubbleToReturn = new Bubble(entry, mSuppressionListener);
+ bubbleToReturn = new Bubble(entry, mSuppressionListener, mCancelledListener);
} else {
// Persisted bubble being promoted
bubbleToReturn = persistedBubble;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 1211fb4..3d31712 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -183,6 +183,9 @@
// Apply flags to make behaviour match documentLaunchMode=always.
fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ if (mBubble != null) {
+ mBubble.setIntentActive();
+ }
mActivityView.startActivity(mPendingIntent, fillInIntent, options);
}
} catch (RuntimeException e) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index ed4e686..315caee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -107,6 +107,9 @@
@Mock
private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+ @Mock
+ private BubbleController.PendingIntentCanceledListener mPendingIntentCanceledListener;
+
@Before
public void setUp() throws Exception {
mNotificationTestHelper = new NotificationTestHelper(
@@ -127,20 +130,20 @@
modifyRanking(mEntryInterruptive)
.setVisuallyInterruptive(true)
.build();
- mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener);
+ mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener, null);
ExpandableNotificationRow row = mNotificationTestHelper.createBubble();
mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d");
mEntryDismissed.setRow(row);
- mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener);
+ mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener, null);
- mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener);
- mBubbleA2 = new Bubble(mEntryA2, mSuppressionListener);
- mBubbleA3 = new Bubble(mEntryA3, mSuppressionListener);
- mBubbleB1 = new Bubble(mEntryB1, mSuppressionListener);
- mBubbleB2 = new Bubble(mEntryB2, mSuppressionListener);
- mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener);
- mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener);
+ mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener, mPendingIntentCanceledListener);
+ mBubbleA2 = new Bubble(mEntryA2, mSuppressionListener, mPendingIntentCanceledListener);
+ mBubbleA3 = new Bubble(mEntryA3, mSuppressionListener, mPendingIntentCanceledListener);
+ mBubbleB1 = new Bubble(mEntryB1, mSuppressionListener, mPendingIntentCanceledListener);
+ mBubbleB2 = new Bubble(mEntryB2, mSuppressionListener, mPendingIntentCanceledListener);
+ mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener, mPendingIntentCanceledListener);
+ mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener, mPendingIntentCanceledListener);
mBubbleData = new BubbleData(getContext());
@@ -847,14 +850,6 @@
when(entry.getSbn().getPostTime()).thenReturn(postTime);
}
- private void setOngoing(NotificationEntry entry, boolean ongoing) {
- if (ongoing) {
- entry.getSbn().getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
- } else {
- entry.getSbn().getNotification().flags &= ~Notification.FLAG_FOREGROUND_SERVICE;
- }
- }
-
/**
* No ExpandableNotificationRow is required to test BubbleData. This setup is all that is
* required for BubbleData functionality and verification. NotificationTestHelper is used only
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
index be03923..2bcc22c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -71,7 +71,7 @@
.setNotification(mNotif)
.build();
- mBubble = new Bubble(mEntry, mSuppressionListener);
+ mBubble = new Bubble(mEntry, mSuppressionListener, null);
Intent target = new Intent(mContext, BubblesTestActivity.class);
Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(