Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt +34 −15 Original line number Original line Diff line number Diff line Loading @@ -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 Loading @@ -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) } } } /** /** Loading Loading @@ -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 packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt +12 −0 Original line number Original line Diff line number Diff line Loading @@ -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) Loading @@ -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) Loading @@ -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 packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt +1 −7 Original line number Original line Diff line number Diff line Loading @@ -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. Loading @@ -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 Loading packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt +35 −0 Original line number Original line Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 } No newline at end of file Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt +34 −15 Original line number Original line Diff line number Diff line Loading @@ -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 Loading @@ -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) } } } /** /** Loading Loading @@ -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
packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt +12 −0 Original line number Original line Diff line number Diff line Loading @@ -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) Loading @@ -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) Loading @@ -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
packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt +1 −7 Original line number Original line Diff line number Diff line Loading @@ -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. Loading @@ -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 Loading
packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt +35 −0 Original line number Original line Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 } No newline at end of file