Filtrado personalizado del selector de intenciones basado en el nombre del paquete de Android instalado

Me gustaría aprovechar el selector de intenciones incorporado para mostrar una lista filtrada personalizada de aplicaciones para que el usuario pueda seleccionarlas e iniciarlas.

Sé cómo obtener una lista de paquetes instalados:

final Intent myIntent = new Intent(android.content.Intent.ACTION_MAIN); List resInfoList = getPackageManager().queryIntentActivities(myIntent, 0); 

En este punto, quiero filtrar la lista en función de una cadena específica (o variación de cadenas) contenida dentro del nombre del paquete, que también puedo descifrar cómo hacerlo.

Pero aquí es donde me quedo atascado. Hasta donde yo sé, Intent.createChooser() toma solo un único objective como parámetro. Esperaba que hubiera una sobrecarga que tomara una lista de intenciones basada en nombres de paquetes y clases o algo así. Pero no veo nada como eso. ¿Extrañé eso en alguna parte?

Entonces, la pregunta es, ¿es posible hacer esto con un selector incorporado, o tengo que construir el mío con AlertDialog Builder? Espero evitar lo posterior.

Gracias por adelantado.

El único parámetro adicional para el selector es Intent.EXTRA_INITIAL_INTENTS . Su descripción es:

A Parcelable [] de Intent o LabeledIntent establece objetos con putExtra (String, Parcelable []) de actividades adicionales para colocarlos al principio de la lista de opciones, cuando se muestran al usuario con ACTION_CHOOSER.

No he encontrado ninguna forma en las fonts de Android para excluir otras actividades de la lista, por lo que parece que no hay forma de hacer lo que desea hacer con el selector.

EDITAR : Eso es realmente fácil de descubrir. Solo revisa el código fuente de ChooserActivity y ResolverActivity . Estas clases son bastante pequeñas.

Aquí hay una solución que armé. Lo uso para tener diferentes datos de intención para cada selección en el selector, pero también puede eliminar fácilmente un bash de la lista. Espero que esto ayude:

 List targetedShareIntents = new ArrayList(); Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND); shareIntent.setType("text/plain"); List resInfo = getPackageManager().queryIntentActivities(shareIntent, 0); if (!resInfo.isEmpty()) { for (ResolveInfo resolveInfo : resInfo) { String packageName = resolveInfo.activityInfo.packageName; Intent targetedShareIntent = new Intent(android.content.Intent.ACTION_SEND); targetedShareIntent.setType("text/plain"); targetedShareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "subject to be shared"); if (TextUtils.equals(packageName, "com.facebook.katana")) { targetedShareIntent.putExtra(android.content.Intent.EXTRA_TEXT, "http://link-to-be-shared.com"); } else { targetedShareIntent.putExtra(android.content.Intent.EXTRA_TEXT, "text message to shared"); } targetedShareIntent.setPackage(packageName); targetedShareIntents.add(targetedShareIntent); } Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(0), "Select app to share"); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[targetedShareIntents.size()])); startActivity(chooserIntent); } 

EDITAR

Mientras uso este método, obtengo nombres de algunas aplicaciones como sistema Android. Si alguien obtiene este error, por favor agregue las siguientes líneas antes de targetedShareIntents.add(targetedShareIntent);

  targetedShareIntent.setClassName( resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name); 

fuente: intención de compartir Android Twitter: ¿Cómo compartir solo por Tweet o Mensaje directo?

Mi implementación del selector abierto personalizado.

