diff --git a/AGENT.md b/AGENT.md new file mode 100644 index 0000000000..3d986956dd --- /dev/null +++ b/AGENT.md @@ -0,0 +1,18 @@ +**I. Core Architectural Principles (The "Lawnchair Way")** + +* **Pragmatic Modernization:** The primary architectural goal is to incrementally refactor the + legacy AOSP codebase towards a modern, testable, and maintainable structure. We do not rewrite for + the sake of rewriting. +* **Decoupling & Abstraction:** New features and refactors must prioritize decoupling from the core + `Launcher3` (`src`) module wherever possible. The preferred pattern is to create clean Kotlin + interfaces (`lawnchair` package) that act as a bridge to legacy systems. +* **Unidirectional Data Flow (UDF) for UI:** All new, complex UI screens (especially in Settings) + are to be built using a ViewModel-centric UDF architecture. State should be exposed via + `StateFlow` and consumed by "dumb" Jetpack Compose UI. +* **Embrace Kotlin & Coroutines:** All new asynchronous code must use Kotlin Coroutines, preferably + `Flow`. Legacy callbacks and `AsyncTask`-style patterns are to be eliminated during refactoring. +* **Respect for Constraints:** The architecture must work within the project's real-world + constraints: +* **DI is a Service Locator:** We use a `MainThreadInitializedObject` singleton pattern, not + Hilt/Dagger. Note that this pattern is slowly being migrated to use Hilt/Dagger (without the + Android Gradle Plugin) by Google, so expect inconsistency. diff --git a/lawnchair/src/app/lawnchair/allapps/AllAppsSearchInput.kt b/lawnchair/src/app/lawnchair/allapps/AllAppsSearchInput.kt index aa44969f27..35a5c2fc84 100644 --- a/lawnchair/src/app/lawnchair/allapps/AllAppsSearchInput.kt +++ b/lawnchair/src/app/lawnchair/allapps/AllAppsSearchInput.kt @@ -201,7 +201,6 @@ class AllAppsSearchInput(context: Context, attrs: AttributeSet?) : // Sometimes the user has to click the input bar one more time // for the keyboard to show. - input.showKeyboard() } else { setBackgroundVisibility(true, 1f) animateHintVisibility(false) diff --git a/lawnchair/src/app/lawnchair/data/iconoverride/IconOverride.kt b/lawnchair/src/app/lawnchair/data/iconoverride/IconOverride.kt index af6136b4f0..cb92854398 100644 --- a/lawnchair/src/app/lawnchair/data/iconoverride/IconOverride.kt +++ b/lawnchair/src/app/lawnchair/data/iconoverride/IconOverride.kt @@ -3,7 +3,7 @@ package app.lawnchair.data.iconoverride import androidx.room.Embedded import androidx.room.Entity import androidx.room.PrimaryKey -import app.lawnchair.icons.IconPickerItem +import app.lawnchair.icons.picker.IconPickerItem import com.android.launcher3.util.ComponentKey @Entity diff --git a/lawnchair/src/app/lawnchair/data/iconoverride/IconOverrideRepository.kt b/lawnchair/src/app/lawnchair/data/iconoverride/IconOverrideRepository.kt index 1430fac0d2..51c227b827 100644 --- a/lawnchair/src/app/lawnchair/data/iconoverride/IconOverrideRepository.kt +++ b/lawnchair/src/app/lawnchair/data/iconoverride/IconOverrideRepository.kt @@ -2,7 +2,7 @@ package app.lawnchair.data.iconoverride import android.content.Context import app.lawnchair.data.AppDatabase -import app.lawnchair.icons.IconPickerItem +import app.lawnchair.icons.picker.IconPickerItem import com.android.launcher3.LauncherAppState import com.android.launcher3.dagger.ApplicationContext import com.android.launcher3.dagger.LauncherAppComponent diff --git a/lawnchair/src/app/lawnchair/icons/IconEntryWithDrawable.kt b/lawnchair/src/app/lawnchair/icons/IconEntryWithDrawable.kt deleted file mode 100644 index 131ad711f5..0000000000 --- a/lawnchair/src/app/lawnchair/icons/IconEntryWithDrawable.kt +++ /dev/null @@ -1,8 +0,0 @@ -package app.lawnchair.icons - -import android.graphics.drawable.Drawable - -data class IconEntryWithDrawable( - val entry: IconEntry, - val drawable: Drawable, -) diff --git a/lawnchair/src/app/lawnchair/icons/LawnchairIconProvider.kt b/lawnchair/src/app/lawnchair/icons/LawnchairIconProvider.kt index c42149cc3f..3af7a74b17 100644 --- a/lawnchair/src/app/lawnchair/icons/LawnchairIconProvider.kt +++ b/lawnchair/src/app/lawnchair/icons/LawnchairIconProvider.kt @@ -14,95 +14,80 @@ import android.content.Intent.ACTION_TIME_CHANGED import android.content.Intent.ACTION_TIME_TICK import android.content.IntentFilter import android.content.pm.ApplicationInfo -import android.content.pm.ComponentInfo import android.content.pm.PackageItemInfo import android.content.res.Resources import android.graphics.drawable.Drawable import android.os.Handler import android.os.UserHandle import android.os.UserManager -import android.text.TextUtils import android.util.ArrayMap import android.util.Log import androidx.core.content.getSystemService +import androidx.core.graphics.drawable.toDrawable import app.lawnchair.data.iconoverride.IconOverrideRepository +import app.lawnchair.icons.iconpack.IconPack +import app.lawnchair.icons.iconpack.IconPackProvider +import app.lawnchair.icons.picker.IconEntry +import app.lawnchair.icons.picker.IconType import app.lawnchair.preferences.PreferenceManager -import app.lawnchair.util.Constants.LAWNICONS_PACKAGE_NAME import app.lawnchair.util.MultiSafeCloseable -import app.lawnchair.util.getPackageVersionCode import app.lawnchair.util.isPackageInstalled import com.android.launcher3.R -import com.android.launcher3.config.FeatureFlags +import com.android.launcher3.dagger.ApplicationContext +import com.android.launcher3.dagger.LauncherAppSingleton import com.android.launcher3.graphics.ThemeManager -import com.android.launcher3.icons.IconProvider -import com.android.launcher3.icons.LauncherIconProvider.ATTR_DRAWABLE -import com.android.launcher3.icons.LauncherIconProvider.ATTR_PACKAGE -import com.android.launcher3.icons.LauncherIconProvider.TAG_ICON +import com.android.launcher3.icons.ClockDrawableWrapper +import com.android.launcher3.icons.LauncherIconProvider +import com.android.launcher3.icons.mono.ThemedIconDrawable import com.android.launcher3.util.ComponentKey import com.android.launcher3.util.SafeCloseable +import javax.inject.Inject import org.xmlpull.v1.XmlPullParser -class LawnchairIconProvider @JvmOverloads constructor( - private val context: Context, - supportsIconTheme: Boolean = false, -) : IconProvider(context) { - +@LauncherAppSingleton +class LawnchairIconProvider @Inject constructor( + @ApplicationContext private val context: Context, + val themeManager: ThemeManager, +) : LauncherIconProvider( + context, + themeManager, +) { private val prefs = PreferenceManager.getInstance(context) + private val themedIconsEnabled = prefs.themedIcons.get() + private val iconPackPref = prefs.iconPackPackage - private val themedIconPackPref = prefs.themedIconPackPackage + private val themedIconSourcePref = prefs.themedIconPackPackage private val iconPackProvider = IconPackProvider.INSTANCE.get(context) private val overrideRepo = IconOverrideRepository.INSTANCE.get(context) private val iconPack get() = iconPackProvider.getIconPack(iconPackPref.get())?.apply { loadBlocking() } - private val themedIconPack - get() = iconPackProvider.getIconPack(themedIconPackPref.get())?.apply { loadBlocking() } - - private var isOlderLawniconsInstalled = context.packageManager.getPackageVersionCode(LAWNICONS_PACKAGE_NAME) in 1..3 - - private var iconPackVersion = 0L + private val themedIconSource + get() = iconPackProvider.getIconPack(themedIconSourcePref.get())?.apply { loadBlocking() } private var themeMapName: String = "" - private var mThemedIconMap: Map? = null - - private val mThemeManager: ThemeManager? = null + private var _themeMap: Map? = null val themeMap: Map get() { - if (!context.isThemedIconsEnabled()) { - mThemedIconMap = DISABLED_MAP + if (!themedIconsEnabled) { + _themeMap = DISABLED_MAP } - if (mThemedIconMap == null) { - mThemedIconMap = createThemedIconMap() + if (_themeMap == null) { + _themeMap = getThemedIconMap() } - if (isOlderLawniconsInstalled) { - themeMapName = themedIconPackPref.get() - mThemedIconMap = createThemedIconMap() + if (themedIconSource != null && themeMapName == "") { + _themeMap = super.getThemedIconMap() } - if (themedIconPack != null && themeMapName != themedIconPack!!.packPackageName) { - themeMapName = themedIconPack!!.packPackageName - mThemedIconMap = createThemedIconMap() + if (themedIconSource != null && themeMapName != themedIconSource!!.packPackageName) { + themeMapName = themedIconSource!!.packPackageName + _themeMap = getThemedIconMap() } - return mThemedIconMap!! + return _themeMap!! } - private val supportsIconTheme get() = themeMap != DISABLED_MAP - init { - setIconThemeSupported(supportsIconTheme) - } - - /** - * Enables or disables icon theme support (Lawnchair) - * @see com.android.launcher3.icons.LauncherIconProvider.setIconThemeSupported - */ - fun setIconThemeSupported(isSupported: Boolean) { - if (isSupported && isOlderLawniconsInstalled && FeatureFlags.USE_LOCAL_ICON_OVERRIDES.get()) { - mThemedIconMap = null - } else { - mThemedIconMap = DISABLED_MAP - } - } + val systemIconState = themeManager.iconState private fun resolveIconEntry(componentName: ComponentName, user: UserHandle): IconEntry? { val componentKey = ComponentKey(componentName, user) @@ -112,7 +97,7 @@ class LawnchairIconProvider @JvmOverloads constructor( return overrideItem.toIconEntry() } - val iconPack = this.iconPack ?: return null + val iconPack = iconPack ?: return null // then look for dynamic calendar val calendarEntry = iconPack.getCalendar(componentName) if (calendarEntry != null) { @@ -122,79 +107,136 @@ class LawnchairIconProvider @JvmOverloads constructor( return iconPack.getIcon(componentName) } - fun isThemeEnabled(): Boolean { - return mThemedIconMap != DISABLED_MAP - } + override fun getIcon( + info: PackageItemInfo, + appInfo: ApplicationInfo, + iconDpi: Int, + ): Drawable { + val packageName = appInfo.packageName + val componentName = context.packageManager.getLaunchIntentForPackage(packageName)?.component + val user = UserHandle.getUserHandleForUid(appInfo.uid) - override fun getThemeDataForPackage(packageName: String?): ThemeData? { - return getThemedIconMap().get(packageName) - } - - fun getThemedIconMap(): MutableMap { - if (mThemedIconMap != null) { - return mThemedIconMap!!.toMutableMap() // Lawnchair-TODO: This feels cursed? + var iconEntry: IconEntry? = null + if (componentName != null) { + iconEntry = resolveIconEntry(componentName, user) } - val map = ArrayMap() - val res = mContext.getResources() - try { - res.getXml(R.xml.grayscale_icon_map).use { parser -> - val depth = parser.getDepth() - var type: Int - while ((parser.next().also { type = it }) != XmlPullParser.START_TAG && - type != XmlPullParser.END_DOCUMENT - ); - while (( - (parser.next().also { type = it }) != XmlPullParser.END_TAG || - parser.getDepth() > depth - ) && - type != XmlPullParser.END_DOCUMENT - ) { - if (type != XmlPullParser.START_TAG) { - continue - } - if (TAG_ICON == parser.getName()) { - val pkg = parser.getAttributeValue(null, ATTR_PACKAGE) - val iconId = parser.getAttributeResourceValue( - null, - ATTR_DRAWABLE, - 0, + + var iconPackEntry = iconEntry + + val themeData = getThemeDataForPackage(packageName) + var themedIcon: Drawable? = null + + val themedColors = ThemedIconDrawable.getColors(context) + + if (iconEntry != null) { + val clock = iconPackProvider.getClockMetadata(iconEntry) + + if (iconEntry.type == IconType.Calendar) { + iconPackEntry = iconEntry.resolveDynamicCalendar(getDay()) + } + + when { + !themedIconsEnabled -> { + // theming is disabled, don't populate theme data + themedIcon = null + } + + clock != null -> { + // the icon supports dynamic clock, use dynamic themed clock + themedIcon = + ClockDrawableWrapper.forPackage(mContext, mClock.packageName, iconDpi) + .getMonochrome() + } + + packageName == mClock.packageName -> { + // is clock app but icon might not be adaptive, fallback to static themed clock + val clockThemedData = + ThemeData(context.resources, R.drawable.themed_icon_static_clock) + themedIcon = CustomAdaptiveIconDrawable( + themedColors[0].toDrawable(), + clockThemedData.loadPaddedDrawable().apply { setTint(themedColors[1]) }, + ) + } + + packageName == mCalendar.packageName -> { + // calendar app, apply the dynamic calendar icon + themedIcon = loadCalendarDrawable(iconDpi, themeData) + } + + else -> { + // regular icon + themedIcon = if (themeData != null) { + CustomAdaptiveIconDrawable( + themedColors[0].toDrawable(), + themeData.loadPaddedDrawable().apply { setTint(themedColors[1]) }, ) - if (iconId != 0 && !TextUtils.isEmpty(pkg)) { - map.put(pkg, ThemeData(res, iconId)) - } + } else { + null } } } - } catch (e: java.lang.Exception) { - Log.e(TAG, "Unable to parse icon map", e) } - mThemedIconMap = map - return mThemedIconMap!!.toMutableMap() // Lawnchair-TODO: This feels cursed? + + val iconPackIcon = iconPackEntry?.let { iconPackProvider.getDrawable(it, iconDpi, user) } + + return themedIcon ?: iconPackIcon ?: super.getIcon(info, appInfo, iconDpi) } - override fun getIcon(info: ComponentInfo?): Drawable { - return CustomAdaptiveIconDrawable.wrapNonNull(super.getIcon(info)) + override fun getThemeDataForPackage(packageName: String?): ThemeData? { + return themeMap[packageName] } - override fun getIcon(info: ComponentInfo?, iconDpi: Int): Drawable { - return CustomAdaptiveIconDrawable.wrapNonNull(super.getIcon(info, iconDpi)) - } + override fun getThemedIconMap(): MutableMap { + val themedIconMap = ArrayMap() - override fun getIcon(info: ApplicationInfo?): Drawable { - return CustomAdaptiveIconDrawable.wrapNonNull(super.getIcon(info)) - } + fun ArrayMap.updateFromResources( + resources: Resources, + packageName: String, + ) { + try { + @SuppressLint("DiscouragedApi") + val xmlId = resources.getIdentifier("grayscale_icon_map", "xml", packageName) + if (xmlId != 0) { + val parser = resources.getXml(xmlId) + val depth = parser.depth + var type: Int + while ( + ( + parser.next() + .also { type = it } != XmlPullParser.END_TAG || parser.depth > depth + ) && + type != XmlPullParser.END_DOCUMENT + ) { + if (type != XmlPullParser.START_TAG) continue + if (TAG_ICON == parser.name) { + val pkg = parser.getAttributeValue(null, ATTR_PACKAGE) + val iconId = parser.getAttributeResourceValue(null, ATTR_DRAWABLE, 0) + if (iconId != 0 && pkg.isNotEmpty()) { + this[pkg] = ThemeData(resources, iconId) + } + } + } + } + } catch (e: Exception) { + Log.e(TAG, "Unable to parse icon map.", e) + } + } - override fun getIcon(info: ApplicationInfo?, iconDpi: Int): Drawable { - return CustomAdaptiveIconDrawable.wrapNonNull(super.getIcon(info, iconDpi)) - } + // first, get Lawnchair's internal grayscale icon map + themedIconMap.updateFromResources( + resources = context.resources, + packageName = context.packageName, + ) - override fun getIcon(info: PackageItemInfo?, appInfo: ApplicationInfo?, iconDpi: Int): Drawable { - return CustomAdaptiveIconDrawable.wrapNonNull(super.getIcon(info, appInfo, iconDpi)) - } + if (context.packageManager.isPackageInstalled(packageName = themeMapName)) { + // get the grayscale icon map of the supported icon pack + themedIconMap.updateFromResources( + resources = context.packageManager.getResourcesForApplication(themeMapName), + packageName = themeMapName, + ) + } - override fun updateSystemState() { - super.updateSystemState() - mSystemState += "," + mThemeManager?.iconState?.toUniqueId() + return themedIconMap } override fun registerIconChangeListener( @@ -204,7 +246,7 @@ class LawnchairIconProvider @JvmOverloads constructor( return MultiSafeCloseable().apply { add(super.registerIconChangeListener(callback, handler)) add(IconPackChangeReceiver(context, handler, callback)) - add(LawniconsChangeReceiver(context, handler, callback)) + add(LawniconsChangeReceiver(context, handler)) } } @@ -219,12 +261,12 @@ class LawnchairIconProvider @JvmOverloads constructor( field?.close() field = value } - private var iconState = mThemeManager?.iconState + private var iconState = themeManager.iconState private val iconPackPref = PreferenceManager.getInstance(context).iconPackPackage private val themedIconPackPref = PreferenceManager.getInstance(context).themedIconPackPackage private val subscription = iconPackPref.subscribeChanges { - val newState = mThemeManager?.iconState + val newState = themeManager.iconState if (iconState != newState) { iconState = newState updateSystemState() @@ -232,7 +274,7 @@ class LawnchairIconProvider @JvmOverloads constructor( } } private val themedIconSubscription = themedIconPackPref.subscribeChanges { - val newState = mThemeManager?.iconState + val newState = themeManager.iconState if (iconState != newState) { iconState = newState updateSystemState() @@ -307,7 +349,6 @@ class LawnchairIconProvider @JvmOverloads constructor( private inner class LawniconsChangeReceiver( private val context: Context, handler: Handler, - private val callback: IconChangeListener, ) : BroadcastReceiver(), SafeCloseable { @@ -321,9 +362,6 @@ class LawnchairIconProvider @JvmOverloads constructor( } override fun onReceive(context: Context, intent: Intent) { - if (isThemeEnabled()) { - setIconThemeSupported(true) - } updateSystemState() } @@ -332,56 +370,7 @@ class LawnchairIconProvider @JvmOverloads constructor( } } - private fun createThemedIconMap(): MutableMap { - val map = ArrayMap() - - fun updateMapFromResources(resources: Resources, packageName: String) { - try { - @SuppressLint("DiscouragedApi") - val xmlId = resources.getIdentifier("grayscale_icon_map", "xml", packageName) - if (xmlId != 0) { - val parser = resources.getXml(xmlId) - val depth = parser.depth - var type: Int - while ( - (parser.next().also { type = it } != XmlPullParser.END_TAG || parser.depth > depth) && - type != XmlPullParser.END_DOCUMENT - ) { - if (type != XmlPullParser.START_TAG) continue - if (TAG_ICON == parser.name) { - val pkg = parser.getAttributeValue(null, ATTR_PACKAGE) - val iconId = parser.getAttributeResourceValue(null, ATTR_DRAWABLE, 0) - if (iconId != 0 && !pkg.isNullOrEmpty()) { - map[pkg] = ThemeData(resources, iconId) - } - } - } - } - } catch (e: Exception) { - Log.e(TAG, "Unable to parse icon map.", e) - } - } - updateMapFromResources( - resources = context.resources, - packageName = context.packageName, - ) - if (context.packageManager.isPackageInstalled(packageName = themeMapName)) { - iconPackVersion = context.packageManager.getPackageVersionCode(themeMapName) - updateMapFromResources( - resources = context.packageManager.getResourcesForApplication(themeMapName), - packageName = themeMapName, - ) - if (isOlderLawniconsInstalled) { - // updateMapWithDynamicIcons(context, map) - } - } - - return map - } - companion object { const val TAG = "LawnchairIconProvider" - - val DISABLED_MAP = emptyMap() } } diff --git a/lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt b/lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt new file mode 100644 index 0000000000..aadb7288bd --- /dev/null +++ b/lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt @@ -0,0 +1,74 @@ +package app.lawnchair.icons + +import android.content.Context +import android.util.Log +import app.lawnchair.icons.shape.IconShape +import app.lawnchair.icons.shape.PathShapeDelegate +import app.lawnchair.preferences2.PreferenceManager2 +import com.android.launcher3.LauncherPrefs +import com.android.launcher3.concurrent.annotations.Ui +import com.android.launcher3.dagger.ApplicationContext +import com.android.launcher3.dagger.LauncherAppSingleton +import com.android.launcher3.graphics.ThemeManager +import com.android.launcher3.util.DaggerSingletonTracker +import com.android.launcher3.util.LooperExecutor +import com.patrykmichalik.opto.core.firstBlocking +import javax.inject.Inject + +@LauncherAppSingleton +class LawnchairThemeManager +@Inject +constructor( + @ApplicationContext private val context: Context, + @Ui private val uiExecutor: LooperExecutor, + private val prefs: LauncherPrefs, + private val iconControllerFactory: IconControllerFactory, + private val lifecycle: DaggerSingletonTracker, + private val prefs2: PreferenceManager2, +) : ThemeManager( + context, + uiExecutor, + prefs, + iconControllerFactory, + lifecycle, +) { + override var iconState = parseIconStateV2(null) + + override fun verifyIconState() { + val newState = parseIconStateV2(iconState) + if (newState == iconState) return + iconState = newState + + listeners.forEach { it.onThemeChanged() } + } + + private fun parseIconStateV2(oldState: IconState?): IconState { + val currentShape: IconShape = try { + prefs2.iconShape.firstBlocking() + } catch (e: Exception) { + Log.d(TAG, "Error getting icon shape", e) + IconShape.Circle + } + + val shapePath = currentShape.getMaskPath() + val shapeKey = currentShape.getHashString() + + val iconShape = + if (oldState != null && oldState.iconMask == shapeKey) { + oldState.iconShape + } else { + PathShapeDelegate(shapePath) + } + + return IconState( + iconMask = shapeKey, + folderRadius = 1f, + shapeRadius = 1f, + themeController = iconControllerFactory.createThemeController(), + iconShape = iconShape, + folderShape = iconShape, + ) + } +} + +private const val TAG = "LawnchairThemeManager" diff --git a/lawnchair/src/app/lawnchair/icons/ThemeManagerModule.kt b/lawnchair/src/app/lawnchair/icons/ThemeManagerModule.kt new file mode 100644 index 0000000000..11ff23dd48 --- /dev/null +++ b/lawnchair/src/app/lawnchair/icons/ThemeManagerModule.kt @@ -0,0 +1,37 @@ +package app.lawnchair.icons + +import android.content.Context +import app.lawnchair.preferences2.PreferenceManager2 +import com.android.launcher3.LauncherPrefs +import com.android.launcher3.concurrent.annotations.Ui +import com.android.launcher3.dagger.ApplicationContext +import com.android.launcher3.dagger.LauncherAppSingleton +import com.android.launcher3.graphics.ThemeManager +import com.android.launcher3.util.DaggerSingletonTracker +import com.android.launcher3.util.LooperExecutor +import dagger.Module +import dagger.Provides + +@Module +class ThemeManagerModule { + + @Provides + @LauncherAppSingleton + fun provideThemeManager( + @ApplicationContext context: Context, + @Ui uiExecutor: LooperExecutor, + prefs: LauncherPrefs, + lifecycle: DaggerSingletonTracker, + iconControllerFactory: ThemeManager.IconControllerFactory, + prefs2: PreferenceManager2, + ): ThemeManager { + return LawnchairThemeManager( + context = context, + uiExecutor = uiExecutor, + prefs = prefs, + lifecycle = lifecycle, + iconControllerFactory = iconControllerFactory, + prefs2 = prefs2, + ) + } +} diff --git a/lawnchair/src/app/lawnchair/icons/CustomIconPack.kt b/lawnchair/src/app/lawnchair/icons/iconpack/CustomIconPack.kt similarity index 96% rename from lawnchair/src/app/lawnchair/icons/CustomIconPack.kt rename to lawnchair/src/app/lawnchair/icons/iconpack/CustomIconPack.kt index 327fa8ca66..1c3828b68f 100644 --- a/lawnchair/src/app/lawnchair/icons/CustomIconPack.kt +++ b/lawnchair/src/app/lawnchair/icons/iconpack/CustomIconPack.kt @@ -1,4 +1,4 @@ -package app.lawnchair.icons +package app.lawnchair.icons.iconpack import android.annotation.SuppressLint import android.content.ComponentName @@ -9,6 +9,12 @@ import android.content.res.Resources import android.content.res.XmlResourceParser import android.graphics.drawable.Drawable import android.util.Xml +import app.lawnchair.icons.ClockMetadata +import app.lawnchair.icons.ExtendedBitmapDrawable +import app.lawnchair.icons.picker.IconEntry +import app.lawnchair.icons.picker.IconPickerCategory +import app.lawnchair.icons.picker.IconPickerItem +import app.lawnchair.icons.picker.IconType import com.android.launcher3.R import java.io.IOException import kotlinx.coroutines.Dispatchers diff --git a/lawnchair/src/app/lawnchair/icons/IconPack.kt b/lawnchair/src/app/lawnchair/icons/iconpack/IconPack.kt similarity index 92% rename from lawnchair/src/app/lawnchair/icons/IconPack.kt rename to lawnchair/src/app/lawnchair/icons/iconpack/IconPack.kt index cac8638552..a451fa6e7f 100644 --- a/lawnchair/src/app/lawnchair/icons/IconPack.kt +++ b/lawnchair/src/app/lawnchair/icons/iconpack/IconPack.kt @@ -1,8 +1,12 @@ -package app.lawnchair.icons +package app.lawnchair.icons.iconpack import android.content.ComponentName import android.content.Context import android.graphics.drawable.Drawable +import app.lawnchair.icons.ClockMetadata +import app.lawnchair.icons.picker.IconEntry +import app.lawnchair.icons.picker.IconPickerCategory +import app.lawnchair.icons.picker.IconPickerItem import com.android.launcher3.compat.AlphabeticIndexCompat import java.util.concurrent.Semaphore import kotlinx.coroutines.CoroutineName diff --git a/lawnchair/src/app/lawnchair/icons/IconPackProvider.kt b/lawnchair/src/app/lawnchair/icons/iconpack/IconPackProvider.kt similarity index 63% rename from lawnchair/src/app/lawnchair/icons/IconPackProvider.kt rename to lawnchair/src/app/lawnchair/icons/iconpack/IconPackProvider.kt index 09ecaa08df..f5f8bf39ba 100644 --- a/lawnchair/src/app/lawnchair/icons/IconPackProvider.kt +++ b/lawnchair/src/app/lawnchair/icons/iconpack/IconPackProvider.kt @@ -1,17 +1,19 @@ -package app.lawnchair.icons +package app.lawnchair.icons.iconpack import android.content.Context import android.content.pm.PackageManager -import android.graphics.drawable.AdaptiveIconDrawable import android.graphics.drawable.Drawable import android.os.Build import android.os.Process import android.os.UserHandle +import app.lawnchair.icons.ClockMetadata +import app.lawnchair.icons.CustomAdaptiveIconDrawable +import app.lawnchair.icons.picker.IconEntry +import app.lawnchair.icons.shouldTransparentBGIcons import com.android.launcher3.dagger.ApplicationContext import com.android.launcher3.dagger.LauncherAppComponent import com.android.launcher3.dagger.LauncherAppSingleton import com.android.launcher3.icons.ClockDrawableWrapper -import com.android.launcher3.icons.IconProvider import com.android.launcher3.util.DaggerSingletonObject import com.android.launcher3.util.SafeCloseable import javax.inject.Inject @@ -49,26 +51,17 @@ class IconPackProvider @Inject constructor( fun getDrawable(iconEntry: IconEntry, iconDpi: Int, user: UserHandle): Drawable? { val iconPack = getIconPackOrSystem(iconEntry.packPackageName) ?: return null iconPack.loadBlocking() - val packageManager = context.packageManager val drawable = iconPack.getIcon(iconEntry, iconDpi) ?: return null - val shouldTintBackgrounds = context.shouldTintIconPackBackgrounds() val clockMetadata = if (user == Process.myUserHandle()) iconPack.getClock(iconEntry) else null try { if (clockMetadata != null) { val clockDrawable: ClockDrawableWrapper = ClockDrawableWrapper.forMeta(Build.VERSION.SDK_INT, clockMetadata) { - if (shouldTintBackgrounds) { - wrapThemedData( - packageManager, - iconEntry, - drawable, - ) - } else { - drawable - } + drawable } - return if (shouldTintBackgrounds && context.shouldTransparentBGIcons()) { + + return if (context.shouldTransparentBGIcons()) { clockDrawable.foreground } else { CustomAdaptiveIconDrawable( @@ -81,31 +74,6 @@ class IconPackProvider @Inject constructor( // Ignore } - if (shouldTintBackgrounds) { - return wrapThemedData(packageManager, iconEntry, drawable) - } - return drawable - } - - private fun wrapThemedData( - packageManager: PackageManager, - iconEntry: IconEntry, - drawable: Drawable, - ): Drawable { - val res = packageManager.getResourcesForApplication(iconEntry.packPackageName) - val resId = res.getIdentifier(iconEntry.name, "drawable", iconEntry.packPackageName) - val td = IconProvider.ThemeData(res, resId) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && - drawable is AdaptiveIconDrawable && - drawable.monochrome == null - ) { - return AdaptiveIconDrawable( - drawable.background, - drawable.foreground, - td.loadPaddedDrawable(), - ) - } return drawable } diff --git a/lawnchair/src/app/lawnchair/icons/SystemIconPack.kt b/lawnchair/src/app/lawnchair/icons/iconpack/SystemIconPack.kt similarity index 89% rename from lawnchair/src/app/lawnchair/icons/SystemIconPack.kt rename to lawnchair/src/app/lawnchair/icons/iconpack/SystemIconPack.kt index 2a356af97c..530b398922 100644 --- a/lawnchair/src/app/lawnchair/icons/SystemIconPack.kt +++ b/lawnchair/src/app/lawnchair/icons/iconpack/SystemIconPack.kt @@ -1,10 +1,15 @@ -package app.lawnchair.icons +package app.lawnchair.icons.iconpack import android.content.ComponentName import android.content.Context import android.content.pm.LauncherApps import android.graphics.drawable.Drawable import android.os.Process +import app.lawnchair.icons.ClockMetadata +import app.lawnchair.icons.picker.IconEntry +import app.lawnchair.icons.picker.IconPickerCategory +import app.lawnchair.icons.picker.IconPickerItem +import app.lawnchair.icons.picker.IconType import app.lawnchair.util.requireSystemService import com.android.launcher3.R import com.android.launcher3.pm.UserCache diff --git a/lawnchair/src/app/lawnchair/icons/IconEntry.kt b/lawnchair/src/app/lawnchair/icons/picker/IconEntry.kt similarity index 91% rename from lawnchair/src/app/lawnchair/icons/IconEntry.kt rename to lawnchair/src/app/lawnchair/icons/picker/IconEntry.kt index 1d8640cd71..f8c35e10ff 100644 --- a/lawnchair/src/app/lawnchair/icons/IconEntry.kt +++ b/lawnchair/src/app/lawnchair/icons/picker/IconEntry.kt @@ -1,4 +1,4 @@ -package app.lawnchair.icons +package app.lawnchair.icons.picker data class IconEntry( val packPackageName: String, diff --git a/lawnchair/src/app/lawnchair/icons/IconPickerCategory.kt b/lawnchair/src/app/lawnchair/icons/picker/IconPickerCategory.kt similarity index 90% rename from lawnchair/src/app/lawnchair/icons/IconPickerCategory.kt rename to lawnchair/src/app/lawnchair/icons/picker/IconPickerCategory.kt index 2cb20a82f0..dc96984236 100644 --- a/lawnchair/src/app/lawnchair/icons/IconPickerCategory.kt +++ b/lawnchair/src/app/lawnchair/icons/picker/IconPickerCategory.kt @@ -1,4 +1,4 @@ -package app.lawnchair.icons +package app.lawnchair.icons.picker data class IconPickerCategory( val title: String, diff --git a/lawnchair/src/app/lawnchair/icons/IconPickerItem.kt b/lawnchair/src/app/lawnchair/icons/picker/IconPickerItem.kt similarity index 91% rename from lawnchair/src/app/lawnchair/icons/IconPickerItem.kt rename to lawnchair/src/app/lawnchair/icons/picker/IconPickerItem.kt index 28b7b3837a..f335c7ccde 100644 --- a/lawnchair/src/app/lawnchair/icons/IconPickerItem.kt +++ b/lawnchair/src/app/lawnchair/icons/picker/IconPickerItem.kt @@ -1,4 +1,4 @@ -package app.lawnchair.icons +package app.lawnchair.icons.picker import android.os.Parcelable import kotlinx.parcelize.Parcelize diff --git a/lawnchair/src/app/lawnchair/icons/shape/IconShape.kt b/lawnchair/src/app/lawnchair/icons/shape/IconShape.kt index cff94fc69d..cd948a4a27 100644 --- a/lawnchair/src/app/lawnchair/icons/shape/IconShape.kt +++ b/lawnchair/src/app/lawnchair/icons/shape/IconShape.kt @@ -83,6 +83,9 @@ open class IconShape( private val tmpPoint = PointF() open val windowTransitionRadius = 1f + /** The icon scale used by Launcher3 */ + open val iconScale = 1f + open fun getMaskPath(): Path { return Path().also { addToPath(it, 0f, 0f, 100f, 100f, 50f) } } @@ -594,6 +597,8 @@ open class IconShape( path.addPath(tempPath) } + override val iconScale = 72f / 83.4f + override fun toString(): String { return "foursidedcookie" } @@ -635,6 +640,8 @@ open class IconShape( path.addPath(tempPath) } + override val iconScale = 72f / 80f + override fun toString(): String { return "sevensidedcookie" } diff --git a/lawnchair/src/app/lawnchair/icons/shape/PathShapeDelegate.kt b/lawnchair/src/app/lawnchair/icons/shape/PathShapeDelegate.kt new file mode 100644 index 0000000000..3a4ff7fad7 --- /dev/null +++ b/lawnchair/src/app/lawnchair/icons/shape/PathShapeDelegate.kt @@ -0,0 +1,78 @@ +package app.lawnchair.icons.shape + +import android.graphics.Canvas +import android.graphics.Matrix +import android.graphics.Paint +import android.graphics.Path +import android.graphics.Rect +import android.util.Log +import android.view.View +import com.android.launcher3.graphics.ShapeDelegate +import com.android.launcher3.views.ClipPathView + +/** + * A ShapeDelegate that is initialized directly with a Path object, + * bypassing SVG string conversion. The provided path is assumed to be + * defined within a [0, 0, 100, 100] viewport. + */ +data class PathShapeDelegate(private val basePath: Path) : ShapeDelegate { + + private val tmpPath = Path() + private val tmpMatrix = Matrix() + + override fun drawShape( + canvas: Canvas, + offsetX: Float, + offsetY: Float, + radius: Float, + paint: Paint, + ) { + tmpPath.reset() + addToPath(tmpPath, offsetX, offsetY, radius, tmpMatrix) + canvas.drawPath(tmpPath, paint) + } + + override fun addToPath(path: Path, offsetX: Float, offsetY: Float, radius: Float) { + addToPath(path, offsetX, offsetY, radius, Matrix()) + } + + private fun addToPath( + path: Path, + offsetX: Float, + offsetY: Float, + radius: Float, + matrix: Matrix, + ) { + // The base path is 100x100. We need to scale it to fit the desired 2 * radius. + // The radius in ShapeDelegate is half the size of the icon. + val scale = radius / 50f + matrix.setScale(scale, scale) + matrix.postTranslate(offsetX, offsetY) + // Apply the transformation to our base path and add it to the destination path. + basePath.transform(matrix, path) + } + + override fun createRevealAnimator( + target: T, + startRect: Rect, + endRect: Rect, + endRadius: Float, + isReversed: Boolean, + ): android.animation.ValueAnimator where T : View, T : ClipPathView { + // This is complex to implement correctly without a proper morph. + // For now, we can fall back to a simple Rect-based animation, + // which is what would happen if a proper morph isn't possible anyway. + // A more advanced implementation would use androidx.graphics.shapes.Morph. + Log.w( + "PathShapeDelegate", + "createRevealAnimator is not fully implemented for custom paths.", + ) + return ShapeDelegate.RoundedSquare(0f).createRevealAnimator( + target, + startRect, + endRect, + endRadius, + isReversed, + ) + } +} diff --git a/lawnchair/src/app/lawnchair/preferences/PreferenceManager.kt b/lawnchair/src/app/lawnchair/preferences/PreferenceManager.kt index 9a8cf14dc4..a09cea1934 100644 --- a/lawnchair/src/app/lawnchair/preferences/PreferenceManager.kt +++ b/lawnchair/src/app/lawnchair/preferences/PreferenceManager.kt @@ -29,6 +29,7 @@ import com.android.launcher3.InvariantDeviceProfile.INDEX_DEFAULT import com.android.launcher3.dagger.ApplicationContext import com.android.launcher3.dagger.LauncherAppComponent import com.android.launcher3.dagger.LauncherAppSingleton +import com.android.launcher3.graphics.ThemeManager import com.android.launcher3.model.DeviceGridState import com.android.launcher3.util.ComponentKey import com.android.launcher3.util.DaggerSingletonObject @@ -43,6 +44,7 @@ class PreferenceManager @Inject constructor( SafeCloseable { private val idp get() = InvariantDeviceProfile.INSTANCE.get(context) private val mRecentsModel get() = RecentsModel.INSTANCE.get(context) + private val themeManager = ThemeManager.INSTANCE.get(context) private val reloadIcons: () -> Unit = { mRecentsModel.onThemeChanged() } private val reloadGrid: () -> Unit = { idp.onPreferencesChanged(context) } diff --git a/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt b/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt index 0526aee758..dd8106d18a 100644 --- a/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt +++ b/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt @@ -125,6 +125,9 @@ class PreferenceManager2 @Inject constructor( ?: IconShapeManager.getSystemIconShape(context) }, save = { it.toString() }, + onSet = { + reloadHelper.reloadIcons() + }, ) val customIconShape = preference( diff --git a/lawnchair/src/app/lawnchair/preferences2/ReloadHelper.kt b/lawnchair/src/app/lawnchair/preferences2/ReloadHelper.kt index deecdbd3b6..54a771fc50 100644 --- a/lawnchair/src/app/lawnchair/preferences2/ReloadHelper.kt +++ b/lawnchair/src/app/lawnchair/preferences2/ReloadHelper.kt @@ -17,7 +17,6 @@ package app.lawnchair.preferences2 import android.content.Context -import androidx.annotation.Discouraged import app.lawnchair.LawnchairLauncher import com.android.launcher3.InvariantDeviceProfile import com.android.launcher3.LauncherAppState @@ -44,9 +43,8 @@ class ReloadHelper(private val context: Context) { recreate() } - @Discouraged("This literally reload the models like forceRefresh because the old code has been removed in L3") fun reloadIcons() { - LauncherAppState.INSTANCE.get(context).model.forceReload() + LauncherAppState.INSTANCE.get(context).iconCache.clearMemoryCache() } fun reloadTaskbar() { diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/IconPackPreferences.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/IconPackPreferences.kt index d1572ad539..3169e64d43 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/IconPackPreferences.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/IconPackPreferences.kt @@ -68,7 +68,6 @@ import app.lawnchair.ui.preferences.components.WallpaperPreview import app.lawnchair.ui.preferences.components.WithWallpaper import app.lawnchair.ui.preferences.components.controls.ListPreference import app.lawnchair.ui.preferences.components.controls.ListPreferenceEntry -import app.lawnchair.ui.preferences.components.controls.SwitchPreference import app.lawnchair.ui.preferences.components.invariantDeviceProfile import app.lawnchair.ui.preferences.components.layout.Chip import app.lawnchair.ui.preferences.components.layout.NestedScrollStretch @@ -203,14 +202,6 @@ fun IconPackPreferences( adapter = iconPackAdapter, false, ) - PreferenceGroup { - Item { - SwitchPreference( - adapter = prefs.tintIconPackBackgrounds.getAdapter(), - label = stringResource(id = R.string.themed_icon_pack_tint), - ) - } - } } 1 -> { @@ -280,11 +271,7 @@ fun IconPackGrid( val lazyListState = rememberLazyListState() val padding = 12.dp - val iconPacksLocal = if (isThemedIconPack) { - themedIconPacks.filter { it.packageName != "" } - } else { - iconPacks - } + val iconPacksLocal = iconPacks val selectedPack = adapter.state.value LaunchedEffect(selectedPack) { diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/IconPickerPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/IconPickerPreference.kt index a3179c2c50..a1d8f54389 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/IconPickerPreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/IconPickerPreference.kt @@ -34,11 +34,11 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import app.lawnchair.icons.CustomIconPack -import app.lawnchair.icons.IconPack -import app.lawnchair.icons.IconPackProvider -import app.lawnchair.icons.IconPickerItem -import app.lawnchair.icons.filter +import app.lawnchair.icons.iconpack.CustomIconPack +import app.lawnchair.icons.iconpack.IconPack +import app.lawnchair.icons.iconpack.IconPackProvider +import app.lawnchair.icons.picker.IconPickerItem +import app.lawnchair.icons.picker.filter import app.lawnchair.ui.OverflowMenu import app.lawnchair.ui.preferences.components.layout.PreferenceGroupDescription import app.lawnchair.ui.preferences.components.layout.PreferenceLazyColumn diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/SelectIconPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/SelectIconPreference.kt index 9ef5cbcdb3..ddaf0256f8 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/SelectIconPreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/SelectIconPreference.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.res.stringResource import androidx.core.graphics.drawable.toBitmap import androidx.lifecycle.compose.collectAsStateWithLifecycle import app.lawnchair.data.iconoverride.IconOverrideRepository -import app.lawnchair.icons.IconPickerItem +import app.lawnchair.icons.picker.IconPickerItem import app.lawnchair.ui.preferences.LocalNavController import app.lawnchair.ui.preferences.LocalPreferenceInteractor import app.lawnchair.ui.preferences.components.AppItem diff --git a/quickstep/dagger/com/android/launcher3/dagger/LauncherAppComponent.java b/quickstep/dagger/com/android/launcher3/dagger/LauncherAppComponent.java index a4cb42037f..e7995a5407 100644 --- a/quickstep/dagger/com/android/launcher3/dagger/LauncherAppComponent.java +++ b/quickstep/dagger/com/android/launcher3/dagger/LauncherAppComponent.java @@ -19,13 +19,19 @@ package com.android.launcher3.dagger; import com.android.quickstep.dagger.QuickstepBaseAppComponent; +import app.lawnchair.icons.ThemeManagerModule; import dagger.Component; /** * Root component for Dagger injection for Launcher Quickstep. */ @LauncherAppSingleton -@Component(modules = LauncherAppModule.class) +@Component( + modules = { + LauncherAppModule.class, + ThemeManagerModule.class + } +) public interface LauncherAppComponent extends QuickstepBaseAppComponent { /** Builder for quickstep LauncherAppComponent. */ @Component.Builder diff --git a/src/com/android/launcher3/LauncherAppState.kt b/src/com/android/launcher3/LauncherAppState.kt index ff84c3c863..a5f55c26a0 100644 --- a/src/com/android/launcher3/LauncherAppState.kt +++ b/src/com/android/launcher3/LauncherAppState.kt @@ -16,9 +16,9 @@ package com.android.launcher3 import android.content.Context +import app.lawnchair.icons.LawnchairIconProvider import com.android.launcher3.dagger.ApplicationContext import com.android.launcher3.icons.IconCache -import com.android.launcher3.icons.LauncherIconProvider import com.android.launcher3.util.DaggerSingletonObject import javax.inject.Inject import javax.inject.Named @@ -29,7 +29,7 @@ data class LauncherAppState @Inject constructor( @ApplicationContext val context: Context, - val iconProvider: LauncherIconProvider, + val iconProvider: LawnchairIconProvider, val iconCache: IconCache, val model: LauncherModel, val invariantDeviceProfile: InvariantDeviceProfile, diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java index 99824ccd59..485933f51e 100644 --- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java +++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java @@ -20,24 +20,6 @@ import android.content.Context; import androidx.annotation.Nullable; -import app.lawnchair.DeviceProfileOverrides; -import app.lawnchair.HeadlessWidgetsManager; -import app.lawnchair.NotificationManager; -import app.lawnchair.data.folder.service.FolderService; -import app.lawnchair.data.iconoverride.IconOverrideRepository; -import app.lawnchair.data.wallpaper.service.WallpaperService; -import app.lawnchair.font.FontCache; -import app.lawnchair.font.FontManager; -import app.lawnchair.font.googlefonts.GoogleFontsListing; -import app.lawnchair.icons.IconPackProvider; -import app.lawnchair.icons.shape.IconShapeManager; -import app.lawnchair.preferences.PreferenceManager; -import app.lawnchair.preferences2.PreferenceManager2; -import app.lawnchair.smartspace.provider.SmartspaceProvider; -import app.lawnchair.theme.ThemeProvider; -import app.lawnchair.ui.preferences.components.colorpreference.ColorPreferenceModelList; -import app.lawnchair.ui.preferences.data.liveinfo.LiveInformationManager; -import app.lawnchair.util.LawnchairWindowManagerProxy; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherPrefs; @@ -73,10 +55,28 @@ import com.android.launcher3.widget.LauncherWidgetHolder.WidgetHolderFactory; import com.android.launcher3.widget.custom.CustomWidgetManager; import com.android.launcher3.widget.util.WidgetSizeHandler; -import dagger.BindsInstance; - import javax.inject.Named; +import app.lawnchair.DeviceProfileOverrides; +import app.lawnchair.HeadlessWidgetsManager; +import app.lawnchair.NotificationManager; +import app.lawnchair.data.folder.service.FolderService; +import app.lawnchair.data.iconoverride.IconOverrideRepository; +import app.lawnchair.data.wallpaper.service.WallpaperService; +import app.lawnchair.font.FontCache; +import app.lawnchair.font.FontManager; +import app.lawnchair.font.googlefonts.GoogleFontsListing; +import app.lawnchair.icons.iconpack.IconPackProvider; +import app.lawnchair.icons.shape.IconShapeManager; +import app.lawnchair.preferences.PreferenceManager; +import app.lawnchair.preferences2.PreferenceManager2; +import app.lawnchair.smartspace.provider.SmartspaceProvider; +import app.lawnchair.theme.ThemeProvider; +import app.lawnchair.ui.preferences.components.colorpreference.ColorPreferenceModelList; +import app.lawnchair.ui.preferences.data.liveinfo.LiveInformationManager; +import app.lawnchair.util.LawnchairWindowManagerProxy; +import dagger.BindsInstance; + /** * Launcher base component for Dagger injection. * diff --git a/src/com/android/launcher3/graphics/ThemeManager.kt b/src/com/android/launcher3/graphics/ThemeManager.kt index a24754c652..957d02aa11 100644 --- a/src/com/android/launcher3/graphics/ThemeManager.kt +++ b/src/com/android/launcher3/graphics/ThemeManager.kt @@ -42,7 +42,7 @@ import javax.inject.Inject /** Centralized class for managing Launcher icon theming */ @LauncherAppSingleton -class ThemeManager +open class ThemeManager @Inject constructor( @ApplicationContext private val context: Context, @@ -53,8 +53,8 @@ constructor( ) { /** Representation of the current icon state */ - var iconState = parseIconState(null) - private set + open var iconState = parseIconState(null) + protected set var isMonoThemeEnabled set(value) = prefs.put(THEMED_ICONS, value) @@ -72,7 +72,7 @@ constructor( val folderShape get() = iconState.folderShape - private val listeners = CopyOnWriteArrayList() + protected val listeners = CopyOnWriteArrayList() init { val receiver = SimpleBroadcastReceiver( @@ -93,7 +93,7 @@ constructor( } } - private fun verifyIconState() { + protected open fun verifyIconState() { val newState = parseIconState(iconState) if (newState == iconState) return iconState = newState @@ -105,7 +105,7 @@ constructor( fun removeChangeListener(listener: ThemeChangeListener) = listeners.remove(listener) - private fun parseIconState(oldState: IconState?): IconState { + protected open fun parseIconState(oldState: IconState?): IconState { val shapeModel = prefs.get(PREF_ICON_SHAPE).let { shapeOverride -> ShapesProvider.iconShapes.firstOrNull { it.key == shapeOverride } diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java index a72c375c74..7ab4f5691e 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -22,7 +22,6 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.LooperExecutor.CALLER_ICON_CACHE; import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY; - import static java.util.stream.Collectors.groupingBy; import android.content.ComponentName; @@ -87,6 +86,8 @@ import java.util.stream.Stream; import javax.inject.Inject; import javax.inject.Named; +import app.lawnchair.icons.LawnchairIconProvider; + /** * Cache of application icons. Icons can be made from any thread. */ @@ -113,16 +114,14 @@ public class IconCache extends BaseIconCache { private final SparseArray mWidgetCategoryBitmapInfos; private int mPendingIconRequestCount = 0; - + @Inject public IconCache( @ApplicationContext Context context, InvariantDeviceProfile idp, @Nullable @Named("ICONS_DB") String dbFileName, UserCache userCache, - LauncherIconProvider iconProvider, - // TODO: Lawnchair stuff - // IconProvider iconProvider, + LawnchairIconProvider iconProvider, InstallSessionHelper installSessionHelper, LauncherIcons.IconPool iconPool, InstantAppResolver instantAppResolver, @@ -184,6 +183,7 @@ public class IconCache extends BaseIconCache { return mIconPool.obtain(); } + /** /** * Updates the entries related to the given package in memory and persistent DB. */ diff --git a/src/com/android/launcher3/icons/LauncherIconProvider.java b/src/com/android/launcher3/icons/LauncherIconProvider.java index f395fdc605..eacc1c854c 100644 --- a/src/com/android/launcher3/icons/LauncherIconProvider.java +++ b/src/com/android/launcher3/icons/LauncherIconProvider.java @@ -46,10 +46,10 @@ public class LauncherIconProvider extends IconProvider { public static final String ATTR_DRAWABLE = "drawable"; public static final String ATTR_COMPONENT = "component"; - private static final String TAG = "LIconProvider"; - private static final Map DISABLED_MAP = Collections.emptyMap(); + protected static final String TAG = "LIconProvider"; + protected static final Map DISABLED_MAP = Collections.emptyMap(); - private Map mThemedIconMap; + protected Map mThemedIconMap; protected final ThemeManager mThemeManager; @@ -73,7 +73,7 @@ public class LauncherIconProvider extends IconProvider { mSystemState += "," + mThemeManager.getIconState().toUniqueId(); } - private Map getThemedIconMap() { + protected Map getThemedIconMap() { if (mThemedIconMap != null) { return mThemedIconMap; } diff --git a/src/com/android/launcher3/model/ModelInitializer.kt b/src/com/android/launcher3/model/ModelInitializer.kt index cda4d34e26..dd691b57aa 100644 --- a/src/com/android/launcher3/model/ModelInitializer.kt +++ b/src/com/android/launcher3/model/ModelInitializer.kt @@ -21,6 +21,7 @@ import android.content.ComponentName import android.content.Context import android.content.pm.LauncherApps import android.content.pm.LauncherApps.ArchiveCompatibilityParams +import app.lawnchair.icons.LawnchairIconProvider import com.android.launcher3.BuildConfigs import com.android.launcher3.Flags import com.android.launcher3.InvariantDeviceProfile @@ -31,7 +32,6 @@ import com.android.launcher3.dagger.ApplicationContext import com.android.launcher3.graphics.ThemeManager import com.android.launcher3.graphics.ThemeManager.ThemeChangeListener import com.android.launcher3.icons.IconCache -import com.android.launcher3.icons.LauncherIconProvider import com.android.launcher3.icons.LauncherIcons.IconPool import com.android.launcher3.notification.NotificationListener import com.android.launcher3.pm.InstallSessionHelper @@ -57,7 +57,7 @@ constructor( private val themeManager: ThemeManager, private val userCache: UserCache, private val settingsCache: SettingsCache, - private val iconProvider: LauncherIconProvider, + private val iconProvider: LawnchairIconProvider, private val customWidgetManager: CustomWidgetManager, private val installSessionHelper: InstallSessionHelper, private val lifeCycle: DaggerSingletonTracker,