如何过滤 ACTION_SEND 意图的特定应用程序(并为每个应用程序设置不同的文本)
Posted
技术标签:
【中文标题】如何过滤 ACTION_SEND 意图的特定应用程序(并为每个应用程序设置不同的文本)【英文标题】:How to filter specific apps for ACTION_SEND intent (and set a different text for each app) 【发布时间】:2012-04-01 13:57:27 【问题描述】:如何在使用 ACTION_SEND Intent 时过滤掉特定的应用程序? 已通过各种方式提出了这个问题,但我无法根据给出的答案收集解决方案。希望有人可以提供帮助。我想提供在应用程序内共享的能力。在android Dev Alexander Lucas' advice 之后,我更愿意使用意图而不是使用 Facebook/Twitter API。
使用 ACTION_SEND 意图分享很棒,但 问题是 (1) 我不希望那里的每个分享选项都存在,我宁愿将其限制为 FB、Twitter 和电子邮件,以及 (2) 我不想与每个共享应用共享相同的内容。例如,在我的 twitter 分享中,我将包含一些提及和主题标签,将其限制为 140 个字符或更少,而 facebook 分享将包含一个链接和一张特色图片。
是否可以限制 ACTION_SEND(分享)意图的选项?我已经看到了一些关于使用 PackageManager 和 queryIntentActivities 的信息,但无法弄清楚 PackageManager 和 ACTION_SEND 意图之间的联系。
或
如果我可以使用 ACTION_SEND 意图直接转到 facebook 或 twitter 而不是弹出对话框,我的问题也可以解决,而不是过滤共享应用程序。如果是这种情况,那么我可以创建自己的对话框,当他们单击“Facebook”时,创建一个特定于 Facebook 的意图,然后将它们一直发送到 Facebook。推特也一样。
或者这不可能吗? Facebook 和 Twitter API 是唯一的方法吗?
【问题讨论】:
可能重复:[custom-filtering-of-intent-chooser-based-on-installed-android-package-name][1] [1]:***.com/questions/5734678/… 这篇博文似乎是完美的答案:hkdevtips.blogspot.com/2013/02/… 嘿朋友...当我点击我的发送按钮然后打开共享对话框和共享对话框列表是“gmail,email,zapiya,hookup”等。但不显示 facebook,whatsapp,facebook messanger,hike hangouts ...我如何显示它?? 如何在android 6.0 上只有一项意图操作/选项时不显示选择器?该问题在低于 android 6.0 上不显示 【参考方案1】:据我所知,*** 有很多人以各种方式提出这个问题,但还没有人完全回答。
我的规范要求用户能够选择电子邮件、推特、脸书或短信,并为每个人选择自定义文本。我是这样实现的:
public void onShareClick(View v)
Resources resources = getResources();
Intent emailIntent = new Intent();
emailIntent.setAction(Intent.ACTION_SEND);
// Native email client doesn't currently support html, but it doesn't hurt to try in case they fix it
emailIntent.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(resources.getString(R.string.share_email_native)));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, resources.getString(R.string.share_email_subject));
emailIntent.setType("message/rfc822");
PackageManager pm = getPackageManager();
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("text/plain");
Intent openInChooser = Intent.createChooser(emailIntent, resources.getString(R.string.share_chooser_text));
List<ResolveInfo> resInfo = pm.queryIntentActivities(sendIntent, 0);
List<LabeledIntent> intentList = new ArrayList<LabeledIntent>();
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;
if(packageName.contains("android.email"))
emailIntent.setPackage(packageName);
else if(packageName.contains("twitter") || packageName.contains("facebook") || packageName.contains("mms") || packageName.contains("android.gm"))
Intent intent = new Intent();
intent.setComponent(new ComponentName(packageName, ri.activityInfo.name));
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
if(packageName.contains("twitter"))
intent.putExtra(Intent.EXTRA_TEXT, resources.getString(R.string.share_twitter));
else if(packageName.contains("facebook"))
// Warning: Facebook IGNORES our text. They say "These fields are intended for users to express themselves. Pre-filling these fields erodes the authenticity of the user voice."
// One workaround is to use the Facebook SDK to post, but that doesn't allow the user to choose how they want to share. We can also make a custom landing page, and the link
// will show the <meta content ="..."> text from that page with our link in Facebook.
intent.putExtra(Intent.EXTRA_TEXT, resources.getString(R.string.share_facebook));
else if(packageName.contains("mms"))
intent.putExtra(Intent.EXTRA_TEXT, resources.getString(R.string.share_sms));
else if(packageName.contains("android.gm")) // If Gmail shows up twice, try removing this else-if clause and the reference to "android.gm" above
intent.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(resources.getString(R.string.share_email_gmail)));
intent.putExtra(Intent.EXTRA_SUBJECT, resources.getString(R.string.share_email_subject));
intent.setType("message/rfc822");
intentList.add(new LabeledIntent(intent, packageName, ri.loadLabel(pm), ri.icon));
// convert intentList to array
LabeledIntent[] extraIntents = intentList.toArray( new LabeledIntent[ intentList.size() ]);
openInChooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents);
startActivity(openInChooser);
我在不同的地方找到了一些如何做到这一点,但我还没有在其他任何地方看到所有这些。
请注意,此方法还隐藏了我不想要的所有愚蠢选项,例如通过 wifi 和蓝牙共享。
希望这对某人有所帮助。
编辑:
在评论中,我被要求解释这段代码在做什么。基本上,它只为本地电子邮件客户端创建一个ACTION_SEND
意图,然后将其他意图附加到选择器上。使原始意图电子邮件特定于摆脱所有额外的垃圾,如 wifi 和蓝牙,然后我从纯文本类型的通用 ACTION_SEND
中获取我想要的其他意图,并在显示选择器之前将它们添加。
当我获取其他意图时,我为每个意图设置了自定义文本。
Edit2:自从我发布此内容以来已经有一段时间了,事情发生了一些变化。如果您在选项列表中看到 gmail 两次,请尝试按照下面@h_k 的评论中的建议删除“android.gm”的特殊处理。
由于这个答案几乎是我所有的 *** 信誉积分的来源,我必须至少尝试使其保持最新状态。
【讨论】:
我正在使用此代码,但不知何故,evernote 潜入了列表。当我检查包名称时,它是 com.evernote,所以我不确定为什么会这样。 @user2249287 我建议您逐步执行代码,直到您看到消息应用程序被跳过,然后查看包名以确定您需要添加到白名单中的字符串才能显示该应用程序。 @Gilbou 嗨!抱歉 - 我已经很久没有看过这段代码了。 . .我记得, setPackage 命令选择您将附加所有其他内容的单一意图。要包含或排除各种其他意图,我建议单步执行代码并查看包名称。 要仅过滤用户拥有的电子邮件应用程序,您可以使用此问题的第二个答案:***.com/questions/8701634/send-email-intent。它不需要使用其他应用程序(例如本例中的 EverNote)也使用的 message/rfc822 数据类型。 @dacoinminster 您的代码非常棒,让我可以为 Twitter 和 Whatsapp 等应用程序定义不同的文本。要删除重复的 gmail,我只是将“android.gm”排除在外。我仍然在选择器列表中获得 gmail 和内置邮件应用程序,并且主题和文本仍然完好无损。【参考方案2】:如果您想要一个自定义选项,那么您不应该依赖 android 提供的默认对话框来执行此操作。
您需要做的是推出自己的。您将需要查询 PackageManager 哪些包处理您需要的操作,然后根据回复,您应用过滤和自定义文本。
具体看一下PackageManager类的方法queryIntentActivities。您构建将启动默认对话框的意图(ACTION_SEND 意图),将其传递给此方法,您将收到一个对象列表,其中包含有关可以处理该意图的活动的信息。使用它,你可以选择你想要的。
一旦构建了要展示的包列表,您需要构建自己的列表对话框(最好是带有对话框主题的活动),它将显示该列表。
需要注意的一点是,很难让自定义对话框看起来像默认对话框。问题是该对话框中使用的主题是内部主题,您的应用程序无法使用。您可以根据需要尝试使其与原生外观相似,也可以选择完全自定义的外观(许多应用程序都这样做,例如图库应用程序等)
【讨论】:
将此答案标记为正确,因为它最接近地回答了原始问题,即使我最终选择了不同的路线(请参阅我的答案)。谢谢。【参考方案3】:找到了一个适合我的解决方案,看起来 here(请参阅第一个答案的第三条评论)。此代码查找有效的 Twitter 客户端并使用它发布推文。注意:它不会为您提供各种 Twitter 客户端的 Intent 并允许您进行选择。
使用推特分享:
Intent shareIntent = findTwitterClient();
shareIntent.putExtra(Intent.EXTRA_TEXT, "test");
startActivity(Intent.createChooser(shareIntent, "Share"));
调用这个方法:
public Intent findTwitterClient()
final String[] twitterApps =
// package // name - nb installs (thousands)
"com.twitter.android", // official - 10 000
"com.twidroid", // twidroid - 5 000
"com.handmark.tweetcaster", // Tweecaster - 5 000
"com.thedeck.android" ; // TweetDeck - 5 000 ;
Intent tweetIntent = new Intent();
tweetIntent.setType("text/plain");
final PackageManager packageManager = getPackageManager();
List<ResolveInfo> list = packageManager.queryIntentActivities(
tweetIntent, PackageManager.MATCH_DEFAULT_ONLY);
for (int i = 0; i < twitterApps.length; i++)
for (ResolveInfo resolveInfo : list)
String p = resolveInfo.activityInfo.packageName;
if (p != null && p.startsWith(twitterApps[i]))
tweetIntent.setPackage(p);
return tweetIntent;
return null;
Facebook 将类似使用“com.facebook.katana”,但您仍然无法设置消息文本(2011 年 7 月已弃用)。
代码来源:Intent to open twitter client on Android
【讨论】:
我不喜欢这个答案,因为它依赖于知道所有 twitter 应用程序的包名称。另一种方式见***.com/questions/6827407/… 我同意你的观点,尽管你链接到的答案有类似的问题。我从不喜欢依赖字符串比较,尤其是当我无法控制或保证字符串不会改变时。【参考方案4】:试试这个,只分享三个应用——Facebook、Twitter、KakaoStory。
public void onShareClick(View v)
List<Intent> targetShareIntents=new ArrayList<Intent>();
Intent shareIntent=new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
List<ResolveInfo> resInfos=getPackageManager().queryIntentActivities(shareIntent, 0);
if(!resInfos.isEmpty())
System.out.println("Have package");
for(ResolveInfo resInfo : resInfos)
String packageName=resInfo.activityInfo.packageName;
Log.i("Package Name", packageName);
if(packageName.contains("com.twitter.android") || packageName.contains("com.facebook.katana") || packageName.contains("com.kakao.story"))
Intent intent=new Intent();
intent.setComponent(new ComponentName(packageName, resInfo.activityInfo.name));
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "Text");
intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
intent.setPackage(packageName);
targetShareIntents.add(intent);
if(!targetShareIntents.isEmpty())
System.out.println("Have Intent");
Intent chooserIntent=Intent.createChooser(targetShareIntents.remove(0), "Choose app to share");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetShareIntents.toArray(new Parcelable[]));
startActivity(chooserIntent);
else
System.out.println("Do not Have Intent");
showDialaog(this);
【讨论】:
如果您尝试与特定应用共享,此代码可以完美运行【参考方案5】:感谢@dacoinminster。我对他的回答做了一些修改,包括流行应用的包名和这些应用的排序。
List<Intent> targetShareIntents = new ArrayList<Intent>();
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
PackageManager pm = getActivity().getPackageManager();
List<ResolveInfo> resInfos = pm.queryIntentActivities(shareIntent, 0);
if (!resInfos.isEmpty())
System.out.println("Have package");
for (ResolveInfo resInfo : resInfos)
String packageName = resInfo.activityInfo.packageName;
Log.i("Package Name", packageName);
if (packageName.contains("com.twitter.android") || packageName.contains("com.facebook.katana")
|| packageName.contains("com.whatsapp") || packageName.contains("com.google.android.apps.plus")
|| packageName.contains("com.google.android.talk") || packageName.contains("com.slack")
|| packageName.contains("com.google.android.gm") || packageName.contains("com.facebook.orca")
|| packageName.contains("com.yahoo.mobile") || packageName.contains("com.skype.raider")
|| packageName.contains("com.android.mms")|| packageName.contains("com.linkedin.android")
|| packageName.contains("com.google.android.apps.messaging"))
Intent intent = new Intent();
intent.setComponent(new ComponentName(packageName, resInfo.activityInfo.name));
intent.putExtra("AppName", resInfo.loadLabel(pm).toString());
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "https://website.com/");
intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_text));
intent.setPackage(packageName);
targetShareIntents.add(intent);
if (!targetShareIntents.isEmpty())
Collections.sort(targetShareIntents, new Comparator<Intent>()
@Override
public int compare(Intent o1, Intent o2)
return o1.getStringExtra("AppName").compareTo(o2.getStringExtra("AppName"));
);
Intent chooserIntent = Intent.createChooser(targetShareIntents.remove(0), "Select app to share");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetShareIntents.toArray(new Parcelable[]));
startActivity(chooserIntent);
else
Toast.makeText(getActivity(), "No app to share.", Toast.LENGTH_LONG).show();
【讨论】:
谢谢@Oguz,这对我有用,第一个答案对我不起作用【参考方案6】:你可以试试下面的代码,效果很好。
我们在这里分享给一些特定的应用程序,即 Facebook、Messenger、Twitter、Google Plus 和 Gmail。
public void shareIntentSpecificApps()
List<Intent> intentShareList = new ArrayList<Intent>();
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
List<ResolveInfo> resolveInfoList = getPackageManager().queryIntentActivities(shareIntent, 0);
for (ResolveInfo resInfo : resolveInfoList)
String packageName = resInfo.activityInfo.packageName;
String name = resInfo.activityInfo.name;
Log.d(TAG, "Package Name : " + packageName);
Log.d(TAG, "Name : " + name);
if (packageName.contains("com.facebook") ||
packageName.contains("com.twitter.android") ||
packageName.contains("com.google.android.apps.plus") ||
packageName.contains("com.google.android.gm"))
if (name.contains("com.twitter.android.DMActivity"))
continue;
Intent intent = new Intent();
intent.setComponent(new ComponentName(packageName, name));
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_SUBJECT, "Your Subject");
intent.putExtra(Intent.EXTRA_TEXT, "Your Content");
intentShareList.add(intent);
if (intentShareList.isEmpty())
Toast.makeText(MainActivity.this, "No apps to share !", Toast.LENGTH_SHORT).show();
else
Intent chooserIntent = Intent.createChooser(intentShareList.remove(0), "Share via");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentShareList.toArray(new Parcelable[]));
startActivity(chooserIntent);
【讨论】:
"if(name.contains("com.twitter.android.DMActivity")) continue ; 的原因是什么?【参考方案7】:此解决方案在类似于选择器的 ListView 对话框中显示应用程序列表:
由您决定:
-
获取相关应用包列表
给定包名,调用相关意图
适配器类:
import java.util.List;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
public class ChooserArrayAdapter extends ArrayAdapter<String>
PackageManager mPm;
int mTextViewResourceId;
List<String> mPackages;
public ChooserArrayAdapter(Context context, int resource, int textViewResourceId, List<String> packages)
super(context, resource, textViewResourceId, packages);
mPm = context.getPackageManager();
mTextViewResourceId = textViewResourceId;
mPackages = packages;
@Override
public View getView(int position, View convertView, ViewGroup parent)
String pkg = mPackages.get(position);
View view = super.getView(position, convertView, parent);
try
ApplicationInfo ai = mPm.getApplicationInfo(pkg, 0);
CharSequence appName = mPm.getApplicationLabel(ai);
Drawable appIcon = mPm.getApplicationIcon(pkg);
TextView textView = (TextView) view.findViewById(mTextViewResourceId);
textView.setText(appName);
textView.setCompoundDrawablesWithIntrinsicBounds(appIcon, null, null, null);
textView.setCompoundDrawablePadding((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12, getContext().getResources().getDisplayMetrics()));
catch (NameNotFoundException e)
e.printStackTrace();
return view;
及其用法:
void doXxxButton()
final List<String> packages = ...;
if (packages.size() > 1)
ArrayAdapter<String> adapter = new ChooserArrayAdapter(MyActivity.this, android.R.layout.select_dialog_item, android.R.id.text1, packages);
new AlertDialog.Builder(MyActivity.this)
.setTitle(R.string.app_list_title)
.setAdapter(adapter, new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog, int item )
invokeApplication(packages.get(item));
)
.show();
else if (packages.size() == 1)
invokeApplication(packages.get(0));
void invokeApplication(String packageName)
// given a package name, create an intent and fill it with data
...
startActivityForResult(intent, rq);
【讨论】:
【参考方案8】:最简洁的方法是复制以下类:ShareActionProvider、ActivityChooserView、ActivityChooserModel。在 ActivityChooserModel 中添加过滤意图的功能,并在 ShareActionProvider 中添加适当的支持方法。我创建了必要的类,您可以将它们复制到您的项目中 (https://gist.github.com/saulpower/10557956)。这不仅增加了过滤您想要共享的应用程序的功能(如果您知道包名称),而且还可以关闭历史记录。
private final String[] INTENT_FILTER = new String[]
"com.twitter.android",
"com.facebook.katana"
;
@Override
public boolean onCreateOptionsMenu(Menu menu)
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.journal_entry_menu, menu);
// Set up ShareActionProvider's default share intent
MenuItem shareItem = menu.findItem(R.id.action_share);
if (shareItem instanceof SupportMenuItem)
mShareActionProvider = new ShareActionProvider(this);
mShareActionProvider.setShareIntent(ShareUtils.share(mJournalEntry));
mShareActionProvider.setIntentFilter(Arrays.asList(INTENT_FILTER));
mShareActionProvider.setShowHistory(false);
((SupportMenuItem) shareItem).setSupportActionProvider(mShareActionProvider);
return super.onCreateOptionsMenu(menu);
【讨论】:
如何添加 google+ 和其他包含剩余应用程序的选项【参考方案9】:我改进了@dacoinminster 的答案,这是一个分享您的应用的示例的结果:
// Intents with SEND action
PackageManager packageManager = context.getPackageManager();
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("text/plain");
List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(sendIntent, 0);
List<LabeledIntent> intentList = new ArrayList<LabeledIntent>();
Resources resources = context.getResources();
for (int j = 0; j < resolveInfoList.size(); j++)
ResolveInfo resolveInfo = resolveInfoList.get(j);
String packageName = resolveInfo.activityInfo.packageName;
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setComponent(new ComponentName(packageName,
resolveInfo.activityInfo.name));
intent.setType("text/plain");
if (packageName.contains("twitter"))
intent.putExtra(Intent.EXTRA_TEXT, resources.getString(R.string.twitter) + "https://play.google.com/store/apps/details?id=" + context.getPackageName());
else
// skip android mail and gmail to avoid adding to the list twice
if (packageName.contains("android.email") || packageName.contains("android.gm"))
continue;
intent.putExtra(Intent.EXTRA_TEXT, resources.getString(R.string.largeTextForFacebookWhatsapp) + "https://play.google.com/store/apps/details?id=" + context.getPackageName());
intentList.add(new LabeledIntent(intent, packageName, resolveInfo.loadLabel(packageManager), resolveInfo.icon));
Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:"));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, resources.getString(R.string.subjectForMailApps));
emailIntent.putExtra(Intent.EXTRA_TEXT, resources.getString(R.string.largeTextForMailApps) + "https://play.google.com/store/apps/details?id=" + context.getPackageName());
context.startActivity(Intent.createChooser(emailIntent, resources.getString(R.string.compartirEn)).putExtra(Intent.EXTRA_INITIAL_INTENTS, intentList.toArray(new LabeledIntent[intentList.size()])));
【讨论】:
【参考方案10】:我有同样的问题,这个公认的解决方案没有帮助我,如果有人有同样的问题,你可以使用mycode sn-p:
// example of filtering and sharing multiple images with texts
// remove facebook from sharing intents
private void shareFilter()
String share = getShareTexts();
ArrayList<Uri> uris = getImageUris();
List<Intent> targets = new ArrayList<>();
Intent template = new Intent(Intent.ACTION_SEND_MULTIPLE);
template.setType("image/*");
List<ResolveInfo> candidates = getActivity().getPackageManager().
queryIntentActivities(template, 0);
// remove facebook which has a broken share intent
for (ResolveInfo candidate : candidates)
String packageName = candidate.activityInfo.packageName;
if (!packageName.equals("com.facebook.katana"))
Intent target = new Intent(Intent.ACTION_SEND_MULTIPLE);
target.setType("image/*");
target.putParcelableArrayListExtra(Intent.EXTRA_STREAM,uris);
target.putExtra(Intent.EXTRA_TEXT, share);
target.setPackage(packageName);
targets.add(target);
Intent chooser = Intent.createChooser(targets.remove(0), "Share Via");
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targets.toArray(new Parcelable[targets.size()]));
startActivity(chooser);
【讨论】:
【参考方案11】:Intent emailIntent = new Intent(Intent.ACTION_SENDTO,
Uri.fromParts("mailto", "android@gmail.com", null));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, text);
startActivity(Intent.createChooser(emailIntent, "Send email..."));
【讨论】:
【参考方案12】:如此简单明了。感谢开源开发者cketti 分享此解决方案:
String mailto = "mailto:bob@example.org" +
"?cc=" + "alice@example.com" +
"&subject=" + Uri.encode(subject) +
"&body=" + Uri.encode(bodyText);
Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
emailIntent.setData(Uri.parse(mailto));
try
startActivity(emailIntent);
catch (ActivityNotFoundException e)
//TODO: Handle case where no email app is available
this 是指向他/她的要点的链接。
【讨论】:
以上是关于如何过滤 ACTION_SEND 意图的特定应用程序(并为每个应用程序设置不同的文本)的主要内容,如果未能解决你的问题,请参考以下文章
将仅在特定应用程序中可见的操作添加到 ACTION_SEND?