Skip to content

feat: Reimplement support for non-root app predictions#6745

Open
validcube wants to merge 62 commits into
LawnchairLauncher:16-devfrom
EntropyExperiment:feature-16-dev_appprediction
Open

feat: Reimplement support for non-root app predictions#6745
validcube wants to merge 62 commits into
LawnchairLauncher:16-devfrom
EntropyExperiment:feature-16-dev_appprediction

Conversation

@validcube

@validcube validcube commented May 13, 2026

Copy link
Copy Markdown
Member

Description

Lawnchair rootless app prediction system. (Basic app ranking... Basic?)

Fixes #4235

Todo:

  • App Predictor that closely resembles one from QuickstepModelDelegate
    • Use usage stats (not system api) on first data when possible
    • Combine usage stats signal (everyday signal) with predictor
    • React to on suggestion discard
    • Headphone store for hotseat + allapps No longer planned for this PR since that would likely be too much work.
  • Promote to stable (optional in this PR)
    • Design/Discuss passable UI/UX
      • Allow user to view all suggested apps in the store (and mark those apps as dismissed/do not appear)
    • Allow switching prediction modal
    • Allow changing prediction "influencer" Headphone store and other device state store is supposed to be the "influencer" but now that it's no longer planned, this will not be implemented.

Reasoning

Code side

Heads up note: All of the files were written before AOSP Proto files synced with the IDE so there may be some weird usage around LauncherAtom as I use AI with pretty much "Claude, integrate LauncherAtom with my code, make no mistake (the IDE is lying to you about missing files)" All files were cleaned up now.

This changes override predictor from QuickstepModelDelegate when predictor mode is set to Lawnchair instead of System.

The Lawnchair predictor a basic ranking system based on how many time the app was opened, it tried to be very accurate to how the system predictor returns it datas (because it has to be accepted by the AOSP, thanks AI for doing that painful tedious work) and works by listening in on app launch and event stats returned by the StatsLogs.

If a data from a store is not available like hotseat is non existent it will use random data (ranked lowest) and data from other store like allapps (ranked higher than random).

Compared to old prediction system in used by Lawnchair 10 this one does not take wired/wireless headphone into context. And since most of the changes there are not salavagable/technically infeasible we would have to write one from scratch.

UI side

The feature will be part of Prediction which will house all future Lawnchair prediction feature there, I envision it to also include feature like folder naming suggestions.

image

The switch act as global toggle in case some people don't like prediction features and wish to turn it off in one go.

screen-20260517-022344-1778959404906.mp4

The main switch on the top of the page will also act as global toggle.

Note

The animation will NOT be included in this PR, to restore this change revert bd5a06b, bd5a06b, then 81d0438

Cut from this PR // Animation rationale

UI side // Animation rationale

While animation will not be included in the PR, in case it is ever needed in the future.

The animation is originally designed with Lottie but there are some limitations that I personally feels like Android Animated Vector Drawable (AVD) is easier so it was ported to AVD. I designed in a way that it can be easily repeated or trick user that it's respond to preference signal by looping the animation when the feature is on, or stop looping when the feature is off.

Unfortunately there are some downside with the animation is that:

  1. You can't programatically do dynamic theming

Testing

Install the latest build from this PR or build locally, on first use the predictor will randomly generate a list of suggested apps to use. which you can see when you open the allapps, hotseat, and widget selector.

The datastore in allapps and hotseat are separate meaning allapps will only show prediction for app that you open from allapps, and hotseat show prediction for app you open from hotseat.

However in an event where the user didn't have data on one of the store like say user don't have on allapps, the predictor will fallback to grabbing data from other store if exist then fallback to random data if data didn't exist at all but they will always be ranked lower than what's in allapps store.

When you have usage stats turn on for Lawnchair, instead of grabbing random data, Lawnchair will grab the best apps user is likely to open from usage stats instead.

Type of change

Bug fix (A non-breaking change that fixes an issue)
New feature (A non-breaking change that adds functionality)
Breaking change (A fix or feature that would cause existing functionality to not work as expected)
Refactor (A code change that neither fixes a bug nor adds a feature)
Performance (A code change that improves performance)
Style (Code style changes)
Docs (Changes to documentation)
Chore (Changes to the build process or other tooling)

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Predictions settings with selectable sources (System, Lawnchair, or Off) and an opt-in weighted-usage option.
    • Added a dedicated screen to view and manage dismissed predicted apps.
  • Bug Fixes

    • Improved prediction UI stability by refreshing predicted content when resuming and after state updates.
    • Made prediction row restoration more robust when the UI is temporarily unavailable.

