Prevent duplicates from persisted bubbles.
Bug: 157070577
Test: add some bubbles, reboot, verify it doesn't generate duplicates
Change-Id: I95898fa6a1c54e2e57fde5990ba6cfefd1a5c2f0
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 8cfa2d6..15eda06 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -15,7 +15,6 @@
*/
package com.android.systemui.bubbles;
-
import static android.app.Notification.FLAG_BUBBLE;
import static android.os.AsyncTask.Status.FINISHED;
import static android.view.Display.INVALID_DISPLAY;
@@ -106,34 +105,14 @@
private int mFlags;
/**
- * Generate a unique identifier for this bubble based on given {@link NotificationEntry}. If
- * {@link ShortcutInfo} was found in the notification entry, the identifier would be generated
- * from {@link ShortcutInfo} instead. See also {@link #key(ShortcutInfo)}.
- */
- @NonNull
- public static String key(@NonNull final NotificationEntry entry) {
- final ShortcutInfo shortcutInfo = entry.getRanking().getShortcutInfo();
- if (shortcutInfo != null) return key(shortcutInfo);
- return entry.getKey();
- }
-
- /**
- * Generate a unique identifier for this bubble based on given {@link ShortcutInfo}.
- * See also {@link #key(NotificationEntry)}.
- */
- @NonNull
- public static String key(@NonNull final ShortcutInfo shortcutInfo) {
- return shortcutInfo.getUserId() + "|" + shortcutInfo.getPackage() + "|"
- + shortcutInfo.getId();
- }
-
- /**
* Create a bubble with limited information based on given {@link ShortcutInfo}.
* Note: Currently this is only being used when the bubble is persisted to disk.
*/
- Bubble(ShortcutInfo shortcutInfo) {
+ Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo) {
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(shortcutInfo);
mShortcutInfo = shortcutInfo;
- mKey = key(shortcutInfo);
+ mKey = key;
mFlags = 0;
}
@@ -142,7 +121,7 @@
Bubble(NotificationEntry e,
BubbleController.NotificationSuppressionChangedListener listener) {
mEntry = e;
- mKey = key(e);
+ mKey = e.getKey();
mLastUpdated = e.getSbn().getPostTime();
mSuppressionListener = listener;
mFlags = e.getSbn().getNotification().flags;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 0ec2418..3b58e53 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -606,8 +606,6 @@
mStackView.setUnbubbleConversationCallback(notificationEntry ->
onUserChangedBubble(notificationEntry, false /* shouldBubble */));
- // Lazy load overflow bubbles from disk
- loadOverflowBubblesFromDisk();
}
addToWindowManagerMaybe();
@@ -1112,6 +1110,8 @@
@Override
public void applyUpdate(BubbleData.Update update) {
+ // Lazy load overflow bubbles from disk
+ loadOverflowBubblesFromDisk();
// Update bubbles in overflow.
if (mOverflowCallback != null) {
mOverflowCallback.run();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
index a0ec7cd..1c5e98b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
@@ -77,7 +77,7 @@
var shortcutId = b.shortcutInfo?.id
if (shortcutId == null) shortcutId = b.entry?.bubbleMetadata?.shortcutId
if (shortcutId == null) return@mapNotNull null
- BubbleEntity(userId, b.packageName, shortcutId)
+ BubbleEntity(userId, b.packageName, shortcutId, b.key)
}
}
@@ -133,17 +133,17 @@
val shortcutKeys = entities.map { ShortcutKey(it.userId, it.packageName) }.toSet()
/**
* Retrieve shortcuts with given userId/packageName combination, then construct a mapping
- * between BubbleEntity and ShortcutInfo.
+ * from the userId/packageName pair to a list of associated ShortcutInfo.
* e.g.
* {
- * BubbleEntity(0, "com.example.messenger", "id-0") ->
+ * ShortcutKey(0, "com.example.messenger") -> [
* ShortcutInfo(userId=0, pkg="com.example.messenger", id="id-0"),
- * BubbleEntity(0, "com.example.messenger", "id-2") ->
- * ShortcutInfo(userId=0, pkg="com.example.messenger", id="id-2"),
- * BubbleEntity(10, "com.example.chat", "id-1") ->
+ * ShortcutInfo(userId=0, pkg="com.example.messenger", id="id-2")
+ * ]
+ * ShortcutKey(10, "com.example.chat") -> [
* ShortcutInfo(userId=10, pkg="com.example.chat", id="id-1"),
- * BubbleEntity(10, "com.example.chat", "id-3") ->
* ShortcutInfo(userId=10, pkg="com.example.chat", id="id-3")
+ * ]
* }
*/
val shortcutMap = shortcutKeys.flatMap { key ->
@@ -151,11 +151,15 @@
LauncherApps.ShortcutQuery()
.setPackage(key.pkg)
.setQueryFlags(SHORTCUT_QUERY_FLAG), UserHandle.of(key.userId))
- ?.map { BubbleEntity(key.userId, key.pkg, it.id) to it } ?: emptyList()
- }.toMap()
+ ?: emptyList()
+ }.groupBy { ShortcutKey(it.userId, it.`package`) }
// For each entity loaded from xml, find the corresponding ShortcutInfo then convert them
// into Bubble.
- val bubbles = entities.mapNotNull { entity -> shortcutMap[entity]?.let { Bubble(it) } }
+ val bubbles = entities.mapNotNull { entity ->
+ shortcutMap[ShortcutKey(entity.userId, entity.packageName)]
+ ?.first { shortcutInfo -> entity.shortcutId == shortcutInfo.id }
+ ?.let { shortcutInfo -> Bubble(entity.key, shortcutInfo) }
+ }
uiScope.launch { cb(bubbles) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
index 4690a8e..4348261 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
@@ -20,5 +20,6 @@
data class BubbleEntity(
@UserIdInt val userId: Int,
val packageName: String,
- val shortcutId: String
+ val shortcutId: String,
+ val key: String
)
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
index 821b64c..1df9f72 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
@@ -30,6 +30,7 @@
private const val ATTR_USER_ID = "uid"
private const val ATTR_PACKAGE = "pkg"
private const val ATTR_SHORTCUT_ID = "sid"
+private const val ATTR_KEY = "key"
/**
* Writes the bubbles in xml format into given output stream.
@@ -48,7 +49,7 @@
/**
* Creates a xml entry for given bubble in following format:
* ```
- * <bb uid="0" pkg="com.example.messenger" sid="my-shortcut" />
+ * <bb uid="0" pkg="com.example.messenger" sid="my-shortcut" key="my-key" />
* ```
*/
private fun writeXmlEntry(serializer: XmlSerializer, bubble: BubbleEntity) {
@@ -57,6 +58,7 @@
serializer.attribute(null, ATTR_USER_ID, bubble.userId.toString())
serializer.attribute(null, ATTR_PACKAGE, bubble.packageName)
serializer.attribute(null, ATTR_SHORTCUT_ID, bubble.shortcutId)
+ serializer.attribute(null, ATTR_KEY, bubble.key)
serializer.endTag(null, TAG_BUBBLE)
} catch (e: IOException) {
throw RuntimeException(e)
@@ -83,7 +85,8 @@
return BubbleEntity(
parser.getAttributeWithName(ATTR_USER_ID)?.toInt() ?: return null,
parser.getAttributeWithName(ATTR_PACKAGE) ?: return null,
- parser.getAttributeWithName(ATTR_SHORTCUT_ID) ?: return null
+ parser.getAttributeWithName(ATTR_SHORTCUT_ID) ?: return null,
+ parser.getAttributeWithName(ATTR_KEY) ?: return null
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
index d49d021..f468192 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
@@ -29,9 +29,9 @@
class BubblePersistentRepositoryTest : SysuiTestCase() {
private val bubbles = listOf(
- BubbleEntity(0, "com.example.messenger", "shortcut-1"),
- BubbleEntity(10, "com.example.chat", "alice and bob"),
- BubbleEntity(0, "com.example.messenger", "shortcut-2")
+ BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1"),
+ BubbleEntity(10, "com.example.chat", "alice and bob", "key-2"),
+ BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3")
)
private lateinit var repository: BubblePersistentRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 7acc937..ee48846 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -28,9 +28,9 @@
@RunWith(AndroidTestingRunner::class)
class BubbleVolatileRepositoryTest : SysuiTestCase() {
- private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1")
- private val bubble2 = BubbleEntity(10, "com.example.chat", "alice and bob")
- private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2")
+ private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1")
+ private val bubble2 = BubbleEntity(10, "com.example.chat", "alice and bob", "k2")
+ private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3")
private val bubbles = listOf(bubble1, bubble2, bubble3)
private lateinit var repository: BubbleVolatileRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
index ef4580c..79701ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
@@ -31,17 +31,17 @@
class BubbleXmlHelperTest : SysuiTestCase() {
private val bubbles = listOf(
- BubbleEntity(0, "com.example.messenger", "shortcut-1"),
- BubbleEntity(10, "com.example.chat", "alice and bob"),
- BubbleEntity(0, "com.example.messenger", "shortcut-2")
+ BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1"),
+ BubbleEntity(10, "com.example.chat", "alice and bob", "k2"),
+ BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3")
)
@Test
fun testWriteXml() {
val expectedEntries = """
- <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" />
- <bb uid="10" pkg="com.example.chat" sid="alice and bob" />
- <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" />
+ <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" />
+ <bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" />
+ <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" />
""".trimIndent()
ByteArrayOutputStream().use {
writeXml(it, bubbles)
@@ -56,9 +56,9 @@
val src = """
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<bs>
- <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" />
- <bb uid="10" pkg="com.example.chat" sid="alice and bob" />
- <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" />
+ <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" />
+ <bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" />
+ <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" />
</bs>
""".trimIndent()
val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))