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

Change-Id: I82cb35965b4e0b523a6d8ba99d22c5624e3ea366
This commit is contained in:
Pinyao Ting
2020-05-06 16:53:57 +00:00
committed by Automerger Merge Worker
4 changed files with 82 additions and 22 deletions

View File

@@ -16,6 +16,7 @@
package com.android.systemui.bubbles
import android.annotation.UserIdInt
import android.util.Log
import com.android.systemui.bubbles.storage.BubblePersistentRepository
import com.android.systemui.bubbles.storage.BubbleVolatileRepository
import com.android.systemui.bubbles.storage.BubbleXmlEntity
@@ -35,36 +36,39 @@ internal class BubbleDataRepository @Inject constructor(
) {
private val ioScope = CoroutineScope(Dispatchers.IO)
private val uiScope = CoroutineScope(Dispatchers.Main)
private var job: Job? = null
/**
* Adds the bubble in memory, then persists the snapshot after adding the bubble to disk
* asynchronously.
*/
fun addBubble(@UserIdInt userId: Int, bubble: Bubble) {
volatileRepository.addBubble(
BubbleXmlEntity(userId, bubble.packageName, bubble.shortcutInfo?.id ?: return))
persistToDisk()
}
fun addBubble(@UserIdInt userId: Int, bubble: Bubble) = addBubbles(userId, listOf(bubble))
/**
* Adds the bubble in memory, then persists the snapshot after adding the bubble to disk
* asynchronously.
*/
fun addBubbles(@UserIdInt userId: Int, bubbles: List<Bubble>) {
volatileRepository.addBubbles(bubbles.mapNotNull {
val shortcutId = it.shortcutInfo?.id ?: return@mapNotNull null
BubbleXmlEntity(userId, it.packageName, shortcutId)
})
persistToDisk()
if (DEBUG) Log.d(TAG, "adding ${bubbles.size} bubbles")
val entities = transform(userId, bubbles).also(volatileRepository::addBubbles)
if (entities.isNotEmpty()) persistToDisk()
}
/**
* Removes the bubbles from memory, then persists the snapshot to disk asynchronously.
*/
fun removeBubbles(@UserIdInt userId: Int, bubbles: List<Bubble>) {
volatileRepository.removeBubbles(bubbles.mapNotNull {
val shortcutId = it.shortcutInfo?.id ?: return@mapNotNull null
BubbleXmlEntity(userId, it.packageName, shortcutId)
})
persistToDisk()
if (DEBUG) Log.d(TAG, "removing ${bubbles.size} bubbles")
val entities = transform(userId, bubbles).also(volatileRepository::removeBubbles)
if (entities.isNotEmpty()) 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)
}
}
/**
* 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")
fun persistsToDisk(bubbles: List<BubbleXmlEntity>): Boolean {
if (DEBUG) Log.d(TAG, "persisting ${bubbles.size} bubbles")
synchronized(bubbleFile) {
val stream: FileOutputStream = try { bubbleFile.startWrite() } catch (e: IOException) {
Log.e(TAG, "Failed to save bubble file", e)
@@ -41,6 +42,7 @@ class BubblePersistentRepository @Inject constructor(
try {
writeXml(stream, bubbles)
bubbleFile.finishWrite(stream)
if (DEBUG) Log.d(TAG, "persisted ${bubbles.size} bubbles")
return true
} catch (e: Exception) {
Log.e(TAG, "Failed to save bubble file, restoring backup", e)
@@ -49,6 +51,16 @@ class BubblePersistentRepository @Inject constructor(
}
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 DEBUG = false

View File

@@ -38,12 +38,6 @@ class BubbleVolatileRepository @Inject constructor() {
@Synchronized
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,
* it will be moved to the last.
@@ -55,7 +49,7 @@ class BubbleVolatileRepository @Inject constructor() {
if (entities.size + bubbles.size >= CAPACITY) {
entities.drop(entities.size + bubbles.size - CAPACITY)
}
entities.addAll(bubbles.reversed())
entities.addAll(bubbles)
}
@Synchronized

View File

@@ -18,6 +18,10 @@ package com.android.systemui.bubbles.storage
import com.android.internal.util.FastXmlSerializer
import org.xmlpull.v1.XmlSerializer
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.nio.charset.StandardCharsets
@@ -57,4 +61,35 @@ private fun writeXmlEntry(serializer: XmlSerializer, bubble: BubbleXmlEntity) {
} catch (e: IOException) {
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
}