Don't store compat themed icons in database
This commit is contained in:
parent
59b8f17fc4
commit
23252726b0
@ -16,9 +16,6 @@ interface IconDao {
|
|||||||
@Query("SELECT * FROM Icons WHERE componentName = :componentName AND iconPack = :iconPack AND (type = 'app' OR type = 'calendar') LIMIT 1")
|
@Query("SELECT * FROM Icons WHERE componentName = :componentName AND iconPack = :iconPack AND (type = 'app' OR type = 'calendar') LIMIT 1")
|
||||||
suspend fun getIcon(componentName: String, iconPack: String): IconEntity?
|
suspend fun getIcon(componentName: String, iconPack: String): IconEntity?
|
||||||
|
|
||||||
@Query("SELECT * FROM Icons WHERE componentName = :componentName AND (type = 'themed-compat') LIMIT 1")
|
|
||||||
suspend fun getCompatThemedIcon(componentName: String): IconEntity?
|
|
||||||
|
|
||||||
@Query("SELECT * FROM Icons WHERE componentName = :componentName AND (type = 'app' OR type = 'calendar')")
|
@Query("SELECT * FROM Icons WHERE componentName = :componentName AND (type = 'app' OR type = 'calendar')")
|
||||||
suspend fun getIconsFromAllPacks(componentName: String): List<IconEntity>
|
suspend fun getIconsFromAllPacks(componentName: String): List<IconEntity>
|
||||||
|
|
||||||
@ -80,9 +77,6 @@ interface IconDao {
|
|||||||
@Query("DELETE FROM IconPack WHERE packageName NOT IN (:packs)")
|
@Query("DELETE FROM IconPack WHERE packageName NOT IN (:packs)")
|
||||||
fun deleteAllPacksExcept(packs: List<String>)
|
fun deleteAllPacksExcept(packs: List<String>)
|
||||||
|
|
||||||
@Query("DELETE FROM Icons WHERE type = 'themed-compat'")
|
|
||||||
fun deleteAllCompatThemedIcons()
|
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
fun uninstallIconPacksExcept(packs: List<String>) {
|
fun uninstallIconPacksExcept(packs: List<String>) {
|
||||||
deleteAllIconsPackIconsExcept(packs)
|
deleteAllIconsPackIconsExcept(packs)
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
package de.mm20.launcher2.ktx
|
package de.mm20.launcher2.ktx
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import org.xmlpull.v1.XmlPullParser
|
import org.xmlpull.v1.XmlPullParser
|
||||||
|
|
||||||
fun XmlPullParser.skipToNextTag(): Boolean {
|
fun XmlPullParser.skipToNextTag(): Boolean {
|
||||||
while (next() != XmlPullParser.END_DOCUMENT) {
|
while (next() != XmlPullParser.END_DOCUMENT) {
|
||||||
if (eventType == XmlPullParser.START_TAG) return true
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -3,6 +3,8 @@ package de.mm20.launcher2.icons
|
|||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.graphics.drawable.AdaptiveIconDrawable
|
import android.graphics.drawable.AdaptiveIconDrawable
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import de.mm20.launcher2.icons.compat.AdaptiveIconDrawableCompat
|
||||||
|
import de.mm20.launcher2.icons.compat.toLauncherIcon
|
||||||
import de.mm20.launcher2.icons.transformations.LauncherIconTransformation
|
import de.mm20.launcher2.icons.transformations.LauncherIconTransformation
|
||||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -25,66 +27,30 @@ internal class DynamicCalendarIcon(
|
|||||||
val day = Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()).dayOfMonth
|
val day = Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()).dayOfMonth
|
||||||
val resId = resourceIds[day - 1]
|
val resId = resourceIds[day - 1]
|
||||||
|
|
||||||
val drawable = try {
|
val adaptiveIcon = AdaptiveIconDrawableCompat.from(resources, resId)
|
||||||
ResourcesCompat.getDrawable(resources, resId, null)
|
|
||||||
} catch (e: Resources.NotFoundException) {
|
|
||||||
null
|
|
||||||
} ?: return@withContext StaticLauncherIcon(
|
|
||||||
foregroundLayer = TextLayer(day.toString()),
|
|
||||||
backgroundLayer = ColorLayer()
|
|
||||||
)
|
|
||||||
|
|
||||||
var icon = when {
|
var icon = if (adaptiveIcon != null) {
|
||||||
isThemed && drawable is AdaptiveIconDrawable -> {
|
adaptiveIcon.toLauncherIcon(themed = isThemed)
|
||||||
if (isAtLeastApiLevel(33) && drawable.monochrome != null) {
|
} else {
|
||||||
return@withContext StaticLauncherIcon(
|
try {
|
||||||
|
val drawable = ResourcesCompat.getDrawable(resources, resId, null)
|
||||||
|
|
||||||
|
when {
|
||||||
|
drawable is AdaptiveIconDrawable -> AdaptiveIconDrawableCompat.from(drawable).toLauncherIcon(themed = isThemed)
|
||||||
|
drawable != null -> StaticLauncherIcon(
|
||||||
foregroundLayer = TintedIconLayer(
|
foregroundLayer = TintedIconLayer(
|
||||||
icon = drawable.monochrome!!,
|
icon = drawable,
|
||||||
scale = 1f,
|
scale = 1f,
|
||||||
),
|
),
|
||||||
backgroundLayer = ColorLayer()
|
backgroundLayer = TransparentLayer,
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return@withContext StaticLauncherIcon(
|
|
||||||
foregroundLayer = TintedIconLayer(
|
|
||||||
icon = drawable.foreground!!,
|
|
||||||
scale = 1.5f,
|
|
||||||
),
|
|
||||||
backgroundLayer = ColorLayer()
|
|
||||||
)
|
)
|
||||||
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
} catch (e: Resources.NotFoundException) {
|
||||||
isThemed -> {
|
null
|
||||||
StaticLauncherIcon(
|
} ?: return@withContext StaticLauncherIcon(
|
||||||
foregroundLayer = TintedIconLayer(
|
foregroundLayer = TextLayer(day.toString()),
|
||||||
icon = drawable,
|
backgroundLayer = ColorLayer()
|
||||||
scale = 0.5f,
|
|
||||||
),
|
|
||||||
backgroundLayer = ColorLayer()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
drawable is AdaptiveIconDrawable -> {
|
|
||||||
return@withContext StaticLauncherIcon(
|
|
||||||
foregroundLayer = drawable.foreground?.let {
|
|
||||||
StaticIconLayer(
|
|
||||||
icon = it,
|
|
||||||
scale = 1.5f,
|
|
||||||
)
|
|
||||||
} ?: TransparentLayer,
|
|
||||||
backgroundLayer = drawable.background?.let {
|
|
||||||
StaticIconLayer(
|
|
||||||
icon = it,
|
|
||||||
scale = 1.5f,
|
|
||||||
)
|
|
||||||
} ?: TransparentLayer,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else -> StaticLauncherIcon(
|
|
||||||
foregroundLayer = StaticIconLayer(
|
|
||||||
icon = drawable,
|
|
||||||
scale = 1f,
|
|
||||||
),
|
|
||||||
backgroundLayer = TransparentLayer
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,6 @@ import androidx.core.content.res.ResourcesCompat
|
|||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||||
import de.mm20.launcher2.database.AppDatabase
|
import de.mm20.launcher2.database.AppDatabase
|
||||||
import de.mm20.launcher2.icons.loaders.CompatThemedIconInstaller
|
|
||||||
import de.mm20.launcher2.icons.loaders.GrayscaleMapInstaller
|
import de.mm20.launcher2.icons.loaders.GrayscaleMapInstaller
|
||||||
import de.mm20.launcher2.icons.loaders.IconPackInstaller
|
import de.mm20.launcher2.icons.loaders.IconPackInstaller
|
||||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||||
@ -55,7 +54,6 @@ class IconPackManager(
|
|||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
IconPackInstaller(context, appDatabase).installIcons()
|
IconPackInstaller(context, appDatabase).installIcons()
|
||||||
GrayscaleMapInstaller(context, appDatabase).installIcons()
|
GrayscaleMapInstaller(context, appDatabase).installIcons()
|
||||||
CompatThemedIconInstaller(context, appDatabase).installIcons()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,37 +234,6 @@ class IconPackManager(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getCompatThemedIcon(componentName: ComponentName): LauncherIcon? {
|
|
||||||
val iconDao = appDatabase.iconDao()
|
|
||||||
val icon = iconDao.getCompatThemedIcon(componentName.flattenToString())
|
|
||||||
?: return null
|
|
||||||
|
|
||||||
val drawableName = icon.drawable ?: return null
|
|
||||||
|
|
||||||
val res = try {
|
|
||||||
context.packageManager.getResourcesForApplication(componentName.packageName)
|
|
||||||
} catch (e: Resources.NotFoundException) {
|
|
||||||
return null
|
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val resourceId = res.getIdentifier(drawableName, null, null)
|
|
||||||
val drawable = try {
|
|
||||||
ResourcesCompat.getDrawable(res, resourceId, null)
|
|
||||||
} catch (e: Resources.NotFoundException) {
|
|
||||||
return null
|
|
||||||
} ?: return null
|
|
||||||
|
|
||||||
return StaticLauncherIcon(
|
|
||||||
foregroundLayer = TintedIconLayer(
|
|
||||||
icon = drawable,
|
|
||||||
scale = 1f,
|
|
||||||
),
|
|
||||||
backgroundLayer = ColorLayer()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getAllIconPackIcons(componentName: ComponentName): List<IconPackIcon> {
|
suspend fun getAllIconPackIcons(componentName: ComponentName): List<IconPackIcon> {
|
||||||
val iconDao = appDatabase.iconDao()
|
val iconDao = appDatabase.iconDao()
|
||||||
return iconDao.getIconsFromAllPacks(componentName.flattenToString())
|
return iconDao.getIconsFromAllPacks(componentName.flattenToString())
|
||||||
@ -301,7 +268,7 @@ class IconPackManager(
|
|||||||
iconPack: String,
|
iconPack: String,
|
||||||
baseIconName: String,
|
baseIconName: String,
|
||||||
themed: Boolean,
|
themed: Boolean,
|
||||||
): DynamicCalendarIcon? {
|
): LauncherIcon? {
|
||||||
val resources = try {
|
val resources = try {
|
||||||
context.packageManager.getResourcesForApplication(iconPack)
|
context.packageManager.getResourcesForApplication(iconPack)
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
@ -313,10 +280,16 @@ class IconPackManager(
|
|||||||
if (id == 0) return null
|
if (id == 0) return null
|
||||||
id
|
id
|
||||||
}.toIntArray()
|
}.toIntArray()
|
||||||
|
|
||||||
|
if (themed) {
|
||||||
|
return ThemedDynamicCalendarIcon(
|
||||||
|
resources = resources,
|
||||||
|
resourceIds = drawableIds,
|
||||||
|
)
|
||||||
|
}
|
||||||
return DynamicCalendarIcon(
|
return DynamicCalendarIcon(
|
||||||
resources = resources,
|
resources = resources,
|
||||||
resourceIds = drawableIds,
|
resourceIds = drawableIds,
|
||||||
isThemed = themed,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,12 +417,11 @@ class IconPackManager(
|
|||||||
val array = resources.obtainTypedArrayOrNull(resId) ?: return null
|
val array = resources.obtainTypedArrayOrNull(resId) ?: return null
|
||||||
if (array.length() != 31) return null
|
if (array.length() != 31) return null
|
||||||
|
|
||||||
return DynamicCalendarIcon(
|
return ThemedDynamicCalendarIcon(
|
||||||
resources = resources,
|
resources = resources,
|
||||||
resourceIds = IntArray(31) {
|
resourceIds = IntArray(31) {
|
||||||
array.getResourceId(it, 0).takeIf { it != 0 } ?: return null
|
array.getResourceId(it, 0).takeIf { it != 0 } ?: return null
|
||||||
},
|
},
|
||||||
isThemed = true
|
|
||||||
)
|
)
|
||||||
} catch (e: Resources.NotFoundException) {
|
} catch (e: Resources.NotFoundException) {
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import de.mm20.launcher2.data.customattrs.DefaultPlaceholderIcon
|
|||||||
import de.mm20.launcher2.data.customattrs.ForceThemedIcon
|
import de.mm20.launcher2.data.customattrs.ForceThemedIcon
|
||||||
import de.mm20.launcher2.data.customattrs.UnmodifiedSystemDefaultIcon
|
import de.mm20.launcher2.data.customattrs.UnmodifiedSystemDefaultIcon
|
||||||
import de.mm20.launcher2.icons.providers.CalendarIconProvider
|
import de.mm20.launcher2.icons.providers.CalendarIconProvider
|
||||||
import de.mm20.launcher2.icons.providers.CompatThemedIconProvider
|
import de.mm20.launcher2.icons.providers.CompatIconProvider
|
||||||
import de.mm20.launcher2.icons.providers.CustomIconPackIconProvider
|
import de.mm20.launcher2.icons.providers.CustomIconPackIconProvider
|
||||||
import de.mm20.launcher2.icons.providers.CustomThemedIconProvider
|
import de.mm20.launcher2.icons.providers.CustomThemedIconProvider
|
||||||
import de.mm20.launcher2.icons.providers.GoogleClockIconProvider
|
import de.mm20.launcher2.icons.providers.GoogleClockIconProvider
|
||||||
@ -106,14 +106,14 @@ class IconRepository(
|
|||||||
Log.w("MM20", "Icon pack ${settings.iconPack} not found")
|
Log.w("MM20", "Icon pack ${settings.iconPack} not found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
providers.add(GoogleClockIconProvider(context))
|
|
||||||
providers.add(CalendarIconProvider(context))
|
|
||||||
if (settings.themedIcons) {
|
if (settings.themedIcons) {
|
||||||
if (!isAtLeastApiLevel(33)) {
|
|
||||||
providers.add(CompatThemedIconProvider(iconPackManager))
|
|
||||||
}
|
|
||||||
providers.add(ThemedIconProvider(iconPackManager))
|
providers.add(ThemedIconProvider(iconPackManager))
|
||||||
}
|
}
|
||||||
|
providers.add(GoogleClockIconProvider(context))
|
||||||
|
providers.add(CalendarIconProvider(context, settings.themedIcons))
|
||||||
|
if (!isAtLeastApiLevel(33)) {
|
||||||
|
providers.add(CompatIconProvider(context, settings.themedIcons))
|
||||||
|
}
|
||||||
providers.add(SystemIconProvider(context, settings.themedIcons))
|
providers.add(SystemIconProvider(context, settings.themedIcons))
|
||||||
providers.add(placeholderProvider)
|
providers.add(placeholderProvider)
|
||||||
cache.evictAll()
|
cache.evictAll()
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
package de.mm20.launcher2.icons
|
package de.mm20.launcher2.icons
|
||||||
|
|
||||||
import de.mm20.launcher2.icons.compat.ThemedIconsCompatManager
|
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
package de.mm20.launcher2.icons
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.graphics.drawable.AdaptiveIconDrawable
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import de.mm20.launcher2.icons.transformations.LauncherIconTransformation
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.ZoneId
|
||||||
|
|
||||||
|
internal class ThemedDynamicCalendarIcon(
|
||||||
|
val resources: Resources,
|
||||||
|
val resourceIds: IntArray,
|
||||||
|
private var transformations: List<LauncherIconTransformation> = emptyList(),
|
||||||
|
) : DynamicLauncherIcon, TransformableDynamicLauncherIcon {
|
||||||
|
override suspend fun getIcon(time: Long): StaticLauncherIcon = withContext(Dispatchers.IO) {
|
||||||
|
val day = Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()).dayOfMonth
|
||||||
|
val resId = resourceIds[day - 1]
|
||||||
|
|
||||||
|
val drawable = try {
|
||||||
|
ResourcesCompat.getDrawable(resources, resId, null)
|
||||||
|
} catch (e: Resources.NotFoundException) {
|
||||||
|
null
|
||||||
|
} ?: return@withContext StaticLauncherIcon(
|
||||||
|
foregroundLayer = TextLayer(day.toString()),
|
||||||
|
backgroundLayer = ColorLayer()
|
||||||
|
)
|
||||||
|
|
||||||
|
var icon = when (drawable) {
|
||||||
|
is AdaptiveIconDrawable -> StaticLauncherIcon(
|
||||||
|
foregroundLayer = TintedIconLayer(
|
||||||
|
icon = drawable.foreground,
|
||||||
|
scale = 1.5f,
|
||||||
|
),
|
||||||
|
backgroundLayer = ColorLayer()
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> StaticLauncherIcon(
|
||||||
|
foregroundLayer = TintedIconLayer(
|
||||||
|
icon = drawable,
|
||||||
|
scale = 0.5f,
|
||||||
|
),
|
||||||
|
backgroundLayer = ColorLayer()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (transformation in transformations) {
|
||||||
|
icon = transformation.transform(icon)
|
||||||
|
}
|
||||||
|
return@withContext icon
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setTransformations(transformations: List<LauncherIconTransformation>) {
|
||||||
|
this.transformations = transformations
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,150 @@
|
|||||||
|
package de.mm20.launcher2.icons.compat
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.content.res.XmlResourceParser
|
||||||
|
import android.graphics.drawable.AdaptiveIconDrawable
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.util.Log
|
||||||
|
import android.util.Xml
|
||||||
|
import android.view.InflateException
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||||
|
import de.mm20.launcher2.icons.ColorLayer
|
||||||
|
import de.mm20.launcher2.icons.StaticIconLayer
|
||||||
|
import de.mm20.launcher2.icons.StaticLauncherIcon
|
||||||
|
import de.mm20.launcher2.icons.TintedIconLayer
|
||||||
|
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||||
|
import de.mm20.launcher2.ktx.skipToNextTag
|
||||||
|
import org.xmlpull.v1.XmlPullParserException
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
|
||||||
|
data class AdaptiveIconDrawableCompat(
|
||||||
|
val background: Drawable,
|
||||||
|
val foreground: Drawable,
|
||||||
|
val monochrome: Drawable?,
|
||||||
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun from(adaptiveIconDrawable: AdaptiveIconDrawable): AdaptiveIconDrawableCompat {
|
||||||
|
return AdaptiveIconDrawableCompat(
|
||||||
|
background = adaptiveIconDrawable.background,
|
||||||
|
foreground = adaptiveIconDrawable.foreground,
|
||||||
|
monochrome = if (isAtLeastApiLevel(33)) adaptiveIconDrawable.monochrome else null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun from(resources: Resources, resId: Int): AdaptiveIconDrawableCompat? {
|
||||||
|
var xmlParser: XmlResourceParser? = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
xmlParser = resources.getXml(resId)
|
||||||
|
val attrs = Xml.asAttributeSet(xmlParser)
|
||||||
|
if (!xmlParser.skipToNextTag()) return null
|
||||||
|
|
||||||
|
Log.d("MM20", "xmlParser.name: ${xmlParser.name}")
|
||||||
|
|
||||||
|
if (xmlParser.name != "adaptive-icon") {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
var background: Drawable? = null
|
||||||
|
var foreground: Drawable? = null
|
||||||
|
var monochrome: Drawable? = null
|
||||||
|
|
||||||
|
while (xmlParser.skipToNextTag()) {
|
||||||
|
when (xmlParser.name) {
|
||||||
|
"monochrome" -> {
|
||||||
|
monochrome = parseLayer(resources, xmlParser, attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
"background" -> {
|
||||||
|
background = parseLayer(resources, xmlParser, attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
"foreground" -> {
|
||||||
|
foreground = parseLayer(resources, xmlParser, attrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d(
|
||||||
|
"MM20",
|
||||||
|
"background: $background, foreground: $foreground, monochrome: $monochrome"
|
||||||
|
)
|
||||||
|
if (foreground != null && background != null) {
|
||||||
|
return AdaptiveIconDrawableCompat(
|
||||||
|
background = background,
|
||||||
|
foreground = foreground,
|
||||||
|
monochrome = monochrome,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: Resources.NotFoundException) {
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
return null
|
||||||
|
} catch (e: IOException) {
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
return null
|
||||||
|
} catch (e: XmlPullParserException) {
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
return null
|
||||||
|
} finally {
|
||||||
|
xmlParser?.close()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(
|
||||||
|
XmlPullParserException::class,
|
||||||
|
IOException::class,
|
||||||
|
Resources.NotFoundException::class
|
||||||
|
)
|
||||||
|
private fun parseLayer(
|
||||||
|
resources: Resources,
|
||||||
|
parser: XmlResourceParser,
|
||||||
|
attrs: AttributeSet
|
||||||
|
): Drawable? {
|
||||||
|
val drawableId = parser.getAttributeResourceValue(
|
||||||
|
"http://schemas.android.com/apk/res/android",
|
||||||
|
"drawable",
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
if (drawableId != 0) {
|
||||||
|
return ResourcesCompat.getDrawable(resources, drawableId, null)
|
||||||
|
}
|
||||||
|
if (!parser.skipToNextTag()) return null
|
||||||
|
return try {
|
||||||
|
Drawable.createFromXmlInner(resources, parser, attrs)
|
||||||
|
} catch (e: InflateException) {
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun AdaptiveIconDrawableCompat.toLauncherIcon(
|
||||||
|
themed: Boolean = false,
|
||||||
|
): StaticLauncherIcon {
|
||||||
|
if (themed && this.monochrome != null) {
|
||||||
|
return StaticLauncherIcon(
|
||||||
|
foregroundLayer = TintedIconLayer(
|
||||||
|
scale = 1f,
|
||||||
|
icon = this.monochrome,
|
||||||
|
),
|
||||||
|
backgroundLayer = ColorLayer()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return StaticLauncherIcon(
|
||||||
|
foregroundLayer = StaticIconLayer(
|
||||||
|
scale = 1.5f,
|
||||||
|
icon = this.foreground,
|
||||||
|
),
|
||||||
|
backgroundLayer = StaticIconLayer(
|
||||||
|
scale = 1.5f,
|
||||||
|
icon = this.background,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,26 +0,0 @@
|
|||||||
package de.mm20.launcher2.icons.compat
|
|
||||||
|
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.ActivityInfo
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.content.res.Resources
|
|
||||||
import android.content.res.XmlResourceParser
|
|
||||||
import android.util.Log
|
|
||||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
|
||||||
import de.mm20.launcher2.icons.IconPackIcon
|
|
||||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
|
||||||
import de.mm20.launcher2.ktx.skipToNextTag
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.xmlpull.v1.XmlPullParserException
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
|
|
||||||
internal class ThemedIconsCompatManager(
|
|
||||||
private val context: Context,
|
|
||||||
) {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,109 +0,0 @@
|
|||||||
package de.mm20.launcher2.icons.loaders
|
|
||||||
|
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.ActivityInfo
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.content.res.Resources
|
|
||||||
import android.content.res.XmlResourceParser
|
|
||||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
|
||||||
import de.mm20.launcher2.database.AppDatabase
|
|
||||||
import de.mm20.launcher2.icons.IconPackIcon
|
|
||||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
|
||||||
import de.mm20.launcher2.ktx.skipToNextTag
|
|
||||||
import org.xmlpull.v1.XmlPullParserException
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
class CompatThemedIconInstaller(
|
|
||||||
private val context: Context,
|
|
||||||
private val database: AppDatabase,
|
|
||||||
) {
|
|
||||||
fun installIcons() {
|
|
||||||
if (isAtLeastApiLevel(33)) return
|
|
||||||
val launcherActivities = getLauncherActivities()
|
|
||||||
|
|
||||||
val dao = database.iconDao()
|
|
||||||
|
|
||||||
val icons = mutableListOf<IconPackIcon>()
|
|
||||||
database.runInTransaction {
|
|
||||||
dao.deleteAllCompatThemedIcons()
|
|
||||||
for (activity in launcherActivities) {
|
|
||||||
val componentName = ComponentName(activity.applicationInfo.packageName, activity.name)
|
|
||||||
val monochromeIcon = getMonochromeIconResource(activity)
|
|
||||||
|
|
||||||
if (monochromeIcon != null) {
|
|
||||||
val icon = IconPackIcon(
|
|
||||||
type = "themed-compat",
|
|
||||||
componentName = componentName,
|
|
||||||
name = null,
|
|
||||||
drawable = monochromeIcon,
|
|
||||||
iconPack = componentName.packageName
|
|
||||||
)
|
|
||||||
icons.add(icon)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (icons.size > 100) {
|
|
||||||
dao.insertAll(icons.map { it.toDatabaseEntity() })
|
|
||||||
icons.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (icons.isNotEmpty()) {
|
|
||||||
dao.insertAll(icons.map { it.toDatabaseEntity() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getMonochromeIconResource(activityInfo: ActivityInfo): String? {
|
|
||||||
val iconResource = activityInfo.iconResource
|
|
||||||
val resources = try {
|
|
||||||
context.packageManager.getResourcesForApplication(activityInfo.packageName)
|
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
var xmlParser: XmlResourceParser? = null
|
|
||||||
try {
|
|
||||||
xmlParser = resources.getXml(iconResource)
|
|
||||||
if (!xmlParser.skipToNextTag()) return null
|
|
||||||
|
|
||||||
if (xmlParser.name != "adaptive-icon") {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
while (xmlParser.skipToNextTag()) {
|
|
||||||
if (xmlParser.name == "monochrome") {
|
|
||||||
val drawable = xmlParser.getAttributeResourceValue(
|
|
||||||
"http://schemas.android.com/apk/res/android",
|
|
||||||
"drawable",
|
|
||||||
0
|
|
||||||
)
|
|
||||||
if (drawable == 0) return null
|
|
||||||
return resources.getResourceName(drawable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Resources.NotFoundException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
return null
|
|
||||||
} catch (e: IOException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
return null
|
|
||||||
} catch (e: XmlPullParserException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
return null
|
|
||||||
} finally {
|
|
||||||
xmlParser?.close()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLauncherActivities(): List<ActivityInfo> {
|
|
||||||
val resolveInfos = context.packageManager.queryIntentActivities(
|
|
||||||
Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER), 0
|
|
||||||
)
|
|
||||||
|
|
||||||
return resolveInfos.mapNotNull { it.activityInfo }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,7 +9,7 @@ import de.mm20.launcher2.ktx.obtainTypedArrayOrNull
|
|||||||
import de.mm20.launcher2.search.SavableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.data.LauncherApp
|
import de.mm20.launcher2.search.data.LauncherApp
|
||||||
|
|
||||||
class CalendarIconProvider(val context: Context): IconProvider {
|
class CalendarIconProvider(val context: Context, val themed: Boolean): IconProvider {
|
||||||
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
|
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
|
||||||
if(searchable !is LauncherApp) return null
|
if(searchable !is LauncherApp) return null
|
||||||
val component = ComponentName(searchable.`package`, searchable.activity)
|
val component = ComponentName(searchable.`package`, searchable.activity)
|
||||||
@ -40,7 +40,8 @@ class CalendarIconProvider(val context: Context): IconProvider {
|
|||||||
typedArray.recycle()
|
typedArray.recycle()
|
||||||
return DynamicCalendarIcon(
|
return DynamicCalendarIcon(
|
||||||
resources = resources,
|
resources = resources,
|
||||||
resourceIds = drawableIds
|
resourceIds = drawableIds,
|
||||||
|
isThemed = themed
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
package de.mm20.launcher2.icons.providers
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
|
import de.mm20.launcher2.icons.compat.AdaptiveIconDrawableCompat
|
||||||
|
import de.mm20.launcher2.icons.compat.toLauncherIcon
|
||||||
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
|
import de.mm20.launcher2.search.data.LauncherApp
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class CompatIconProvider(
|
||||||
|
private val context: Context,
|
||||||
|
private val themed: Boolean = false,
|
||||||
|
) : IconProvider {
|
||||||
|
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
|
||||||
|
if (searchable !is LauncherApp) return null
|
||||||
|
val component = ComponentName(searchable.`package`, searchable.activity)
|
||||||
|
val activityInfo = try {
|
||||||
|
context.packageManager.getActivityInfo(component, 0)
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val iconRes = activityInfo.iconResource
|
||||||
|
val resources = try {
|
||||||
|
context.packageManager.getResourcesForApplication(activityInfo.packageName)
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val icon = withContext(Dispatchers.IO) {
|
||||||
|
AdaptiveIconDrawableCompat.from(resources, iconRes)
|
||||||
|
} ?: return null
|
||||||
|
|
||||||
|
return icon.toLauncherIcon(themed = themed)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,17 +0,0 @@
|
|||||||
package de.mm20.launcher2.icons.providers
|
|
||||||
|
|
||||||
import android.content.ComponentName
|
|
||||||
import de.mm20.launcher2.icons.IconPackManager
|
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
|
||||||
import de.mm20.launcher2.search.SavableSearchable
|
|
||||||
import de.mm20.launcher2.search.data.LauncherApp
|
|
||||||
|
|
||||||
class CompatThemedIconProvider(
|
|
||||||
private val iconPackManager: IconPackManager,
|
|
||||||
): IconProvider {
|
|
||||||
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
|
|
||||||
if (searchable !is LauncherApp) return null
|
|
||||||
val component = ComponentName(searchable.`package`, searchable.activity)
|
|
||||||
return iconPackManager.getCompatThemedIcon(component)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user