Themed icons
This commit is contained in:
parent
be50fb2276
commit
ccda48c559
@ -95,6 +95,11 @@ class PreferencesAppearanceFragment : PreferenceFragmentCompat() {
|
||||
true
|
||||
}
|
||||
|
||||
findPreference<Preference>("themed_icons")?.setOnPreferenceChangeListener { _, _ ->
|
||||
iconRepository.recreate()
|
||||
true
|
||||
}
|
||||
|
||||
val shapePreference = findPreference<Preference>("icon_shape")!!
|
||||
shapePreference.summary = getShapeName()
|
||||
shapePreference.setOnPreferenceClickListener {
|
||||
|
||||
@ -22,6 +22,12 @@
|
||||
app:summary="@string/preference_cards_summary"
|
||||
app:title="@string/preference_cards" />
|
||||
<PreferenceCategory app:title="@string/preference_category_icons">
|
||||
<SwitchPreference
|
||||
app:key="themed_icons"
|
||||
android:defaultValue="false"
|
||||
app:title="@string/preference_themed_icons"
|
||||
app:summary="@string/preference_themed_icons_summary"
|
||||
/>
|
||||
<ListPreference
|
||||
app:key="icon_pack"
|
||||
app:persistent="false"
|
||||
|
||||
@ -413,4 +413,6 @@
|
||||
<item quantity="other">+%1$d laufende Termine aus vergangenen Tagen</item>
|
||||
</plurals>
|
||||
<string name="weather_wind">Wind:</string>
|
||||
<string name="preference_themed_icons">Eingefärbte Symbole</string>
|
||||
<string name="preference_themed_icons_summary">Symbole an das Farbschema der App anpassen</string>
|
||||
</resources>
|
||||
@ -203,6 +203,8 @@
|
||||
<string name="preference_automatic_location_disabled_summary">Not supported by this provider</string>
|
||||
<string name="date_format_long_without_year">EE, MMMM d</string>
|
||||
<string name="weather_wind_direction_speed">%1$s • %2$s</string>
|
||||
<string name="preference_themed_icons">Themed icons</string>
|
||||
<string name="preference_themed_icons_summary">Color icons with the application\'s color scheme</string>
|
||||
<string name="preference_icon_pack">Icon pack</string>
|
||||
<string name="preference_icon_pack_summary_empty">No icon packs installed</string>
|
||||
<string name="preference_dynamic_icon_bg">Dynamic background</string>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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<IconPack> {
|
||||
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<ResolveInfo> {
|
||||
@ -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<Icon>()
|
||||
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"
|
||||
|
||||
@ -86,13 +86,20 @@ class IconRepository(
|
||||
}
|
||||
|
||||
fun recreate() {
|
||||
placeholderProvider = PlaceholderIconProvider(context)
|
||||
placeholderProvider = if (LauncherPreferences.instance.themedIcons) {
|
||||
ThemedPlaceholderIconProvider(context)
|
||||
} else {
|
||||
PlaceholderIconProvider(context)
|
||||
}
|
||||
val providers = mutableListOf<IconProvider>()
|
||||
|
||||
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))
|
||||
|
||||
@ -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()) }
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user