web-dev-qa-db-fra.com

Comment filtrer des applications spécifiques pour l'intention ACTION_SEND (et définir un texte différent pour chaque application)

Comment pouvez-vous filtrer des applications spécifiques lorsque vous utilisez l'intention ACTION_SEND? Cette question a été posée de différentes manières, mais je n'ai pas pu rassembler solution basée sur les réponses données. J'espère que quelqu'un pourra vous aider. Je souhaite fournir la possibilité de partager au sein d'une application. Suivant conseil d'Android Dev Alexander Lucas , je préférerais le faire avec des intentions et non avec les API Facebook/Twitter.

Sharing using ACTION_SEND intent

Partager l’intention d’ACTION_SEND est une bonne chose, mais le problème est que (1) je ne veux pas que toutes les options de partage soient là, je préfère les limiter à FB, Twitter et Email, et ( 2) Je ne veux pas partager la même chose avec chaque application de partage . Par exemple, dans mon partage Twitter, je vais inclure des mentions et des hashtags limités à 140 caractères ou moins, tandis que le partage sur Facebook comprendra un lien et une image de fonctionnalité.

Est-il possible de limiter les options pour l'intention d'ACTION_SEND (partage)? J'ai déjà vu quelque chose à propos de l'utilisation de PackageManager et de queryIntentActivities, mais je n'ai pas pu comprendre la connexion entre PackageManager et l'intention ACTION_SEND.

OR

Plutôt que de filtrer les applications de partage, mon problème pourrait également être résolu si je pouvais utiliser l'intention d'ACTION_SEND pour accéder directement à Facebook ou à Twitter plutôt que d'afficher la boîte de dialogue. Si tel était le cas, je pourrais créer mon propre dialogue et quand ils cliquent sur "Facebook" créer une intention spécifique à Facebook et les envoyer tout le chemin à Facebook. Même avec Twitter.

OU n'est-ce pas possible? Les API Facebook et Twitter sont-elles le seul moyen?

172
Kyle Clegg

À ma connaissance, StackOverflow a beaucoup de gens qui posent cette question de différentes manières, mais personne n'y a encore répondu complètement.

Ma spécification a appelé l'utilisateur à choisir le courrier électronique, Twitter, Facebook ou SMS, avec un texte personnalisé pour chacun. Voici comment j'ai accompli cela:

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

J'ai trouvé comment faire cela à différents endroits, mais je ne l'ai pas encore vu ailleurs.

Notez que cette méthode masque également toutes les options idiotes que je ne souhaite pas, telles que le partage via wifi et Bluetooth.

J'espère que ça aide quelqu'un.

Edit: Dans un commentaire, on m'a demandé d'expliquer ce que faisait ce code. Fondamentalement, il crée UNIQUEMENT une intention ACTION_SEND pour le client de messagerie natif, puis ajoute d’autres intentions au sélecteur. Rendre l'intention originale spécifique au courrier électronique supprime toutes les informations inutiles comme le wifi et le bluetooth, puis j'attrape les autres intentions que je veux d'un générique ACTION_SEND de type texte brut et les cloue dessus avant de montrer le sélecteur.

Lorsque je saisis les intentions supplémentaires, je définis un texte personnalisé pour chacune d’elles.

Edit2: Cela fait longtemps que je n'ai pas posté ça, et les choses ont un peu changé. Si vous voyez gmail deux fois dans la liste des options, essayez de supprimer le traitement spécial pour "Android.gm", comme suggéré dans un commentaire de @h_k ci-dessous.

Comme cette réponse est la source de presque tous mes points de réputation stackoverflow, je dois au moins essayer de la maintenir à jour.

304
dacoinminster

Si vous souhaitez une option personnalisée, ne vous fiez pas à la boîte de dialogue par défaut fournie par Android pour cette action.

Ce que vous devez faire à la place, c'est déployer le vôtre. Vous devrez interroger PackageManager sur les packages qui gèrent l'action requise, puis, en fonction de la réponse, appliquez un filtrage et du texte personnalisé.

