Custom filtering of intent chooser based on installed Android package name

AndroidAndroid Intent

Android Problem Overview


I'd like to leverage the built-in intent chooser to display a custom filtered list of apps for user to select from and launch.

I know how to get a list of installed packages:

final Intent myIntent = new Intent(android.content.Intent.ACTION_MAIN);  
List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(myIntent, 0);

At this point I want to filter the list based on a specific string (or variation of strings) contained within the package name, which I can figure out how to do as well.

But here's where I get stuck. As far as I know, Intent.createChooser() takes only a single target Intent as a parameter. I was hoping there was an overload that took a list of intents based on package and class names or something. But I don't see anything like that. Did I miss that somewhere?

So the question is, is this possible to do with a built-in chooser, or do I have to construct my own with AlertDialog Builder? I'm hoping to avoid the later.

Thanks in advance.

Android Solutions


Solution 1 - Android

Here is a solution i whipped up. I use it to have different intent data for each selection in the chooser, but you can easily remove an intent from the list as well. Hope this helps:

List<Intent> targetedShareIntents = new ArrayList<Intent>();
Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND);
shareIntent.setType("text/plain");
List<ResolveInfo> resInfo = getPackageManager().queryIntentActivities(shareIntent, 0);
if (!resInfo.isEmpty()) {
	for (ResolveInfo resolveInfo : resInfo) {
		String packageName = resolveInfo.activityInfo.packageName;
		Intent targetedShareIntent = new Intent(android.content.Intent.ACTION_SEND);
		targetedShareIntent.setType("text/plain");
		targetedShareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "subject to be shared");
		if (TextUtils.equals(packageName, "com.facebook.katana")) {
			targetedShareIntent.putExtra(android.content.Intent.EXTRA_TEXT, "http://link-to-be-shared.com");
		} else {
			targetedShareIntent.putExtra(android.content.Intent.EXTRA_TEXT, "text message to shared");
		}
		targetedShareIntent.setPackage(packageName);
		targetedShareIntents.add(targetedShareIntent);
	}
	Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(0), "Select app to share");
	chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[targetedShareIntents.size()]));
	startActivity(chooserIntent);
}

EDIT

While using this method i get names of some apps as android system. If anybody get this error pls add below lines before targetedShareIntents.add(targetedShareIntent);

 targetedShareIntent.setClassName(
                        resolveInfo.activityInfo.packageName,
                        resolveInfo.activityInfo.name);

source : https://stackoverflow.com/questions/33233966/android-share-intent-twitter-how-to-share-only-by-tweet-or-direct-message

Solution 2 - Android

The only one additional parameter for the chooser is Intent.EXTRA_INITIAL_INTENTS. Its description is:

> A Parcelable[] of Intent or LabeledIntent objects as set with putExtra(String, Parcelable[]) of additional activities to place a the front of the list of choices, when shown to the user with a ACTION_CHOOSER.

I haven't found any way in Android sources to exclude other activities from the list, so it seems there's no way to do what you want to do using the chooser.

EDIT: That's really easy to find out. Just check ChooserActivity and ResolverActivity source code. These classes are rather small.

Solution 3 - Android

I did an small modification to have a list of the apps that you want to share with by name. It is almost what you already posted but adding the apps to share by name

String[] nameOfAppsToShareWith = new String[] { "facebook", "twitter", "gmail" };
String[] blacklist = new String[]{"com.any.package", "net.other.package"};
// your share intent
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "some text");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "a subject");
// ... anything else you want to add invoke custom chooser
startActivity(generateCustomChooserIntent(intent, blacklist));

