Как отфильтровать определенные приложения для намерения ACTION_SEND (и задать отдельный текст для каждого приложения) - PullRequest
171 голосов
/ 16 марта 2012

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

Sharing using ACTION_SEND intent

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

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

OR

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

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

Ответы [ 11 ]

301 голосов
/ 06 августа 2013

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

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

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, я должен, по крайней мере, постараться поддерживать его в актуальном состоянии.

25 голосов
/ 18 марта 2012

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

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

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

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

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

22 голосов
/ 13 марта 2014

Попробуйте это приложение, чтобы поделиться только тремя приложениями - 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);
        }
    }
}
22 голосов
/ 06 апреля 2012

Нашел решение, которое работает для меня, посмотрев здесь (см. Третий комментарий к первому ответу). Этот код ищет действующий клиент 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

8 голосов
/ 20 июля 2016

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

Здесь мы публикуем некоторые конкретные приложения, такие как 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);
        }
    }
8 голосов
/ 24 сентября 2013

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

screenshot

Вам решать:

  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);
    }
6 голосов
/ 26 января 2017

Благодаря @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();
    }
}
4 голосов
/ 13 апреля 2014

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

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

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

}
3 голосов
/ 02 января 2015
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..."));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...