I have, thanks to the help of a certain blogpost, some code to retrieve a list of user-installed (i.e. non-system) apps on the user's device:
class NotificationPermissionHelper(private val context: Context) {
fun getAllApps(): List<ApplicationInfo> {
val packageManager = context.packageManager
val mainIntent = Intent(Intent.ACTION_MAIN, null)
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER)
val resolveInfoList: List<ResolveInfo> = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageManager.queryIntentActivities(
mainIntent,
PackageManager.ResolveInfoFlags.of(0L)
)
} else {
packageManager.queryIntentActivities(mainIntent, 0)
}
return resolveInfoList.mapNotNull { resolveInfo ->
try {
packageManager.getApplicationInfo(resolveInfo.activityInfo.packageName, 0)
} catch (e: PackageManager.NameNotFoundException) {
// Handle cases where the application info can't be found.
// This could happen if the app was uninstalled.
null
}
}
}
}
However, my attempt to filter that list against the notification permission, fails miserably....
Like this, of course:
fun getAppsWithNotificationPermission(): List<ApplicationInfo> {
val allApps = getAllApps()
return allApps.filter { hasNotificationPermission(it.packageName) }
}
private fun hasNotificationPermission(packageName: String): Boolean {
// Check if the app is opted into notifications
val appOpsManager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
val mode = getMode(appOpsManager, packageName)
return mode == AppOpsManager.MODE_ALLOWED
}
private fun getMode(appOpsManager: AppOpsManager, packageName: String): Int {
return onGetOp()(appOpsManager,
"android:post_notification",
android.os.Process.myUid(),
packageName,
)
}
private fun onGetOp(): (AppOpsManager, String, Int, String) -> Int {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return AppOpsManager::unsafeCheckOpNoThrow
}
return AppOpsManager::checkOpNoThrow
}
We have an PackageListDataSource
that uses an event bus to start the PackageQueryFetcherActivity
which:
We get a list of ApplicationInfo
containing at least the Phone and Messages apps
We get an empty list, as for every app....
when I place debug breakpoint on the return statement of hasNotificationPermission
,
mode == 1
but
AppOpsManager.MODE_ALLOWED == 0
Speaking of permissions, we don't get the authentication screen for querying all packages, as this method as defined on our BaseFetcherActivity
returns true for permission = android.Manifest.permission.QUERY_ALL_PACKAGES
:
protected fun hasDataAccessPermission(): Boolean {
return ContextCompat.checkSelfPermission(
this,
this.permission,
) == PackageManager.PERMISSION_GRANTED
}
So, it just performs the fetch of all apps (as discussed eariler), and the check for whether the app package has the notification permission seems to always return AppOpsManager.MODE_IGNORED
return onGetOp()(appOpsManager,
"android:post_notification",
android.os.Process.myUid(),
packageName,
)
According to the documentation, your second parameter needs to be "The uid of the application attempting to perform the operation", just as the third parameter is "The name of the application attempting to perform the operation" (emphasis added). packageName
presumably is correct for the third parameter value, but you are passing your own app's uid, not the uid of packageName
.