diff --git a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt index ed9622e4..a7eb7522 100644 --- a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt @@ -95,6 +95,11 @@ class PreferencesAppearanceFragment : PreferenceFragmentCompat() { true } + findPreference("themed_icons")?.setOnPreferenceChangeListener { _, _ -> + iconRepository.recreate() + true + } + val shapePreference = findPreference("icon_shape")!! shapePreference.summary = getShapeName() shapePreference.setOnPreferenceClickListener { diff --git a/app/src/main/res/xml/preferences_appearance.xml b/app/src/main/res/xml/preferences_appearance.xml index 40392ee6..6bdbe910 100644 --- a/app/src/main/res/xml/preferences_appearance.xml +++ b/app/src/main/res/xml/preferences_appearance.xml @@ -22,6 +22,12 @@ app:summary="@string/preference_cards_summary" app:title="@string/preference_cards" /> + +%1$d laufende Termine aus vergangenen Tagen Wind: + Eingefärbte Symbole + Symbole an das Farbschema der App anpassen \ No newline at end of file diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 564dd65b..03911f30 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -203,6 +203,8 @@ Not supported by this provider EE, MMMM d %1$s • %2$s + Themed icons + Color icons with the application\'s color scheme Icon pack No icon packs installed Dynamic background diff --git a/icons/src/main/java/de/mm20/launcher2/icons/CalendarDynamicLauncherIcon.kt b/icons/src/main/java/de/mm20/launcher2/icons/CalendarDynamicLauncherIcon.kt index 5583e991..0565747c 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/CalendarDynamicLauncherIcon.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/CalendarDynamicLauncherIcon.kt @@ -10,24 +10,20 @@ import java.util.* import java.util.concurrent.Executors class CalendarDynamicLauncherIcon( - context: Context, - foreground: Drawable, - background: Drawable?, - foregroundScale: Float, - backgroundScale: Float, - badgeNumber: Float = 0f, - val packageName: String, - val drawableIds: IntArray, - autoGenerateBackgroundMode: Int + foreground: Drawable, + background: Drawable?, + foregroundScale: Float, + backgroundScale: Float, + val packageName: String, + val drawableIds: IntArray, + autoGenerateBackgroundMode: Int ) : DynamicLauncherIcon( - foreground, - background, - foregroundScale, - backgroundScale, + foreground, + background, + foregroundScale, + backgroundScale, /** Not needed, we already have a background **/ - autoGenerateBackgroundMode, - badgeNumber, - null + autoGenerateBackgroundMode ) { var currentDay = 0 diff --git a/icons/src/main/java/de/mm20/launcher2/icons/ClockDynamicLauncherIcon.kt b/icons/src/main/java/de/mm20/launcher2/icons/ClockDynamicLauncherIcon.kt index 50fead08..b65293a6 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/ClockDynamicLauncherIcon.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/ClockDynamicLauncherIcon.kt @@ -7,37 +7,33 @@ import android.graphics.drawable.LayerDrawable import android.graphics.drawable.RotateDrawable import android.os.Build import androidx.annotation.RequiresApi -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject import java.util.* import kotlin.math.roundToInt @RequiresApi(Build.VERSION_CODES.O) class ClockDynamicLauncherIcon( - context: Context, - foreground: LayerDrawable, - background: Drawable?, - foregroundScale: Float, - backgroundScale: Float, - badgeNumber: Float, - val hourLayer: Int, - val minuteLayer: Int, - val secondLayer: Int + foreground: LayerDrawable, + background: Drawable?, + foregroundScale: Float, + backgroundScale: Float, + val hourLayer: Int, + val minuteLayer: Int, + val secondLayer: Int ) : DynamicLauncherIcon( - foreground, - background, - foregroundScale, - backgroundScale, + foreground, + background, + foregroundScale, + backgroundScale, /** Not needed, we already have a background **/ - LauncherIcon.BACKGROUND_WHITE, - badgeNumber, - null + LauncherIcon.BACKGROUND_WHITE ) { init { foreground.also { - it.setDrawable(secondLayer, ColorDrawable(0)) + try { + it.setDrawable(secondLayer, ColorDrawable(0)) + } catch (e: IndexOutOfBoundsException) {} (it.getDrawable(hourLayer) as? RotateDrawable)?.fromDegrees = 0f (it.getDrawable(hourLayer) as? RotateDrawable)?.toDegrees = 360f (it.getDrawable(minuteLayer) as? RotateDrawable)?.fromDegrees = 0f diff --git a/icons/src/main/java/de/mm20/launcher2/icons/DynamicLauncherIcon.kt b/icons/src/main/java/de/mm20/launcher2/icons/DynamicLauncherIcon.kt index 4e3504e0..b4959932 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/DynamicLauncherIcon.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/DynamicLauncherIcon.kt @@ -2,17 +2,14 @@ package de.mm20.launcher2.icons import android.content.Context import android.graphics.drawable.Drawable -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject abstract class DynamicLauncherIcon( - foreground: Drawable, - background: Drawable?, - foregroundScale: Float, - backgroundScale: Float, - autoGenerateBackgroundMode: Int, - badgeNumber: Float, - badgeDrawable: Drawable?) + foreground: Drawable, + background: Drawable?, + foregroundScale: Float, + backgroundScale: Float, + autoGenerateBackgroundMode: Int +) : LauncherIcon( foreground, background, diff --git a/icons/src/main/java/de/mm20/launcher2/icons/IconPackManager.kt b/icons/src/main/java/de/mm20/launcher2/icons/IconPackManager.kt index 9e26a1b9..bff25e08 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/IconPackManager.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/IconPackManager.kt @@ -3,38 +3,30 @@ package de.mm20.launcher2.icons import android.content.ComponentName import android.content.Context import android.content.Intent -import android.content.pm.LauncherActivityInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo -import android.content.res.Resources import android.content.res.XmlResourceParser import android.graphics.* -import android.graphics.drawable.AdaptiveIconDrawable -import android.graphics.drawable.BitmapDrawable -import android.graphics.drawable.ColorDrawable -import android.graphics.drawable.LayerDrawable -import android.os.Build import android.util.Log -import androidx.core.content.res.ResourcesCompat -import androidx.core.graphics.drawable.toBitmap import de.mm20.launcher2.crashreporter.CrashReporter import de.mm20.launcher2.database.AppDatabase -import de.mm20.launcher2.ktx.obtainTypedArrayOrNull -import de.mm20.launcher2.ktx.randomElementOrNull -import de.mm20.launcher2.preferences.IconShape -import de.mm20.launcher2.preferences.LauncherPreferences import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParserException import org.xmlpull.v1.XmlPullParserFactory import java.io.InputStreamReader -import kotlin.math.roundToInt + +private val SUPPORTED_GRAYSCALE_MAP_PROVIDERS = arrayOf( + "com.google.android.apps.nexuslauncher", // Pixel Launcher + "app.lawnchair.lawnicons", // Lawnicons + "app.lawnchair", // Lawnchair + "de.mm20.launcher2.themedicons", +) class IconPackManager( - val context: Context, - val dynamicIconController: DynamicIconController + val context: Context ) { var selectedIconPack: String get() { @@ -54,52 +46,6 @@ class IconPackManager( selectedIconPack = iconPack } - private fun getCalendarIcon( - context: Context, - activity: LauncherActivityInfo - ): CalendarDynamicLauncherIcon? { - val component = ComponentName(activity.applicationInfo.packageName, activity.name) - val pm = context.packageManager - val ai = try { - pm.getActivityInfo(component, PackageManager.GET_META_DATA) - } catch (e: PackageManager.NameNotFoundException) { - return null - } - val resources = pm.getResourcesForActivity(component) - var arrayId = ai.metaData?.getInt("com.teslacoilsw.launcher.calendarIconArray") ?: 0 - if (arrayId == 0) arrayId = ai.metaData?.getInt("com.google.android.calendar.dynamic_icons") - ?: return null - if (arrayId == 0) return null - val typedArray = resources.obtainTypedArrayOrNull(arrayId) ?: return null - if (typedArray.length() != 31) { - typedArray.recycle() - return null - } - val drawableIds = IntArray(31) - for (i in 0 until 31) { - drawableIds[i] = typedArray.getResourceId(i, 0) - } - typedArray.recycle() - return CalendarDynamicLauncherIcon( - context = context, - background = ColorDrawable(0), - foreground = ColorDrawable(0), - foregroundScale = 1.5f, - backgroundScale = 1.5f, - packageName = component.packageName, - drawableIds = drawableIds, - autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt() - ) - } - - private fun getScale(): Float { - return when (LauncherPreferences.instance.iconShape) { - IconShape.CIRCLE, IconShape.PLATFORM_DEFAULT -> 0.7f - else -> 0.8f - - } - } - suspend fun getInstalledIconPacks(): List { return withContext(Dispatchers.IO) { AppDatabase.getInstance(context).iconDao().getInstalledIconPacks().map { @@ -108,11 +54,6 @@ class IconPackManager( } } - companion object { - const val GOOGLE_DESK_CLOCK_PACKAGE_NAME = "com.google.android.deskclock" - const val GOOGLE_CALENDAR_PACKAGE_NAME = "com.google.android.calendar" - } - @Synchronized suspend fun updateIconPacks() { withContext(Dispatchers.IO) { @@ -143,6 +84,9 @@ class UpdateIconPacksWorker(val context: Context) { continue } } + + val supportedGrayscaleMapPackages = SUPPORTED_GRAYSCALE_MAP_PROVIDERS + supportedGrayscaleMapPackages.forEach { installGrayscaleIconMap(it) } } private fun loadInstalledPacks(context: Context): List { @@ -296,6 +240,46 @@ class UpdateIconPacksWorker(val context: Context) { CrashReporter.logException(e) } } + + private fun installGrayscaleIconMap(packageName: String) { + val iconDao = AppDatabase.getInstance(context).iconDao() + try { + val resources = context.packageManager.getResourcesForApplication(packageName) + val resId = resources.getIdentifier("grayscale_icon_map", "xml", packageName) + if (resId == 0) { + iconDao.deleteIcons(packageName) + return + } + val icons = mutableListOf() + val parser = resources.getXml(resId) + loop@ while (parser.next() != XmlPullParser.END_DOCUMENT) { + if (parser.eventType != XmlPullParser.START_TAG) continue + when (parser.name) { + "icon" -> { + val drawable = + parser.getAttributeResourceValue(null, "drawable", 0).toString() + val pkg = parser.getAttributeValue(null, "package") + for (i in 0 until parser.attributeCount) { + Log.d("MM20", "${parser.getAttributeName(0)}") + } + val componentName = ComponentName(pkg, pkg) + val icon = Icon( + drawable = drawable, + componentName = componentName, + iconPack = packageName, + type = "greyscale_icon" + ) + icons.add(icon) + Log.d("MM20", "Installed icon ${icon.toString()}") + } + } + } + iconDao.installGrayscaleIconMap(packageName, icons.map { it.toDatabaseEntity() }) + } catch (e: PackageManager.NameNotFoundException) { + iconDao.deleteIcons(packageName) + return + } + } } private const val PREFERENCE_NAME = "icon_pack" diff --git a/icons/src/main/java/de/mm20/launcher2/icons/IconRepository.kt b/icons/src/main/java/de/mm20/launcher2/icons/IconRepository.kt index cc9acf86..07a01874 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/IconRepository.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/IconRepository.kt @@ -86,13 +86,20 @@ class IconRepository( } fun recreate() { - placeholderProvider = PlaceholderIconProvider(context) + placeholderProvider = if (LauncherPreferences.instance.themedIcons) { + ThemedPlaceholderIconProvider(context) + } else { + PlaceholderIconProvider(context) + } val providers = mutableListOf() + if (LauncherPreferences.instance.themedIcons) { + providers.add(ThemedIconProvider(context)) + } + if (iconPackManager.selectedIconPack.isNotBlank()) { providers.add(IconPackIconProvider(context, iconPackManager.selectedIconPack)) } - providers.add(GoogleClockIconProvider(context)) providers.add(CalendarIconProvider(context)) providers.add(SystemIconProvider(context)) diff --git a/icons/src/main/java/de/mm20/launcher2/icons/Module.kt b/icons/src/main/java/de/mm20/launcher2/icons/Module.kt index dca1c618..911b47a9 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/Module.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/Module.kt @@ -5,6 +5,6 @@ import org.koin.dsl.module val iconsModule = module { single { DynamicIconController(androidContext()) } - single { IconPackManager(androidContext(), get()) } + single { IconPackManager(androidContext()) } single { IconRepository(androidContext(), get(), get()) } } \ No newline at end of file diff --git a/icons/src/main/java/de/mm20/launcher2/icons/ThemedCalendarDynamicLauncherIcon.kt b/icons/src/main/java/de/mm20/launcher2/icons/ThemedCalendarDynamicLauncherIcon.kt new file mode 100644 index 00000000..ce4d6bfc --- /dev/null +++ b/icons/src/main/java/de/mm20/launcher2/icons/ThemedCalendarDynamicLauncherIcon.kt @@ -0,0 +1,46 @@ +package de.mm20.launcher2.icons + +import android.content.Context +import android.content.pm.PackageManager +import android.graphics.drawable.AdaptiveIconDrawable +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.os.Build +import de.mm20.launcher2.ktx.getDrawableOrNull +import java.util.* +import java.util.concurrent.Executors + +class ThemedCalendarDynamicLauncherIcon( + foregroundScale: Float, + val packageName: String, + val foregroundIds: IntArray, + val foregroundTint: Int, + background: Drawable, +) : DynamicLauncherIcon( + foreground = ColorDrawable(0), + background = background, + foregroundScale = foregroundScale, + backgroundScale = 1f, + /** Not needed, we already have a background **/ + BACKGROUND_WHITE +) { + + var currentDay = 0 + override fun update(context: Context) { + val calendar = Calendar.getInstance() + val day = calendar[Calendar.DAY_OF_MONTH] + if (day == currentDay || foregroundIds.size < currentDay) return + val resources = try { + context.packageManager.getResourcesForApplication(packageName) + } catch (e: PackageManager.NameNotFoundException) { + return + } + Executors.newSingleThreadExecutor().execute { + val currentDayDrawable = resources.getDrawableOrNull(foregroundIds[day - 1]) + ?: return@execute + currentDayDrawable.setTint(foregroundTint) + foreground = currentDayDrawable + } + currentDay = day + } +} \ No newline at end of file diff --git a/icons/src/main/java/de/mm20/launcher2/icons/providers/CalendarIconProvider.kt b/icons/src/main/java/de/mm20/launcher2/icons/providers/CalendarIconProvider.kt index 7d753300..6c25aaac 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/providers/CalendarIconProvider.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/providers/CalendarIconProvider.kt @@ -37,9 +37,8 @@ class CalendarIconProvider(val context: Context): IconProvider { } typedArray.recycle() return CalendarDynamicLauncherIcon( - context = context, - background = ColorDrawable(0), foreground = ColorDrawable(0), + background = ColorDrawable(0), foregroundScale = 1.5f, backgroundScale = 1.5f, packageName = component.packageName, diff --git a/icons/src/main/java/de/mm20/launcher2/icons/providers/GoogleClockIconProvider.kt b/icons/src/main/java/de/mm20/launcher2/icons/providers/GoogleClockIconProvider.kt index 9ec5a657..bdd88d08 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/providers/GoogleClockIconProvider.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/providers/GoogleClockIconProvider.kt @@ -8,7 +8,6 @@ import android.graphics.drawable.LayerDrawable import android.os.Build import androidx.core.content.res.ResourcesCompat import de.mm20.launcher2.icons.ClockDynamicLauncherIcon -import de.mm20.launcher2.icons.IconPackManager import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.search.data.Application import de.mm20.launcher2.search.data.Searchable @@ -21,7 +20,7 @@ class GoogleClockIconProvider(val context: Context) : IconProvider { val pm = context.packageManager val appInfo = try { pm.getApplicationInfo( - IconPackManager.GOOGLE_DESK_CLOCK_PACKAGE_NAME, + "com.google.android.deskclock", PackageManager.GET_META_DATA ) } catch (e: PackageManager.NameNotFoundException) { @@ -44,12 +43,10 @@ class GoogleClockIconProvider(val context: Context) : IconProvider { val secondLayer = appInfo.metaData.getInt("com.google.android.apps.nexuslauncher.SECOND_LAYER_INDEX") return ClockDynamicLauncherIcon( - context = context, - background = baseIcon.background, - backgroundScale = 1.5f, foreground = foreground, + background = baseIcon.background, foregroundScale = 1.5f, - badgeNumber = 0f, + backgroundScale = 1.5f, hourLayer = hourLayer, minuteLayer = minuteLayer, secondLayer = secondLayer diff --git a/icons/src/main/java/de/mm20/launcher2/icons/providers/IconPackIconProvider.kt b/icons/src/main/java/de/mm20/launcher2/icons/providers/IconPackIconProvider.kt index 954f2d5c..eadeebe0 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/providers/IconPackIconProvider.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/providers/IconPackIconProvider.kt @@ -194,9 +194,8 @@ class IconPackIconProvider(val context: Context, val iconPack: String): IconProv id }.toIntArray() return CalendarDynamicLauncherIcon( - context = context, - background = ColorDrawable(0), foreground = ColorDrawable(0), + background = ColorDrawable(0), foregroundScale = 1.5f, backgroundScale = 1.5f, packageName = iconPack, diff --git a/icons/src/main/java/de/mm20/launcher2/icons/providers/SystemIconProvider.kt b/icons/src/main/java/de/mm20/launcher2/icons/providers/SystemIconProvider.kt index 9cb49cb8..330ba85b 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/providers/SystemIconProvider.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/providers/SystemIconProvider.kt @@ -6,6 +6,6 @@ import de.mm20.launcher2.search.data.Searchable class SystemIconProvider(val context: Context) : IconProvider { override suspend fun getIcon(searchable: Searchable, size: Int): LauncherIcon? { - return searchable.loadIconAsync(context, size) + return searchable.loadIcon(context, size) } } \ No newline at end of file diff --git a/icons/src/main/java/de/mm20/launcher2/icons/providers/ThemedIconProvider.kt b/icons/src/main/java/de/mm20/launcher2/icons/providers/ThemedIconProvider.kt new file mode 100644 index 00000000..01eddfd1 --- /dev/null +++ b/icons/src/main/java/de/mm20/launcher2/icons/providers/ThemedIconProvider.kt @@ -0,0 +1,150 @@ +package de.mm20.launcher2.icons.providers + +import android.content.ComponentName +import android.content.Context +import android.content.pm.PackageManager +import android.content.res.Configuration +import android.content.res.Resources +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.LayerDrawable +import android.os.Build +import android.util.TypedValue +import androidx.core.content.res.ResourcesCompat +import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.database.AppDatabase +import de.mm20.launcher2.icons.* +import de.mm20.launcher2.ktx.obtainTypedArrayOrNull +import de.mm20.launcher2.search.data.Application +import de.mm20.launcher2.search.data.Searchable + +class ThemedIconProvider( + private val context: Context +) : IconProvider { + private val fgColor: Int + private val bgColor: Int + + init { + val theme = context.resources.newTheme() + theme.applyStyle(R.style.DefaultColors, true) + val typedValue = TypedValue() + val isDarkMode = + context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES != 0 + + val bgAttr = + if (isDarkMode) R.attr.colorOnPrimaryContainer else R.attr.colorPrimaryContainer + val fgAttr = if (isDarkMode) R.attr.colorOnSurfaceInverse else R.attr.colorOnSurfaceVariant + bgColor = theme.resolveAttribute(bgAttr, typedValue, true).let { + typedValue.data + } + fgColor = theme.resolveAttribute(fgAttr, typedValue, true).let { + typedValue.data + } + } + + override suspend fun getIcon(searchable: Searchable, size: Int): LauncherIcon? { + if (searchable !is Application) return null + val icon = getGreyscaleIcon(searchable.`package`) ?: return null + val resId = icon.drawable?.toIntOrNull() ?: return null + try { + val resources = context.packageManager.getResourcesForApplication(icon.iconPack) + return getClockIcon(resources, resId) ?: getCalendarIcon( + resources, + resId, + iconProviderPackage = icon.iconPack + ) ?: getStaticIcon(resources, resId) + } catch (e: PackageManager.NameNotFoundException) { + CrashReporter.logException(e) + } + return null + } + + + private suspend fun getGreyscaleIcon(packageName: String): Icon? { + val iconDao = AppDatabase.getInstance(context).iconDao() + return iconDao.getGreyscaleIcon(ComponentName(packageName, packageName).flattenToString()) + ?.let { Icon(it) } + + } + + private fun getStaticIcon(resources: Resources, resId: Int): LauncherIcon? { + try { + val fg = ResourcesCompat.getDrawable(resources, resId, null) ?: return null + fg.setTint(fgColor) + return LauncherIcon( + foreground = fg, + foregroundScale = 0.5f, + background = ColorDrawable(bgColor) + ) + } catch (e: Resources.NotFoundException) { + return null + } + } + + private fun getClockIcon(resources: Resources, resId: Int): LauncherIcon? { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + return null + } + try { + val array = resources.obtainTypedArrayOrNull(resId) ?: return null + var i = 0 + var drawable: LayerDrawable? = null + var minuteIndex: Int? = null + var hourIndex: Int? = null + while (i < array.length()) { + when (array.getString(i)) { + "com.android.launcher3.LEVEL_PER_TICK_ICON_ROUND" -> { + i++ + drawable = array.getDrawable(i) as? LayerDrawable + } + "com.android.launcher3.HOUR_LAYER_INDEX" -> { + i++ + hourIndex = array.getInt(i, -1).takeIf { it != -1 } + } + "com.android.launcher3.MINUTE_LAYER_INDEX" -> { + i++ + minuteIndex = array.getInt(i, -1).takeIf { it != -1 } + } + } + i++ + } + if (drawable != null && minuteIndex != null && hourIndex != null) { + drawable.setTint(fgColor) + return ClockDynamicLauncherIcon( + foreground = drawable, + background = ColorDrawable(bgColor), + foregroundScale = 1.5f, + backgroundScale = 1f, + hourLayer = hourIndex, + minuteLayer = minuteIndex, + secondLayer = -1, + ) + } + } catch (e: Resources.NotFoundException) { + } + return null + } + + private fun getCalendarIcon( + resources: Resources, + resId: Int, + iconProviderPackage: String + ): LauncherIcon? { + try { + val array = resources.obtainTypedArrayOrNull(resId) ?: return null + if (array.length() != 31) return null + + return ThemedCalendarDynamicLauncherIcon( + background = ColorDrawable(bgColor), + packageName = iconProviderPackage, + foregroundIds = IntArray(31) { + array.getResourceId(it, 0).takeIf { it != 0 } ?: return null + }, + foregroundTint = fgColor, + foregroundScale = 0.5f, + ) + + } catch (e: Resources.NotFoundException) { + } + return null + } +} \ No newline at end of file diff --git a/icons/src/main/java/de/mm20/launcher2/icons/providers/ThemedPlaceholderIconProvider.kt b/icons/src/main/java/de/mm20/launcher2/icons/providers/ThemedPlaceholderIconProvider.kt new file mode 100644 index 00000000..7dd873ff --- /dev/null +++ b/icons/src/main/java/de/mm20/launcher2/icons/providers/ThemedPlaceholderIconProvider.kt @@ -0,0 +1,45 @@ +package de.mm20.launcher2.icons.providers + +import android.content.Context +import android.content.res.Configuration +import android.util.TypedValue +import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.icons.R +import de.mm20.launcher2.search.data.Searchable + +class ThemedPlaceholderIconProvider( + val context: Context +) : IconProvider { + + private val fgColor: Int + private val bgColor: Int + + init { + val theme = context.resources.newTheme() + theme.applyStyle(R.style.DefaultColors, true) + val typedValue = TypedValue() + val isDarkMode = + context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES != 0 + + val bgAttr = + if (isDarkMode) R.attr.colorOnPrimaryContainer else R.attr.colorPrimaryContainer + val fgAttr = if (isDarkMode) R.attr.colorOnSurfaceInverse else R.attr.colorOnSurfaceVariant + bgColor = theme.resolveAttribute(bgAttr, typedValue, true).let { + typedValue.data + } + fgColor = theme.resolveAttribute(fgAttr, typedValue, true).let { + typedValue.data + } + } + + override suspend fun getIcon(searchable: Searchable, size: Int): LauncherIcon { + + + val icon = searchable.getPlaceholderIcon(context) + + icon.foreground.setTint(fgColor) + icon.background?.setTint(bgColor) + return icon + } + +} \ No newline at end of file diff --git a/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherPreferences.kt b/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherPreferences.kt index 315943e3..67fbc4d0 100644 --- a/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherPreferences.kt +++ b/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherPreferences.kt @@ -80,6 +80,7 @@ class LauncherPreferences(val context: Application, version: Int = 3) { var calendarMaxEvents by StringPreference("calendar_max_events", default = "10") + var themedIcons by BooleanPreference("themed_icons", default = false) var legacyIconBg by StringPreference("legacy_icon_bg", default = "1") var blurCards by BooleanPreference("blur_cards", default = false) var searchStyle by EnumPreference("search_style", default = SearchStyles.NO_BG)