1

Based on this post, I am opening an email client to send multiple attachments with the following code:

Intent emailSelectorIntent = new Intent(Intent.ACTION_SENDTO);
emailSelectorIntent.setData(Uri.parse("mailto:"));

Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
intent.putExtra(Intent.EXTRA_EMAIL, addresses);
intent.putExtra(Intent.EXTRA_SUBJECT, subject);

ArrayList<Uri> uriList = new ArrayList<>();
uriList.add(FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", fileA));
uriList.add(FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", fileB));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_STREAM, uriList);

intent.setSelector(emailSelectorIntent);

if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(intent);
}

Note that, as taught in the linked post above, I need two Intents to make it work for sending attachments (and limiting the selector to only email clients)... emailSelectorIntent and intent, with the former being passed to the latter via intent.setSelector(emailSelectorIntent).

In order to make the selector open up, I also need the following in my Manifest:

<intent-filter>
    <action android:name="android.intent.action.SENDTO" />
    <data android:scheme="mailto" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

But when the selector opens, not only do I get email clients showing (e.g. gmail) I am also getting my own app in the list... which is not capable of sending emails.

This post suggests not putting android.intent.action.SENDTO in the Manifest, but if I remove that then the selector no longer opens at all. If put it back, the selector opens but with my own app as an option for an email client (which it is not).

So how do I avoid my own app showing up in the selector list in this context?

Note that my targetSdk is 32 in case that's relevant, which it may be (see comment from @CommonsWare here).

drmrbrewer
  • 9,593
  • 16
  • 68
  • 156
  • You can exclude your app from selector list by following this (https://stackoverflow.com/questions/4064848/how-to-exclude-your-own-app-from-the-share-menu#answer-17781876) – Umesh May 18 '22 at 12:54
  • "This post suggests not putting android.intent.action.SENDTO in the Manifest" -- "suggests" is understating it. By putting that manifest entry there, you are advertising **to every app on the device** that you support that action. So, your app will show up as an option in many other apps, not just as an option in your own app. That does not appear to be what you want. "So how do I avoid my own app showing up in the selector list in this context?" -- get rid of that ``, as your problem is not limited to "this context". – CommonsWare May 18 '22 at 13:07
  • "if I remove that then the selector no longer opens at all" -- what specifically happens? What version(s) of Android are you testing on? – CommonsWare May 18 '22 at 13:08
  • @CommonsWare interestingly, without `android.intent.action.SENDTO` in the Manifest, `startActivity()` is never called because the test `intent.resolveActivity(getPackageManager()) != null` fails. And more interestingly, removing that test entirely, and just calling `startActivity()` regardless, does open up the selector with only email clients showing (and not my app). But surely that test is there for a reason, i.e. to ensure that there is an email client available? I'm testing this on `32` only... perhaps the solution does relate to `` as you suggested? – drmrbrewer May 18 '22 at 13:35
  • @CommonsWare yes I confirm that removing the `` and instead putting an equivalent entry in the top level of the Manifest as `...` makes it work... though not sure how this will work on older versions of Android? If you add an answer I can mark it as correct. – drmrbrewer May 18 '22 at 13:47

2 Answers2

1

I'm somewhat mystified that having that <intent-filter> changes anything. Regardless, you really don't want that there, as it can cause other apps to route ACTION_SENDTO requests to your app, which you don't want.

without android.intent.action.SENDTO in the Manifest, startActivity() is never called because the test intent.resolveActivity(getPackageManager()) != null fails

That is due to package visibility rules. Your options are to either add <queries> to say that you need to see who all supports your Intent, or to replace the resolveActivity() call with a try/catch:

try {
    startActivity(intent);
} catch (Exception e) {
    // whatever you want to do to gracefully degrade in case there is no suitable activity to start
}

not sure how this will work on older versions of Android?

<queries> will be ignored on older versions of Android, but package visibility isn't a problem on older versions of Android. Personally, I prefer try/catch, as it handles more scenarios.

CommonsWare
  • 954,112
  • 185
  • 2,315
  • 2,367
  • 1
    Thanks, very helpful. Just for the record, I added another answer to expand on the `` alternative, but have marked yours as the accepted answer. – drmrbrewer May 18 '22 at 14:59
1

The answer from @CommonsWare is (as ever) very pragmatic and informative. I think I prefer the try/catch solution proposed there too, and will adopt it. But the alternative of leaving the resolveActivity() check and adding a <queries> item in the top level of the Manifest also works, like so:

<queries>
    <intent>
        <action android:name="android.intent.action.SENDTO" />
        <data android:scheme="mailto" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent>
</queries>
drmrbrewer
  • 9,593
  • 16
  • 68
  • 156