可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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.
回答2:
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 : Android share intent Twitter: How to share only by Tweet or Direct Message?
回答3:
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.
回答4:
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());
}
}
回答5:
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
回答6:
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);
}