【问题标题】:Custom filtering of intent chooser based on installed Android package name根据已安装的 Android 包名称自定义过滤意图选择器
【发布时间】:2011-08-09 17:34:26
【问题描述】:

我想利用内置的意图选择器来显示自定义过滤的应用列表,供用户选择和启动。

我知道如何获取已安装软件包的列表:

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

此时我想根据包名称中包含的特定字符串(或字符串的变体)过滤列表,我也可以弄清楚如何做。

但这就是我卡住的地方。据我所知,Intent.createChooser() 只接受一个目标 Intent 作为参数。我希望有一个基于包和类名或其他东西的意图列表的重载。但我没有看到类似的东西。我在某个地方错过了吗?

所以问题是,这可能与内置选择器有关,还是我必须使用 AlertDialog Builder 构建自己的选择器?我希望避免后者。

提前致谢。

【问题讨论】:

    标签: android android-intent


    【解决方案1】:

    这是我提出的一个解决方案。我使用它为选择器中的每个选择提供不同的意图数据,但您也可以轻松地从列表中删除一个意图。希望这会有所帮助:

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

    编辑

    在使用此方法时,我将某些应用程序的名称获取为 android 系统。如果有人收到此错误,请在targetedShareIntents.add(targetedShareIntent);之前添加以下行

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

    来源:Android share intent Twitter: How to share only by Tweet or Direct Message?

    【讨论】:

    • 谢谢,还有一点提示:为了保留来自getPackageManager().queryIntentActivities(shareIntent, 0); 的应用程序顺序,请调用Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size()-1), "Select app to share");,选择器将其初始化意图添加到extra_initial_intents 的末尾:)
    • gumbercules,我尝试了上面的代码,但帖子上没有出现消息。它向我显示了一个带有空消息的 Facebook 页面...
    • 做了更多的研究,重要的是使用targetedShareIntent.setPackage(packageName) (developer.android.com/reference/android/content/…) 只允许来自给定包的意图(这将是从targetedShareIntents中选择的一个。然后附加自定义意图。要进一步改进此代码,请进一步指定 targetedShareIntent.setClassName(packageName,className)
    • 我根据这个答案 gist.github.com/mediavrog/5625602 创建了一个可重用解决方案的要点
    • 我没有获取/导入 StringUtils @gumbercules
    【解决方案2】:

    选择器的唯一一个附加参数是Intent.EXTRA_INITIAL_INTENTS。它的描述是:

    当使用ACTION_CHOOSER 向用户显示时,使用putExtra(String, Parcelable[]) 设置的其他活动的Parcelable[]LabeledIntent 对象将放置在选项列表的前面。

    我没有在 Android 源代码中找到任何从列表中排除其他活动的方法,因此似乎无法使用选择器执行您想要执行的操作。

    编辑:这很容易找到。只需检查ChooserActivityResolverActivity 源代码。这些类相当小。

    【讨论】:

    • 看来我要找的东西是不可能的,所以你是最接近的(也是唯一的)。
    • 这是正确答案。在尝试了这两种方法并阅读了文档之后,绝对不可能从列表中删除项目。
    • 正确。我已经尝试了各种方法,但没有结果。最后,我必须像 Intent.createChooser() 一样自定义我自己的“createChooser()”方法。
    • 自定义解决方案这个链接-> stackoverflow.com/questions/9730243/…
    【解决方案3】:

    我做了一个小修改,以列出您要按名称与之共享的应用程序。这几乎是您已经发布的内容,但添加了要按名称共享的应用

    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 发布的解决方案几乎相同,但添加了一些内容,以便通过添加名称来轻松选择您想要共享的应用程序,这样您就不会遇到任何问题,以防他们更改包名称或类似的东西。只要他们不改名字。

    【讨论】:

    • 这可行,但由于某种原因,在 Android 6 上,它可以为应用程序显示空名称(例如“Gogobot”),并且还会导致显示的项目网格中出现空单元格(在我的 Nexus 5 案例,一个位于网格最后一行末尾的空单元格)。
    【解决方案4】:

    我的自定义打开选择器的实现。

    特点:

    • 应用按字母顺序排序
    • 处理用户定义的默认应用
    • 处理无应用案例
    • 过滤自身

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

    【讨论】:

    • 要获得与动作选择器显示的标签相同的标签,您应该使用ResolveInfo.loadLabel(packageManager) 而不是packageManager.getApplicationLabel(ResolveInfo.activityInfo.applicationInfo)。结果并不总是相同的。 (这用于在步骤 3 中排序时覆盖 compare())。例如,对于 Dropbox,loadLabel() 将返回“添加到 Dropbox”,getApplicationLabel() 将返回“Dropbox”。
    【解决方案5】:

    我试图用 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

    【讨论】:

      【解决方案6】:

      此解决方案基于此帖子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);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-06-04
        • 2017-04-19
        • 2011-10-17
        • 1970-01-01
        • 2015-09-11
        • 1970-01-01
        • 1970-01-01
        • 2018-06-16
        相关资源
        最近更新 更多