diff --git a/services/icons/src/main/java/de/mm20/launcher2/icons/loaders/AppFilterIconPackInstaller.kt b/services/icons/src/main/java/de/mm20/launcher2/icons/loaders/AppFilterIconPackInstaller.kt index a870ddec..9e158ec7 100644 --- a/services/icons/src/main/java/de/mm20/launcher2/icons/loaders/AppFilterIconPackInstaller.kt +++ b/services/icons/src/main/java/de/mm20/launcher2/icons/loaders/AppFilterIconPackInstaller.kt @@ -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 { + val parser = getAppfilterParser(packageName) ?: return emptyMap() + val map = mutableMapOf() + 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 { val packs = mutableListOf() 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() + } } \ No newline at end of file