Add support for dynamic clock icons in icon packs

This commit is contained in:
MM20 2023-05-25 21:17:42 +02:00
parent 2f610f1884
commit 2806032b48
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389

View File

@ -10,10 +10,12 @@ import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.database.AppDatabase
import de.mm20.launcher2.icons.AppIcon
import de.mm20.launcher2.icons.CalendarIcon
import de.mm20.launcher2.icons.ClockIcon
import de.mm20.launcher2.icons.IconBack
import de.mm20.launcher2.icons.IconMask
import de.mm20.launcher2.icons.IconPack
import de.mm20.launcher2.icons.IconUpon
import de.mm20.launcher2.icons.compat.ClockIconConfig
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.xmlpull.v1.XmlPullParser
@ -31,40 +33,8 @@ class AppFilterIconPackInstaller(
val pkgName = iconPack.packageName
try {
val res = context.packageManager.getResourcesForApplication(pkgName)
val parser: XmlPullParser
var inStream: Reader? = null
val xmlId = res.getIdentifier("appfilter", "xml", pkgName)
val rawId = res.getIdentifier("appfilter", "raw", pkgName)
parser = when {
xmlId != 0 -> res.getXml(xmlId)
rawId != 0 -> {
inStream = res.openRawResource(rawId).reader()
XmlPullParserFactory.newInstance().newPullParser().apply {
setInput(inStream)
}
}
else -> {
val iconPackContext = context.createPackageContext(
pkgName,
Context.CONTEXT_IGNORE_SECURITY
)
inStream = try {
iconPackContext.assets.open("appfilter.xml").reader()
} catch (e: IOException) {
CrashReporter.logException(e)
Log.e(
"MM20",
"appfilter.xml not found in $pkgName. Searched locations: res/xml/appfilter.xml, res/raw/appfilter.xml, assets/appfilter.xml"
)
return@withContext
}
XmlPullParserFactory.newInstance().newPullParser().apply {
setInput(inStream)
}
}
}
val dynamicClocks = getDynamicClockIcons(pkgName)
val parser = getAppfilterParser(pkgName) ?: return@withContext
loop@ while (parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.eventType != XmlPullParser.START_TAG) continue
@ -85,14 +55,27 @@ class AppFilterIconPackInstaller(
val name = parser.getAttributeValue(null, "name")
val icon = AppIcon(
packageName = componentName.packageName,
activityName = componentName.shortClassName,
drawable = drawable,
iconPack = pkgName,
name = name,
themed = iconPack.themed,
)
val icon = if (dynamicClocks.containsKey(drawable)) {
ClockIcon(
packageName = componentName.packageName,
activityName = componentName.shortClassName,
iconPack = pkgName,
themed = iconPack.themed,
name = name,
drawable = drawable,
config = dynamicClocks[drawable]!!,
)
} else {
AppIcon(
packageName = componentName.packageName,
activityName = componentName.shortClassName,
drawable = drawable,
iconPack = pkgName,
name = name,
themed = iconPack.themed,
)
}
addIcon(icon)
}
@ -168,8 +151,7 @@ class AppFilterIconPackInstaller(
}
}
}
(parser as? XmlResourceParser)?.close()
inStream?.close()
parser.close()
Log.d("MM20", "Icon pack $pkgName has been installed successfully")
} catch (e: PackageManager.NameNotFoundException) {
@ -180,6 +162,74 @@ class AppFilterIconPackInstaller(
}
}
private fun getDynamicClockIcons(packageName: String): Map<String, ClockIconConfig> {
val parser = getAppfilterParser(packageName) ?: return emptyMap()
val map = mutableMapOf<String, ClockIconConfig>()
loop@ while (parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.eventType != XmlPullParser.START_TAG) continue
if (parser.name == "dynamic-clock") {
val drawable = parser.getAttributeValue(null, "drawable") ?: continue@loop
val defaultHour = parser.getAttributeValue(null, "defaultHour")?.toIntOrNull() ?: 0
val defaultMinute =
parser.getAttributeValue(null, "defaultMinute")?.toIntOrNull() ?: 0
val defaultSecond =
parser.getAttributeValue(null, "defaultSecond")?.toIntOrNull() ?: 0
val hourLayerIndex =
parser.getAttributeValue(null, "hourLayerIndex")?.toIntOrNull() ?: -1
val minuteLayerIndex =
parser.getAttributeValue(null, "minuteLayerIndex")?.toIntOrNull() ?: -1
val secondLayerIndex =
parser.getAttributeValue(null, "secondLayerIndex")?.toIntOrNull() ?: -1
map[drawable] = ClockIconConfig(
defaultHour = defaultHour,
defaultMinute = defaultMinute,
defaultSecond = defaultSecond,
hourLayer = hourLayerIndex,
minuteLayer = minuteLayerIndex,
secondLayer = secondLayerIndex,
)
}
}
return map
}
private fun getAppfilterParser(packageName: String): ClosableXmlParser? {
val res = context.packageManager.getResourcesForApplication(packageName)
val xmlId = res.getIdentifier("appfilter", "xml", packageName)
val rawId = res.getIdentifier("appfilter", "raw", packageName)
return when {
xmlId != 0 -> ClosableXmlResourceParser(res.getXml(xmlId))
rawId != 0 -> {
val inStream = res.openRawResource(rawId).reader()
val parser = XmlPullParserFactory.newInstance().newPullParser().apply {
setInput(inStream)
}
ClosableXmlPullParser(parser, inStream)
}
else -> {
val iconPackContext = context.createPackageContext(
packageName,
Context.CONTEXT_IGNORE_SECURITY
)
val inStream = try {
iconPackContext.assets.open("appfilter.xml").reader()
} catch (e: IOException) {
CrashReporter.logException(e)
Log.e(
"MM20",
"appfilter.xml not found in $packageName. Searched locations: res/xml/appfilter.xml, res/raw/appfilter.xml, assets/appfilter.xml"
)
return null
}
val parser = XmlPullParserFactory.newInstance().newPullParser().apply {
setInput(inStream)
}
ClosableXmlPullParser(parser, inStream)
}
}
}
override fun getInstalledIconPacks(): List<IconPack> {
val packs = mutableListOf<IconPack>()
val pm = context.packageManager
@ -194,4 +244,25 @@ class AppFilterIconPackInstaller(
packs.addAll(novaPacks.map { IconPack(context, it, false) })
return packs.distinctBy { it.packageName }
}
}
internal interface ClosableXmlParser : XmlPullParser {
fun close()
}
internal class ClosableXmlResourceParser(private val parser: XmlResourceParser) : ClosableXmlParser,
XmlPullParser by parser {
override fun close() {
parser.close()
}
}
internal class ClosableXmlPullParser(
private val parser: XmlPullParser,
private val reader: Reader
) : ClosableXmlParser,
XmlPullParser by parser {
override fun close() {
reader.close()
}
}