Merge "cache/uncache ShortcutInfo associated with the bubbles" into rvc-dev

This commit is contained in:
TreeHugger Robot
2020-05-28 22:00:33 +00:00
committed by Android (Google) Code Review
3 changed files with 104 additions and 18 deletions

View File

@@ -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 =

View File

@@ -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)
}
}
}

View File

@@ -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"