Skip to content

fix: Refresh icon preferences - #6690

Merged
validcube merged 7 commits into
LawnchairLauncher:16-devfrom
EntropyExperiment:feature-16-dev_refresh-icon-on-update
Apr 25, 2026
Merged

fix: Refresh icon preferences#6690
validcube merged 7 commits into
LawnchairLauncher:16-devfrom
EntropyExperiment:feature-16-dev_refresh-icon-on-update

Conversation

@validcube

@validcube validcube commented Apr 25, 2026

Copy link
Copy Markdown
Member

Description

Make icon update instant, you don't have to force restart the app (workaround of LC16), and wait lawnchair to recreate the workspace (like it is on LC15)!

KSP incremental build fail, i have no idea why?? (Maybe it's because of my cache?)

Fixes #6684
Fixes #6413

Reasoning

Make Lawnchair PreferenceManager (1) work with LawnchairThemeManager! Woo, no more weird workaround with using AOSP preference system!

And make toggling switch that affect icons feels so much smoother (before we recreate the launcher every time these changes, now we refresh it!)

Testing

Change any of the icon preferences.

Type of change

Bug fix (A non-breaking change that fixes an issue)

Summary by CodeRabbit

  • Refactor

    • Centralized theme/icon preference handling with a new multi-preference listener and a dedicated coroutine context for theme management.
  • Performance

    • Icon updates run asynchronously, clear in-memory icon cache, and avoid full app restart when updating related settings.
  • New Behavior

    • Changes to icon shape, background, monochrome and tint settings immediately refresh icons and recompute shape selection without recreating the app.

Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
@validcube
validcube force-pushed the feature-16-dev_refresh-icon-on-update branch from 0678047 to c633a6c Compare April 25, 2026 13:20
@coderabbitai

coderabbitai Bot commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fe2c3b95-d782-4e1e-8fdc-5c9cb5dd5510

📥 Commits

Reviewing files that changed from the base of the PR and between f2d3216 and fba50be.

📒 Files selected for processing (1)
  • lawnchair/src/app/lawnchair/preferences/PreferenceManager.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • lawnchair/src/app/lawnchair/preferences/PreferenceManager.kt

📝 Walkthrough

Walkthrough

LawnchairThemeManager now takes a PreferenceManager, registers a preference-change listener for five prefs, and includes prefs1 state in icon-shape key generation. PreferenceManager replaces several recreate callbacks with reloadIcons() that notifies recents, clears icon cache asynchronously, and reloads the active model.

Changes

Cohort / File(s) Summary
Theme manager & DI
lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt, lawnchair/src/app/lawnchair/icons/ThemeManagerModule.kt
Adds prefs1: PreferenceManager to LawnchairThemeManager constructor and DI provider; registers/unregisters a PreferenceChangeListener for five prefs and schedules verifyIconState() on uiExecutor.
Icon state key & parsing
lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt
parseIconStateV2 now derives a prefs1State() string and embeds it into appShapeKey and folderShapeKey, causing PathShapeDelegate selection and cache/mask decisions to recompute when those prefs change.
Preference handling & icon reload
lawnchair/src/app/lawnchair/preferences/PreferenceManager.kt
Introduces reloadIcons() that calls mRecentsModel.onThemeChanged(), clears in-memory icon cache on a background executor, and calls model.reloadIfActive(); multiple icon/theme prefs switch from recreate to reloadIcons() or remove recreate callbacks.
Coroutine scope metadata
lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt
Adds CoroutineName("LawnchairThemeManager") to the scope used for prefs2 shape-merge work.

Sequence Diagram

sequenceDiagram
    participant User
    participant Prefs as PreferenceManager
    participant ThemeMgr as LawnchairThemeManager
    participant Recents as RecentsModel
    participant Cache as IconCache
    participant Model as LauncherModel

    User->>Prefs: change icon/appearance preference
    Prefs->>ThemeMgr: PreferenceChangeListener triggers (watched prefs)
    ThemeMgr->>ThemeMgr: parseIconStateV2 (includes prefs1State + prefs2)
    Prefs->>Recents: onThemeChanged()
    Prefs->>Cache: clear in-memory icon cache (async)
    Cache-->>Prefs: cleared
    Prefs->>Model: model.reloadIfActive() (background executor)
    Model-->>User: icons refreshed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • yasanglass
  • MrSluffy

Poem

🐰 I nibbled prefs beneath the moon,
One little listener hums a tune,
Shapes reshuffle, caches flee,
Recents cheer — icons roam free,
Hoppity-hop, a fresher view!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 10.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly aligns with the main objective of the PR: making icon preference changes take effect immediately without requiring app restart.
Description check ✅ Passed The description covers the main objectives, reasoning, and testing instructions. It references the linked issues and explains the fix approach, though it lacks detailed technical rationale for the solution approach.
Linked Issues check ✅ Passed The changes address both issues: icon pack changes (#6684) and icon updates (#6413) now work immediately by integrating PreferenceManager with LawnchairThemeManager to trigger instant icon refresh instead of full app recreation.
Out of Scope Changes check ✅ Passed All changes are directly related to the objectives: integrating PreferenceManager with LawnchairThemeManager and replacing full recreation with icon refresh callbacks to fix icon update issues.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@validcube validcube changed the title feat: Make icon update faster!!!! fix: Refresh icon preferences Apr 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt (1)

46-63: ⚠️ Potential issue | 🟡 Minor

Store listener references and remove them on close.

The five prefs1.<pref>.addListener { verifyIconState() } registrations (lines 54–58) create listeners that cannot be removed because the lambda references are never stored. removeListener requires the exact listener object, and since addListener returns no token or unsubscribe function, these listeners will persist indefinitely.

While this is benign for a @LauncherAppSingleton that lives for the app's lifetime, it diverges from the coroutine cleanup pattern shown below it. Store each listener and call removeListener in the lifecycle.addCloseable { ... } block to keep behavior consistent and correct should the singleton lifetime change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt` around lines 46 -
63, The five prefs1.<pref>.addListener registrations
(prefs1.wrapAdaptiveIcons.addListener,
prefs1.transparentIconBackground.addListener, prefs1.shadowBGIcons.addListener,
prefs1.coloredBackgroundLightness.addListener,
prefs1.forceIconMonochrome.addListener) register lambdas that are never stored
and thus cannot be removed; capture each listener in a val (e.g.,
wrapAdaptiveListener, transparentBgListener, etc.) when calling addListener, use
those references in lifecycle.addCloseable to call
prefs1.<pref>.removeListener(listener) on close, and keep the existing
scope.cancel() behavior so verifyIconState() callbacks are cleaned up
consistently.
🧹 Nitpick comments (2)
lawnchair/src/app/lawnchair/icons/ThemeManagerModule.kt (1)

27-28: Consider a clearer name than prefs1.

Pairing prefs1 with prefs2 reads as ordinal numbering, but the two refer to different classes (PreferenceManager vs PreferenceManager2), and prefs1 is the older one — not the "first". Something like lawnchairPrefs or legacyPrefs would be less confusing for future readers. The same applies to the parameter name in LawnchairThemeManager's constructor.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lawnchair/src/app/lawnchair/icons/ThemeManagerModule.kt` around lines 27 -
28, Rename the ambiguous parameter prefs1 to a clearer identifier (e.g.,
legacyPrefs or lawnchairPrefs) everywhere it's used: in the ThemeManagerModule
function signature and where it's passed into LawnchairThemeManager's
constructor, and update the corresponding constructor parameter name inside
LawnchairThemeManager to match; ensure the type stays PreferenceManager (prefs2
remains PreferenceManager2) and update all references to the old name to avoid
compile errors.
lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt (1)

88-93: Minor readability: extract the suffix builder.

The single-line concatenation is hard to scan. A joinToString over a list of the five preference values reads better and makes future additions safer.

♻️ Suggested change
-        var appShapeKey = currentAppShape.getHashString()
-        var folderShapeKey = currentFolderShape.getHashString()
-
-        val prefSuffix = "${prefs1.wrapAdaptiveIcons.get()},${prefs1.transparentIconBackground.get()},${prefs1.shadowBGIcons.get()},${prefs1.coloredBackgroundLightness.get()},${prefs1.forceIconMonochrome.get()}"
-        appShapeKey += prefSuffix
-        folderShapeKey += prefSuffix
+        val prefSuffix = listOf(
+            prefs1.wrapAdaptiveIcons.get(),
+            prefs1.transparentIconBackground.get(),
+            prefs1.shadowBGIcons.get(),
+            prefs1.coloredBackgroundLightness.get(),
+            prefs1.forceIconMonochrome.get(),
+        ).joinToString(",")
+        val appShapeKey = currentAppShape.getHashString() + prefSuffix
+        val folderShapeKey = currentFolderShape.getHashString() + prefSuffix
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt` around lines 88 -
93, The code builds prefSuffix with an inline string template which hurts
readability; extract the suffix construction into a clearer builder using a list
of the five preference values (prefs1.wrapAdaptiveIcons.get(),
prefs1.transparentIconBackground.get(), prefs1.shadowBGIcons.get(),
prefs1.coloredBackgroundLightness.get(), prefs1.forceIconMonochrome.get()) and
call joinToString(",") to produce prefSuffix, then append that prefSuffix to
appShapeKey and folderShapeKey as before (references: appShapeKey,
folderShapeKey, prefSuffix, prefs1.* getters).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt`:
- Around line 24-25: Remove or replace the informal/profane developer note in
LawnchairThemeManager.kt and replace it with a professional tracked TODO or
remove it entirely; if this is a real KSP incremental staleness concern, create
an issue and add a concise TODO comment referencing that issue (e.g., "TODO:
Investigate KSP incremental update staleness — see ISSUE-1234") so future
maintainers can find the bug, otherwise delete the comment line entirely.

---

Outside diff comments:
In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt`:
- Around line 46-63: The five prefs1.<pref>.addListener registrations
(prefs1.wrapAdaptiveIcons.addListener,
prefs1.transparentIconBackground.addListener, prefs1.shadowBGIcons.addListener,
prefs1.coloredBackgroundLightness.addListener,
prefs1.forceIconMonochrome.addListener) register lambdas that are never stored
and thus cannot be removed; capture each listener in a val (e.g.,
wrapAdaptiveListener, transparentBgListener, etc.) when calling addListener, use
those references in lifecycle.addCloseable to call
prefs1.<pref>.removeListener(listener) on close, and keep the existing
scope.cancel() behavior so verifyIconState() callbacks are cleaned up
consistently.

---

Nitpick comments:
In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt`:
- Around line 88-93: The code builds prefSuffix with an inline string template
which hurts readability; extract the suffix construction into a clearer builder
using a list of the five preference values (prefs1.wrapAdaptiveIcons.get(),
prefs1.transparentIconBackground.get(), prefs1.shadowBGIcons.get(),
prefs1.coloredBackgroundLightness.get(), prefs1.forceIconMonochrome.get()) and
call joinToString(",") to produce prefSuffix, then append that prefSuffix to
appShapeKey and folderShapeKey as before (references: appShapeKey,
folderShapeKey, prefSuffix, prefs1.* getters).

In `@lawnchair/src/app/lawnchair/icons/ThemeManagerModule.kt`:
- Around line 27-28: Rename the ambiguous parameter prefs1 to a clearer
identifier (e.g., legacyPrefs or lawnchairPrefs) everywhere it's used: in the
ThemeManagerModule function signature and where it's passed into
LawnchairThemeManager's constructor, and update the corresponding constructor
parameter name inside LawnchairThemeManager to match; ensure the type stays
PreferenceManager (prefs2 remains PreferenceManager2) and update all references
to the old name to avoid compile errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 84c5a95e-a86f-43e9-ad12-33dae8ca9241

📥 Commits

Reviewing files that changed from the base of the PR and between 73cbb72 and c633a6c.

📒 Files selected for processing (3)
  • lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt
  • lawnchair/src/app/lawnchair/icons/ThemeManagerModule.kt
  • lawnchair/src/app/lawnchair/preferences/PreferenceManager.kt

Comment thread lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt Outdated
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt (1)

45-59: ⚠️ Potential issue | 🟠 Major

Fix thread safety: post verifyIconState() callback to uiExecutor.

The removal of drawer_themed_icons listener is correct—parseIconStateV2() doesn't read it, so it has no impact on iconState or iconMask computation. The preference still triggers icon reloads via reloadIcons, which is separate from LawnchairThemeManager.

However, prefListener callbacks from PreferenceChangeListener.onPreferenceChange() are invoked synchronously on the thread that committed the SharedPreferences change (often not the UI thread). This means verifyIconState()listeners.forEach { it.onThemeChanged() } can execute off-UI-thread. Since the merge flow explicitly uses MainScope() to ensure UI-thread execution, wrap the prefListener callback:

private val prefListener = PreferenceChangeListener { 
    uiExecutor.execute { verifyIconState() }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt` around lines 45 -
59, prefListener currently calls verifyIconState() directly which can run off
the UI thread because PreferenceChangeListener callbacks are invoked on the
committing thread; change prefListener (the PreferenceChangeListener instance)
so it posts the work to the UI thread by calling uiExecutor.execute {
verifyIconState() } (i.e., replace the direct verifyIconState() invocation with
a uiExecutor.execute wrapper) so verifyIconState() and subsequent
listeners.forEach { it.onThemeChanged() } always run on the UI thread.
🧹 Nitpick comments (1)
lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt (1)

55-67: Reduce duplication for the five listener registrations.

The five addListener/removeListener calls and the matching pref reads in prefSuffix reference the same set of prefs. Collecting them once avoids drift if a sixth pref is added (or one is removed) and one of the three sites is missed.

♻️ Proposed refactor
-        prefs1.wrapAdaptiveIcons.addListener(prefListener)
-        prefs1.transparentIconBackground.addListener(prefListener)
-        prefs1.shadowBGIcons.addListener(prefListener)
-        prefs1.coloredBackgroundLightness.addListener(prefListener)
-        prefs1.forceIconMonochrome.addListener(prefListener)
+        watchedIconPrefs.forEach { it.addListener(prefListener) }

         lifecycle.addCloseable {
             scope.cancel()
-            prefs1.wrapAdaptiveIcons.removeListener(prefListener)
-            prefs1.transparentIconBackground.removeListener(prefListener)
-            prefs1.shadowBGIcons.removeListener(prefListener)
-            prefs1.coloredBackgroundLightness.removeListener(prefListener)
-            prefs1.forceIconMonochrome.removeListener(prefListener)
+            watchedIconPrefs.forEach { it.removeListener(prefListener) }
         }
     }

And in the class body:

private val watchedIconPrefs by lazy {
    listOf(
        prefs1.wrapAdaptiveIcons,
        prefs1.transparentIconBackground,
        prefs1.shadowBGIcons,
        prefs1.coloredBackgroundLightness,
        prefs1.forceIconMonochrome,
    )
}

Then prefSuffix becomes:

val prefSuffix = watchedIconPrefs.joinToString(",") { it.get().toString() }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt` around lines 55 -
67, The repeated addListener/removeListener calls and the prefSuffix
construction should use a single collection to avoid drift: create a private val
watchedIconPrefs (e.g., listOf(prefs1.wrapAdaptiveIcons,
prefs1.transparentIconBackground, prefs1.shadowBGIcons,
prefs1.coloredBackgroundLightness, prefs1.forceIconMonochrome)) and replace the
five addListener/removeListener calls to iterate watchedIconPrefs.forEach {
it.addListener(prefListener) } and watchedIconPrefs.forEach {
it.removeListener(prefListener) } inside lifecycle.addCloseable (keep
scope.cancel as-is), and compute prefSuffix as
watchedIconPrefs.joinToString(",") { it.get().toString() } so all three sites
reference the same source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt`:
- Around line 45-59: prefListener currently calls verifyIconState() directly
which can run off the UI thread because PreferenceChangeListener callbacks are
invoked on the committing thread; change prefListener (the
PreferenceChangeListener instance) so it posts the work to the UI thread by
calling uiExecutor.execute { verifyIconState() } (i.e., replace the direct
verifyIconState() invocation with a uiExecutor.execute wrapper) so
verifyIconState() and subsequent listeners.forEach { it.onThemeChanged() }
always run on the UI thread.

---

Nitpick comments:
In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt`:
- Around line 55-67: The repeated addListener/removeListener calls and the
prefSuffix construction should use a single collection to avoid drift: create a
private val watchedIconPrefs (e.g., listOf(prefs1.wrapAdaptiveIcons,
prefs1.transparentIconBackground, prefs1.shadowBGIcons,
prefs1.coloredBackgroundLightness, prefs1.forceIconMonochrome)) and replace the
five addListener/removeListener calls to iterate watchedIconPrefs.forEach {
it.addListener(prefListener) } and watchedIconPrefs.forEach {
it.removeListener(prefListener) } inside lifecycle.addCloseable (keep
scope.cancel as-is), and compute prefSuffix as
watchedIconPrefs.joinToString(",") { it.get().toString() } so all three sites
reference the same source of truth.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2014bab5-b4b9-4f90-978e-09196e352188

📥 Commits

Reviewing files that changed from the base of the PR and between c633a6c and 494f677.

📒 Files selected for processing (1)
  • lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt

Signed-off-by: Pun Butrach <pun.butrach@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt (2)

100-105: Prefer immutable val + single-expression construction.

Switching appShapeKey/folderShapeKey from val to var just to append a suffix is unnecessary mutation; the suffix can be concatenated at initialization. Also consider extracting the suffix into a small helper so the format isn't duplicated if another path needs the same key in the future.

♻️ Proposed cleanup
-        var appShapeKey = currentAppShape.getHashString()
-        var folderShapeKey = currentFolderShape.getHashString()
-
-        val prefSuffix = "${prefs1.wrapAdaptiveIcons.get()},${prefs1.transparentIconBackground.get()},${prefs1.shadowBGIcons.get()},${prefs1.coloredBackgroundLightness.get()},${prefs1.forceIconMonochrome.get()}"
-        appShapeKey += prefSuffix
-        folderShapeKey += prefSuffix
+        val prefSuffix = buildString {
+            append(prefs1.wrapAdaptiveIcons.get()).append(',')
+            append(prefs1.transparentIconBackground.get()).append(',')
+            append(prefs1.shadowBGIcons.get()).append(',')
+            append(prefs1.coloredBackgroundLightness.get()).append(',')
+            append(prefs1.forceIconMonochrome.get())
+        }
+        val appShapeKey = currentAppShape.getHashString() + prefSuffix
+        val folderShapeKey = currentFolderShape.getHashString() + prefSuffix
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt` around lines 100
- 105, The current code mutates appShapeKey and folderShapeKey using var to
append a duplicated suffix; change both to val and construct them in one
expression by concatenating the hash and suffix at initialization (e.g., val
appShapeKey = currentAppShape.getHashString() + prefSuffix), and extract the
repeated suffix construction into a small helper function (e.g.,
buildPrefSuffix() that returns the string built from
prefs1.wrapAdaptiveIcons.get(), prefs1.transparentIconBackground.get(),
prefs1.shadowBGIcons.get(), prefs1.coloredBackgroundLightness.get(),
prefs1.forceIconMonochrome.get()) so both appShapeKey and folderShapeKey can
call it without duplicating logic.

39-39: Rename prefs1 to something descriptive.

The name prefs1 is confusing alongside the existing prefs (LauncherPrefs) and prefs2 (PreferenceManager2) — the "1" suggests an ordering that doesn't exist. Consider preferenceManager, lcPrefs, or lawnchairPrefs to match the surrounding conventions, and update the references on lines 61–65, 69–73, and 103.

♻️ Suggested rename
-    private val prefs1: PreferenceManager,
+    private val lawnchairPrefs: PreferenceManager,

(then update all prefs1. usages in the file)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt` at line 39, The
field name prefs1 in LawnchairThemeManager is ambiguous next to prefs and
prefs2; rename prefs1 to a descriptive identifier like preferenceManager or
lawnchairPrefs across the class (update the declaration of prefs1 and all usages
where prefs1. is referenced, e.g., in methods that read or write theme
preferences and in any places referenced alongside prefs and prefs2) and adjust
import/constructor parameter names accordingly so all references (existing
prefs1. usages) compile and follow the project's naming convention.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt`:
- Around line 100-105: The current code mutates appShapeKey and folderShapeKey
using var to append a duplicated suffix; change both to val and construct them
in one expression by concatenating the hash and suffix at initialization (e.g.,
val appShapeKey = currentAppShape.getHashString() + prefSuffix), and extract the
repeated suffix construction into a small helper function (e.g.,
buildPrefSuffix() that returns the string built from
prefs1.wrapAdaptiveIcons.get(), prefs1.transparentIconBackground.get(),
prefs1.shadowBGIcons.get(), prefs1.coloredBackgroundLightness.get(),
prefs1.forceIconMonochrome.get()) so both appShapeKey and folderShapeKey can
call it without duplicating logic.
- Line 39: The field name prefs1 in LawnchairThemeManager is ambiguous next to
prefs and prefs2; rename prefs1 to a descriptive identifier like
preferenceManager or lawnchairPrefs across the class (update the declaration of
prefs1 and all usages where prefs1. is referenced, e.g., in methods that read or
write theme preferences and in any places referenced alongside prefs and prefs2)
and adjust import/constructor parameter names accordingly so all references
(existing prefs1. usages) compile and follow the project's naming convention.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6aaa6a0f-c7a0-4644-9d2e-4d92c13b61dc

📥 Commits

Reviewing files that changed from the base of the PR and between 494f677 and 4d19d23.

📒 Files selected for processing (1)
  • lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt

Signed-off-by: Pun Butrach <pun.butrach@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt (2)

85-105: Minor: cache prefs1State() once per parseIconStateV2 invocation.

prefs1State() makes 5 .get() calls and is invoked twice on lines 104 and 105 for keys that always share the same prefs1 component. Caching it once keeps the two keys provably consistent (no risk of a change between calls) and avoids the duplicated .get() work.

♻️ Proposed refactor
+        val prefs1Snapshot = prefs1State()
-        val appShapeKey = currentAppShape.getHashString() + prefs1State()
-        val folderShapeKey = currentFolderShape.getHashString() + prefs1State()
+        val appShapeKey = currentAppShape.getHashString() + prefs1Snapshot
+        val folderShapeKey = currentFolderShape.getHashString() + prefs1Snapshot
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt` around lines 85 -
105, In parseIconStateV2, call prefs1State() once and store its result in a
local val (e.g., val prefs1StateVal) before computing appShapeKey and
folderShapeKey, then use that cached prefs1StateVal when forming appShapeKey and
folderShapeKey instead of calling prefs1State() twice; update references to
prefs1State() in the appShapeKey and folderShapeKey assignments to use the new
local variable.

49-75: Optional: consolidate the five add/remove listener calls.

The list of monitored prefs1 entries is duplicated in init and addCloseable, which is easy to drift out of sync if a future pref is added or removed (e.g., adding it only to init). Iterating once over a single list keeps both sides in lockstep.

♻️ Proposed refactor
     init {
         val scope = MainScope() + CoroutineName("LawnchairThemeManager")
         merge(
             prefs2.iconShape.get(),
             prefs2.customIconShape.get(),
         ).onEach { verifyIconState() }
             .launchIn(scope)

-        prefs1.wrapAdaptiveIcons.addListener(prefListener)
-        prefs1.transparentIconBackground.addListener(prefListener)
-        prefs1.shadowBGIcons.addListener(prefListener)
-        prefs1.coloredBackgroundLightness.addListener(prefListener)
-        prefs1.forceIconMonochrome.addListener(prefListener)
+        val watchedPrefs = listOf(
+            prefs1.wrapAdaptiveIcons,
+            prefs1.transparentIconBackground,
+            prefs1.shadowBGIcons,
+            prefs1.coloredBackgroundLightness,
+            prefs1.forceIconMonochrome,
+        )
+        watchedPrefs.forEach { it.addListener(prefListener) }

         lifecycle.addCloseable {
             scope.cancel()
-            prefs1.wrapAdaptiveIcons.removeListener(prefListener)
-            prefs1.transparentIconBackground.removeListener(prefListener)
-            prefs1.shadowBGIcons.removeListener(prefListener)
-            prefs1.coloredBackgroundLightness.removeListener(prefListener)
-            prefs1.forceIconMonochrome.removeListener(prefListener)
+            watchedPrefs.forEach { it.removeListener(prefListener) }
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt` around lines 49 -
75, Consolidate the repeated add/remove listener calls by collecting the prefs
to monitor into a single list (e.g., val iconPrefs =
listOf(prefs1.wrapAdaptiveIcons, prefs1.transparentIconBackground,
prefs1.shadowBGIcons, prefs1.coloredBackgroundLightness,
prefs1.forceIconMonochrome)) and then iterate over that list to call
addListener(prefListener) in the init setup and iterate again inside
lifecycle.addCloseable to call removeListener(prefListener); keep existing use
of prefListener, verifyIconState(), scope cancellation, and the merge/onEach
flow unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt`:
- Around line 85-105: In parseIconStateV2, call prefs1State() once and store its
result in a local val (e.g., val prefs1StateVal) before computing appShapeKey
and folderShapeKey, then use that cached prefs1StateVal when forming appShapeKey
and folderShapeKey instead of calling prefs1State() twice; update references to
prefs1State() in the appShapeKey and folderShapeKey assignments to use the new
local variable.
- Around line 49-75: Consolidate the repeated add/remove listener calls by
collecting the prefs to monitor into a single list (e.g., val iconPrefs =
listOf(prefs1.wrapAdaptiveIcons, prefs1.transparentIconBackground,
prefs1.shadowBGIcons, prefs1.coloredBackgroundLightness,
prefs1.forceIconMonochrome)) and then iterate over that list to call
addListener(prefListener) in the init setup and iterate again inside
lifecycle.addCloseable to call removeListener(prefListener); keep existing use
of prefListener, verifyIconState(), scope cancellation, and the merge/onEach
flow unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ac000045-4eac-491a-b200-4ef907715cc5

📥 Commits

Reviewing files that changed from the base of the PR and between 4d19d23 and 5da9a2d.

📒 Files selected for processing (1)
  • lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt

@SuperDragonXD SuperDragonXD left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. You can just apply the nitpick comments of Coderabbit, then merge afterwards.

Signed-off-by: Pun Butrach <pun.butrach@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt (1)

59-66: Centralize the watched-prefs list to avoid drift with prefs1State().

The exact same five prefs are enumerated here (registration) and again in prefs1State() at lines 82-84 (state hash). If a future pref is added/removed in only one of the two places, the listener and the cache key go out of sync — either icons fail to refresh on change, or the state key won't differ when expected, suppressing a real refresh. Extract a single source of truth.

♻️ Proposed refactor
+    private val statePrefs1 by lazy {
+        listOf(
+            prefs1.wrapAdaptiveIcons,
+            prefs1.transparentIconBackground,
+            prefs1.shadowBGIcons,
+            prefs1.coloredBackgroundLightness,
+            prefs1.forceIconMonochrome,
+        )
+    }
+
     init {
         val scope = MainScope() + CoroutineName("LawnchairThemeManager")
         merge(
             prefs2.iconShape.get(),
             prefs2.customIconShape.get(),
         ).onEach { verifyIconState() }
             .launchIn(scope)

-        val statePrefs1 = listOf(
-            prefs1.wrapAdaptiveIcons,
-            prefs1.transparentIconBackground,
-            prefs1.shadowBGIcons,
-            prefs1.coloredBackgroundLightness,
-            prefs1.forceIconMonochrome,
-        )
         statePrefs1.forEach { it.addListener(prefListener) }
@@
-    private fun prefs1State(): String = "${prefs1.wrapAdaptiveIcons.get()},${prefs1.transparentIconBackground.get()}," +
-        "${prefs1.shadowBGIcons.get()},${prefs1.coloredBackgroundLightness.get()}," +
-        "${prefs1.forceIconMonochrome.get()}"
+    private fun prefs1State(): String =
+        statePrefs1.joinToString(",") { it.get().toString() }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt` around lines 59 -
66, The same five prefs are duplicated between the registration block
(statePrefs1) and prefs1State(), causing potential drift; extract a single
shared list (e.g., val watchedPrefs1 or ICON_STATE_PREFS) and replace both the
statePrefs1 declaration and the manual listing in prefs1State() to reference
that single list so the listener registration (statePrefs1.forEach {
it.addListener(prefListener) }) and the state-hash computation in prefs1State()
always stay in sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt`:
- Around line 59-66: The same five prefs are duplicated between the registration
block (statePrefs1) and prefs1State(), causing potential drift; extract a single
shared list (e.g., val watchedPrefs1 or ICON_STATE_PREFS) and replace both the
statePrefs1 declaration and the manual listing in prefs1State() to reference
that single list so the listener registration (statePrefs1.forEach {
it.addListener(prefListener) }) and the state-hash computation in prefs1State()
always stay in sync.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f3311d96-e226-4377-a29b-a5af989d2fdb

📥 Commits

Reviewing files that changed from the base of the PR and between 5da9a2d and a42ee3a.

📒 Files selected for processing (1)
  • lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt

Signed-off-by: Pun Butrach <pun.butrach@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt`:
- Around line 45-72: The five prefs used in LawnchairThemeManager
(prefs1.wrapAdaptiveIcons, prefs1.transparentIconBackground,
prefs1.shadowBGIcons, prefs1.coloredBackgroundLightness,
prefs1.forceIconMonochrome) are causing duplicate refreshes because
PreferenceManager currently registers reloadIcons as their primary listener;
remove reloadIcons as the primary listener for those five preferences in
PreferenceManager.kt so they no longer trigger the reloadIcons path, leaving
LawnchairThemeManager's prefListener/verifyIconState() path (which calls
listeners.forEach { it.onThemeChanged() }) to own cache clears and model
reloads; ensure BasePreferenceManager.onSharedPreferenceChange() no longer
invokes reloadIcons first for these specific prefs to avoid redundant
MODEL_EXECUTOR clears/reloads.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 09f5f0f7-e335-40c4-8e7e-56afaa142bbd

📥 Commits

Reviewing files that changed from the base of the PR and between 5da9a2d and f2d3216.

📒 Files selected for processing (1)
  • lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt

Comment thread lawnchair/src/app/lawnchair/icons/LawnchairThemeManager.kt
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom icon applied and trying to change icon but not changing [BUG] Lawnchair 16 dev bug

2 participants