Пользовательская фильтрация выбора намерения на основе установленного имени пакета Android
Я хотел бы использовать встроенный выбор намерений для отображения настраиваемого отфильтрованного списка приложений для выбора и запуска пользователем.
Я знаю, как получить список установленных пакетов:
final Intent myIntent = new Intent(android.content.Intent.ACTION_MAIN);
List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(myIntent, 0);
на этом этапе я хочу отфильтровать список на основе определенной строки (или вариации строк), содержащейся в имени пакета, что я могу выяснить, как это сделать.
но вот где я застрял. Насколько я знаю, Intent.createChooser()
принимает только a одно целевое намерение в качестве параметра. Я надеялся, что была перегрузка, которая взяла список намерений на основе имен пакетов и классов или что-то в этом роде. Но я не вижу ничего подобного. Я где-то это пропустил?
Итак, вопрос в том, возможно ли это сделать со встроенным выбором, или мне нужно построить свой собственный с помощью AlertDialog Builder? Я надеюсь избежать этого позже.
спасибо заранее.
6 ответов:
единственным дополнительным параметром для выбора является
Intent.EXTRA_INITIAL_INTENTS
. Его описание:Parcelable [] намерения или LabeledIntent объектов, как установлено с putExtra (String, Parcelable[]) дополнительных действий, чтобы разместить перед списком вариантов, когда показано пользователю с ACTION_CHOOSER.
Я не нашел способа в источниках Android исключить другие действия из списка, поэтому, похоже, нет способа сделать то, что вы хотите сделать с помощью селектора.
EDIT: это очень легко выяснить. Просто проверьте ChooserActivity и ResolverActivity исходный код. Эти классы довольно малы.
вот решение на скорую руку. Я использую его, чтобы иметь разные данные о намерениях для каждого выбора в селекторе, но вы также можете легко удалить намерение из списка. Надеюсь, это поможет:
List<Intent> targetedShareIntents = new ArrayList<Intent>(); Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND); shareIntent.setType("text/plain"); List<ResolveInfo> 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); }
EDIT
при использовании этого метода я получаю имена некоторых приложений в качестве системы android. Если кто-нибудь получит эту ошибку, пожалуйста, добавьте ниже строки перед
targetedShareIntents.add(targetedShareIntent);
targetedShareIntent.setClassName( resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
источник : Android share intent Twitter: как поделиться только твитом или Прямое Сообщение?
моя реализация пользовательского открытого выбора.
характеристики:
- приложения сортируются в алфавитном порядке
- обработка приложения по умолчанию, определенного пользователем
- обработка без приложения case
- сама фильтрация
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<Intent> targetedOpenIntents = new ArrayList<Intent>(); List<ResolveInfo> 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<ResolveInfo>() { @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()); } }
Я сделал небольшое изменение, чтобы иметь список приложений, которые вы хотите поделиться с именем. Это почти то, что вы уже опубликовали, но добавление приложений для обмена по имени
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<Intent> targetedShareIntents = new ArrayList<Intent>(); List<HashMap<String, String>> intentMetaInfo = new ArrayList<HashMap<String, String>>(); Intent chooserIntent; Intent dummy = new Intent(prototype.getAction()); dummy.setType(prototype.getType()); List<ResolveInfo> 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<String, String> info = new HashMap<String, String>(); 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<HashMap<String, String>>() { @Override public int compare( HashMap<String, String> map, HashMap<String, String> map2) { return map.get("simpleName").compareTo( map2.get("simpleName")); } }); // create the custom intent list for (HashMap<String, String> 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)); }
это почти то же самое решение, которое опубликовал Makibo, но с небольшим добавлением, чтобы сделать легкую форму выбора приложений, которыми вы хотите поделиться, просто добавив имя, чтобы у вас не было никаких проблем, если они изменят имя пакета или что-то в этом роде. До тех пор, пока они не изменят название.
Я пытался сделать то же самое, но с ShareActionProvider. Метод в сообщении gumbercules не работал хорошо с ShareActionProvider, поэтому я скопировал и изменил связанные классы ShareActionProvider, чтобы разрешить пользовательскую фильтрацию предложений ShareActionProvider.
единственное изменение-это ActivityChooserModel.loadActivitiesIfNeeded() метод. В моем случае, я хотел, чтобы отфильтровать пакет с YouTube.
public static final String YOUTUBE_PACKAGE = "com.google.android.youtube"; private boolean loadActivitiesIfNeeded() { if (mReloadActivities && mIntent != null) { mReloadActivities = false; mActivities.clear(); List<ResolveInfo> 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; }
код https://github.com/danghiskhan/FilteredShareActionProvider
это решение основано на этом посте https://rogerkeays.com/how-to-remove-the-facebook-android-sharing-intent
// get available share intents final String packageToBeFiltered = "com.example.com" List<Intent> targets = new ArrayList<Intent>(); Intent template = new Intent(Intent.ACTION_SEND); template.setType("text/plain"); List<ResolveInfo> 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); }