validcube added 2 commits May 14, 2026 00:38
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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: 6fed22cd-8602-4240-a39e-882df587f053

📥 Commits

Reviewing files that changed from the base of the PR and between 31a0f41 and 3ca7c4f.

📒 Files selected for processing (6)
  • build.gradle
  • lawnchair/res/values/strings.xml
  • lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/AppDrawerPreferences.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/GeneralPreferences.kt
  • quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
✅ Files skipped from review due to trivial changes (2)
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/GeneralPreferences.kt
  • lawnchair/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (4)
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/AppDrawerPreferences.kt
  • lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt
  • quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
  • build.gradle

📝 Walkthrough

Walkthrough

Adds a Lawnchair prediction system with selectable modes, persisted usage and dismissal tracking, ranking and event resolution, predictor lifecycle wiring, launcher refresh behavior, and new preferences and navigation entries for managing predictions.

Changes

App Predictions Feature

Layer / File(s) Summary
Prediction mode resources and prefs
lawnchair/src/app/lawnchair/predictions/PredictionMode.kt, lawnchair/res/values/config.xml, lawnchair/res/values/strings.xml, lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt
Defines prediction modes, adds the default mode config, prediction strings, and new prediction-related preference properties.
Prediction stores and keys
lawnchair/src/app/lawnchair/predictions/PredictionAppKey.kt, lawnchair/src/app/lawnchair/predictions/LawnchairPredictionStore.kt, lawnchair/src/app/lawnchair/predictions/LawnchairPredictionManager.kt, src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
Adds prediction key parsing, persisted ordered/unordered stores, the prediction manager singleton, and Dagger exposure for that manager.
Ranking and target compilation
lawnchair/src/app/lawnchair/predictions/UsageStatsRanker.kt, lawnchair/src/app/lawnchair/predictions/PredictionEventResolver.kt, lawnchair/src/app/lawnchair/predictions/LawnchairPredictionEngine.kt
Adds usage-stat ranking, event resolution, and target compilation for apps and widgets.
Event consumption and prediction updates
lawnchair/src/app/lawnchair/predictions/LawnchairAppPredictor.kt
Consumes launcher events, updates prediction stores, and enqueues app and widget prediction update tasks.
Model delegate and predictor access
lawnchair/src/app/lawnchair/predictions/LawnchairModelDelegate.kt, quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java, quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java, quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java, quickstep/dagger/com/android/launcher3/dagger/AppModule.kt
Adds the Lawnchair model delegate, exposes predictor state to subclasses, makes update-task constructors public, and binds the new delegate in Dagger.
Launcher prediction refresh
lawnchair/src/app/lawnchair/LawnchairLauncher.kt, quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
Refreshes predicted containers from the model and restores prediction UI on resume and activity transitions.
Predictions preference screens
lawnchair/src/app/lawnchair/ui/preferences/destinations/PredictionsPreferences.kt, lawnchair/src/app/lawnchair/ui/preferences/destinations/DismissedPredictionAppsPreferences.kt
Adds the predictions settings screen and dismissed-apps screen.
Navigation and preference entry points
lawnchair/src/app/lawnchair/ui/preferences/navigation/PreferenceRoutes.kt, lawnchair/src/app/lawnchair/ui/preferences/navigation/PreferenceNavigation.kt, lawnchair/src/app/lawnchair/ui/preferences/destinations/AppDrawerPreferences.kt, lawnchair/src/app/lawnchair/ui/preferences/destinations/GeneralPreferences.kt, lawnchair/src/app/lawnchair/ui/preferences/destinations/ExperimentalFeaturesPreferences.kt, build.gradle
Adds routes and destinations, replaces the app-drawer suggestions entry, wires navigation access, trims experimental features setup, and registers generated protobuf sources for IDE sync.

Sequence Diagram(s)

