Merge "cache/uncache ShortcutInfo associated with the bubbles" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
5a3a7c3985
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
repository.removeBubbles(listOf(bubble3))
|
||||
assertEquals(listOf(bubble2, bubble1), repository.bubbles)
|
||||
verifyNoMoreInteractions(launcherApps)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveBubbles() {
|
||||
repository.addBubbles(bubbles)
|
||||
assertEquals(bubbles, repository.bubbles)
|
||||
|
||||
repository.removeBubbles(listOf(bubble3))
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user