Merge "Persists bubbles to disk (part 2)" into rvc-dev
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user