From f72e7a1993603f6949024c962b8271ae0788aad0 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 28 Jan 2022 23:15:27 +0100 Subject: [PATCH] Migrate icon settings --- .../fragment/PreferencesAppearanceFragment.kt | 8 - .../launcher2/search/data/AppInstallation.kt | 4 +- .../mm20/launcher2/search/data/AppShortcut.kt | 6 +- .../mm20/launcher2/search/data/LauncherApp.kt | 38 ++-- .../de/mm20/launcher2/icons/LauncherIcon.kt | 6 +- .../de/mm20/launcher2/search/data/Contact.kt | 8 +- .../mm20/launcher2/search/data/GDriveFile.kt | 4 - .../mm20/launcher2/search/data/LocalFile.kt | 20 +- .../launcher2/search/data/OneDriveFile.kt | 4 - i18n/src/main/res/values-de/strings.xml | 1 + i18n/src/main/res/values/strings.xml | 1 + .../icons/CalendarDynamicLauncherIcon.kt | 5 +- .../icons/ClockDynamicLauncherIcon.kt | 4 +- .../launcher2/icons/DynamicLauncherIcon.kt | 4 +- .../mm20/launcher2/icons/IconPackManager.kt | 18 -- .../de/mm20/launcher2/icons/IconRepository.kt | 108 ++++++----- .../java/de/mm20/launcher2/icons/Module.kt | 2 +- .../ThemedCalendarDynamicLauncherIcon.kt | 2 - .../icons/providers/CalendarIconProvider.kt | 3 +- .../icons/providers/IconPackIconProvider.kt | 20 +- .../icons/providers/SystemIconProvider.kt | 8 +- .../mm20/launcher2/search/data/Searchable.kt | 4 +- .../mm20/launcher2/ui/ShapedLauncherIcon.kt | 4 +- .../launcher2/ui/launcher/LauncherActivity.kt | 2 - .../appearance/AppearanceSettingsScreen.kt | 171 ++++++++++++++++-- .../appearance/AppearanceSettingsScreenVM.kt | 61 ++++++- .../de/mm20/launcher2/search/data/Website.kt | 17 +- .../mm20/launcher2/search/data/Wikipedia.kt | 4 - 28 files changed, 347 insertions(+), 190 deletions(-) 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 c4bb8b17..0a5bfa89 100644 --- a/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt +++ b/app/src/main/java/de/mm20/launcher2/fragment/PreferencesAppearanceFragment.kt @@ -62,27 +62,19 @@ class PreferencesAppearanceFragment : PreferenceFragmentCompat() { } else { isEnabled = true summary = "%s" - value = packs.indexOfFirst { it.packageName == iconPackManager.selectedIconPack }.toString() } setOnPreferenceChangeListener { _, newValue -> val index = (newValue as String).toInt() - if (index == -1) iconPackManager.selectIconPack("") - else { - iconPackManager.selectIconPack(packs[index].packageName) - } - iconRepository.recreate() true } } } findPreference("legacy_icon_bg")?.setOnPreferenceChangeListener { _, _ -> - iconRepository.recreate() true } findPreference("themed_icons")?.setOnPreferenceChangeListener { _, _ -> - iconRepository.recreate() true } diff --git a/applications/src/main/java/de/mm20/launcher2/search/data/AppInstallation.kt b/applications/src/main/java/de/mm20/launcher2/search/data/AppInstallation.kt index 35c91a57..8de8028c 100644 --- a/applications/src/main/java/de/mm20/launcher2/search/data/AppInstallation.kt +++ b/applications/src/main/java/de/mm20/launcher2/search/data/AppInstallation.kt @@ -10,6 +10,8 @@ import android.graphics.drawable.ColorDrawable import androidx.core.content.ContextCompat import de.mm20.launcher2.applications.R import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.Settings.IconSettings.LegacyIconBackground class AppInstallation( val session: PackageInstaller.SessionInfo @@ -38,7 +40,7 @@ class AppInstallation( foregroundScale = 0.5f) } - override suspend fun loadIcon(context: Context, size: Int): LauncherIcon? { + override suspend fun loadIcon(context: Context, size: Int, legacyIconBackground: LegacyIconBackground): LauncherIcon? { val icon = session.appIcon ?: return getPlaceholderIcon(context) val foreground = BitmapDrawable(context.resources, icon) foreground.colorFilter = ColorMatrixColorFilter(ColorMatrix().apply { diff --git a/applications/src/main/java/de/mm20/launcher2/search/data/AppShortcut.kt b/applications/src/main/java/de/mm20/launcher2/search/data/AppShortcut.kt index bfcf5fc5..ea883673 100644 --- a/applications/src/main/java/de/mm20/launcher2/search/data/AppShortcut.kt +++ b/applications/src/main/java/de/mm20/launcher2/search/data/AppShortcut.kt @@ -17,6 +17,8 @@ import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.ktx.getSerialNumber import de.mm20.launcher2.ktx.isAtLeastApiLevel import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.Settings.IconSettings.LegacyIconBackground import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -68,7 +70,7 @@ class AppShortcut( ) } - override suspend fun loadIcon(context: Context, size: Int): LauncherIcon? { + override suspend fun loadIcon(context: Context, size: Int, legacyIconBackground: LegacyIconBackground): LauncherIcon? { val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps val icon = withContext(Dispatchers.IO) { launcherApps.getShortcutIconDrawable( @@ -87,7 +89,7 @@ class AppShortcut( return LauncherIcon( foreground = icon, foregroundScale = 1f, - autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt() + autoGenerateBackgroundMode = legacyIconBackground.number ) } } \ No newline at end of file diff --git a/applications/src/main/java/de/mm20/launcher2/search/data/LauncherApp.kt b/applications/src/main/java/de/mm20/launcher2/search/data/LauncherApp.kt index 2b723338..4fe3d563 100644 --- a/applications/src/main/java/de/mm20/launcher2/search/data/LauncherApp.kt +++ b/applications/src/main/java/de/mm20/launcher2/search/data/LauncherApp.kt @@ -8,11 +8,16 @@ import android.content.pm.LauncherApps import android.content.pm.PackageManager import android.content.pm.ShortcutInfo import android.graphics.drawable.AdaptiveIconDrawable -import android.os.* +import android.os.Build +import android.os.Bundle +import android.os.Process +import android.os.UserHandle import androidx.core.content.getSystemService import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.ktx.getSerialNumber import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.Settings.IconSettings.LegacyIconBackground import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.koin.core.component.KoinComponent @@ -68,29 +73,26 @@ class LauncherApp( return launcherActivityInfo.user } - override suspend fun loadIcon(context: Context, size: Int): LauncherIcon? { + override suspend fun loadIcon(context: Context, size: Int, legacyIconBackground: LegacyIconBackground): LauncherIcon? { try { val icon = withContext(Dispatchers.IO) { launcherActivityInfo.getIcon(context.resources.displayMetrics.densityDpi) } ?: return null - when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && icon is AdaptiveIconDrawable -> { - return LauncherIcon( - foreground = icon.foreground ?: return null, - background = icon.background, - foregroundScale = 1.5f, - backgroundScale = 1.5f - ) - } - else -> { - return LauncherIcon( - foreground = icon, - foregroundScale = 0.7f, - autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt() - ) - } + if (icon is AdaptiveIconDrawable) { + return LauncherIcon( + foreground = icon.foreground ?: return null, + background = icon.background, + foregroundScale = 1.5f, + backgroundScale = 1.5f + ) + } else { + return LauncherIcon( + foreground = icon, + foregroundScale = 0.7f, + autoGenerateBackgroundMode = legacyIconBackground.number + ) } } catch (e: PackageManager.NameNotFoundException) { return null diff --git a/base/src/main/java/de/mm20/launcher2/icons/LauncherIcon.kt b/base/src/main/java/de/mm20/launcher2/icons/LauncherIcon.kt index 43e6b344..b030719a 100644 --- a/base/src/main/java/de/mm20/launcher2/icons/LauncherIcon.kt +++ b/base/src/main/java/de/mm20/launcher2/icons/LauncherIcon.kt @@ -25,7 +25,7 @@ open class LauncherIcon( private fun updateBackgroundColor() { if (background == null) { when (autoGenerateBackgroundMode) { - BACKGROUND_COLOR -> { + BACKGROUND_DYNAMIC -> { val palette = Palette .from(foreground.toBitmap()) .generate() @@ -77,8 +77,8 @@ open class LauncherIcon( } companion object { - const val BACKGROUND_NONE = 0 - const val BACKGROUND_COLOR = 1 + const val BACKGROUND_NONE = 1 + const val BACKGROUND_DYNAMIC = 0 const val BACKGROUND_WHITE = 2 } } \ No newline at end of file diff --git a/contacts/src/main/java/de/mm20/launcher2/search/data/Contact.kt b/contacts/src/main/java/de/mm20/launcher2/search/data/Contact.kt index b7512570..aa8dca29 100644 --- a/contacts/src/main/java/de/mm20/launcher2/search/data/Contact.kt +++ b/contacts/src/main/java/de/mm20/launcher2/search/data/Contact.kt @@ -17,6 +17,8 @@ import de.mm20.launcher2.ktx.sp import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.Settings.IconSettings.LegacyIconBackground import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.koin.core.component.KoinComponent @@ -58,7 +60,7 @@ class Contact( ) } - override suspend fun loadIcon(context: Context, size: Int): LauncherIcon? { + override suspend fun loadIcon(context: Context, size: Int, legacyIconBackground: LegacyIconBackground): LauncherIcon? { val contentResolver = context.contentResolver val bmp = withContext(Dispatchers.IO) { val uri = ContactsContract.Contacts.getLookupUri(id, lookupKey) ?: return@withContext null @@ -66,7 +68,9 @@ class Contact( ?.asBitmap() } ?: return null return LauncherIcon( - foreground = bmp.toDrawable(context.resources) + foreground = bmp.toDrawable(context.resources), + background = null, + autoGenerateBackgroundMode = legacyIconBackground.number ) } diff --git a/files/src/main/java/de/mm20/launcher2/search/data/GDriveFile.kt b/files/src/main/java/de/mm20/launcher2/search/data/GDriveFile.kt index 94fc0eb3..d476df07 100644 --- a/files/src/main/java/de/mm20/launcher2/search/data/GDriveFile.kt +++ b/files/src/main/java/de/mm20/launcher2/search/data/GDriveFile.kt @@ -35,8 +35,4 @@ class GDriveFile( flags = Intent.FLAG_ACTIVITY_NEW_TASK } } - - override suspend fun loadIcon(context: Context, size: Int): LauncherIcon? { - return null - } } \ No newline at end of file diff --git a/files/src/main/java/de/mm20/launcher2/search/data/LocalFile.kt b/files/src/main/java/de/mm20/launcher2/search/data/LocalFile.kt index c59c83d8..12c7a92a 100644 --- a/files/src/main/java/de/mm20/launcher2/search/data/LocalFile.kt +++ b/files/src/main/java/de/mm20/launcher2/search/data/LocalFile.kt @@ -13,21 +13,17 @@ import android.provider.MediaStore import android.text.format.DateUtils import android.util.Size import androidx.core.content.FileProvider -import androidx.core.database.getStringOrNull import androidx.exifinterface.media.ExifInterface import de.mm20.launcher2.files.R import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.ktx.formatToString import de.mm20.launcher2.media.ThumbnailUtilsCompat -import de.mm20.launcher2.permissions.PermissionGroup -import de.mm20.launcher2.permissions.PermissionsManager -import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.Settings.IconSettings.LegacyIconBackground import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.koin.core.component.KoinComponent -import org.koin.core.component.get import java.io.IOException -import java.util.* import java.io.File as JavaIOFile open class LocalFile( @@ -45,7 +41,7 @@ open class LocalFile( override val isStoredInCloud = false - override suspend fun loadIcon(context: Context, size: Int): LauncherIcon? { + override suspend fun loadIcon(context: Context, size: Int, legacyIconBackground: LegacyIconBackground): LauncherIcon? { if (!JavaIOFile(path).exists()) return null when { mimeType.startsWith("image/") -> { @@ -58,7 +54,7 @@ open class LocalFile( return LauncherIcon( foreground = BitmapDrawable(context.resources, thumbnail), - autoGenerateBackgroundMode = LauncherIcon.BACKGROUND_COLOR + autoGenerateBackgroundMode = legacyIconBackground.number ) } mimeType.startsWith("video/") -> { @@ -70,7 +66,7 @@ open class LocalFile( } ?: return null return LauncherIcon( foreground = BitmapDrawable(context.resources, thumbnail), - autoGenerateBackgroundMode = LauncherIcon.BACKGROUND_COLOR + autoGenerateBackgroundMode = legacyIconBackground.number ) } mimeType.startsWith("audio/") -> { @@ -97,7 +93,7 @@ open class LocalFile( thumbnail ?: return null return LauncherIcon( foreground = BitmapDrawable(context.resources, thumbnail), - autoGenerateBackgroundMode = LauncherIcon.BACKGROUND_COLOR + autoGenerateBackgroundMode = legacyIconBackground.number ) } @@ -107,7 +103,7 @@ open class LocalFile( pkgInfo?.applicationInfo?.loadIcon(context.packageManager) } ?: return null when { - Build.VERSION.SDK_INT > Build.VERSION_CODES.O && icon is AdaptiveIconDrawable -> { + icon is AdaptiveIconDrawable -> { return LauncherIcon( foreground = icon.foreground, background = icon.background, @@ -119,7 +115,7 @@ open class LocalFile( return LauncherIcon( foreground = icon, foregroundScale = 0.7f, - autoGenerateBackgroundMode = LauncherIcon.BACKGROUND_COLOR + autoGenerateBackgroundMode = legacyIconBackground.number ) } } diff --git a/files/src/main/java/de/mm20/launcher2/search/data/OneDriveFile.kt b/files/src/main/java/de/mm20/launcher2/search/data/OneDriveFile.kt index a6d7ac0b..a37f2a41 100644 --- a/files/src/main/java/de/mm20/launcher2/search/data/OneDriveFile.kt +++ b/files/src/main/java/de/mm20/launcher2/search/data/OneDriveFile.kt @@ -26,10 +26,6 @@ class OneDriveFile( override val isStoredInCloud = true - override suspend fun loadIcon(context: Context, size: Int): LauncherIcon? { - return null - } - override fun getLaunchIntent(context: Context): Intent? { return Intent(Intent.ACTION_VIEW).apply { data = Uri.parse(webUrl) diff --git a/i18n/src/main/res/values-de/strings.xml b/i18n/src/main/res/values-de/strings.xml index b583ade8..64e6079c 100644 --- a/i18n/src/main/res/values-de/strings.xml +++ b/i18n/src/main/res/values-de/strings.xml @@ -257,6 +257,7 @@ Von diesem Wetterdienst nicht unterstützt Einheitenrechner Symbol-Hintergrund + Stil von Legacy-Icons Keiner Dynamisch Weiß diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 1efe0556..e63145b0 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -257,6 +257,7 @@ Number of events Not supported by this provider Icon background + Legacy icon style None Dynamic White 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 0565747c..462e05c0 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/CalendarDynamicLauncherIcon.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/CalendarDynamicLauncherIcon.kt @@ -16,14 +16,11 @@ class CalendarDynamicLauncherIcon( backgroundScale: Float, val packageName: String, val drawableIds: IntArray, - autoGenerateBackgroundMode: Int ) : DynamicLauncherIcon( foreground, background, foregroundScale, backgroundScale, - /** Not needed, we already have a background **/ - autoGenerateBackgroundMode ) { var currentDay = 0 @@ -39,7 +36,7 @@ class CalendarDynamicLauncherIcon( Executors.newSingleThreadExecutor().execute { val currentDayDrawable = resources.getDrawableOrNull(drawableIds[day - 1]) ?: return@execute - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && currentDayDrawable is AdaptiveIconDrawable) { + if (currentDayDrawable is AdaptiveIconDrawable) { foreground = currentDayDrawable.foreground background = currentDayDrawable.background foregroundScale = 1.5f 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 b65293a6..b74fd8e5 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/ClockDynamicLauncherIcon.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/ClockDynamicLauncherIcon.kt @@ -23,9 +23,7 @@ class ClockDynamicLauncherIcon( foreground, background, foregroundScale, - backgroundScale, - /** Not needed, we already have a background **/ - LauncherIcon.BACKGROUND_WHITE + backgroundScale ) { 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 b4959932..1cb92c49 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/DynamicLauncherIcon.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/DynamicLauncherIcon.kt @@ -7,15 +7,13 @@ abstract class DynamicLauncherIcon( foreground: Drawable, background: Drawable?, foregroundScale: Float, - backgroundScale: Float, - autoGenerateBackgroundMode: Int + backgroundScale: Float ) : LauncherIcon( foreground, background, foregroundScale, backgroundScale, - autoGenerateBackgroundMode ) { abstract fun update(context: Context) 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 076bc1e3..6df3132e 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/IconPackManager.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/IconPackManager.kt @@ -28,24 +28,6 @@ private val SUPPORTED_GRAYSCALE_MAP_PROVIDERS = arrayOf( class IconPackManager( val context: Context ) { - var selectedIconPack: String - get() { - return context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) - .getString(KEY_ICON_PACK, "")!! - } - set(value) { - Log.d("MM20", "Selected icon pack: $value") - context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) - .edit() - .putString(KEY_ICON_PACK, value) - .apply() - } - - - fun selectIconPack(iconPack: String) { - selectedIconPack = iconPack - } - suspend fun getInstalledIconPacks(): List { return withContext(Dispatchers.IO) { AppDatabase.getInstance(context).iconDao().getInstalledIconPacks().map { 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 f04269d1..9cee7c70 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/IconRepository.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/IconRepository.kt @@ -6,16 +6,17 @@ import android.content.Intent import android.content.IntentFilter import android.util.LruCache import de.mm20.launcher2.icons.providers.* +import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.LauncherPreferences import de.mm20.launcher2.search.data.Searchable import kotlinx.coroutines.* -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.* class IconRepository( val context: Context, private val iconPackManager: IconPackManager, private val dynamicIconController: DynamicIconController, + private val dataStore: LauncherDataStore ) { private val appReceiver = object : BroadcastReceiver() { @@ -28,7 +29,7 @@ class IconRepository( private val cache = LruCache(200) - private var iconProviders: List = listOf() + private var iconProviders: MutableStateFlow> = MutableStateFlow(listOf()) private lateinit var placeholderProvider: IconProvider init { @@ -41,33 +42,64 @@ class IconRepository( addAction(Intent.ACTION_PACKAGE_CHANGED) addDataScheme("package") }) - recreate() + + scope.launch { + dataStore.data.map { it.icons }.distinctUntilChanged().collectLatest { + val placeholderProvider = if (it.themedIcons) { + ThemedPlaceholderIconProvider(context) + } else { + PlaceholderIconProvider(context) + } + val providers = mutableListOf() + + if (it.themedIcons) { + providers.add(ThemedIconProvider(context)) + } + + if (it.iconPack.isNotBlank()) { + providers.add(IconPackIconProvider(context, it.iconPack, it.legacyIconBg)) + } + providers.add(GoogleClockIconProvider(context)) + providers.add(CalendarIconProvider(context)) + providers.add(SystemIconProvider(context, it.legacyIconBg)) + providers.add(placeholderProvider) + cache.evictAll() + + this@IconRepository.placeholderProvider = placeholderProvider + iconProviders.value = providers + } + } } - fun getIcon(searchable: Searchable, size: Int): Flow = flow { - var icon = cache.get(searchable.key) - if (icon != null) { - emit(icon) - return@flow - } - - icon = placeholderProvider.getIcon(searchable, size) - emit(icon) - - for (provider in iconProviders) { - val ic = provider.getIcon(searchable, size) - if (ic != null) { - icon = ic - if (icon is DynamicLauncherIcon) { - dynamicIconController.registerIcon(icon) - } - break + fun getIcon(searchable: Searchable, size: Int): Flow = channelFlow { + iconProviders.collectLatest { providers -> + var icon = cache.get(searchable.key) + if (icon != null) { + send(icon) + return@collectLatest + } + + val placeholder = placeholderProvider.getIcon(searchable, size) + placeholder?.let { send(it) } + + for (provider in providers) { + val ic = provider.getIcon(searchable, size) + if (ic != null) { + if (ic is DynamicLauncherIcon) { + dynamicIconController.registerIcon(ic) + } + icon = ic + break + } + } + if (icon != null) { + cache.put(searchable.key, icon) + send(icon) + } else { + icon = placeholderProvider.getIcon(searchable, size) + send(icon) } - } - if (icon != null) { - cache.put(searchable.key, icon) - emit(icon) } } @@ -85,27 +117,7 @@ class IconRepository( } } - fun recreate() { - 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)) - providers.add(placeholderProvider) - cache.evictAll() - - iconProviders = providers + suspend fun getInstalledIconPacks(): List { + return iconPackManager.getInstalledIconPacks() } } \ No newline at end of file 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 911b47a9..4a38ba86 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/Module.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/Module.kt @@ -6,5 +6,5 @@ import org.koin.dsl.module val iconsModule = module { single { DynamicIconController(androidContext()) } single { IconPackManager(androidContext()) } - single { IconRepository(androidContext(), get(), get()) } + single { IconRepository(androidContext(), get(), 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 index ce4d6bfc..bdbbf603 100644 --- a/icons/src/main/java/de/mm20/launcher2/icons/ThemedCalendarDynamicLauncherIcon.kt +++ b/icons/src/main/java/de/mm20/launcher2/icons/ThemedCalendarDynamicLauncherIcon.kt @@ -21,8 +21,6 @@ class ThemedCalendarDynamicLauncherIcon( background = background, foregroundScale = foregroundScale, backgroundScale = 1f, - /** Not needed, we already have a background **/ - BACKGROUND_WHITE ) { var currentDay = 0 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 6c25aaac..bd2a42f5 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 @@ -42,8 +42,7 @@ class CalendarIconProvider(val context: Context): IconProvider { foregroundScale = 1.5f, backgroundScale = 1.5f, packageName = component.packageName, - drawableIds = drawableIds, - autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt() + drawableIds = drawableIds ) } } \ 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 eadeebe0..1957c529 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 @@ -17,13 +17,16 @@ import de.mm20.launcher2.database.AppDatabase import de.mm20.launcher2.icons.CalendarDynamicLauncherIcon import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.ktx.randomElementOrNull -import de.mm20.launcher2.preferences.IconShape -import de.mm20.launcher2.preferences.LauncherPreferences +import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.search.data.LauncherApp import de.mm20.launcher2.search.data.Searchable import kotlin.math.roundToInt -class IconPackIconProvider(val context: Context, val iconPack: String): IconProvider { +class IconPackIconProvider( + private val context: Context, + private val iconPack: String, + private val legacyIconBackground: Settings.IconSettings.LegacyIconBackground + ): IconProvider { override suspend fun getIcon(searchable: Searchable, size: Int): LauncherIcon? { if (searchable !is LauncherApp) return null val res = try { @@ -57,18 +60,14 @@ class IconPackIconProvider(val context: Context, val iconPack: String): IconProv LauncherIcon( foreground = drawable, foregroundScale = getScale(), - autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt() + autoGenerateBackgroundMode = legacyIconBackground.number ) } } } private fun getScale(): Float { - return when (LauncherPreferences.instance.iconShape) { - IconShape.CIRCLE, IconShape.PLATFORM_DEFAULT -> 0.7f - else -> 0.8f - - } + return 0.7f } private suspend fun generateIcon( @@ -151,7 +150,7 @@ class IconPackIconProvider(val context: Context, val iconPack: String): IconProv return LauncherIcon( foreground = BitmapDrawable(context.resources, bitmap), foregroundScale = getScale(), - autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt() + autoGenerateBackgroundMode = legacyIconBackground.number ) } @@ -200,7 +199,6 @@ class IconPackIconProvider(val context: Context, val iconPack: String): IconProv backgroundScale = 1.5f, packageName = iconPack, drawableIds = drawableIds, - autoGenerateBackgroundMode = LauncherPreferences.instance.legacyIconBg.toInt() ) } } \ No newline at end of file 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 330ba85b..23323fe7 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 @@ -2,10 +2,14 @@ package de.mm20.launcher2.icons.providers import android.content.Context import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.search.data.Searchable -class SystemIconProvider(val context: Context) : IconProvider { +class SystemIconProvider( + private val context: Context, + private val legacyIconBackground: Settings.IconSettings.LegacyIconBackground + ) : IconProvider { override suspend fun getIcon(searchable: Searchable, size: Int): LauncherIcon? { - return searchable.loadIcon(context, size) + return searchable.loadIcon(context, size, legacyIconBackground) } } \ No newline at end of file diff --git a/search/src/main/java/de/mm20/launcher2/search/data/Searchable.kt b/search/src/main/java/de/mm20/launcher2/search/data/Searchable.kt index 754bbc50..35d16f05 100644 --- a/search/src/main/java/de/mm20/launcher2/search/data/Searchable.kt +++ b/search/src/main/java/de/mm20/launcher2/search/data/Searchable.kt @@ -6,6 +6,8 @@ import android.content.Intent import android.os.Bundle import android.widget.Toast import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.Settings.IconSettings.LegacyIconBackground import de.mm20.launcher2.search.R import java.text.Collator @@ -32,7 +34,7 @@ abstract class Searchable : Comparable { } } - open suspend fun loadIcon(context: Context, size: Int): LauncherIcon? = null + open suspend fun loadIcon(context: Context, size: Int, legacyIconBackground: LegacyIconBackground): LauncherIcon? = null abstract fun getPlaceholderIcon(context: Context): LauncherIcon override fun compareTo(other: Searchable): Int { diff --git a/ui/src/main/java/de/mm20/launcher2/ui/ShapedLauncherIcon.kt b/ui/src/main/java/de/mm20/launcher2/ui/ShapedLauncherIcon.kt index a4cc7bc1..321b61e5 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/ShapedLauncherIcon.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/ShapedLauncherIcon.kt @@ -23,6 +23,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.Settings.IconSettings.LegacyIconBackground import de.mm20.launcher2.search.data.Searchable import de.mm20.launcher2.ui.icons.PlaceholderIcon import de.mm20.launcher2.ui.icons.getPlaceholderIcon @@ -48,7 +50,7 @@ fun ShapedLauncherIcon( LaunchedEffect(item) { icon = withContext(Dispatchers.IO) { - item.loadIcon(context, iconSize) + item.loadIcon(context, iconSize, LegacyIconBackground.Dynamic) } } diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivity.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivity.kt index d317b7ef..eca825fb 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivity.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivity.kt @@ -35,8 +35,6 @@ class LauncherActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val iconRepository: IconRepository by inject() - iconRepository.recreate() WindowCompat.setDecorFitsSystemWindows(window, false) diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt index 2d3f0f95..50c33fa4 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt @@ -1,13 +1,15 @@ package de.mm20.launcher2.ui.settings.appearance -import android.content.Context import android.graphics.drawable.ColorDrawable import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.content.res.AppCompatResources import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.GridCells import androidx.compose.foundation.lazy.LazyVerticalGrid import androidx.compose.foundation.lazy.items @@ -30,7 +32,6 @@ import com.google.accompanist.pager.HorizontalPagerIndicator import com.google.accompanist.pager.rememberPagerState import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.ktx.dp -import de.mm20.launcher2.preferences.IconShape import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme import de.mm20.launcher2.preferences.Settings.IconSettings @@ -111,7 +112,7 @@ fun AppearanceSettingsScreen() { ) } PreferenceCategory(stringResource(R.string.preference_category_icons)) { - val iconShape by viewModel.iconShape.observeAsState() + val iconShape by viewModel.iconShape.observeAsState(IconSettings.IconShape.PlatformDefault) IconShapePreference( title = stringResource(R.string.preference_icon_shape), summary = getShapeName(iconShape), @@ -120,6 +121,46 @@ fun AppearanceSettingsScreen() { viewModel.setIconShape(it) } ) + val themedIcons by viewModel.themedIcons.observeAsState() + SwitchPreference( + title = stringResource(R.string.preference_themed_icons), + summary = stringResource(R.string.preference_themed_icons_summary), + value = themedIcons == true, + onValueChanged = { + viewModel.setThemedIcons(it) + } + ) + + val iconPack by viewModel.iconPack.observeAsState() + val installedIconPacks by viewModel.installedIconPacks.observeAsState(emptyList()) + val items = installedIconPacks.map { + it.name to it.packageName + } + ListPreference( + title = stringResource(R.string.preference_icon_pack), + items = items, + summary = if (items.size <= 1) { + stringResource(R.string.preference_icon_pack_summary_empty) + } else { + items.firstOrNull { iconPack == it.value }?.label ?: "System" + }, + enabled = installedIconPacks.size > 1, + value = iconPack, + onValueChanged = { + if (it != null) viewModel.setIconPack(it) + } + ) + + val legacyIconBackground by viewModel.legacyIconBackground.observeAsState() + LegacyIconBackgroundPreference( + title = stringResource(R.string.preference_legacy_icon_bg), + summary = stringResource(R.string.preference_legacy_icon_bg_summary), + value = legacyIconBackground, + onValueChanged = { + viewModel.setLegacyIconBackground(it) + }, + iconShape = iconShape + ) } PreferenceCategory(stringResource(R.string.preference_category_searchbar)) { val searchBarStyle by viewModel.searchBarStyle.observeAsState() @@ -263,14 +304,19 @@ fun IconShapePreference( .padding(bottom = 16.dp, start = 16.dp, end = 16.dp) ) { items(shapes) { - Column(modifier = Modifier - .padding(8.dp), - horizontalAlignment = Alignment.CenterHorizontally) { + Column( + modifier = Modifier + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { AndroidView(factory = { context -> LauncherIconView(context).apply { shape = it icon = LauncherIcon( - foreground = AppCompatResources.getDrawable(context, R.mipmap.ic_launcher_foreground)!!, + foreground = AppCompatResources.getDrawable( + context, + R.mipmap.ic_launcher_foreground + )!!, background = ColorDrawable(context.getColor(R.color.ic_launcher_background)) ) setOnClickListener { _ -> @@ -287,7 +333,90 @@ fun IconShapePreference( getShapeName(it) ?: "", textAlign = TextAlign.Center, style = MaterialTheme.typography.labelMedium, - modifier = Modifier.padding(top = 4.dp)) + modifier = Modifier.padding(top = 4.dp) + ) + } + } + } + } + } + } + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun LegacyIconBackgroundPreference( + title: String, + summary: String? = null, + value: IconSettings.LegacyIconBackground?, + onValueChanged: (IconSettings.LegacyIconBackground) -> Unit, + iconShape: IconSettings.IconShape +) { + var showDialog by remember { mutableStateOf(false) } + Preference(title = title, summary = summary, onClick = { showDialog = true }) + + if (showDialog && value != null) { + val colors = remember { + IconSettings.LegacyIconBackground.values() + .filter { it != IconSettings.LegacyIconBackground.UNRECOGNIZED } + } + Dialog(onDismissRequest = { showDialog = false }) { + Surface( + tonalElevation = 16.dp, + shadowElevation = 16.dp, + shape = RoundedCornerShape(16.dp), + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp), + ) { + Column( + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = title, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding( + start = 24.dp, end = 24.dp, top = 16.dp, bottom = 8.dp + ) + ) + LazyVerticalGrid( + cells = GridCells.Adaptive(96.dp), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp, start = 16.dp, end = 16.dp) + ) { + items(colors) { + Column( + modifier = Modifier + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + AndroidView(factory = { context -> + LauncherIconView(context).apply { + shape = iconShape + icon = LauncherIcon( + foreground = AppCompatResources.getDrawable( + context, + R.mipmap.ic_launcher_foreground + )!!, + background = null, + autoGenerateBackgroundMode = when (it) { + IconSettings.LegacyIconBackground.Dynamic -> LauncherIcon.BACKGROUND_DYNAMIC + IconSettings.LegacyIconBackground.None -> LauncherIcon.BACKGROUND_NONE + else -> LauncherIcon.BACKGROUND_WHITE + } + ) + setOnClickListener { _ -> + onValueChanged(it) + showDialog = false + } + layoutParams = ViewGroup.LayoutParams( + (48 * context.dp).toInt(), + (48 * context.dp).toInt(), + ) + } + }) } } } @@ -300,15 +429,17 @@ fun IconShapePreference( @Composable private fun getShapeName(shape: IconSettings.IconShape?): String? { - return stringResource(when (shape) { - IconSettings.IconShape.Triangle -> R.string.preference_icon_shape_triangle - IconSettings.IconShape.Hexagon -> R.string.preference_icon_shape_hexagon - IconSettings.IconShape.RoundedSquare -> R.string.preference_icon_shape_rounded_square - IconSettings.IconShape.Squircle -> R.string.preference_icon_shape_squircle - IconSettings.IconShape.Square -> R.string.preference_icon_shape_square - IconSettings.IconShape.Pentagon -> R.string.preference_icon_shape_pentagon - IconSettings.IconShape.PlatformDefault -> R.string.preference_icon_shape_platform - IconSettings.IconShape.Circle -> R.string.preference_icon_shape_circle - else -> return null - }) + return stringResource( + when (shape) { + IconSettings.IconShape.Triangle -> R.string.preference_icon_shape_triangle + IconSettings.IconShape.Hexagon -> R.string.preference_icon_shape_hexagon + IconSettings.IconShape.RoundedSquare -> R.string.preference_icon_shape_rounded_square + IconSettings.IconShape.Squircle -> R.string.preference_icon_shape_squircle + IconSettings.IconShape.Square -> R.string.preference_icon_shape_square + IconSettings.IconShape.Pentagon -> R.string.preference_icon_shape_pentagon + IconSettings.IconShape.PlatformDefault -> R.string.preference_icon_shape_platform + IconSettings.IconShape.Circle -> R.string.preference_icon_shape_circle + else -> return null + } + ) } \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt index ff4d4590..6cbc329e 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt @@ -2,13 +2,14 @@ package de.mm20.launcher2.ui.settings.appearance import android.content.Intent import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.ViewModel -import androidx.lifecycle.asLiveData -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.* +import de.mm20.launcher2.icons.IconPack +import de.mm20.launcher2.icons.IconRepository import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme +import de.mm20.launcher2.preferences.Settings.IconSettings.LegacyIconBackground import de.mm20.launcher2.preferences.Settings.SearchBarSettings import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -18,6 +19,8 @@ import org.koin.core.component.inject class AppearanceSettingsScreenVM : ViewModel(), KoinComponent { private val dataStore: LauncherDataStore by inject() + private val iconRepository: IconRepository by inject() + val theme = dataStore.data.map { it.appearance.theme }.asLiveData() fun setTheme(theme: Theme) { viewModelScope.launch { @@ -93,4 +96,56 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent { } } } + + val legacyIconBackground = dataStore.data.map { it.icons.legacyIconBg }.asLiveData() + fun setLegacyIconBackground(legacyIconBackground: LegacyIconBackground) { + viewModelScope.launch { + dataStore.updateData { + it.toBuilder() + .setIcons( + it.icons.toBuilder() + .setLegacyIconBg(legacyIconBackground) + ) + .build() + } + } + } + + val themedIcons = dataStore.data.map { it.icons.themedIcons }.asLiveData() + fun setThemedIcons(themedIcons: Boolean) { + viewModelScope.launch { + dataStore.updateData { + it.toBuilder() + .setIcons( + it.icons.toBuilder() + .setThemedIcons(themedIcons) + ) + .build() + } + } + } + + val installedIconPacks: LiveData> = liveData { + emit( + listOf(IconPack( + name = "System", + packageName = "", + version = "", + )) + + iconRepository.getInstalledIconPacks() + ) + } + val iconPack = dataStore.data.map { it.icons.iconPack }.asLiveData() + fun setIconPack(iconPack: String) { + viewModelScope.launch { + dataStore.updateData { + it.toBuilder() + .setIcons( + it.icons.toBuilder() + .setIconPack(iconPack) + ) + .build() + } + } + } } \ No newline at end of file diff --git a/websites/src/main/java/de/mm20/launcher2/search/data/Website.kt b/websites/src/main/java/de/mm20/launcher2/search/data/Website.kt index 37f1ae57..49c5682b 100644 --- a/websites/src/main/java/de/mm20/launcher2/search/data/Website.kt +++ b/websites/src/main/java/de/mm20/launcher2/search/data/Website.kt @@ -13,6 +13,8 @@ import coil.request.ImageRequest import de.mm20.launcher2.graphics.TextDrawable import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.ktx.sp +import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.Settings.IconSettings.LegacyIconBackground import de.mm20.launcher2.websites.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -28,7 +30,7 @@ class Website( ) : Searchable() { override val key = "web://$url" - override suspend fun loadIcon(context: Context, size: Int): LauncherIcon? { + override suspend fun loadIcon(context: Context, size: Int, legacyIconBackground: LegacyIconBackground): LauncherIcon? { if (favicon.isEmpty()) return null try { val request = ImageRequest.Builder(context) @@ -36,18 +38,11 @@ class Website( .size(size) .build() val icon = context.imageLoader.execute(request).drawable ?: return null - val color = if (color != 0) color else { - withContext(Dispatchers.Default) { - Palette - .from(icon.toBitmap()) - .generate() - .getLightMutedColor(Color.WHITE) - } - } return LauncherIcon( foreground = icon, - background = ColorDrawable(color), - foregroundScale = 0.7f + background = color.let { ColorDrawable(it) }, + foregroundScale = 0.7f, + autoGenerateBackgroundMode = legacyIconBackground.number ) } catch (e: ExecutionException) { return null diff --git a/wikipedia/src/main/java/de/mm20/launcher2/search/data/Wikipedia.kt b/wikipedia/src/main/java/de/mm20/launcher2/search/data/Wikipedia.kt index fc264f3f..d23b60f6 100644 --- a/wikipedia/src/main/java/de/mm20/launcher2/search/data/Wikipedia.kt +++ b/wikipedia/src/main/java/de/mm20/launcher2/search/data/Wikipedia.kt @@ -30,10 +30,6 @@ class Wikipedia( ) : Searchable() { override val key = "wikipedia://$wikipediaUrl:$id" - override suspend fun loadIcon(context: Context, size: Int): LauncherIcon? { - return null - } - override fun getPlaceholderIcon(context: Context): LauncherIcon { return LauncherIcon( foreground = context.getDrawable(R.drawable.ic_wikipedia)!!,