Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,16 @@ fun SharedTransitionScope.AppListComponent(
val checkedApps = MainApplication.Companion.store.getStringSet("app_list")
val apps = mutableListOf<AppListData>()

packages?.sortBy { it.loadLabel(packageManager).toString() }

var index = 0
packages?.forEach {
val app = AppListData(
val appList = packages?.map {
AppListData(
it.loadLabel(packageManager).toString(), // app name
it.packageName, it.loadIcon(packageManager), // icon
(it.flags and ApplicationInfo.FLAG_SYSTEM) > 0, // is system
)
}?.sortedBy { it.appName }

var index = 0
appList?.forEach { app ->
if (checkedApps.contains(app.packageName)) apps.add(index++, app)
else apps.add(app)
}
Comment on lines +113 to 125

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.

medium

The current implementation has a few areas for improvement:

  1. Nullability & Smart Casting: packageManager is nullable, but loadLabel and loadIcon expect a non-null PackageManager. Since packages is only non-null if packageManager is non-null, you should use a local non-null reference or !! to avoid potential type mismatches or lint errors.
  2. Efficiency: Using apps.add(index++, app) on an ArrayList (the default for mutableListOf) results in $O(N^2)$ complexity in the worst case because inserting at an index requires shifting subsequent elements. Using partition is more idiomatic and $O(N)$.
  3. UX Bug: If packages is null (which happens if packageManager is null), the code returns an empty list. This causes the UI to hide the loading indicator and show an empty screen. Returning null instead would correctly preserve the loading state.
  4. Performance Note: While caching labels reduces IPC calls during sorting, loadIcon is still called eagerly for every app. For devices with many apps, this can be slow and memory-intensive. Consider loading icons lazily in the UI layer in a future refactor.
Suggested change
val appList = packages?.map {
AppListData(
it.loadLabel(packageManager).toString(), // app name
it.packageName, it.loadIcon(packageManager), // icon
(it.flags and ApplicationInfo.FLAG_SYSTEM) > 0, // is system
)
}?.sortedBy { it.appName }
var index = 0
appList?.forEach { app ->
if (checkedApps.contains(app.packageName)) apps.add(index++, app)
else apps.add(app)
}
val appList = packages?.let { pkgList ->
val pm = packageManager!!
pkgList.map {
AppListData(
it.loadLabel(pm).toString(), // app name
it.packageName, it.loadIcon(pm), // icon
(it.flags and ApplicationInfo.FLAG_SYSTEM) > 0, // is system
)
}
}?.sortedBy { it.appName } ?: return@withContext null
val (checked, unchecked) = appList.partition { checkedApps.contains(it.packageName) }
apps.addAll(checked)
apps.addAll(unchecked)

Expand Down
Loading