Как отфильтровать определенные приложения для намерения ACTION_SEND (и задать отдельный текст для каждого приложения)
Как можно отфильтровать определенные приложения при использовании намерения ACTION_SEND? Этот вопрос задавался по-разному, но я не смог найти решение на основе полученных ответов. Надеюсь, кто-то может помочь. Я хотел бы предоставить возможность поделиться в приложении. Следуя совету Android Dev Александра Лукаса, я бы предпочел сделать это с помощью намерений, а не с помощью API Facebook/Twitter.
Совместное использование с помощью намерения ACTION_SEND - это замечательно, но проблема в том, что (1) я не хочу, чтобы там были доступны все параметры общего доступа , я бы предпочел ограничить его FB, Twitter и электронной почтой, и (2) я не хочу делиться то же самое для каждого приложения обмена. Например, в своем твиттере я собираюсь включить некоторые упоминания, и хештеги ограничивают его до 140 символов или менее, в то время как на фейсбуке будет ссылка и изображение функции.
Можно ли ограничить параметры для намерения ACTION_SEND (поделиться)? Я видел кое-что об использовании PackageManager и queryIntentActivities, но не смог выяснить связь между PackageManager и намерением ACTION_SEND.
ИЛИ ЖЕ
Вместо того, чтобы фильтровать приложения для обмена, моя проблема также может быть решена, если бы я мог использовать намерение ACTION_SEND, чтобы перейти непосредственно в Facebook или Twitter, а не всплывать в диалоговом окне. Если бы это было так, то я мог бы создать свой собственный диалог, и когда они нажимали "Facebook", создавали специфические для Facebook намерения и просто отправляли их полностью в Facebook. То же самое с Twitter.
ИЛИ это не возможно? Являются ли API Facebook и Twitter единственным способом?
11 ответов
Насколько мне известно, в Stackru множество людей задают этот вопрос по-разному, но никто еще не ответил на него полностью.
Моя спецификация требовала, чтобы пользователь мог выбирать электронную почту, твиттер, фейсбук или смс с индивидуальным текстом для каждого. Вот как я это сделал:
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);
}
Я нашел кое-что о том, как это сделать, в разных местах, но я не видел всего этого нигде в другом месте.
Обратите внимание, что этот метод также скрывает все глупые варианты, которые я не хочу, например, обмен через Wi-Fi и Bluetooth.
Надеюсь, это кому-нибудь поможет.
Изменить: В комментарии меня попросили объяснить, что делает этот код. По сути, это создает ACTION_SEND
намерение ТОЛЬКО для родного почтового клиента, затем добавление других намерений на выбор. Создание оригинального намерения, специфичного для электронной почты, избавляет от всего лишнего мусора, такого как Wi-Fi и Bluetooth, затем я беру другие намерения, которые я хочу, от общего ACTION_SEND
наберите обычный текст и прикрепите их перед тем, как показывать селектор.
Когда я беру дополнительные намерения, я устанавливаю собственный текст для каждого.
Edit2: Прошло некоторое время с тех пор, как я опубликовал это, и все немного изменилось. Если вы видите gmail дважды в списке параметров, попробуйте удалить специальную обработку для "android.gm", как это предлагается в комментарии @h_k ниже.
Так как этот один ответ является источником почти всех моих очков репутации stackru, я должен, по крайней мере, попытаться поддерживать его в актуальном состоянии.
Если вы хотите настраиваемую опцию, вам не следует полагаться на диалоговое окно по умолчанию, предоставляемое android для этого действия.
Вместо этого вам нужно развернуть свое собственное. Вам нужно будет запросить PackageManager, в каких пакетах обрабатывается требуемое действие, а затем на основе ответа вы примените фильтрацию и настроенный текст.
В частности, взгляните на метод queryIntentActivities класса PackageManager. Вы строите намерение, которое запускает диалоговое окно по умолчанию (намерение ACTION_SEND), передаете его этому методу, и вы получите список объектов, которые содержат информацию о действиях, которые могут обработать это намерение. Используя это, вы можете выбрать те, которые вы хотите.
Как только вы создадите свой список пакетов, которые вы хотите представить, вам нужно создать свой собственный диалог со списком (желательно с темой диалога), который будет отображать этот список.
Однако следует отметить, что сделать этот пользовательский диалог очень похожим на диалог по умолчанию. Проблема в том, что тема, используемая в этом диалоговом окне, является внутренней темой и не может использоваться вашим приложением. Вы можете либо сделать его максимально похожим на нативный, либо перейти на совершенно другой внешний вид (многие приложения делают это, например, приложение галереи и т. Д.)
Нашел решение, которое работает для меня, посмотрев здесь (см. Третий комментарий к первому ответу). Этот код ищет действующий клиент Twitter и использует его для публикации твита. Примечание. Это не дает вам намерения работать с различными клиентами Twitter и позволяет вам выбирать.
Поделиться с помощью твиттера:
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 г.).
Источник кода: Намерение открыть клиент Twitter на Android
Попробуйте этот, чтобы поделиться только тремя приложениями -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);
}
}
}
Спасибо @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();
}
}
Вы можете попробовать код ниже, он работает отлично.
Здесь мы делимся с некоторыми конкретными приложениями, такими как 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);
}
}
Это решение показывает список приложений в диалоге 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);
}
Самый простой способ - скопировать следующие классы: 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);
}
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..."));
Я улучшил ответ @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()])));
У меня была та же проблема, и это принятое решение не помогло мне, если у кого-то есть такая же проблема, вы можете использовать мой фрагмент кода:
// 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);
}
Так просто и лаконично. Спасибо разработчику с открытым исходным кодом 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
}
И это ссылка на его суть.