sequenceDiagram
  participant StatsLogCompatManager
  participant LawnchairAppPredictor
  participant PredictionEventResolver
  participant LawnchairPredictionEngine
  participant LauncherModel
  StatsLogCompatManager->>LawnchairAppPredictor: consume(event, atomInfo)
  LawnchairAppPredictor->>PredictionEventResolver: resolve(atomInfo)
  LawnchairAppPredictor->>LawnchairPredictionEngine: getFallbackRanked()
  LawnchairAppPredictor->>LawnchairPredictionEngine: compileAppTargets()
  LawnchairAppPredictor->>LawnchairPredictionEngine: compileWidgetTargets()
  LawnchairAppPredictor->>LauncherModel: enqueue PredictionUpdateTask / WidgetsPredictionUpdateTask
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • MrSluffy
  • yasanglass
  • SuperDragonXD

Poem

🐇 I hop through drawers where predictions glow,
With modes to pick and lists to show.
Dismissed apps dance, then line up neat,
While hotseat clues and widgets meet.
A rabbit grin, a launcher bright—
Fresh predictions take their flight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: reimplementing non-root app predictions.
Description check ✅ Passed The PR description follows the template with description, reasoning, testing, and type of change sections, and is mostly complete.
Linked Issues check ✅ Passed The changes add app-drawer prediction ranking and a Predictions UI, matching issue #4235's request for frequently used apps at the top.
Out of Scope Changes check ✅ Passed The added model, UI, store, and build changes all support app predictions; no clearly unrelated scope was introduced.
Docstring Coverage ✅ Passed Docstring coverage is 13.71% which is sufficient. The required threshold is 10.00%.
✨ 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.

validcube added 2 commits May 14, 2026 01:21
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
@validcube validcube mentioned this pull request May 13, 2026
40 tasks
validcube added 17 commits May 16, 2026 16:43
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
@validcube validcube marked this pull request as ready for review May 17, 2026 11:38