caracteristicas:

  • las aplicaciones se ordenan en orden alfabético
  • manejo de la aplicación predeterminada definida por el usuario
  • no manejando ningún caso de aplicaciones
  • filtrándose

 public static Intent createOpenFileIntent(Context context, String pathToFile) { File file = new File(pathToFile); String extension = extensionFromName(file.getName()); String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); if (mimeType == null) { //If android doesn't know extension we can check our own list. mimeType = KNOWN_MIME_TYPES.get(DataViewHelper.extensionFromName(file.getName())); } Intent openIntent = new Intent(); openIntent.setAction(android.content.Intent.ACTION_VIEW); openIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); openIntent.setDataAndType(Uri.fromFile(file), mimeType); // 1. Check if there is a default app opener for this type of content. final PackageManager packageManager = context.getPackageManager(); ResolveInfo defaultAppInfo = packageManager.resolveActivity(openIntent, PackageManager.MATCH_DEFAULT_ONLY); if (!defaultAppInfo.activityInfo.name.endsWith("ResolverActivity")) { return openIntent; } // 2. Retrieve all apps for our intent. If there are no apps - return usual already created intent. List targetedOpenIntents = new ArrayList(); List appInfoList = packageManager.queryIntentActivities(openIntent, PackageManager.MATCH_DEFAULT_ONLY); if (appInfoList.isEmpty()) { return openIntent; } // 3. Sort in alphabetical order, filter itself and create intent with the rest of the apps. Collections.sort(appInfoList, new Comparator() { @Override public int compare(ResolveInfo first, ResolveInfo second) { String firstName = packageManager.getApplicationLabel(first.activityInfo.applicationInfo).toString(); String secondName = packageManager.getApplicationLabel(second.activityInfo.applicationInfo).toString(); return firstName.compareToIgnoreCase(secondName); } }); for (ResolveInfo appInfo : appInfoList) { String packageName = appInfo.activityInfo.packageName; if (packageName.equals(context.getPackageName())) { continue; } Intent targetedOpenIntent = new Intent(android.content.Intent.ACTION_VIEW) .setDataAndType(Uri.fromFile(file), mimeType) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .setPackage(packageName); targetedOpenIntents.add(targetedOpenIntent); } Intent chooserIntent = Intent.createChooser(targetedOpenIntents.remove(targetedOpenIntents.size() - 1), context.getString(R.string.context_menu_open_in)) .putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedOpenIntents.toArray(new Parcelable[] {})); return chooserIntent; } public static String extensionFromName(String fileName) { int dotPosition = fileName.lastIndexOf('.'); // If extension not present or empty if (dotPosition == -1 || dotPosition == fileName.length() - 1) { return ""; } else { return fileName.substring(dotPosition + 1).toLowerCase(Locale.getDefault()); } } 

Hice una pequeña modificación para tener una lista de las aplicaciones con las que desea compartir el nombre. Es casi lo que ya has publicado, pero agregando las aplicaciones para compartir por nombre

 String[] nameOfAppsToShareWith = new String[] { "facebook", "twitter", "gmail" }; String[] blacklist = new String[]{"com.any.package", "net.other.package"}; // your share intent Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_TEXT, "some text"); intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "a subject"); // ... anything else you want to add invoke custom chooser startActivity(generateCustomChooserIntent(intent, blacklist)); private Intent generateCustomChooserIntent(Intent prototype, String[] forbiddenChoices) { List targetedShareIntents = new ArrayList(); List> intentMetaInfo = new ArrayList>(); Intent chooserIntent; Intent dummy = new Intent(prototype.getAction()); dummy.setType(prototype.getType()); List resInfo = getPackageManager().queryIntentActivities(dummy,0); if (!resInfo.isEmpty()) { for (ResolveInfo resolveInfo : resInfo) { if (resolveInfo.activityInfo == null || Arrays.asList(forbiddenChoices).contains( resolveInfo.activityInfo.packageName)) continue; //Get all the posible sharers HashMap info = new HashMap(); info.put("packageName", resolveInfo.activityInfo.packageName); info.put("className", resolveInfo.activityInfo.name); String appName = String.valueOf(resolveInfo.activityInfo .loadLabel(getPackageManager())); info.put("simpleName", appName); //Add only what we want if (Arrays.asList(nameOfAppsToShareWith).contains( appName.toLowerCase())) { intentMetaInfo.add(info); } } if (!intentMetaInfo.isEmpty()) { // sorting for nice readability Collections.sort(intentMetaInfo, new Comparator>() { @Override public int compare( HashMap map, HashMap map2) { return map.get("simpleName").compareTo( map2.get("simpleName")); } }); // create the custom intent list for (HashMap metaInfo : intentMetaInfo) { Intent targetedShareIntent = (Intent) prototype.clone(); targetedShareIntent.setPackage(metaInfo.get("packageName")); targetedShareIntent.setClassName( metaInfo.get("packageName"), metaInfo.get("className")); targetedShareIntents.add(targetedShareIntent); } String shareVia = getString(R.string.offer_share_via); String shareTitle = shareVia.substring(0, 1).toUpperCase() + shareVia.substring(1); chooserIntent = Intent.createChooser(targetedShareIntents .remove(targetedShareIntents.size() - 1), shareTitle); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[] {})); return chooserIntent; } } return Intent.createChooser(prototype, getString(R.string.offer_share_via)); } 

Es casi la misma solución que Makibo publicó pero con un pequeño complemento para hacer una forma fácil de elegir las aplicaciones con las que quieres compartir simplemente agregando el nombre para que no tengas ningún problema en caso de que cambien el nombre del paquete o algo así esta. Siempre y cuando no cambien el nombre.

Intentaba hacer lo mismo pero con ShareActionProvider. El método en la publicación de gumbercules no funcionó bien con ShareActionProvider, así que copié y modifiqué las clases relacionadas con ShareActionProvider para permitir el filtrado personalizado de las sugerencias de ShareActionProvider.

El único cambio es en el método ActivityChooserModel.loadActivitiesIfNeeded (). En mi caso, quería filtrar el paquete de YouTube.

 public static final String YOUTUBE_PACKAGE = "com.google.android.youtube"; private boolean loadActivitiesIfNeeded() { if (mReloadActivities && mIntent != null) { mReloadActivities = false; mActivities.clear(); List resolveInfos = mContext.getPackageManager() .queryIntentActivities(mIntent, 0); final int resolveInfoCount = resolveInfos.size(); for (int i = 0; i < resolveInfoCount; i++) { ResolveInfo resolveInfo = resolveInfos.get(i); // Filter out the YouTube package from the suggestions list if (!resolveInfo.activityInfo.packageName.equals(YOUTUBE_PACKAGE)) { mActivities.add(new ActivityResolveInfo(resolveInfo)); } } return true; } return false; } 

Código en https://github.com/danghiskhan/FilteredShareActionProvider

Esta solución se basa en esta publicación https://rogerkeays.com/how-to-remove-the-facebook-android-sharing-intent

 // get available share intents final String packageToBeFiltered = "com.example.com" List targets = new ArrayList(); Intent template = new Intent(Intent.ACTION_SEND); template.setType("text/plain"); List candidates = this.getPackageManager().queryIntentActivities(template, 0); // filter package here for (ResolveInfo candidate : candidates) { String packageName = candidate.activityInfo.packageName; if (!packageName.equals(packageToBeFiltered)) { Intent target = new Intent(android.content.Intent.ACTION_SEND); target.setType("text/plain"); target.putExtra(Intent.EXTRA_TEXT, "Text to share")); target.setPackage(packageName); targets.add(target); } } if (!targets.isEmpty()) { Intent chooser = Intent.createChooser(targets.get(0), "Share dialog title goes here")); chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targets.toArray(new Parcelable[]{})); startActivity(chooser); }