37

I want to display an activity chooser that shows all apps that can VIEW and/or EDIT some data. Is there an easy way to do this, or do I have to implement my own activity chooser dialog? Or maybe I can just subclass Intent? Thanks.

Ken
  • 2,918
  • 1
  • 25
  • 31

2 Answers2

73

I found a partial solution by using EXTRA_INITIAL_INTENTS:

Intent viewIntent = new Intent(Intent.ACTION_VIEW);
Intent editIntent = new Intent(Intent.ACTION_EDIT);
viewIntent.setDataAndType(uri, type);
editIntent.setDataAndType(uri, type);
Intent chooserIntent = Intent.createChooser(editIntent, "Open in...");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { viewIntent });
startActivity(chooserIntent);

I say partial because if an app supports both ACTION_VIEW and ACTION_EDIT it will show up twice in the list, one of which will open the file for viewing and the other for editing, and you wouldn't necessarily know which is which. I think a complete solution would require a custom app chooser, as Tim suggested.

EDIT (Complete Solution!):

I found a solution that doesn't involving writing a custom app chooser. In order to differentiate ACTION_EDIT apps from ACTION_VIEW apps, I found a way to append a "(for editing)" string to the labels for one of them (in my case, ACTION_EDIT) by using the line of code Tim provided. In addition, to ensure the appended string doesn't appear to be a part of the app name, I changed the color of it to cyan:

PackageManager pm = kyoPrint.getPackageManager();
Intent viewIntent = new Intent(Intent.ACTION_VIEW);
Intent editIntent = new Intent(Intent.ACTION_EDIT);
viewIntent.setDataAndType(uri, type);
editIntent.setDataAndType(uri, type);
Intent openInChooser = Intent.createChooser(viewIntent, "Open in...");

// Append " (for editing)" to applicable apps, otherwise they will show up twice identically
Spannable forEditing = new SpannableString(" (for editing)");
forEditing.setSpan(new ForegroundColorSpan(Color.CYAN), 0, forEditing.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
List<ResolveInfo> resInfo = pm.queryIntentActivities(editIntent, 0);
Intent[] extraIntents = new Intent[resInfo.size()];
for (int i = 0; i < resInfo.size(); i++) {
    // Extract the label, append it, and repackage it in a LabeledIntent
    ResolveInfo ri = resInfo.get(i);
    String packageName = ri.activityInfo.packageName;
    Intent intent = new Intent();
    intent.setComponent(new ComponentName(packageName, ri.activityInfo.name));
    intent.setAction(Intent.ACTION_EDIT);
    intent.setDataAndType(uri, type);
    CharSequence label = TextUtils.concat(ri.loadLabel(pm), forEditing);
    extraIntents[i] = new LabeledIntent(intent, packageName, label, ri.icon);
}

openInChooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents);
startActivity(openInChooser);

enter image description here

EDIT 2: BUG

If there are no activities found by the first intent, NO activities will be displayed, including any found by the second intent. I ended up writing my own chooser. I just populated an ExpandableListView with headings for each type of intent with their respective activities as children (stored as individual LabeledIntents).

Ken
  • 2,918
  • 1
  • 25
  • 31
  • Great answer! I wanted to do a Chooser with Intents coming from several different types and even an in-app Activity, and this allowed me to do merge them all in a single Chooser. – Rafael Nobre Oct 01 '12 at 21:13
  • This is excellent !! I wanted to launch as many browsers as possible to view a local HTML file using all the different intents I discovered. This works perfectly, thank you. – daveD Jul 24 '13 at 21:06
  • @Ken is it possible to avoid duplicate items in the chooser? i'm talking about items that exist from one intent and also in the other intent ? i'm trying to create a chooser for sharing , and wish to set the message to be short for SMS, but i can't find out how to do it. – android developer Aug 19 '13 at 14:18
  • Yes, after getting the ResolveInfo for each Intent via PackageManager.queryIntentActivities(), you can compare their package names via ResolveInfo.activityInfo.packageName and filter out duplicates. – Ken Aug 22 '13 at 21:00
  • @Ken I see. And is it possible to also hide items that I don't wish to show? – android developer May 19 '14 at 23:45
  • Yes, in the same manner as filtering out duplicates. Just don't add it to `extraIntents` – Ken May 20 '14 at 01:37
  • Great one for having an chooserIntent for selecting images from camera and gallery. – velval Feb 05 '16 at 01:01
4

depends on what your data is. But in general using with ACTION_VIEW and some data attached you can use an IntentChoooser to populate the list of choices to the user.

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "some data");  
startActivity(Intent.createChooser(intent, "Open with"));

Be sure to set your type correctly so that applications will know that you are wanting to open something that they may be able to handle.

EDIT: I think you would have to use a package manager query to get your two lists then combine them into one and make your own activity / dialog that will pop-up and get populated with the data contained in your combined list.

Here is an example making the query:

List<ResolveInfo> resInfo = getPackageManager().queryIntentActivities(intent, 0);

so if you make your two Intents and call this twice, passing in each intent you should be able to combine the resulting lists to get your full set of possibilities. Then it is up to to create an activity or dialog to show them with.

FoamyGuy
  • 46,603
  • 18
  • 125
  • 156
  • I want to show all apps that support ACTION_VIEW **or** ACTION_EDIT, not just ACTION_VIEW – Ken Jun 13 '12 at 18:32
  • Ahh, I see. You'll have to do a bit more of the legwork yourself then I think. See my edit. – FoamyGuy Jun 13 '12 at 18:35