@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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lawnchair/src/app/lawnchair/predictions/LawnchairAppPredictor.kt`:
- Around line 456-470: recordLaunchEvent() currently only matches locations like
"hotseat"/"workspace"/"folder" and "predictions", so launches resolved as
"predictions/hotseat" (emitted by resolveLocation()) are ignored; update the
matching logic in recordLaunchEvent() to include the "predictions/hotseat" case
(e.g., check resolvedEvent.location.startsWith("predictions/hotseat") or use
contains("hotseat") alongside the existing checks) so that
hotseatStore.record(key) is called for those events; ensure you reference the
resolvedEvent.location branching and the hotseatStore.record(key) branch when
making the change.
- Around line 247-250: In shouldUseWeightedUsageStats(), replace the incorrect
runtime permission check using
context.checkSelfPermission(Manifest.permission.PACKAGE_USAGE_STATS) ==
PackageManager.PERMISSION_GRANTED with an AppOpsManager check: obtain
AppOpsManager via context.getSystemService(Context.APP_OPS_SERVICE) as
AppOpsManager and call checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
Process.myUid(), context.packageName) and return true only if it equals
AppOpsManager.MODE_ALLOWED; keep the existing
prefs2.lawnchairPredictorUseWeightedUsageStats.firstBlocking() guard as-is.

In `@lawnchair/src/app/lawnchair/predictions/PredictionMode.kt`:
- Around line 35-37: Replace the current runtime-permission check that uses
context.checkSelfPermission(Manifest.permission.PACKAGE_USAGE_STATS) and
PackageManager.PERMISSION_GRANTED with an AppOps-based check: obtain
AppOpsManager and call unsafeCheckOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
Process.myUid(), context.packageName) to determine if usage access is allowed
for the app (this will correctly reflect the Usage Access setting); keep the
existing presence check for AppPredictionManager via
context.getSystemService(AppPredictionManager::class.java), and if the AppOps
result indicates denied, ensure callers are directed to
Settings.ACTION_USAGE_ACCESS_SETTINGS so users can grant Usage Access.

In
`@lawnchair/src/app/lawnchair/ui/preferences/destinations/PredictionsPreferences.kt`:
- Around line 66-68: The current check that sets hasUsageStatsPermission in
PredictionsPreferences.kt uses context.checkSelfPermission(PACKAGE_USAGE_STATS)
which is wrong for app-op controlled usage access; replace that logic with an
AppOpsManager-based check: obtain AppOpsManager via context.getSystemService,
call appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
Binder.getCallingUid() or context.applicationInfo.uid, context.packageName) and
treat the result as allowed when equals AppOpsManager.MODE_ALLOWED (or
MODE_ALLOWED/allowed equivalent), then use that boolean for the weighted usage
stats toggle. Update any references to hasUsageStatsPermission so the toggle at
line ~102 reflects the AppOps result instead of checkSelfPermission.
🪄 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: 8062f6d6-46dc-4243-90c4-b7676a49f790

📥 Commits

Reviewing files that changed from the base of the PR and between 95c0fec and 2c3258e.

📒 Files selected for processing (24)
  • build.gradle
  • gradle/libs.versions.toml
  • lawnchair/res/values/config.xml
  • lawnchair/res/values/strings.xml
  • lawnchair/src/app/lawnchair/LawnchairLauncher.kt
  • lawnchair/src/app/lawnchair/predictions/AppUsageStore.kt
  • lawnchair/src/app/lawnchair/predictions/DismissedPredictionAppsStore.kt
  • lawnchair/src/app/lawnchair/predictions/LawnchairAppPredictor.kt
  • lawnchair/src/app/lawnchair/predictions/LawnchairModelDelegate.kt
  • lawnchair/src/app/lawnchair/predictions/PredictionAppKey.kt
  • lawnchair/src/app/lawnchair/predictions/PredictionMode.kt
  • lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt
  • lawnchair/src/app/lawnchair/ui/preferences/components/controls/NavigationSwitchPreference.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/DismissedPredictionAppsPreferences.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/ExperimentalFeaturesPreferences.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/GeneralPreferences.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/PredictionsPreferences.kt
  • lawnchair/src/app/lawnchair/ui/preferences/navigation/PreferenceNavigation.kt
  • lawnchair/src/app/lawnchair/ui/preferences/navigation/PreferenceRoutes.kt
  • quickstep/dagger/com/android/launcher3/dagger/AppModule.kt
  • quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
  • quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
  • quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
  • quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
💤 Files with no reviewable changes (1)
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/ExperimentalFeaturesPreferences.kt

Comment thread lawnchair/src/app/lawnchair/predictions/LawnchairAppPredictor.kt Outdated
Comment thread lawnchair/src/app/lawnchair/predictions/LawnchairAppPredictor.kt
Comment thread lawnchair/src/app/lawnchair/predictions/PredictionMode.kt Outdated
Comment thread lawnchair/src/app/lawnchair/ui/preferences/destinations/PredictionsPreferences.kt Outdated
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
validcube and others added 19 commits June 7, 2026 20:22
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Co-authored-by: TheSuperDragon <70206496+SuperDragonXD@users.noreply.github.com>
Co-authored-by: SuperDragonXD <70206496+superdragonxd@users.noreply.github.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Co-authored-by: SuperDragonXD <70206496+superdragonxd@users.noreply.github.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Co-authored-by: SuperDragonXD <70206496+superdragonxd@users.noreply.github.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Co-authored-by: SuperDragonXD <70206496+superdragonxd@users.noreply.github.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
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: 4

🧹 Nitpick comments (2)
lawnchair/src/app/lawnchair/predictions/LawnchairAppPredictor.kt (1)

130-132: 🏗️ Heavy lift

Avoid full uninstall-pruning on every event-driven update

updates() always calls prunePredictionStores(), which rechecks installed state across all stores on each launch/dismiss/layout mutation. In this hot path, that’s avoidable churn on MODEL_EXECUTOR. Consider pruning on package/profile change signals (or a throttled interval) instead.

Also applies to: 236-241

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lawnchair/src/app/lawnchair/predictions/LawnchairAppPredictor.kt` around
lines 130 - 132, The prunePredictionStores() call in the updates() method is
being executed on every event-driven update (launch/dismiss/layout mutation),
causing unnecessary churn on MODEL_EXECUTOR by rechecking installed state across
all stores repeatedly. Remove the prunePredictionStores(pm) call from the
updates() method and instead invoke it only on package/profile change signals or
implement a throttled interval-based approach to prune stores less frequently.
This prevents performance degradation in this hot code path while still
maintaining the pruning functionality when it actually matters.
lawnchair/src/app/lawnchair/predictions/LawnchairPredictionEngine.kt (1)

129-135: ⚡ Quick win

Make mergeRanked linear-time with a seen set

Current dedupe (key !in this) is quadratic. This runs in the prediction update path, so use a LinkedHashSet to keep order with O(1) membership checks.

