blob: 6f2d71fd0f59ae1b8a27b934f6b9fdf515338567 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.controls.controller
import android.app.ActivityManager
import android.content.ComponentName
import android.util.AtomicFile
import android.util.Log
import android.util.Slog
import android.util.Xml
import com.android.systemui.util.concurrency.DelayableExecutor
import libcore.io.IoUtils
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.IOException
class ControlsFavoritePersistenceWrapper(
val file: File,
val executor: DelayableExecutor
) {
companion object {
private const val TAG = "ControlsFavoritePersistenceWrapper"
const val FILE_NAME = "controls_favorites.xml"
private const val TAG_CONTROLS = "controls"
private const val TAG_CONTROL = "control"
private const val TAG_COMPONENT = "component"
private const val TAG_ID = "id"
private const val TAG_TITLE = "title"
private const val TAG_TYPE = "type"
}
val currentUser: Int
get() = ActivityManager.getCurrentUser()
fun storeFavorites(list: List<ControlInfo>) {
executor.execute {
val atomicFile = AtomicFile(file)
val writer = try {
atomicFile.startWrite()
} catch (e: IOException) {
Log.e(TAG, "Failed to start write file", e)
return@execute
}
try {
Xml.newSerializer().apply {
setOutput(writer, "utf-8")
setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true)
startDocument(null, true)
startTag(null, TAG_CONTROLS)
list.forEach {
startTag(null, TAG_CONTROL)
attribute(null, TAG_COMPONENT, it.component.flattenToString())
attribute(null, TAG_ID, it.controlId)
attribute(null, TAG_TITLE, it.controlTitle.toString())
attribute(null, TAG_TYPE, it.deviceType.toString())
endTag(null, TAG_CONTROL)
}
endTag(null, TAG_CONTROLS)
endDocument()
atomicFile.finishWrite(writer)
}
} catch (t: Throwable) {
Log.e(TAG, "Failed to write file, reverting to previous version")
atomicFile.failWrite(writer)
} finally {
IoUtils.closeQuietly(writer)
}
}
}
fun readFavorites(): List<ControlInfo> {
if (!file.exists()) {
Log.d(TAG, "No favorites, returning empty list")
return emptyList()
}
val reader = try {
FileInputStream(file)
} catch (fnfe: FileNotFoundException) {
Slog.i(TAG, "No file found")
return emptyList()
}
try {
val parser = Xml.newPullParser()
parser.setInput(reader, null)
return parseXml(parser)
} catch (e: XmlPullParserException) {
throw IllegalStateException("Failed parsing favorites file: $file", e)
} catch (e: IOException) {
throw IllegalStateException("Failed parsing favorites file: $file", e)
} finally {
IoUtils.closeQuietly(reader)
}
}
private fun parseXml(parser: XmlPullParser): List<ControlInfo> {
var type: Int = 0
val infos = mutableListOf<ControlInfo>()
while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue
}
val tagName = parser.name
if (tagName == TAG_CONTROL) {
val component = ComponentName.unflattenFromString(
parser.getAttributeValue(null, TAG_COMPONENT))
val id = parser.getAttributeValue(null, TAG_ID)
val title = parser.getAttributeValue(null, TAG_TITLE)
val type = parser.getAttributeValue(null, TAG_TYPE)?.toInt()
if (component != null && id != null && title != null && type != null) {
infos.add(ControlInfo(component, id, title, type))
}
}
}
return infos
}
}