private Intent generateCustomChooserIntent(Intent prototype,
			String[] forbiddenChoices)
	{
		List<Intent> targetedShareIntents = new ArrayList<Intent>();
		List<HashMap<String, String>> intentMetaInfo = new ArrayList<HashMap<String, String>>();
		Intent chooserIntent;

		Intent dummy = new Intent(prototype.getAction());
		dummy.setType(prototype.getType());
		List<ResolveInfo> resInfo = getPackageManager().queryIntentActivities(dummy,0);

		if (!resInfo.isEmpty())
		{
			for (ResolveInfo resolveInfo : resInfo)
			{
				if (resolveInfo.activityInfo == null
						|| Arrays.asList(forbiddenChoices).contains(
								resolveInfo.activityInfo.packageName))
					continue;
				//Get all the posible sharers
				HashMap<String, String> info = new HashMap<String, String>();
				info.put("packageName", resolveInfo.activityInfo.packageName);
				info.put("className", resolveInfo.activityInfo.name);
				String appName = String.valueOf(resolveInfo.activityInfo
						.loadLabel(getPackageManager()));
				info.put("simpleName", appName);
				//Add only what we want
				if (Arrays.asList(nameOfAppsToShareWith).contains(
						appName.toLowerCase()))
				{
					intentMetaInfo.add(info);
				}
			}

			if (!intentMetaInfo.isEmpty())
			{
				// sorting for nice readability
				Collections.sort(intentMetaInfo,
						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
				for (HashMap<String, String> metaInfo : intentMetaInfo)
				{
					Intent targetedShareIntent = (Intent) prototype.clone();
					targetedShareIntent.setPackage(metaInfo.get("packageName"));
					targetedShareIntent.setClassName(
							metaInfo.get("packageName"),
							metaInfo.get("className"));
					targetedShareIntents.add(targetedShareIntent);
				}
				String shareVia = getString(R.string.offer_share_via);
				String shareTitle = shareVia.substring(0, 1).toUpperCase()
						+ shareVia.substring(1);
				chooserIntent = Intent.createChooser(targetedShareIntents
						.remove(targetedShareIntents.size() - 1), shareTitle);
				chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
						targetedShareIntents.toArray(new Parcelable[] {}));
				return chooserIntent;
			}
		}

		return Intent.createChooser(prototype,
				getString(R.string.offer_share_via));
	}

It almost the same solution that Makibo posted but with a little add to make an easy form of picking the apps you want to share with just by adding the name so you won't have any problem in case they change the package name or something like this. As long as they don't change the name.

Solution 4 - Android

My implementation of custom open chooser.

Features:

  • apps are sorted in alphabetical order
  • handling default app defined by user
  • handling no apps case
  • filtering itself

public static Intent createOpenFileIntent(Context context, String pathToFile) {
	File file = new File(pathToFile);
	String extension = extensionFromName(file.getName());
	String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
	if (mimeType == null) {
		//If android doesn't know extension we can check our own list.
		mimeType = KNOWN_MIME_TYPES.get(DataViewHelper.extensionFromName(file.getName()));
	}

	Intent openIntent = new Intent();
	openIntent.setAction(android.content.Intent.ACTION_VIEW);
	openIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
	openIntent.setDataAndType(Uri.fromFile(file), mimeType);

	// 1. Check if there is a default app opener for this type of content.
	final PackageManager packageManager = context.getPackageManager();
	ResolveInfo defaultAppInfo = packageManager.resolveActivity(openIntent, PackageManager.MATCH_DEFAULT_ONLY);
	if (!defaultAppInfo.activityInfo.name.endsWith("ResolverActivity")) {
		return openIntent;
	}

	// 2. Retrieve all apps for our intent. If there are no apps - return usual already created intent.
	List<Intent> targetedOpenIntents = new ArrayList<Intent>();
	List<ResolveInfo> appInfoList = packageManager.queryIntentActivities(openIntent, PackageManager.MATCH_DEFAULT_ONLY);
	if (appInfoList.isEmpty()) {
		return openIntent;
	}

	// 3. Sort in alphabetical order, filter itself and create intent with the rest of the apps.
	Collections.sort(appInfoList, new Comparator<ResolveInfo>() {
		@Override
		public int compare(ResolveInfo first, ResolveInfo second) {
			String firstName = packageManager.getApplicationLabel(first.activityInfo.applicationInfo).toString();
			String secondName = packageManager.getApplicationLabel(second.activityInfo.applicationInfo).toString();
			return firstName.compareToIgnoreCase(secondName);
		}
	});
	for (ResolveInfo appInfo : appInfoList) {
		String packageName = appInfo.activityInfo.packageName;
		if (packageName.equals(context.getPackageName())) {
			continue;
		}

		Intent targetedOpenIntent = new Intent(android.content.Intent.ACTION_VIEW)
				.setDataAndType(Uri.fromFile(file), mimeType)
				.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
				.setPackage(packageName);
		targetedOpenIntents.add(targetedOpenIntent);
	}
	Intent chooserIntent = Intent.createChooser(targetedOpenIntents.remove(targetedOpenIntents.size() - 1), context.getString(R.string.context_menu_open_in))
			.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedOpenIntents.toArray(new Parcelable[] {}));

	return chooserIntent;
}

