• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.wm.shell.bubbles.storage
17 
18 import android.content.pm.LauncherApps
19 import android.os.UserHandle
20 import android.util.SparseArray
21 import com.android.internal.annotations.VisibleForTesting
22 import com.android.wm.shell.bubbles.ShortcutKey
23 
24 private const val CAPACITY = 16
25 
26 /**
27  * BubbleVolatileRepository holds the most updated snapshot of list of bubbles for in-memory
28  * manipulation.
29  */
30 class BubbleVolatileRepository(private val launcherApps: LauncherApps) {
31 
32     /**
33      * Set of bubbles per user. Each set of bubbles is ordered by recency.
34      */
35     private var entitiesByUser = SparseArray<MutableList<BubbleEntity>>()
36 
37     /**
38      * The capacity of the cache.
39      */
40     @VisibleForTesting
41     var capacity = CAPACITY
42 
43     /**
44      * Returns a snapshot of all the bubbles, a map of the userId to bubble list.
45      */
46     val bubbles: SparseArray<List<BubbleEntity>>
47         @Synchronized
48         get() {
49             val map = SparseArray<List<BubbleEntity>>()
50             for (i in 0 until entitiesByUser.size()) {
51                 val k = entitiesByUser.keyAt(i)
52                 val v = entitiesByUser.valueAt(i)
53                 map.put(k, v.toList())
54             }
55             return map
56         }
57 
58     /**
59      * Returns the entity list of the provided user's bubbles or creates one if it doesn't exist.
60      */
61     @Synchronized
62     fun getEntities(userId: Int): MutableList<BubbleEntity> {
63         val entities = entitiesByUser.get(userId)
64         return when (entities) {
65             null -> mutableListOf<BubbleEntity>().also {
66                 entitiesByUser.put(userId, it)
67             }
68             else -> entities
69         }
70     }
71 
72     /**
73      * Add the bubbles to memory and perform a de-duplication. In case a bubble already exists,
74      * it will be moved to the last.
75      */
76     @Synchronized
77     fun addBubbles(userId: Int, bubbles: List<BubbleEntity>) {
78         if (bubbles.isEmpty()) return
79         // Get the list for this user
80         var entities = getEntities(userId)
81         // Verify the size of given bubbles is within capacity, otherwise trim down to capacity
82         val bubblesInRange = bubbles.takeLast(capacity)
83         // To ensure natural ordering of the bubbles, removes bubbles which already exist
84         val uniqueBubbles = bubblesInRange.filterNot { b: BubbleEntity ->
85             entities.removeIf { e: BubbleEntity -> b.key == e.key } }
86         val overflowCount = entities.size + bubblesInRange.size - capacity
87         if (overflowCount > 0) {
88             // Uncache ShortcutInfo of bubbles that will be removed due to capacity
89             uncache(entities.take(overflowCount))
90             entities = entities.drop(overflowCount).toMutableList()
91         }
92         entities.addAll(bubblesInRange)
93         entitiesByUser.put(userId, entities)
94         cache(uniqueBubbles)
95     }
96 
97     @Synchronized
98     fun removeBubbles(userId: Int, bubbles: List<BubbleEntity>) =
99             uncache(bubbles.filter { b: BubbleEntity ->
100                 getEntities(userId).removeIf { e: BubbleEntity -> b.key == e.key } })
101 
102     private fun cache(bubbles: List<BubbleEntity>) {
103         bubbles.groupBy { ShortcutKey(it.userId, it.packageName) }.forEach { (key, bubbles) ->
104             launcherApps.cacheShortcuts(key.pkg, bubbles.map { it.shortcutId },
105                     UserHandle.of(key.userId), LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)
106         }
107     }
108 
109     private fun uncache(bubbles: List<BubbleEntity>) {
110         bubbles.groupBy { ShortcutKey(it.userId, it.packageName) }.forEach { (key, bubbles) ->
111             launcherApps.uncacheShortcuts(key.pkg, bubbles.map { it.shortcutId },
112                     UserHandle.of(key.userId), LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)
113         }
114     }
115 }
116