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.database.AppDatabase
import de.mm20.launcher2.icons.AppIcon import de.mm20.launcher2.icons.AppIcon
import de.mm20.launcher2.icons.CalendarIcon import de.mm20.launcher2.icons.CalendarIcon
import de.mm20.launcher2.icons.ClockIcon
import de.mm20.launcher2.icons.IconBack import de.mm20.launcher2.icons.IconBack
import de.mm20.launcher2.icons.IconMask import de.mm20.launcher2.icons.IconMask
import de.mm20.launcher2.icons.IconPack import de.mm20.launcher2.icons.IconPack
import de.mm20.launcher2.icons.IconUpon import de.mm20.launcher2.icons.IconUpon
import de.mm20.launcher2.icons.compat.ClockIconConfig
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParser
@ -31,40 +33,8 @@ class AppFilterIconPackInstaller(
val pkgName = iconPack.packageName val pkgName = iconPack.packageName
try { try {
val res = context.packageManager.getResourcesForApplication(pkgName) val dynamicClocks = getDynamicClockIcons(pkgName)
val parser: XmlPullParser val parser = getAppfilterParser(pkgName) ?: return@withContext
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)
}
}
}
loop@ while (parser.next() != XmlPullParser.END_DOCUMENT) { loop@ while (parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.eventType != XmlPullParser.START_TAG) continue if (parser.eventType != XmlPullParser.START_TAG) continue
@ -85,14 +55,27 @@ class AppFilterIconPackInstaller(
val name = parser.getAttributeValue(null, "name") val name = parser.getAttributeValue(null, "name")
val icon = AppIcon(
packageName = componentName.packageName, val icon = if (dynamicClocks.containsKey(drawable)) {
activityName = componentName.shortClassName, ClockIcon(
drawable = drawable, packageName = componentName.packageName,
iconPack = pkgName, activityName = componentName.shortClassName,
name = name, iconPack = pkgName,
themed = iconPack.themed, 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) addIcon(icon)
} }
@ -168,8 +151,7 @@ class AppFilterIconPackInstaller(
} }
} }
} }
(parser as? XmlResourceParser)?.close() parser.close()
inStream?.close()
Log.d("MM20", "Icon pack $pkgName has been installed successfully") Log.d("MM20", "Icon pack $pkgName has been installed successfully")
} catch (e: PackageManager.NameNotFoundException) { } 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> { override fun getInstalledIconPacks(): List<IconPack> {
val packs = mutableListOf<IconPack>() val packs = mutableListOf<IconPack>()
val pm = context.packageManager val pm = context.packageManager
@ -194,4 +244,25 @@ class AppFilterIconPackInstaller(
packs.addAll(novaPacks.map { IconPack(context, it, false) }) packs.addAll(novaPacks.map { IconPack(context, it, false) })
return packs.distinctBy { it.packageName } 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()
}
} }