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.systemui.bubbles.storage
17 
18 import android.content.pm.LauncherApps
19 import android.os.UserHandle
20 import com.android.internal.annotations.VisibleForTesting
21 import com.android.systemui.bubbles.ShortcutKey
22 import javax.inject.Inject
23 import javax.inject.Singleton
24 
25 private const val CAPACITY = 16
26 
27 /**
28  * BubbleVolatileRepository holds the most updated snapshot of list of bubbles for in-memory
29  * manipulation.
30  */
31 @Singleton
32 class BubbleVolatileRepository @Inject constructor(
33     private val launcherApps: LauncherApps
34 ) {
35     /**
36      * An ordered set of bubbles based on their natural ordering.
37      */
38     private var entities = mutableSetOf<BubbleEntity>()
39 
40     /**
41      * The capacity of the cache.
42      */
43     @VisibleForTesting
44     var capacity = CAPACITY
45 
46     /**
47      * Returns a snapshot of all the bubbles.
48      */
49     val bubbles: List<BubbleEntity>
50         @Synchronized
51         get() = entities.toList()
52 
53     /**
54      * Add the bubbles to memory and perform a de-duplication. In case a bubble already exists,
55      * it will be moved to the last.
56      */
57     @Synchronized
58     fun addBubbles(bubbles: List<BubbleEntity>) {
59         if (bubbles.isEmpty()) return
60         // Verify the size of given bubbles is within capacity, otherwise trim down to capacity
61         val bubblesInRange = bubbles.takeLast(capacity)
62         // To ensure natural ordering of the bubbles, removes bubbles which already exist
63         val uniqueBubbles = bubblesInRange.filterNot { b: BubbleEntity ->
64             entities.removeIf { e: BubbleEntity -> b.key == e.key } }
65         val overflowCount = entities.size + bubblesInRange.size - capacity
66         if (overflowCount > 0) {
67             // Uncache ShortcutInfo of bubbles that will be removed due to capacity
68             uncache(entities.take(overflowCount))
69             entities = entities.drop(overflowCount).toMutableSet()
70         }
71         entities.addAll(bubblesInRange)
72         cache(uniqueBubbles)
73     }
74 
75     @Synchronized
76     fun removeBubbles(bubbles: List<BubbleEntity>) =
77             uncache(bubbles.filter { b: BubbleEntity ->
78                 entities.removeIf { e: BubbleEntity -> b.key == e.key } })
79 
80     private fun cache(bubbles: List<BubbleEntity>) {
81         bubbles.groupBy { ShortcutKey(it.userId, it.packageName) }.forEach { (key, bubbles) ->
82             launcherApps.cacheShortcuts(key.pkg, bubbles.map { it.shortcutId },
83                     UserHandle.of(key.userId), LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)
84         }
85     }
86 
87     private fun uncache(bubbles: List<BubbleEntity>) {
88         bubbles.groupBy { ShortcutKey(it.userId, it.packageName) }.forEach { (key, bubbles) ->
89             launcherApps.uncacheShortcuts(key.pkg, bubbles.map { it.shortcutId },
90                     UserHandle.of(key.userId), LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS)
91         }
92     }
93 }
94