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

Commit 8e65e076 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Persists bubbles to disk (part 3)" into rvc-dev

parents 735b2fd3 d4c1ba91
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -102,6 +102,13 @@ class Bubble implements BubbleViewProvider {
        return user.getIdentifier() + "|" + entry.getSbn().getPackageName();
        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. */
    /** Used in tests when no UI is required. */
    @VisibleForTesting(visibility = PRIVATE)
    @VisibleForTesting(visibility = PRIVATE)
    Bubble(NotificationEntry e,
    Bubble(NotificationEntry e,
+63 −10
Original line number Original line Diff line number Diff line
@@ -15,24 +15,32 @@
 */
 */
package com.android.systemui.bubbles
package com.android.systemui.bubbles


import android.annotation.SuppressLint
import android.annotation.UserIdInt
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 android.util.Log
import com.android.systemui.bubbles.storage.BubbleEntity
import com.android.systemui.bubbles.storage.BubblePersistentRepository
import com.android.systemui.bubbles.storage.BubblePersistentRepository
import com.android.systemui.bubbles.storage.BubbleVolatileRepository
import com.android.systemui.bubbles.storage.BubbleVolatileRepository
import com.android.systemui.bubbles.storage.BubbleXmlEntity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
import kotlinx.coroutines.yield

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


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


    private val ioScope = CoroutineScope(Dispatchers.IO)
    private val ioScope = CoroutineScope(Dispatchers.IO)
@@ -64,10 +72,10 @@ internal class BubbleDataRepository @Inject constructor(
        if (entities.isNotEmpty()) persistToDisk()
        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 ->
        return bubbles.mapNotNull { b ->
            val shortcutId = b.shortcutInfo?.id ?: return@mapNotNull null
            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.
     * Load bubbles from disk.
     */
     */
    // TODO: call this method from BubbleController and update UI
    @SuppressLint("WrongConstant")
    fun loadBubbles(cb: (List<Bubble>) -> Unit) = ioScope.launch {
    fun loadBubbles(cb: (List<Bubble>) -> Unit) = ioScope.launch {
        val bubbleXmlEntities = persistentRepository.readFromDisk()
        /**
        volatileRepository.addBubbles(bubbleXmlEntities)
         * Load BubbleEntity from disk.
        uiScope.launch {
         * e.g.
            // TODO: transform bubbleXmlEntities into bubbles
         * [
            // cb(bubbles)
         *     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 TAG = "BubbleDataRepository"
private const val DEBUG = false
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 Original line Diff line number Diff line
@@ -17,7 +17,7 @@ package com.android.systemui.bubbles.storage


import android.annotation.UserIdInt
import android.annotation.UserIdInt


data class BubbleXmlEntity(
data class BubbleEntity(
    @UserIdInt val userId: Int,
    @UserIdInt val userId: Int,
    val packageName: String,
    val packageName: String,
    val shortcutId: String
    val shortcutId: String
+2 −2
Original line number Original line Diff line number Diff line
@@ -32,7 +32,7 @@ class BubblePersistentRepository @Inject constructor(
    private val bubbleFile: AtomicFile = AtomicFile(File(context.filesDir,
    private val bubbleFile: AtomicFile = AtomicFile(File(context.filesDir,
            "overflow_bubbles.xml"), "overflow-bubbles")
            "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")
        if (DEBUG) Log.d(TAG, "persisting ${bubbles.size} bubbles")
        synchronized(bubbleFile) {
        synchronized(bubbleFile) {
            val stream: FileOutputStream = try { bubbleFile.startWrite() } catch (e: IOException) {
            val stream: FileOutputStream = try { bubbleFile.startWrite() } catch (e: IOException) {
@@ -52,7 +52,7 @@ class BubblePersistentRepository @Inject constructor(
        return false
        return false
    }
    }


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


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


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