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

This commit is contained in:
Pinyao Ting
2020-05-06 16:51:36 +00:00
committed by Android (Google) Code Review
4 changed files with 82 additions and 22 deletions

View File

@@ -16,6 +16,7 @@
package com.android.systemui.bubbles package com.android.systemui.bubbles
import android.annotation.UserIdInt import android.annotation.UserIdInt
import android.util.Log
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 com.android.systemui.bubbles.storage.BubbleXmlEntity
@@ -35,36 +36,39 @@ internal class BubbleDataRepository @Inject constructor(
) { ) {
private val ioScope = CoroutineScope(Dispatchers.IO) private val ioScope = CoroutineScope(Dispatchers.IO)
private val uiScope = CoroutineScope(Dispatchers.Main)
private var job: Job? = null private var job: Job? = null
/** /**
* Adds the bubble in memory, then persists the snapshot after adding the bubble to disk * Adds the bubble in memory, then persists the snapshot after adding the bubble to disk
* asynchronously. * asynchronously.
*/ */
fun addBubble(@UserIdInt userId: Int, bubble: Bubble) { fun addBubble(@UserIdInt userId: Int, bubble: Bubble) = addBubbles(userId, listOf(bubble))
volatileRepository.addBubble(
BubbleXmlEntity(userId, bubble.packageName, bubble.shortcutInfo?.id ?: return))
persistToDisk()
}
/** /**
* Adds the bubble in memory, then persists the snapshot after adding the bubble to disk * Adds the bubble in memory, then persists the snapshot after adding the bubble to disk
* asynchronously. * asynchronously.
*/ */
fun addBubbles(@UserIdInt userId: Int, bubbles: List<Bubble>) { fun addBubbles(@UserIdInt userId: Int, bubbles: List<Bubble>) {
volatileRepository.addBubbles(bubbles.mapNotNull { if (DEBUG) Log.d(TAG, "adding ${bubbles.size} bubbles")
val shortcutId = it.shortcutInfo?.id ?: return@mapNotNull null val entities = transform(userId, bubbles).also(volatileRepository::addBubbles)
BubbleXmlEntity(userId, it.packageName, shortcutId) if (entities.isNotEmpty()) persistToDisk()
})
persistToDisk()
} }
/**
* Removes the bubbles from memory, then persists the snapshot to disk asynchronously.
*/
fun removeBubbles(@UserIdInt userId: Int, bubbles: List<Bubble>) { fun removeBubbles(@UserIdInt userId: Int, bubbles: List<Bubble>) {
volatileRepository.removeBubbles(bubbles.mapNotNull { if (DEBUG) Log.d(TAG, "removing ${bubbles.size} bubbles")
val shortcutId = it.shortcutInfo?.id ?: return@mapNotNull null val entities = transform(userId, bubbles).also(volatileRepository::removeBubbles)
BubbleXmlEntity(userId, it.packageName, shortcutId) if (entities.isNotEmpty()) persistToDisk()
}) }
persistToDisk()
private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleXmlEntity> {
return bubbles.mapNotNull { b ->
val shortcutId = b.shortcutInfo?.id ?: return@mapNotNull null
BubbleXmlEntity(userId, b.packageName, shortcutId)
}
} }
/** /**
@@ -92,4 +96,19 @@ internal class BubbleDataRepository @Inject constructor(
persistentRepository.persistsToDisk(volatileRepository.bubbles) persistentRepository.persistsToDisk(volatileRepository.bubbles)
} }
} }
/**
* Load bubbles from disk.
*/
fun loadBubbles(cb: (List<Bubble>) -> Unit) = ioScope.launch {
val bubbleXmlEntities = persistentRepository.readFromDisk()
volatileRepository.addBubbles(bubbleXmlEntities)
uiScope.launch {
// TODO: transform bubbleXmlEntities into bubbles
// cb(bubbles)
} }
}
}
private const val TAG = "BubbleDataRepository"
private const val DEBUG = false

View File

