Reputation: 27441
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.
Upvotes: 61
Views: 55274
Reputation: 331
For Kotlin users this code worked for me if you are targeting API 24 you should use Intent.EXTRA_EXCLUDE_COMPONENTS
instead of Intent.EXTRA_INITIAL_INTENTS
which is ignored by default
@RequiresApi(Build.VERSION_CODES.N)
private fun goToEmailFilteredApps(email: String) {
val intent = Intent(Intent.ACTION_SEND)
intent.setTypeAndNormalize("message/rfc822")
intent.putExtra(Intent.EXTRA_EMAIL, arrayOf(email))
val chooser = Intent.createChooser(intent, "Send email")
val emailAppsPackageNames = arrayListOf(
"com.google.android.gm", // Gmail
"com.microsoft.office.outlook", // Outlook
"com.yahoo.mobile.client.android.mail", // Yahoo Mail
"pl.onet.poczta", // Onet Mail
"pl.interia.poczta_next", // Interia Mail
"pl.wp.pocztao2", // o2 Mail
"pl.wp.wppoczta", // WP Mail
)
val targets = ArrayList<ComponentName>()
try {
val queries = requireActivity().packageManager.queryIntentActivities(intent, 0)
for (candidate in queries) {
val packageName = candidate.activityInfo.packageName
if (!emailAppsPackageNames.contains(packageName.lowercase())) {
targets.add(ComponentName(packageName, candidate.activityInfo.name))
}
}
chooser.putExtra(Intent.EXTRA_EXCLUDE_COMPONENTS, targets.toTypedArray())
startActivity(chooser)
} catch (_: ActivityNotFoundException) {
}
}
Upvotes: 1
Reputation: 1129
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.
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 : Android share intent Twitter: How to share only by Tweet or Direct Message?
Upvotes: 112
Reputation: 54705
The only one additional parameter for the chooser is Intent.EXTRA_INITIAL_INTENTS
. Its description is:
A
Parcelable[]
of Intent orLabeledIntent
objects as set withputExtra(String, Parcelable[])
of additional activities to place a the front of the list of choices, when shown to the user with aACTION_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.
Upvotes: 26
Reputation: 669
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);
}
Upvotes: 1
Reputation: 176
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
Upvotes: 1
Reputation: 789
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.
Upvotes: 7
Reputation: 1908
My implementation of custom open chooser.
Features:
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());
}
}
Upvotes: 6