Как фильтровать определенные приложения для намерения ACTION_SEND (и устанавливать другой текст для каждого приложения)

Как вы можете отфильтровывать конкретные приложения при использовании намерений ACTION_SEND? Этот вопрос задавался по-разному, но я не смог собрать решение, основанное на ответах. Надеюсь, кто-то может помочь. Я хотел бы предоставить возможность совместного использования в приложении. Следуя совету Android Dev Alexander Lucas , я бы предпочел сделать это с использованием намерений и не использовать API Facebook / Twitter.

Совместное использование с использованием намерения ACTION_SEND

Совместное использование с использованием намерения ACTION_SEND отличное, но проблема в том, что (1) я не хочу, чтобы каждый параметр обмена был там, я бы предпочел ограничивать его FB, Twitter и электронной почтой, и (2) я не хочу делиться То же самое для каждого приложения для совместного использования . Например, в моем блоге twitter я собираюсь включить некоторые упоминания, а хэштеги ограничили это до 140 символов или меньше, в то время как доля facebook будет включать ссылку и изображение функции.

Можно ли ограничить варианты намерения ACTION_SEND (share)? Я видел что-то об использовании PackageManager и queryIntentActivities, но не смог выяснить связь между PackageManager и намерением ACTION_SEND.

ИЛИ

Вместо того, чтобы фильтровать приложения для обмена, моя проблема также может быть решена, если я могу использовать намерение ACTION_SEND перейти непосредственно к facebook или twitter, а не появляться в диалоговом окне. Если бы это было так, я мог бы создать свой собственный диалог, и когда они нажмут «Facebook», создайте намерение Facebook и просто отправят их полностью в Facebook. То же самое с Twitter.

ИЛИ это невозможно? Являются ли API Facebook и Twitter единственным способом?

Solutions Collecting From Web of "Как фильтровать определенные приложения для намерения ACTION_SEND (и устанавливать другой текст для каждого приложения)"

Насколько мне известно, у StackOverflow есть много людей, которые задают этот вопрос разными способами, но никто еще не ответил на него полностью.

Моя спецификация призвала пользователя выбирать электронную почту, твиттер, facebook или SMS, с пользовательским текстом для каждого из них. Вот как я это сделал:

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 ниже.

Поскольку этот ответ является источником почти всех моих точек репутации stackoverflow, я должен хотя бы попытаться сохранить его в актуальном состоянии 🙂

Если вам нужен индивидуальный вариант, вы не должны полагаться на диалоговое окно по умолчанию, предоставленное андроидом для этого действия.

Вместо этого вам нужно сделать свой собственный. Вам нужно будет запросить PackageManager, в котором пакеты обрабатывают требуемое действие, а затем на основе ответа вы применяете фильтрацию и настраиваемый текст.

В частности, посмотрите на метод queryIntentActivities класса PackageManager . Вы создаете намерение, которое запустило диалог по умолчанию (намерение ACTION_SEND), передать это этому методу, и вы получите список объектов, содержащих информацию о действиях, которые могут обрабатывать это намерение. Используя это, вы можете выбрать те, которые хотите.

После того, как вы создадите список пакетов, которые вы хотите представить, вам нужно создать собственный диалог со списком (предпочтительно, с темой диалога), который отобразит этот список.

Следует отметить, однако, что очень сложно сделать так, чтобы пользовательский диалог выглядел как по умолчанию. Проблема в том, что тема, используемая в этом диалоговом окне, является внутренней темой и не может использоваться вашим приложением. Вы можете либо попытаться сделать его похожим на родной, либо вы хотите, чтобы он был полностью обычным (многие приложения работают так, как в приложении галереи)

Нашел решение, которое работает для меня, смотрящего здесь (см. Третий комментарий к первому ответу). Этот код ищет действительный клиент Twitter и использует его для публикации твита. Примечание. Это не дает вам намерения с различными клиентами 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 г.).
Источник кода: http://regis.decamps.info/blog/2011/06/intent-to-open-twitter-client-on-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); } } } 

Это решение отображает список приложений в диалоговом окне ListView, который напоминает выбор:

Скриншот

Это зависит от вас:

  1. Получить список соответствующих пакетов приложений
  2. С учетом имени пакета, ссылайтесь на соответствующее намерение

Класс адаптера:

 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); } 

Вы можете попробовать код ниже, он отлично работает.

Здесь мы делимся некоторыми конкретными приложениями, такими как 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); } } 

Самый чистый способ – скопировать следующие классы: 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); } 

Я улучшил ответ @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()]))); 
 Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", "android@bankbazzar.com", null)); emailIntent.putExtra(Intent.EXTRA_SUBJECT, text); startActivity(Intent.createChooser(emailIntent, "Send email...")); 

У меня была такая же проблема, и это принятое решение мне не помогло, если у кого-то есть такая же проблема, вы можете использовать мой фрагмент кода:

 // 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); } 

Благодаря @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(); } }