public static String extensionFromName(String fileName) {
	int dotPosition = fileName.lastIndexOf('.');

	// If extension not present or empty
	if (dotPosition == -1 || dotPosition == fileName.length() - 1) {
		return "";
	} else {
		return fileName.substring(dotPosition + 1).toLowerCase(Locale.getDefault());
	}
}

Solution 5 - Android

I was attempting to do the same thing but with a ShareActionProvider. The method in gumbercules's post didn't work well with the ShareActionProvider so I copied and modified the ShareActionProvider related classes to allow custom filtering of the ShareActionProvider suggestions.

The only change is to the ActivityChooserModel.loadActivitiesIfNeeded() method. In my case, I wanted to filter out the YouTube package.

public static final String YOUTUBE_PACKAGE = "com.google.android.youtube";

private boolean loadActivitiesIfNeeded() {
    if (mReloadActivities && mIntent != null) {
        mReloadActivities = false;
        mActivities.clear();
        List<ResolveInfo> resolveInfos = mContext.getPackageManager()
                .queryIntentActivities(mIntent, 0);
        final int resolveInfoCount = resolveInfos.size();
        for (int i = 0; i < resolveInfoCount; i++) {
            ResolveInfo resolveInfo = resolveInfos.get(i);
            // Filter out the YouTube package from the suggestions list
            if (!resolveInfo.activityInfo.packageName.equals(YOUTUBE_PACKAGE)) {
                mActivities.add(new ActivityResolveInfo(resolveInfo));
            }
        }
        return true;
    }
    return false;
}

Code at https://github.com/danghiskhan/FilteredShareActionProvider

Solution 6 - Android

This solution is based on this post https://rogerkeays.com/how-to-remove-the-facebook-android-sharing-intent

// get available share intents
final String packageToBeFiltered = "com.example.com"
List<Intent> targets = new ArrayList<Intent>();
Intent template = new Intent(Intent.ACTION_SEND);
template.setType("text/plain");
List<ResolveInfo> candidates = this.getPackageManager().queryIntentActivities(template, 0);

// filter package here
for (ResolveInfo candidate : candidates) {
    String packageName = candidate.activityInfo.packageName;
    if (!packageName.equals(packageToBeFiltered)) {
    Intent target = new Intent(android.content.Intent.ACTION_SEND);
    target.setType("text/plain");
    target.putExtra(Intent.EXTRA_TEXT, "Text to share"));
    target.setPackage(packageName);
    targets.add(target);
   }
}
if (!targets.isEmpty()) {
Intent chooser = Intent.createChooser(targets.get(0), "Share dialog title goes here"));
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targets.toArray(new Parcelable[]{}));
startActivity(chooser);
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKonView Question on Stackoverflow
Solution 1 - AndroidgumberculesView Answer on Stackoverflow
Solution 2 - AndroidMichaelView Answer on Stackoverflow
Solution 3 - AndroidpleonasmikView Answer on Stackoverflow
Solution 4 - AndroidoxiedView Answer on Stackoverflow
Solution 5 - Androiddanghis khanView Answer on Stackoverflow
Solution 6 - AndroidSurajView Answer on Stackoverflow