From 76ed7ba8cf3bd88cb0e2efa85d4d34e3ac0314f4 Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Wed, 29 Apr 2020 18:35:39 -0700 Subject: [PATCH] Persists bubbles to disk (part 2) read bubbles entities from XML Bug: 149713060 Test: manually verify the code recreated bubbles entities from XML Change-Id: Ic58a67bc81e068e8616adce9ab577b7d0070537f --- .../systemui/bubbles/BubbleDataRepository.kt | 49 +++++++++++++------ .../storage/BubblePersistentRepository.kt | 12 +++++ .../storage/BubbleVolatileRepository.kt | 8 +-- .../bubbles/storage/BubbleXmlHelper.kt | 35 +++++++++++++ 4 files changed, 82 insertions(+), 22 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt index 63dd801be7caa..b9825e1d21e57 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt @@ -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) { - 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) { - 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): List { + 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) -> 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 diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt index b2495c658948f..149e2c4c40229 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt @@ -33,6 +33,7 @@ class BubblePersistentRepository @Inject constructor( "overflow_bubbles.xml"), "overflow-bubbles") fun persistsToDisk(bubbles: List): 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 { + 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 diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt index 3dba268074859..e1f675b835839 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt index 3192f34b69fd3..1e91653c6db79 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt @@ -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 { + val bubbles = mutableListOf() + 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 } \ No newline at end of file