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