I’m working on an Android app that needs to show all installed applications on the device. My target is Android 15 with SDK 35. I want to create a list that shows both system apps and user installed apps using LazyColumn in Jetpack Compose.
The main challenges I’m facing are:
How to properly use PackageManager to get the app list
What properties are available for each app (like app name, package identifier, etc)
Getting this data into a usable format for my Composable
Here’s what I have so far:
@Composable
fun InstalledAppsScreen(modifier: Modifier = Modifier) {
val currentContext = LocalContext.current
val appManager = currentContext.packageManager // Is this correct?
// Display apps using LazyColumn
// Need help getting the actual list
}
I’ve already added the necessary permissions in my manifest:
Can someone show me the proper way to implement PackageManager for this use case? I’m particularly confused about the Context usage and how to extract the app information I need.
your context usage looks good, but you’re likely to run into memory leaks. try moving that packagemanager call into a viewmodel instead of having it in the composable. also, the permission syntax in your manifest is wrong - the queries tag isn’t meant for permissions!
Been down this road before on a device management app. Both answers got the permission issue right, but here’s what actually worked in production.
Skip getInstalledPackages() and getInstalledApplications() entirely. Use queryIntentActivities() with a launcher intent instead:
val intent = Intent(Intent.ACTION_MAIN, null)
intent.addCategory(Intent.CATEGORY_LAUNCHER)
val apps = packageManager.queryIntentActivities(intent, 0)
This gives you only launchable apps, which is what users expect. Each ResolveInfo has activityInfo.packageName and loadLabel(packageManager) for the name.
The real gotcha: loadLabel() and loadIcon() hit the file system every time. We cached everything in a Room database after the first load. Made scrolling buttery smooth.
Watch out for Android 15 restrictions too. Even with QUERY_ALL_PACKAGES, some system apps get filtered out now. Test on actual Android 15 devices, not just emulators.
One more thing - wrap your PackageManager calls in try-catch. I’ve seen random SecurityExceptions on some OEM ROMs even with proper permissions.
Your permission setup’s wrong for Android 15. Don’t put <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> inside the queries tag - it goes directly in the manifest. The queries tag does something completely different.
For getting the apps, use packageManager.getInstalledPackages(PackageManager.GET_META_DATA). Each PackageInfo gives you applicationInfo.loadLabel() for the name, packageName for the ID, and applicationInfo.loadIcon() for icons.
Heads up - this gets really slow with lots of apps. Run it on a background thread and use ViewModel with StateFlow. I froze my UI testing on a phone with 200+ apps and had to learn this the hard way.
You’re on the right track with getting PackageManager from context. But there’s a problem with your permission - you need <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> at the manifest level, not inside queries.
For getting apps, use getInstalledApplications(PackageManager.GET_META_DATA) instead of getInstalledPackages since you want application info. This gives you ApplicationInfo objects where you can grab loadLabel(packageManager) for display names, packageName for identifiers, and check flags & ApplicationInfo.FLAG_SYSTEM to tell system apps from user-installed ones.
Here’s what caught me off guard - the performance hit is brutal. Loading app labels and icons synchronously will destroy your UI. I moved all data loading to a Repository class with coroutines and cached everything. Your Composable should just observe the data through a ViewModel instead of hitting PackageManager directly.