En particulier, jetez un oeil à la méthode queryIntentActivities de la classe PackageManager . Vous construisez l'intention qui lancerait la boîte de dialogue par défaut (l'intention ACTION_SEND), transmettez-le à cette méthode et vous recevrez une liste d'objets contenant des informations sur les activités pouvant gérer cette intention. En utilisant cela, vous pouvez choisir ceux que vous voulez.

Une fois que vous avez construit votre liste de paquets que vous souhaitez présenter, vous devez créer votre propre dialogue de liste (de préférence une activité avec le thème de dialogue) qui affichera cette liste.

Une chose à noter cependant, c'est qu'il est très difficile de faire en sorte que la boîte de dialogue personnalisée ressemble à celle par défaut. Le problème est que le thème utilisé dans cette boîte de dialogue est un thème interne et ne peut pas être utilisé par votre application. Vous pouvez essayer de le rendre aussi semblable que vous le souhaitez au modèle natif ou opter pour un look totalement personnalisé (de nombreuses applications le font comme l'application de la galerie, etc.).

25
Savvas Dalkitsis

Essayez celui-ci pour ne partager que trois applications: Facebook, Twitter et 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
user3098538

J'ai trouvé une solution qui me convient ici (voir le troisième commentaire de la première réponse). Ce code recherche un client Twitter valide et l'utilise pour publier le Tweet. Note: Cela ne vous donne pas une intention avec les différents clients Twitter et vous permet de choisir.

Partager en utilisant Twitter:

Intent shareIntent = findTwitterClient(); 
shareIntent.putExtra(Intent.EXTRA_TEXT, "test");
startActivity(Intent.createChooser(shareIntent, "Share"));

Appelant cette méthode:

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 sera similaire en utilisant " com.facebook.katana ", bien que vous ne puissiez toujours pas définir le texte du message (obsolète en juillet 2011).

Code source: Intention d'ouvrir le client Twitter sur Android

22
Kyle Clegg

Cette solution affiche une liste d'applications dans une boîte de dialogue ListView qui ressemble au sélecteur:

screenshot

C'est à vous de:

  1. obtenir la liste des packages d'applications pertinents
  2. étant donné un nom de paquet, invoquer l'intention pertinente

La classe d'adaptateur:

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

}

et son utilisation:

    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

Vous pouvez essayer le code ci-dessous, cela fonctionne parfaitement.

Nous partageons ici certaines applications spécifiques, telles que Facebook, Messenger, Twitter, Google Plus et 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
Dang Nguyen

Merci à @dacoinminster. J'apporte quelques modifications à sa réponse, notamment les noms de package des applications populaires et le tri de ces applications.

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();
    }
}
7
Oguz Ozcan

La méthode la plus propre consiste à copier les classes suivantes: ShareActionProvider, ActivityChooserView, ActivityChooserModel. Ajoutez la possibilité de filtrer les intentions dans ActivityChooserModel et les méthodes de support appropriées dans ShareActionProvider. J'ai créé les classes nécessaires, vous pouvez les copier dans votre projet ( https://Gist.github.com/saulpower/10557956 ). Cela ajoute non seulement la possibilité de filtrer les applications avec lesquelles vous souhaitez partager (si vous connaissez le nom du package), mais également de désactiver l'historique.

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);
}
4
saulpower
Intent emailIntent = new Intent(Intent.ACTION_SENDTO, 
    Uri.fromParts("mailto", "[email protected]", null));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, text);
startActivity(Intent.createChooser(emailIntent, "Send email..."));
3
murugan

J'ai amélioré la réponse @dacoinminster et voici le résultat avec un exemple pour partager votre application:

// 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()])));
3
user2528167

J'ai eu le même problème et cette solution acceptée ne m'a pas aidé, si quelqu'un a le même problème, vous pouvez utiliser mon = extrait de code:

// 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
jemo mgebrishvili