Tomáš Repčík - 21. 6. 2023

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:

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!

Subscribe for more
LinkedIn GitHub Medium