Skip to content

Instantly share code, notes, and snippets.

@Nutomic
Last active June 25, 2018 09:42
Show Gist options
  • Save Nutomic/528ac465df545f34193b to your computer and use it in GitHub Desktop.
Save Nutomic/528ac465df545f34193b to your computer and use it in GitHub Desktop.
Create Intent Chooser with Application Whitelist. Based on https://gist.github.com/mediavrog/5625602
package de.videmic.view;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
public class CustomChooserIntent {
/**
* Creates a chooser that only shows installed apps that are allowed by the whitelist.
*
* @param pm PackageManager instance.
* @param target The intent to share.
* @param title The title of the chooser dialog.
* @param whitelist A list of package names that are allowed to show.
* @return Updated intent, to be passed to {@link android.content.Context#startActivity}.
*/
public static Intent create(PackageManager pm, Intent target, String title,
List<String> whitelist) {
Intent dummy = new Intent(target.getAction());
dummy.setType(target.getType());
List<ResolveInfo> resInfo = pm.queryIntentActivities(dummy, 0);
List<HashMap<String, String>> metaInfo = new ArrayList<>();
for (ResolveInfo ri : resInfo) {
if (ri.activityInfo == null || !whitelist.contains(ri.activityInfo.packageName))
continue;
HashMap<String, String> info = new HashMap<>();
info.put("packageName", ri.activityInfo.packageName);
info.put("className", ri.activityInfo.name);
info.put("simpleName", String.valueOf(ri.activityInfo.loadLabel(pm)));
metaInfo.add(info);
}
if (metaInfo.isEmpty()) {
// Force empty chooser by setting a nonexistent target class.
Intent emptyIntent = (Intent) target.clone();
emptyIntent.setPackage("your.package.name");
emptyIntent.setClassName("your.package.name", "NonExistingActivity");
return Intent.createChooser(emptyIntent, title);
}
// Sort items by display name.
Collections.sort(metaInfo, new Comparator<HashMap<String, String>>() {
@Override
public int compare(HashMap<String, String> map, HashMap<String, String> map2) {
return map.get("simpleName").compareTo(map2.get("simpleName"));
}
});
// create the custom intent list
List<Intent> targetedIntents = new ArrayList<>();
for (HashMap<String, String> mi : metaInfo) {
Intent targetedShareIntent = (Intent) target.clone();
targetedShareIntent.setPackage(mi.get("packageName"));
targetedShareIntent.setClassName(mi.get("packageName"), mi.get("className"));
targetedIntents.add(targetedShareIntent);
}
Intent chooserIntent = Intent.createChooser(targetedIntents.get(0), title);
targetedIntents.remove(0);
Parcelable[] targetedIntentsParcelable =
targetedIntents.toArray(new Parcelable[targetedIntents.size()]);
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntentsParcelable);
return chooserIntent;
}
}
@srayhunter
Copy link

I found that when calling targetedIntents.get(0)/targetedIntents.remove(0) that item is added back to the list of apps as the last one. Since there is the sort called, this will mess up the sorted list.

I, instead, used the following:

Intent chooserIntent = Intent.createChooser(targetedIntents.remove(targetedIntents.size() -1), title);

@tonykwok
Copy link

you can use the built-in comparator (ResolveInfo.DisplayNameComparator) instead:

List<ResolveInfo> resInfo = pm.queryIntentActivities(dummy, 0);
Collections.sort(resInfo, new ResolveInfo.DisplayNameComparator(getPackageManager()));
List<HashMap<String, String>> metaInfo = new ArrayList<>();
for (ResolveInfo ri : resInfo) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment