Listing All Installed Apps in Android 13 via PackageManager
Changes in PackageManager in Android Tiramisu
Android 11 restricted querying packages on the phone. The app can search only for individual applications, which can be declared within the manifest. To make available all the apps, the manifest must contain special permission, which is then subject to control at the Google Play store. With Android 13 were introduced some changes in the API, so this is a small refresher on how to handle this scenario.
Photo by Rami Al-zayat on Unsplash
Searching for specific apps
Firstly, the app needs to be declared in the manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
...
<queries> <!-- name the required packages -->
<package android:name="com.your.company.your.app"/>
<package android:name="com.other.company.other.app"/>
</queries>
...
</manifest>
Secondly, you can directly query the specific packages via the package manager. Here comes the change with Android 13, where the method asks for the specific flag type, which is the same as before, but with a different method call.
val pm = context.packageManager
val appInfo: ApplicationInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
pm.getApplicationInfo(
"com.your.company.your.app", // any other package name
PackageManager.ApplicationInfoFlags.of(0)
)
} else {
pm.getApplicationInfo(packageType.packageName, 0)
}
The 0 is the default flag. You should get basic information about the application and its package. In the most cases it is sufficient. However, some fields or information might be missing and here comes the flag specification. It is because, some apps might be large and the PackageManager tries to trim the contained information to minimum.
Some examples of the flags from PackageManager class:
GET_META_DATA
— enables.metadata
, which are<meta-data>
tags from the app’s manifestGET_SHARED_LIBRARY_FILES
— enables.sharedLibraries
, which shows all paths to the shared libraries used by the app- and many more can be found here
Getting all installed apps
After Android 11, the app cannot access all the apps as was mentioned, only with permission. You can add this permission, and the app will be able to search across all the apps on the phone. The app with such permission will be allowed into Google Play Store only if the main functionality of the app requires such permission. It is not runtime permission, so no invocation is required. More on it here.
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
Get all apps — system apps included
To obtain all the apps from the phone you can directly query the PackageManager accordingly:
val pm = context.packageManager
val appInfos = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
pm.getInstalledApplications(PackageManager.ApplicationInfoFlags.of(0L))
} else {
pm.getInstalledApplications(0)
}
However, this approach will return all the applications and system applications included, which are not usually accessible to the basic user and are hidden.
Get all apps — without system apps
There is a way how to filter system apps. A good rule of thumb is if the app does not contain the main activity, it is not a user-facing application and could be considered a system app.
// searching main activities labeled to be launchers of the apps
val pm = context.packageManager
val mainIntent = Intent(Intent.ACTION_MAIN, null)
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER)
val resolvedInfos = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
pm.queryIntentActivities(
mainIntent,
PackageManager.ResolveInfoFlags.of(0L)
)
} else {
pm.queryIntentActivities(mainIntent, 0)
}
These will return a list of the resources resolved by searching for the intent filters in AndroidManifest.xml’s <intent>
tags of the apps. This information contains the package name of the app by resolvedInfo[0].activityInfo.packageName
. With the package name, you can get any specific information which you would need.
If you just need to get the app’s name, package name or icon, you can directly get it from the activity information:
// one of the resolved info from the package manager
val pm = context.packageManager
val resources = pm.getResourcesForApplication(resolveInfo.activityInfo.applicationInfo)
val appName = if (resolveInfo.activityInfo.labelRes != 0) {
// getting proper label from resources
resources.getString(resolveInfo.activityInfo.labelRes)
} else {
// getting it out of app info - equivalent to context.packageManager.getApplicationInfo
resolveInfo.activityInfo.applicationInfo.loadLabel(pm).toString()
}
val packageName = resolveInfo.activityInfo.packageName
val iconDrawable = resovleInfo.activityInfo.loadIcon(pm)
Thanks for reading!