Can't open camera intent. queryIntentActivities returns an empty list on api level 30 but finds results in level 29. What changed?

Viewed 476

I'm querying for activities using queryIntentActivities which worked fine in api level 29, but as soon as I upgraded to level 30 the result is always empty.

This is the intent:

val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.putExtra("android.intent.extras.CAMERA_FACING", typeCamera)
uri?.grantWriteForIntent(activity, intent)
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
if (intent.isIntentSafe(activity)) {
    activity.__waitResult__ = true
    activity.startActivityForResult(intent, requestCode)
}

Then the isIntentSafe function:

fun Intent?.isIntentSafe(context: Context) =
    if (this == null)
        false
    else
        context.packageManager.queryIntentActivities(this, PackageManager.MATCH_DEFAULT_ONLY).size > 0

This is where queryIntentActivites is empty in level 30 and has content in level 29. Does anyone know what changed between these level which could have this effect?

I should add that if I skip the isIntentSafe check in both api levels the activity starts fine anyway. But I want to know why isIntentSafe stopped working in the newer api.

EDIT: I have tried changing the intent for the front facing camera like in this answer How to launch front camera with intent? which doesn't make a difference.

2 Answers

I went through the Android 11 changes again and found "Media intent actions require default system camera".

So I searched a bit more and found a solution from CommonsGuy. Modified his code a little bit to open the front facing camera, worked straight away!

fun enhanceCameraIntent(context: Context, baseIntent: Intent, title: String): Intent {
    val pm = context.packageManager

    val cameraIntents =
        CAMERA_CANDIDATES.map { Intent(baseIntent).setPackage(it) }
            .filter { pm.queryIntentActivities(it, 0).isNotEmpty() }
            .toTypedArray()

    return if (cameraIntents.isEmpty()) {
        baseIntent
    } else {
        Intent
            .createChooser(baseIntent, title)
            .putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents)
    }
}

fun openFrontFacingCamera(activity: BaseActivity, uri: Uri?, requestCode: Int) {
    try {
        val baseIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)

        uri?.grantWriteForIntent(activity, baseIntent)
        baseIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
        baseIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
        baseIntent.putExtra("android.intent.extra.USE_FRONT_CAMERA", true)

        activity.startActivityForResult(
            enhanceCameraIntent(activity, baseIntent, MediaStore.ACTION_IMAGE_CAPTURE),
            requestCode
        )
    } catch (ex: ActivityNotFoundException) {
        Log.d("No default camera", "Activity not found!")
    }
}

As many times before, hat off to CommonsGuy!

In Android 11 there are new rules for interacting with the apps installed on the device.

So if your app targets Android 11 or higher, you might need to add the <queries> element in your app's manifest file:

<queries>
  <intent>
    <action android:name="android.media.action.IMAGE_CAPTURE" />
  </intent>
</queries>

You can also find more details in this guide.

Related