31

I've an extension function for opening an intent for my activities:

fun Activity.openIntent(action: String?, type: String?, uri: Uri?) {
    Intent()
        .apply {
            action?.let { this.action = it }
            uri?.let { this.data = it }
            type?.let { this.type = it }
        }
        .also { intent ->
            packageManager?.let {
                if (intent.resolveActivity(it) != null)
                    startActivity(intent)
                else
                    showToast(R.string.application_not_found)
            }
        }
}

My targetSdkVersion is 30. It gives me a warning in intent.resolveActivity(it):

Consider adding a queries declaration to your manifest when calling this method.

So What should I do to solve this warning?

Saman Sattari
  • 3,322
  • 6
  • 30
  • 46

4 Answers4

40

The simplest solution is to get rid of resolveActivity(). Replace:

            packageManager?.let {
                if (intent.resolveActivity(it) != null)
                    startActivity(intent)
                else
                    showToast(R.string.application_not_found)
            }

with:

            try {
                startActivity(intent)
            } catch (ex: ActivityNotFoundException) {
                showToast(R.string.application_not_found)
            }

This gives you the same result with a bit better performance, and it will get rid of the warning.

Another option would be to add the QUERY_ALL_PACKAGES permission. This may get you banned from the Play Store.

Otherwise, you will need to:

  • Build a list of every possible openIntent() call that your app may make

  • Add a <queries> element to your manifest that lists all of those possible Intent structures that you want to be able to use with resolveActivity()

  • Repeat this process periodically, in case you add new scenarios for openIntent()

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • `The application could not be installed: INSTALL_PARSE_FAILED_MANIFEST_MALFORMED`. I get the this error if I add a queries element to the manifest. https://gist.github.com/sudhirkhanger/fbb613b38f25c44ad0342a237a2d06e6 – Sudhir Singh Khanger Feb 24 '21 at 11:27
  • @SudhirSinghKhanger: `none` is not a valid MIME type, so that element is not helping. Try removing it. – CommonsWare Feb 24 '21 at 11:44
  • @CommonsWare I have similar question [link](https://stackoverflow.com/questions/72865512/android-can-explicit-intents-work-without-adding-query-all-packages-or-package). Do you have some documentation stating `resolveAcitivity` won't work without `QUERY_ALL_PACKAGES` or package name under queries tag. – abhiarora Jul 06 '22 at 09:25
  • @abhiarora: [This official blog post](https://medium.com/androiddevelopers/package-visibility-in-android-11-cc857f221cd9) mentions `resolveActivity()`. It happens to mention it in a specific context (launching a Web browser on a URL) that has other solutions (`FLAG_ACTIVITY_REQUIRE_NON_BROWSER`). – CommonsWare Jul 06 '22 at 11:13
  • But it doesn't mention that resolveActivity would return `null`. I think this article just suggests an alternative without pointing out resolveActivity won't work. – abhiarora Jul 06 '22 at 15:48
  • 1
    @abhiarora: You asked for documentation. In a quick search, that was the closest thing that I found. – CommonsWare Jul 06 '22 at 15:49
  • I have tested `resolveActivity` in an app which targets 30 API. It was working perfectly fine. I am not sure how it is working. This is my [question](https://stackoverflow.com/questions/72865512/android-can-explicit-intents-work-without-adding-query-all-packages-or-package) – abhiarora Jul 06 '22 at 15:49
10

So starting Android 11 (i.e, if your app targets Android 11) not all applications will be visible to your application. Some apps are visible by default but in order to access other applications through your application, you will have to declare queries in your manifest else your application will not be able to access them. You can read about that here.

So if your application targets Android 11 and is to access an application that may not be visible by default you will want to add queries for them in the manifest file.

In your case, this warning is not applicable as I believe you are using implicit intents to open other applications. Using implicit intents, other applications can be accessed irrespective of app visibility. If your app target Android 10 or lower you can suppress the warning as all apps are visible by default.

To suppress the lint warning you can either:

  1. Add the suppress annotation, like so:
@SuppressLint("QueryPermissionsNeeded")
fun Activity.openIntent(action: String?, type: String?, uri: Uri?): Activity {
  1. Add the following to your android block in your app module build gradle file:
lintOptions {
    ignore "QueryPermissionsNeeded"
}
Xid
  • 4,578
  • 2
  • 13
  • 35
  • Seems like just the lintOptions block does the job. Thanks! – Itay Feldman Jan 25 '21 at 00:30
  • 1
    Both do the job. You can use either of the two. – Xid Jan 25 '21 at 05:51
  • "this warning is not applicable as I believe you are using implicit intents to open other applications" -- the warning is from the `resolveActivity()` call, which need the `` element to work. – CommonsWare Jan 31 '21 at 00:21
  • Yes, that's what I have written. Though it is only the case for explicit intents and apps targetting API 30 as specified in the official android documentation. I have provided the apt references for the same in my answer – Xid Jan 31 '21 at 14:20
  • How and why should you explicitly open WhatsApp? Google invented Intents that you no longer have to explicitly say who is doing a certain task. Who cares if the user has an alternative messenger that does it? – The incredible Jan Sep 14 '21 at 14:42
10

Replace

if (intent.resolveActivity(it) != null)

with

if (it.resolveActivity(intent, 0) != null)

and the warning will be gone.

Tatsuya Fujisaki
  • 1,434
  • 15
  • 18
3

From API level 30 package visibility is restricted. So add appropriate query in your AndroidManifest file outside <application> tag.

<queries>
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="http" />
    </intent>
</queries>