From 2e3add0d94fb9c019e533ab764b6bf4a11cd48c3 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Wed, 27 Jul 2022 20:08:45 +0200 Subject: [PATCH] Add option to pick icons from any icon pack --- .../launcher2/customattrs/CustomAttribute.kt | 22 ++- .../de/mm20/launcher2/database/IconDao.kt | 5 +- .../mm20/launcher2/icons/IconPackManager.kt | 18 ++- .../de/mm20/launcher2/icons/IconRepository.kt | 139 +++++++++++++----- .../providers/CustomIconPackIconProvider.kt | 20 +++ .../icons/providers/IconPackIconProvider.kt | 16 +- 6 files changed, 158 insertions(+), 62 deletions(-) create mode 100644 icons/src/main/java/de/mm20/launcher2/icons/providers/CustomIconPackIconProvider.kt diff --git a/customattrs/src/main/java/de/mm20/launcher2/customattrs/CustomAttribute.kt b/customattrs/src/main/java/de/mm20/launcher2/customattrs/CustomAttribute.kt index 9e4891cd..74003595 100644 --- a/customattrs/src/main/java/de/mm20/launcher2/customattrs/CustomAttribute.kt +++ b/customattrs/src/main/java/de/mm20/launcher2/customattrs/CustomAttribute.kt @@ -1,6 +1,5 @@ package de.mm20.launcher2.customattrs -import android.graphics.Color import android.util.Log import de.mm20.launcher2.database.entities.CustomAttributeEntity import de.mm20.launcher2.ktx.jsonObjectOf @@ -76,7 +75,7 @@ sealed class CustomIcon : CustomAttribute { return when (type) { "custom_icon_pack_icon" -> { CustomIconPackIcon( - iconName = payload.getString("icon"), + iconComponentName = payload.getString("icon"), iconPackPackage = payload.getString("icon_pack") ) } @@ -86,6 +85,9 @@ sealed class CustomIcon : CustomAttribute { iconPackPackage = payload.getString("icon_pack") ) } + "default_icon" -> { + UnmodifiedSystemDefaultIcon + } "adaptified_legacy_icon" -> { AdaptifiedLegacyIcon( fgScale = payload.getDouble("fg_scale").toFloat(), @@ -100,12 +102,12 @@ sealed class CustomIcon : CustomAttribute { data class CustomIconPackIcon( val iconPackPackage: String, - val iconName: String, + val iconComponentName: String, ) : CustomIcon() { override fun toDatabaseValue(): String { return jsonObjectOf( "type" to "custom_icon_pack_icon", - "icon" to iconName, + "icon" to iconComponentName, "icon_pack" to iconPackPackage, ).toString() } @@ -152,3 +154,15 @@ data class CustomThemedIcon( ).toString() } } + +/** + * Use default icon, ignore any icon pack, themed icon or force adaptive settings. + */ +object UnmodifiedSystemDefaultIcon: CustomIcon() { + override fun toDatabaseValue(): String { + return jsonObjectOf( + "type" to "default_icon" + ).toString() + } + +} \ No newline at end of file diff --git a/database/src/main/java/de/mm20/launcher2/database/IconDao.kt b/database/src/main/java/de/mm20/launcher2/database/IconDao.kt index 51d7b478..89ca0ffe 100644 --- a/database/src/main/java/de/mm20/launcher2/database/IconDao.kt +++ b/database/src/main/java/de/mm20/launcher2/database/IconDao.kt @@ -13,9 +13,12 @@ interface IconDao { @Query("SELECT drawable FROM Icons WHERE componentName = :componentName AND iconPack = :iconPack") suspend fun getIconName(componentName: String, iconPack: String): String? - @Query("SELECT * FROM Icons WHERE componentName = :componentName AND iconPack = :iconPack") + @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? + @Query("SELECT * FROM Icons WHERE componentName = :componentName AND (type = 'app' OR type = 'calendar')") + suspend fun getIconsFromAllPacks(componentName: String): List + @Query("DELETE FROM Icons WHERE iconPack = :iconPack") fun deleteIcons(iconPack: String) 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 7cfbfd3a..b991b542 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/IconPackManager.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/IconPackManager.kt @@ -51,14 +51,14 @@ class IconPackManager( } } - suspend fun getIcon(iconPack: String, componentName: ComponentName, size: Int): LauncherIcon? { + suspend fun getIcon(iconPack: String, componentName: ComponentName): LauncherIcon? { val res = try { context.packageManager.getResourcesForApplication(iconPack) } catch (e: PackageManager.NameNotFoundException) { Log.e("MM20", "Icon pack package $iconPack not found!") return null } - val iconDao = AppDatabase.getInstance(context).iconDao() + val iconDao = appDatabase.iconDao() val icon = iconDao.getIcon(componentName.flattenToString(), iconPack) ?: return null @@ -185,26 +185,32 @@ class IconPackManager( ) } + suspend fun getIcons(componentName: ComponentName): List { + val iconDao = appDatabase.iconDao() + return iconDao.getIconsFromAllPacks(componentName.flattenToString()) + .map { IconPackIcon(it) } + } + private suspend fun getIconBack(iconPack: String): String? { - val iconDao = AppDatabase.getInstance(context).iconDao() + val iconDao = appDatabase.iconDao() val iconbacks = iconDao.getIconBacks(iconPack) return iconbacks.randomElementOrNull() } private suspend fun getIconUpon(iconPack: String): String? { - val iconDao = AppDatabase.getInstance(context).iconDao() + val iconDao = appDatabase.iconDao() val iconupons = iconDao.getIconUpons(iconPack) return iconupons.randomElementOrNull() } private suspend fun getIconMask(iconPack: String): String? { - val iconDao = AppDatabase.getInstance(context).iconDao() + val iconDao = appDatabase.iconDao() val iconmasks = iconDao.getIconMasks(iconPack) return iconmasks.randomElementOrNull() } private suspend fun getPackScale(iconPack: String): Float { - val iconDao = AppDatabase.getInstance(context).iconDao() + val iconDao = appDatabase.iconDao() return iconDao.getScale(iconPack) ?: 1f } 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 f5545497..6bc2e6d0 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/IconRepository.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/IconRepository.kt @@ -6,13 +6,12 @@ import android.content.Intent import android.content.IntentFilter import android.graphics.Color import android.util.LruCache -import de.mm20.launcher2.customattrs.AdaptifiedLegacyIcon -import de.mm20.launcher2.customattrs.CustomAttributesRepository -import de.mm20.launcher2.customattrs.CustomIcon +import de.mm20.launcher2.customattrs.* import de.mm20.launcher2.icons.providers.* import de.mm20.launcher2.icons.transformations.LauncherIconTransformation import de.mm20.launcher2.icons.transformations.LegacyToAdaptiveTransformation import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.search.data.LauncherApp import de.mm20.launcher2.search.data.Searchable import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -101,6 +100,7 @@ class IconRepository( transformations.collectLatest { transformations -> customAttributesRepository.getCustomIcon(searchable).collectLatest { customIcon -> + val provs = getProviders(customIcon) + providers val transforms = getTransformations(customIcon) ?: transformations var icon = cache.get(searchable.key + customIcon.hashCode()) @@ -112,13 +112,8 @@ class IconRepository( val placeholder = placeholderProvider?.getIcon(searchable, size) placeholder?.let { send(it) } - for (provider in providers) { - val ic = provider.getIcon(searchable, size) - if (ic != null) { - icon = ic - break - } - } + icon = getFirstIcon(searchable, size, provs) + if (icon != null) { icon = applyTransformations(icon, transforms) @@ -130,6 +125,23 @@ class IconRepository( } } + private fun getProviders(customIcon: CustomIcon?): List { + if (customIcon is UnmodifiedSystemDefaultIcon) { + return listOf( + SystemIconProvider(context) + ) + } + if (customIcon is CustomIconPackIcon) { + return listOf( + CustomIconPackIconProvider( + customIcon, + iconPackManager + ) + ) + } + return emptyList() + } + private fun getTransformations(customIcon: CustomIcon?): List? { customIcon ?: return null if (customIcon is AdaptifiedLegacyIcon) { @@ -140,6 +152,9 @@ class IconRepository( ) ) } + if (customIcon is UnmodifiedSystemDefaultIcon) { + return emptyList() + } return null } @@ -160,13 +175,7 @@ class IconRepository( ): List { val suggestions = mutableListOf() - var rawIcon = iconProviders.first().firstNotNullOfOrNull { - it.getIcon(searchable, size) - } - - if (rawIcon == null) { - rawIcon = placeholderProvider?.getIcon(searchable, size) - } + var rawIcon = getFirstIcon(searchable, size, iconProviders.first()) if (rawIcon == null) { return emptyList() @@ -183,56 +192,114 @@ class IconRepository( ) ) + val customIcons = mutableListOf(UnmodifiedSystemDefaultIcon) + if (rawIcon is StaticLauncherIcon && rawIcon.backgroundLayer is TransparentLayer) { - val adaptifyOptions = listOf( - // Legacy icons that simply fill the entire canvas + // Legacy icons that simply fill the entire canvas + customIcons.add( AdaptifiedLegacyIcon( fgScale = 1f, bgColor = 1 - ), - // 48x48 with 5px padding used to be the default icon size for icons generated by - // the Android Studio asset generator. Upscale these icons to remove that padding. + ) + ) + // 48x48 with 5px padding used to be the default icon size for icons generated by + // the Android Studio asset generator. Upscale these icons to remove that padding. + + customIcons.add( AdaptifiedLegacyIcon( fgScale = 48f / 38f, bgColor = 1 - ), + ) + ) + customIcons.add( AdaptifiedLegacyIcon( fgScale = 0.7f, bgColor = 0 - ), + ) + ) + customIcons.add( AdaptifiedLegacyIcon( fgScale = 0.7f, bgColor = Color.WHITE, ) ) - suggestions.addAll( - adaptifyOptions.mapNotNull { - val transformation = - getTransformations(it)?.firstOrNull() ?: return@mapNotNull null - CustomIconSuggestion( - icon = transformation.transform(rawIcon), - data = it, - ) + } + suggestions.addAll( + customIcons.map { + val transformations = getTransformations(it) ?: defaultTransformations + val providers = getProviders(it) + val icon = getFirstIcon(searchable, size, providers) ?: rawIcon + + CustomIconSuggestion( + icon = applyTransformations(icon, transformations), + data = it, + ) + + } + ) + + val providerOptions = mutableListOf() + + if (searchable is LauncherApp) { + val iconPackIcons = iconPackManager.getIcons( + searchable.launcherActivityInfo.componentName + ) + + providerOptions.addAll( + iconPackIcons.mapNotNull { + CustomIconPackIcon( + iconPackPackage = it.iconPack, + iconComponentName = it.componentName?.flattenToString() + ?: return@mapNotNull null + ) } ) } + suggestions.addAll( + providerOptions.mapNotNull { + val providers = getProviders(it) + + val icon = getFirstIcon(searchable, size, providers) ?: return@mapNotNull null + + CustomIconSuggestion( + icon = applyTransformations(icon, defaultTransformations), + data = it, + ) + + } + ) + return suggestions } + private suspend fun getFirstIcon( + searchable: Searchable, + size: Int, + providers: List + ): LauncherIcon? { + for (provider in providers) { + val icon = provider.getIcon(searchable, size) + if (icon != null) { + return icon + } + } + return null + } + private suspend fun applyTransformations( icon: LauncherIcon, transformations: List ): LauncherIcon { - var icon = icon - if (icon is StaticLauncherIcon) { + var transformedIcon = icon + if (transformedIcon is StaticLauncherIcon) { for (transformation in transformations) { - icon = transformation.transform(icon as StaticLauncherIcon) + transformedIcon = transformation.transform(transformedIcon as StaticLauncherIcon) } } - return icon + return transformedIcon } fun setCustomIcon(searchable: Searchable, icon: CustomIcon?) { diff --git a/icons/src/main/java/de/mm20/launcher2/icons/providers/CustomIconPackIconProvider.kt b/icons/src/main/java/de/mm20/launcher2/icons/providers/CustomIconPackIconProvider.kt new file mode 100644 index 00000000..6e202abd --- /dev/null +++ b/icons/src/main/java/de/mm20/launcher2/icons/providers/CustomIconPackIconProvider.kt @@ -0,0 +1,20 @@ +package de.mm20.launcher2.icons.providers + +import de.mm20.launcher2.customattrs.CustomIconPackIcon +import de.mm20.launcher2.icons.IconPackManager +import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.search.data.LauncherApp +import de.mm20.launcher2.search.data.Searchable + +class CustomIconPackIconProvider( + private val customIcon: CustomIconPackIcon, + private val iconPackManager: IconPackManager, +) : IconProvider { + override suspend fun getIcon(searchable: Searchable, size: Int): LauncherIcon? { + if (searchable !is LauncherApp) return null + return iconPackManager.getIcon( + customIcon.iconPackPackage, + searchable.launcherActivityInfo.componentName + ) + } +} \ No newline at end of file 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 48525f9d..b400f14e 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 @@ -2,25 +2,11 @@ package de.mm20.launcher2.icons.providers import android.content.ComponentName import android.content.Context -import android.content.pm.LauncherActivityInfo -import android.content.pm.PackageManager -import android.content.res.Resources -import android.graphics.* -import android.graphics.drawable.AdaptiveIconDrawable -import android.graphics.drawable.BitmapDrawable -import android.graphics.drawable.ColorDrawable -import android.util.Log -import androidx.core.content.res.ResourcesCompat -import androidx.core.graphics.drawable.toBitmap -import de.mm20.launcher2.database.AppDatabase import de.mm20.launcher2.icons.* -import de.mm20.launcher2.ktx.randomElementOrNull -import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.search.data.LauncherApp import de.mm20.launcher2.search.data.Searchable import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import kotlin.math.roundToInt class IconPackIconProvider( private val context: Context, @@ -31,7 +17,7 @@ class IconPackIconProvider( if (searchable !is LauncherApp) return null val component = ComponentName(searchable.`package`, searchable.activity) - return iconPackManager.getIcon(iconPack, component, size) + return iconPackManager.getIcon(iconPack, component) ?: iconPackManager.generateIcon( context, iconPack,