Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt +2 −2 Original line number Diff line number Diff line Loading @@ -162,10 +162,10 @@ internal class BubbleDataRepository @Inject constructor( } uiScope.launch { cb(bubbles) } } private data class ShortcutKey(val userId: Int, val pkg: String) } data class ShortcutKey(val userId: Int, val pkg: String) private const val TAG = "BubbleDataRepository" private const val DEBUG = false private const val SHORTCUT_QUERY_FLAG = Loading packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt +39 −8 Original line number Diff line number Diff line Loading @@ -15,6 +15,10 @@ */ package com.android.systemui.bubbles.storage import android.content.pm.LauncherApps import android.os.UserHandle import com.android.internal.annotations.VisibleForTesting import com.android.systemui.bubbles.ShortcutKey import javax.inject.Inject import javax.inject.Singleton Loading @@ -25,11 +29,19 @@ private const val CAPACITY = 16 * manipulation. */ @Singleton class BubbleVolatileRepository @Inject constructor() { class BubbleVolatileRepository @Inject constructor( private val launcherApps: LauncherApps ) { /** * An ordered set of bubbles based on their natural ordering. */ private val entities = mutableSetOf<BubbleEntity>() private var entities = mutableSetOf<BubbleEntity>() /** * The capacity of the cache. */ @VisibleForTesting var capacity = CAPACITY /** * Returns a snapshot of all the bubbles. Loading @@ -45,15 +57,34 @@ class BubbleVolatileRepository @Inject constructor() { @Synchronized fun addBubbles(bubbles: List<BubbleEntity>) { if (bubbles.isEmpty()) return bubbles.forEach { entities.remove(it) } if (entities.size + bubbles.size >= CAPACITY) { entities.drop(entities.size + bubbles.size - CAPACITY) // Verify the size of given bubbles is within capacity, otherwise trim down to capacity val bubblesInRange = bubbles.takeLast(capacity) // To ensure natural ordering of the bubbles, removes bubbles which already exist val uniqueBubbles = bubblesInRange.filterNot { entities.remove(it) } val overflowCount = entities.size + bubblesInRange.size - capacity if (overflowCount > 0) { // Uncache ShortcutInfo of bubbles that will be removed due to capacity uncache(entities.take(overflowCount)) entities = entities.drop(overflowCount).toMutableSet() } entities.addAll(bubbles) entities.addAll(bubblesInRange) cache(uniqueBubbles) } @Synchronized fun removeBubbles(bubbles: List<BubbleEntity>) { bubbles.forEach { entities.remove(it) } fun removeBubbles(bubbles: List<BubbleEntity>) = uncache(bubbles.filter { entities.remove(it) }) private fun cache(bubbles: List<BubbleEntity>) { bubbles.groupBy { ShortcutKey(it.userId, it.packageName) }.forEach { (key, bubbles) -> launcherApps.cacheShortcuts(key.pkg, bubbles.map { it.shortcutId }, UserHandle.of(key.userId), LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS) } } private fun uncache(bubbles: List<BubbleEntity>) { bubbles.groupBy { ShortcutKey(it.userId, it.packageName) }.forEach { (key, bubbles) -> launcherApps.uncacheShortcuts(key.pkg, bubbles.map { it.shortcutId }, UserHandle.of(key.userId), LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS) } } } packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt +62 −7 Original line number Diff line number Diff line Loading @@ -16,37 +16,92 @@ package com.android.systemui.bubbles.storage import android.content.pm.LauncherApps import android.os.UserHandle import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.eq import junit.framework.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions @SmallTest @RunWith(AndroidTestingRunner::class) class BubbleVolatileRepositoryTest : SysuiTestCase() { 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 user0 = UserHandle.of(0) private val user10 = UserHandle.of(10) private val bubble1 = BubbleEntity(0, PKG_MESSENGER, "shortcut-1", "k1") private val bubble2 = BubbleEntity(10, PKG_CHAT, "alice and bob", "k2") private val bubble3 = BubbleEntity(0, PKG_MESSENGER, "shortcut-2", "k3") private val bubbles = listOf(bubble1, bubble2, bubble3) private lateinit var repository: BubbleVolatileRepository private lateinit var launcherApps: LauncherApps @Before fun setup() { repository = BubbleVolatileRepository() launcherApps = mock(LauncherApps::class.java) repository = BubbleVolatileRepository(launcherApps) } @Test fun testAddAndRemoveBubbles() { fun testAddBubbles() { repository.addBubbles(bubbles) assertEquals(bubbles, repository.bubbles) verify(launcherApps).cacheShortcuts(eq(PKG_MESSENGER), eq(listOf("shortcut-1", "shortcut-2")), eq(user0), eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) verify(launcherApps).cacheShortcuts(eq(PKG_CHAT), eq(listOf("alice and bob")), eq(user10), eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) repository.addBubbles(listOf(bubble1)) assertEquals(listOf(bubble2, bubble3, bubble1), repository.bubbles) verifyNoMoreInteractions(launcherApps) } @Test fun testRemoveBubbles() { repository.addBubbles(bubbles) assertEquals(bubbles, repository.bubbles) repository.removeBubbles(listOf(bubble3)) assertEquals(listOf(bubble2, bubble1), repository.bubbles) assertEquals(listOf(bubble1, bubble2), repository.bubbles) verify(launcherApps).uncacheShortcuts(eq(PKG_MESSENGER), eq(listOf("shortcut-2")), eq(user0), eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) } @Test fun testAddAndRemoveBubblesWhenExceedingCapacity() { repository.capacity = 2 // push bubbles beyond capacity repository.addBubbles(bubbles) // verify it is trim down to capacity assertEquals(listOf(bubble2, bubble3), repository.bubbles) verify(launcherApps).cacheShortcuts(eq(PKG_MESSENGER), eq(listOf("shortcut-2")), eq(user0), eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) verify(launcherApps).cacheShortcuts(eq(PKG_CHAT), eq(listOf("alice and bob")), eq(user10), eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) repository.addBubbles(listOf(bubble1)) // verify the oldest bubble is popped assertEquals(listOf(bubble3, bubble1), repository.bubbles) verify(launcherApps).uncacheShortcuts(eq(PKG_CHAT), eq(listOf("alice and bob")), eq(user10), eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) } } private const val PKG_MESSENGER = "com.example.messenger" private const val PKG_CHAT = "com.example.chat" Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt +2 −2 Original line number Diff line number Diff line Loading @@ -162,10 +162,10 @@ internal class BubbleDataRepository @Inject constructor( } uiScope.launch { cb(bubbles) } } private data class ShortcutKey(val userId: Int, val pkg: String) } data class ShortcutKey(val userId: Int, val pkg: String) private const val TAG = "BubbleDataRepository" private const val DEBUG = false private const val SHORTCUT_QUERY_FLAG = Loading
packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt +39 −8 Original line number Diff line number Diff line Loading @@ -15,6 +15,10 @@ */ package com.android.systemui.bubbles.storage import android.content.pm.LauncherApps import android.os.UserHandle import com.android.internal.annotations.VisibleForTesting import com.android.systemui.bubbles.ShortcutKey import javax.inject.Inject import javax.inject.Singleton Loading @@ -25,11 +29,19 @@ private const val CAPACITY = 16 * manipulation. */ @Singleton class BubbleVolatileRepository @Inject constructor() { class BubbleVolatileRepository @Inject constructor( private val launcherApps: LauncherApps ) { /** * An ordered set of bubbles based on their natural ordering. */ private val entities = mutableSetOf<BubbleEntity>() private var entities = mutableSetOf<BubbleEntity>() /** * The capacity of the cache. */ @VisibleForTesting var capacity = CAPACITY /** * Returns a snapshot of all the bubbles. Loading @@ -45,15 +57,34 @@ class BubbleVolatileRepository @Inject constructor() { @Synchronized fun addBubbles(bubbles: List<BubbleEntity>) { if (bubbles.isEmpty()) return bubbles.forEach { entities.remove(it) } if (entities.size + bubbles.size >= CAPACITY) { entities.drop(entities.size + bubbles.size - CAPACITY) // Verify the size of given bubbles is within capacity, otherwise trim down to capacity val bubblesInRange = bubbles.takeLast(capacity) // To ensure natural ordering of the bubbles, removes bubbles which already exist val uniqueBubbles = bubblesInRange.filterNot { entities.remove(it) } val overflowCount = entities.size + bubblesInRange.size - capacity if (overflowCount > 0) { // Uncache ShortcutInfo of bubbles that will be removed due to capacity uncache(entities.take(overflowCount)) entities = entities.drop(overflowCount).toMutableSet() } entities.addAll(bubbles) entities.addAll(bubblesInRange) cache(uniqueBubbles) } @Synchronized fun removeBubbles(bubbles: List<BubbleEntity>) { bubbles.forEach { entities.remove(it) } fun removeBubbles(bubbles: List<BubbleEntity>) = uncache(bubbles.filter { entities.remove(it) }) private fun cache(bubbles: List<BubbleEntity>) { bubbles.groupBy { ShortcutKey(it.userId, it.packageName) }.forEach { (key, bubbles) -> launcherApps.cacheShortcuts(key.pkg, bubbles.map { it.shortcutId }, UserHandle.of(key.userId), LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS) } } private fun uncache(bubbles: List<BubbleEntity>) { bubbles.groupBy { ShortcutKey(it.userId, it.packageName) }.forEach { (key, bubbles) -> launcherApps.uncacheShortcuts(key.pkg, bubbles.map { it.shortcutId }, UserHandle.of(key.userId), LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS) } } }
packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt +62 −7 Original line number Diff line number Diff line Loading @@ -16,37 +16,92 @@ package com.android.systemui.bubbles.storage import android.content.pm.LauncherApps import android.os.UserHandle import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.eq import junit.framework.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions @SmallTest @RunWith(AndroidTestingRunner::class) class BubbleVolatileRepositoryTest : SysuiTestCase() { 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 user0 = UserHandle.of(0) private val user10 = UserHandle.of(10) private val bubble1 = BubbleEntity(0, PKG_MESSENGER, "shortcut-1", "k1") private val bubble2 = BubbleEntity(10, PKG_CHAT, "alice and bob", "k2") private val bubble3 = BubbleEntity(0, PKG_MESSENGER, "shortcut-2", "k3") private val bubbles = listOf(bubble1, bubble2, bubble3) private lateinit var repository: BubbleVolatileRepository private lateinit var launcherApps: LauncherApps @Before fun setup() { repository = BubbleVolatileRepository() launcherApps = mock(LauncherApps::class.java) repository = BubbleVolatileRepository(launcherApps) } @Test fun testAddAndRemoveBubbles() { fun testAddBubbles() { repository.addBubbles(bubbles) assertEquals(bubbles, repository.bubbles) verify(launcherApps).cacheShortcuts(eq(PKG_MESSENGER), eq(listOf("shortcut-1", "shortcut-2")), eq(user0), eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) verify(launcherApps).cacheShortcuts(eq(PKG_CHAT), eq(listOf("alice and bob")), eq(user10), eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) repository.addBubbles(listOf(bubble1)) assertEquals(listOf(bubble2, bubble3, bubble1), repository.bubbles) verifyNoMoreInteractions(launcherApps) } @Test fun testRemoveBubbles() { repository.addBubbles(bubbles) assertEquals(bubbles, repository.bubbles) repository.removeBubbles(listOf(bubble3)) assertEquals(listOf(bubble2, bubble1), repository.bubbles) assertEquals(listOf(bubble1, bubble2), repository.bubbles) verify(launcherApps).uncacheShortcuts(eq(PKG_MESSENGER), eq(listOf("shortcut-2")), eq(user0), eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) } @Test fun testAddAndRemoveBubblesWhenExceedingCapacity() { repository.capacity = 2 // push bubbles beyond capacity repository.addBubbles(bubbles) // verify it is trim down to capacity assertEquals(listOf(bubble2, bubble3), repository.bubbles) verify(launcherApps).cacheShortcuts(eq(PKG_MESSENGER), eq(listOf("shortcut-2")), eq(user0), eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) verify(launcherApps).cacheShortcuts(eq(PKG_CHAT), eq(listOf("alice and bob")), eq(user10), eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) repository.addBubbles(listOf(bubble1)) // verify the oldest bubble is popped assertEquals(listOf(bubble3, bubble1), repository.bubbles) verify(launcherApps).uncacheShortcuts(eq(PKG_CHAT), eq(listOf("alice and bob")), eq(user10), eq(LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)) } } private const val PKG_MESSENGER = "com.example.messenger" private const val PKG_CHAT = "com.example.chat"