Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4b39be3b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Persists bubbles to disk (part 3)" into rvc-dev am: 8e65e076

Change-Id: I33b3f097c0a90521b890d1e8618994ee0b8e689d
parents da734e4c 8e65e076
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -102,6 +102,13 @@ class Bubble implements BubbleViewProvider {
        return user.getIdentifier() + "|" + entry.getSbn().getPackageName();
    }

    // TODO: Decouple Bubble from NotificationEntry and transform ShortcutInfo into Bubble
    Bubble(ShortcutInfo shortcutInfo) {
        mShortcutInfo = shortcutInfo;
        mKey = shortcutInfo.getId();
        mGroupId = shortcutInfo.getId();
    }

    /** Used in tests when no UI is required. */
    @VisibleForTesting(visibility = PRIVATE)
    Bubble(NotificationEntry e,
+63 −10
Original line number Diff line number Diff line
@@ -15,24 +15,32 @@
 */
package com.android.systemui.bubbles

import android.annotation.SuppressLint
import android.annotation.UserIdInt
import android.content.pm.LauncherApps
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED
import android.os.UserHandle
import android.util.Log
import com.android.systemui.bubbles.storage.BubbleEntity
import com.android.systemui.bubbles.storage.BubblePersistentRepository
import com.android.systemui.bubbles.storage.BubbleVolatileRepository
import com.android.systemui.bubbles.storage.BubbleXmlEntity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield

import javax.inject.Inject
import javax.inject.Singleton

@Singleton
internal class BubbleDataRepository @Inject constructor(
    private val volatileRepository: BubbleVolatileRepository,
    private val persistentRepository: BubblePersistentRepository
    private val persistentRepository: BubblePersistentRepository,
    private val launcherApps: LauncherApps
) {

    private val ioScope = CoroutineScope(Dispatchers.IO)
@@ -64,10 +72,10 @@ internal class BubbleDataRepository @Inject constructor(
        if (entities.isNotEmpty()) persistToDisk()
    }

    private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleXmlEntity> {
    private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleEntity> {
        return bubbles.mapNotNull { b ->
            val shortcutId = b.shortcutInfo?.id ?: return@mapNotNull null
            BubbleXmlEntity(userId, b.packageName, shortcutId)
            BubbleEntity(userId, b.packageName, shortcutId)
        }
    }

@@ -100,15 +108,60 @@ internal class BubbleDataRepository @Inject constructor(
    /**
     * Load bubbles from disk.
     */
    // TODO: call this method from BubbleController and update UI
    @SuppressLint("WrongConstant")
    fun loadBubbles(cb: (List<Bubble>) -> Unit) = ioScope.launch {
        val bubbleXmlEntities = persistentRepository.readFromDisk()
        volatileRepository.addBubbles(bubbleXmlEntities)
        uiScope.launch {
            // TODO: transform bubbleXmlEntities into bubbles
            // cb(bubbles)
        }
        /**
         * Load BubbleEntity from disk.
         * e.g.
         * [
         *     BubbleEntity(0, "com.example.messenger", "id-2"),
         *     BubbleEntity(10, "com.example.chat", "my-id1")
         *     BubbleEntity(0, "com.example.messenger", "id-1")
         * ]
         */
        val entities = persistentRepository.readFromDisk()
        volatileRepository.addBubbles(entities)
        /**
         * Extract userId/packageName from these entities.
         * e.g.
         * [
         *     ShortcutKey(0, "com.example.messenger"), ShortcutKey(0, "com.example.chat")
         * ]
         */
        val shortcutKeys = entities.map { ShortcutKey(it.userId, it.packageName) }.toSet()
        /**
         * Retrieve shortcuts with given userId/packageName combination, then construct a mapping
         * between BubbleEntity and ShortcutInfo.
         * e.g.
         * {
         *     BubbleEntity(0, "com.example.messenger", "id-0") ->
         *         ShortcutInfo(userId=0, pkg="com.example.messenger", id="id-0"),
         *     BubbleEntity(0, "com.example.messenger", "id-2") ->
         *         ShortcutInfo(userId=0, pkg="com.example.messenger", id="id-2"),
         *     BubbleEntity(10, "com.example.chat", "id-1") ->
         *         ShortcutInfo(userId=10, pkg="com.example.chat", id="id-1"),
         *     BubbleEntity(10, "com.example.chat", "id-3") ->
         *         ShortcutInfo(userId=10, pkg="com.example.chat", id="id-3")
         * }
         */
        val shortcutMap = shortcutKeys.flatMap { key ->
            launcherApps.getShortcuts(
                    LauncherApps.ShortcutQuery()
                            .setPackage(key.pkg)
                            .setQueryFlags(SHORTCUT_QUERY_FLAG), UserHandle.of(key.userId))
                    ?.map { BubbleEntity(key.userId, key.pkg, it.id) to it } ?: emptyList()
        }.toMap()
        // For each entity loaded from xml, find the corresponding ShortcutInfo then convert them
        // into Bubble.
        val bubbles = entities.mapNotNull { entity -> shortcutMap[entity]?.let { Bubble(it) } }
        uiScope.launch { cb(bubbles) }
    }

    private 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 =
        FLAG_MATCH_DYNAMIC or FLAG_MATCH_PINNED_BY_ANY_LAUNCHER or FLAG_MATCH_CACHED
 No newline at end of file
+1 −1
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@ package com.android.systemui.bubbles.storage

import android.annotation.UserIdInt

data class BubbleXmlEntity(
data class BubbleEntity(
    @UserIdInt val userId: Int,
    val packageName: String,
    val shortcutId: String
+2 −2
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ class BubblePersistentRepository @Inject constructor(
    private val bubbleFile: AtomicFile = AtomicFile(File(context.filesDir,
            "overflow_bubbles.xml"), "overflow-bubbles")

    fun persistsToDisk(bubbles: List<BubbleXmlEntity>): Boolean {
    fun persistsToDisk(bubbles: List<BubbleEntity>): Boolean {
        if (DEBUG) Log.d(TAG, "persisting ${bubbles.size} bubbles")
        synchronized(bubbleFile) {
            val stream: FileOutputStream = try { bubbleFile.startWrite() } catch (e: IOException) {
@@ -52,7 +52,7 @@ class BubblePersistentRepository @Inject constructor(
        return false
    }

    fun readFromDisk(): List<BubbleXmlEntity> {
    fun readFromDisk(): List<BubbleEntity> {
        synchronized(bubbleFile) {
            try { return bubbleFile.openRead().use(::readXml) } catch (e: Throwable) {
                Log.e(TAG, "Failed to open bubble file", e)
+4 −4
Original line number Diff line number Diff line
@@ -29,12 +29,12 @@ class BubbleVolatileRepository @Inject constructor() {
    /**
     * An ordered set of bubbles based on their natural ordering.
     */
    private val entities = mutableSetOf<BubbleXmlEntity>()
    private val entities = mutableSetOf<BubbleEntity>()

    /**
     * Returns a snapshot of all the bubbles.
     */
    val bubbles: List<BubbleXmlEntity>
    val bubbles: List<BubbleEntity>
        @Synchronized
        get() = entities.toList()

@@ -43,7 +43,7 @@ class BubbleVolatileRepository @Inject constructor() {
     * it will be moved to the last.
     */
    @Synchronized
    fun addBubbles(bubbles: List<BubbleXmlEntity>) {
    fun addBubbles(bubbles: List<BubbleEntity>) {
        if (bubbles.isEmpty()) return
        bubbles.forEach { entities.remove(it) }
        if (entities.size + bubbles.size >= CAPACITY) {
@@ -53,7 +53,7 @@ class BubbleVolatileRepository @Inject constructor() {
    }

    @Synchronized
    fun removeBubbles(bubbles: List<BubbleXmlEntity>) {
    fun removeBubbles(bubbles: List<BubbleEntity>) {
        bubbles.forEach { entities.remove(it) }
    }
}
Loading