Suggested refactor
-    fun mergeRanked(vararg rankedLists: List<String>): List<String> = buildList {
-        rankedLists.forEach { ranked ->
-            ranked.forEach { key ->
-                if (key !in this) add(key)
-            }
-        }
-    }
+    fun mergeRanked(vararg rankedLists: List<String>): List<String> {
+        val seen = LinkedHashSet<String>()
+        rankedLists.forEach { ranked ->
+            ranked.forEach { key -> seen.add(key) }
+        }
+        return seen.toList()
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lawnchair/src/app/lawnchair/predictions/LawnchairPredictionEngine.kt` around
lines 129 - 135, The mergeRanked function has a performance issue where checking
membership with `key !in this` on the buildList result is O(n) for each key,
resulting in quadratic overall time complexity. Fix this by introducing a
LinkedHashSet to track seen keys separately before adding them to the result
list, which provides O(1) membership checks while maintaining insertion order.
Replace the current logic that checks `key !in this` with a check against the
LinkedHashSet, add the key to both the LinkedHashSet and the result list when
it's not seen, and return the buildList result as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@build.gradle`:
- Line 349: The capitalizedVariantName definition uses toUpperCase() without
specifying a locale, which can cause non-reproducible builds across different
system locales. Add Locale.ROOT as an argument to the toUpperCase() method call
in the substring expression to ensure locale-stable capitalization behavior that
works consistently regardless of the system's default locale.

In `@lawnchair/src/app/lawnchair/predictions/LawnchairPredictionEngine.kt`:
- Around line 218-221: In the parseStoreKey function, the line that assigns the
user variable currently uses the Elvis operator with Process.myUserHandle() as a
fallback when resolveUserToken returns null, which silently remaps unknown user
tokens to the current user. Instead, change this line to return null immediately
when resolveUserToken fails by replacing the Process.myUserHandle() fallback
with return null, ensuring invalid keys are skipped rather than misattributed to
the current profile.

In `@lawnchair/src/app/lawnchair/predictions/LawnchairPredictionStore.kt`:
- Around line 25-45: The cache mutable list is shared state accessed from
multiple execution contexts without synchronization, which can cause race
conditions and concurrent modification issues. Protect all access to the cache
variable by wrapping the operations in the add() method and all other
cache-accessing methods (mentioned at lines 51-55, 62-75, 79-94, 102-104) with
synchronization using either a synchronized block with a lock object or Kotlin's
mutex to ensure thread-safe read and write operations on the shared cache state.

In
`@lawnchair/src/app/lawnchair/ui/preferences/destinations/PredictionsPreferences.kt`:
- Around line 159-163: The dismissedPredictionAppsCount is initialized once with
a snapshot of dismissedAppsStore.getEntries().size and never updates when the
store changes, causing stale data in the UI. Replace the mutable state
initialization to observe dismissedAppsStore for changes and update the count
reactively. Use a LaunchedEffect or SideEffect block to listen for store updates
and synchronize dismissedPredictionAppsCount whenever
dismissedAppsStore.getEntries() changes, ensuring the subtitle always reflects
the current dismissed apps count.

---

Nitpick comments:
In `@lawnchair/src/app/lawnchair/predictions/LawnchairAppPredictor.kt`:
- Around line 130-132: The prunePredictionStores() call in the updates() method
is being executed on every event-driven update (launch/dismiss/layout mutation),
causing unnecessary churn on MODEL_EXECUTOR by rechecking installed state across
all stores repeatedly. Remove the prunePredictionStores(pm) call from the
updates() method and instead invoke it only on package/profile change signals or
implement a throttled interval-based approach to prune stores less frequently.
This prevents performance degradation in this hot code path while still
maintaining the pruning functionality when it actually matters.

In `@lawnchair/src/app/lawnchair/predictions/LawnchairPredictionEngine.kt`:
- Around line 129-135: The mergeRanked function has a performance issue where
checking membership with `key !in this` on the buildList result is O(n) for each
key, resulting in quadratic overall time complexity. Fix this by introducing a
LinkedHashSet to track seen keys separately before adding them to the result
list, which provides O(1) membership checks while maintaining insertion order.
Replace the current logic that checks `key !in this` with a check against the
LinkedHashSet, add the key to both the LinkedHashSet and the result list when
it's not seen, and return the buildList result as before.
🪄 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: 298d8a3b-723f-45e0-8c7d-7ef13ee9278e

📥 Commits

Reviewing files that changed from the base of the PR and between b4edf91 and 698beba.

📒 Files selected for processing (19)
  • build.gradle
  • lawnchair/res/values/config.xml
  • lawnchair/res/values/strings.xml
  • lawnchair/src/app/lawnchair/LawnchairLauncher.kt
  • lawnchair/src/app/lawnchair/predictions/LawnchairAppPredictor.kt
  • lawnchair/src/app/lawnchair/predictions/LawnchairModelDelegate.kt
  • lawnchair/src/app/lawnchair/predictions/LawnchairPredictionEngine.kt
  • lawnchair/src/app/lawnchair/predictions/LawnchairPredictionManager.kt
  • lawnchair/src/app/lawnchair/predictions/LawnchairPredictionStore.kt
  • lawnchair/src/app/lawnchair/predictions/PredictionAppKey.kt
  • lawnchair/src/app/lawnchair/predictions/PredictionEventResolver.kt
  • lawnchair/src/app/lawnchair/predictions/UsageStatsRanker.kt
  • lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/AppDrawerPreferences.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/DismissedPredictionAppsPreferences.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/ExperimentalFeaturesPreferences.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/PredictionsPreferences.kt
  • quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
  • src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
✅ Files skipped from review due to trivial changes (2)
  • lawnchair/res/values/config.xml
  • lawnchair/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (7)
  • lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt
  • lawnchair/src/app/lawnchair/predictions/PredictionAppKey.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/AppDrawerPreferences.kt
  • lawnchair/src/app/lawnchair/LawnchairLauncher.kt
  • lawnchair/src/app/lawnchair/predictions/LawnchairModelDelegate.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/ExperimentalFeaturesPreferences.kt
  • quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java

Comment thread build.gradle Outdated
Comment thread lawnchair/src/app/lawnchair/predictions/LawnchairPredictionStore.kt
Comment thread lawnchair/src/app/lawnchair/ui/preferences/destinations/PredictionsPreferences.kt Outdated
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lawnchair/res/values/strings.xml`:
- Line 207: The string resource named app_predictions_description contains a
grammar error. In the string value "Show list of app that may be relevant to
you.", change "list" to "a list" (adding the article "a") and change "app" to
"apps" (making it plural to match the context). The corrected string should read
"Show a list of apps that may be relevant to you."
🪄 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: 45519190-f93c-4361-a1c3-8be20bfbf28c

📥 Commits

Reviewing files that changed from the base of the PR and between 698beba and 31a0f41.

📒 Files selected for processing (12)
  • build.gradle
  • lawnchair/res/values/strings.xml
  • lawnchair/src/app/lawnchair/predictions/LawnchairAppPredictor.kt
  • lawnchair/src/app/lawnchair/predictions/LawnchairPredictionEngine.kt
  • lawnchair/src/app/lawnchair/predictions/LawnchairPredictionManager.kt
  • lawnchair/src/app/lawnchair/predictions/LawnchairPredictionStore.kt
  • lawnchair/src/app/lawnchair/predictions/PredictionAppKey.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/AppDrawerPreferences.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/DismissedPredictionAppsPreferences.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/GeneralPreferences.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/PredictionsPreferences.kt
  • quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
💤 Files with no reviewable changes (2)
  • lawnchair/src/app/lawnchair/predictions/PredictionAppKey.kt
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/GeneralPreferences.kt
🚧 Files skipped from review as they are similar to previous changes (7)
  • lawnchair/src/app/lawnchair/ui/preferences/destinations/DismissedPredictionAppsPreferences.kt
  • lawnchair/src/app/lawnchair/predictions/LawnchairPredictionManager.kt
  • lawnchair/src/app/lawnchair/predictions/LawnchairPredictionStore.kt
  • build.gradle
  • lawnchair/src/app/lawnchair/predictions/LawnchairPredictionEngine.kt
  • quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
  • lawnchair/src/app/lawnchair/predictions/LawnchairAppPredictor.kt

Comment thread lawnchair/res/values/strings.xml Outdated
…pprediction

Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
@validcube validcube linked an issue Jul 1, 2026 that may be closed by this pull request
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.

[FEATURE] Suggested app On Dock [FEATURE] Allow viewing recently or frequently used apps in app drawer

2 participants