@@ -33,6 +33,7 @@ class BubblePersistentRepository @Inject constructor(
"overflow_bubbles.xml"), "overflow-bubbles") "overflow_bubbles.xml"), "overflow-bubbles")
fun persistsToDisk(bubbles: List<BubbleXmlEntity>): Boolean { fun persistsToDisk(bubbles: List<BubbleXmlEntity>): Boolean {
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) {
Log.e(TAG, "Failed to save bubble file", e) Log.e(TAG, "Failed to save bubble file", e)
@@ -41,6 +42,7 @@ class BubblePersistentRepository @Inject constructor(
try { try {
writeXml(stream, bubbles) writeXml(stream, bubbles)
bubbleFile.finishWrite(stream) bubbleFile.finishWrite(stream)
if (DEBUG) Log.d(TAG, "persisted ${bubbles.size} bubbles")
return true return true
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Failed to save bubble file, restoring backup", e) Log.e(TAG, "Failed to save bubble file, restoring backup", e)
@@ -49,6 +51,16 @@ class BubblePersistentRepository @Inject constructor(
} }
return false return false
} }
fun readFromDisk(): List<BubbleXmlEntity> {
synchronized(bubbleFile) {
try { return bubbleFile.openRead().use(::readXml) } catch (e: Throwable) {
Log.e(TAG, "Failed to open bubble file", e)
}
return emptyList()
}
}
} }
private const val TAG = "BubblePersistentRepository" private const val TAG = "BubblePersistentRepository"
private const val DEBUG = false

View File

@@ -38,12 +38,6 @@ class BubbleVolatileRepository @Inject constructor() {
@Synchronized @Synchronized
get() = entities.toList() get() = entities.toList()
/**
* Add the bubble to memory and perform a de-duplication. In case the bubble already exists,
* the bubble will be moved to the last.
*/
fun addBubble(bubble: BubbleXmlEntity) = addBubbles(listOf(bubble))
/** /**
* Add the bubbles to memory and perform a de-duplication. In case a bubble already exists, * Add the bubbles to memory and perform a de-duplication. In case a bubble already exists,
* it will be moved to the last. * it will be moved to the last.
@@ -55,7 +49,7 @@ class BubbleVolatileRepository @Inject constructor() {
if (entities.size + bubbles.size >= CAPACITY) { if (entities.size + bubbles.size >= CAPACITY) {
entities.drop(entities.size + bubbles.size - CAPACITY) entities.drop(entities.size + bubbles.size - CAPACITY)
} }
entities.addAll(bubbles.reversed()) entities.addAll(bubbles)
} }
@Synchronized @Synchronized

View File

@@ -18,6 +18,10 @@ package com.android.systemui.bubbles.storage
import com.android.internal.util.FastXmlSerializer import com.android.internal.util.FastXmlSerializer
import org.xmlpull.v1.XmlSerializer import org.xmlpull.v1.XmlSerializer
import java.io.IOException import java.io.IOException
import android.util.Xml
import com.android.internal.util.XmlUtils
import org.xmlpull.v1.XmlPullParser
import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
@@ -58,3 +62,34 @@ private fun writeXmlEntry(serializer: XmlSerializer, bubble: BubbleXmlEntity) {
throw RuntimeException(e) throw RuntimeException(e)
} }
} }
/**
* Reads the bubbles from xml file.
*/
fun readXml(stream: InputStream): List<BubbleXmlEntity> {
val bubbles = mutableListOf<BubbleXmlEntity>()
val parser: XmlPullParser = Xml.newPullParser()
parser.setInput(stream, StandardCharsets.UTF_8.name())
XmlUtils.beginDocument(parser, TAG_BUBBLES)
val outerDepth = parser.depth
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
bubbles.add(readXmlEntry(parser) ?: continue)
}
return bubbles
}
private fun readXmlEntry(parser: XmlPullParser): BubbleXmlEntity? {
while (parser.eventType != XmlPullParser.START_TAG) { parser.next() }
return BubbleXmlEntity(
parser.getAttributeWithName(ATTR_USER_ID)?.toInt() ?: return null,
parser.getAttributeWithName(ATTR_PACKAGE) ?: return null,
parser.getAttributeWithName(ATTR_SHORTCUT_ID) ?: return null
)
}
private fun XmlPullParser.getAttributeWithName(name: String): String? {
for (i in 0 until attributeCount) {
if (getAttributeName(i) == name) return getAttributeValue(i)
}
return null
}