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