ResolveInfo - 检查是否是默认启动意图

ResolveInfo - check if is default launch intent or not

我做什么

我正在检索所有已安装应用的列表,如下所示(对于像应用这样的启动器):

PackageManager pm = context.getPackageManager();
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null)
        .addCategory(Intent.CATEGORY_LAUNCHER);

// flag PackageManager.GET_RESOLVED_FILTER will set ResolveInfo.filter (not documented, but tested)
// many examples use flag == 0, but then the filter is always null
List<ResolveInfo> ril = pm.queryIntentActivities(mainIntent, PackageManager.GET_RESOLVED_FILTER);
if (ril != null) {
    for (ResolveInfo ri : ril) {
        // I need this info!
        // ri.isDefault is always false...
        boolean isDefault = ri.isDefault || ril.filter.hasCategory(Intent.CATEGORY_DEFAULT);

    }
}

到目前为止,这有效。但是一些应用程序,如三星 phone 应用程序,不尊重它们应该只用 Intent.CATEGORY_DEFAULT 标记一个 activity。以下是三星应用中两项活动的类别:

ActivityInfo{cd007eb com.android.contacts.activities.PeopleActivity}
    filter:
        0 = "android.intent.category.DEFAULT"
        1 = "android.intent.category.LAUNCHER"
        2 = "android.intent.category.BROWSABLE"
        3 = "android.intent.category.APP_CONTACTS"

ActivityInfo{3e9ba8d com.android.dialer.DialtactsActivity}
    filter:
        0 = "android.intent.category.DEFAULT"
        1 = "android.intent.category.LAUNCHER"
        2 = "android.intent.category.BROWSABLE"

要求

问题

如何可靠地确定 ResolveInfo 是否属于默认启动意图?

解决方案

只要您假设只有一些应用程序注册多个默认活动,此解决方案就可用。

  • 得到我问题中的所有ResolveInfos
  • 创建一个 HashMap 并在列表中插入包名称和条目数
  • 使用 HashMap 来确定您是否甚至需要检查 ResolveInfo 是否属于默认 activity 或不
  • 如果需要检查,请对这个进行慢速检查ResolveInfo

代码

List<ResolveInfo> ril = ...;
HashMap<String, Integer> packageCountMap = getPackageCountMap(uniqueRil);
for (ResolveInfo ri :: ril) {
    boolean isDefault = isDefaultIntentForPhoneApp(packageCountMap, ri);
    // use this info...
}

辅助函数:

public static HashMap<String, Integer> getPackageCountMap(List<ResolveInfo> ril) {
    HashMap<String, Integer> map = new HashMap<>();
    ResolveInfo ri;
    Integer count;
    if (ril != null) {
        for (int i = 0; i < ril.size(); i++) {
            ri = ril.get(i);
            count = map.get(ri.activityInfo.packageName);
            if (count == null) {
                count = 1;
            } else {
                count++;
            }
            map.put(ri.activityInfo.packageName, count);
        }
    }
    return map;
}

public static boolean isDefaultIntentForPhoneApp(PackageManager pm, HashMap<String, Integer> packageCountMap, ResolveInfo ri) {
    boolean isDefault;

    // count how often the package name exists => 1x => then the activity must be the default one, no need to do any check
    Integer count = packageCountMap.get(ri.activityInfo.packageName);
    if (count == null || count <= 1) {
        isDefault = true;
    }
    // otherwise get default activity and compare it
    else {
        Intent i = pm.getLaunchIntentForPackage(packageName);
        ComponentName cn = i.resolveActivity(pm);
        isDefault = cn != null && cn.getClassName().equals(ri.activityInfo.name);
    }
    return isDefault;
}

您可以按如下方式使用queryIntentActivities

PackageManager pm = context.getPackageManager();
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null)
        .addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> ril = pm.queryIntentActivities(mainIntent, PackageManager.MATCH_DEFAULT_ONLY);

基于 MATCH_DEFAULT_ONLY 的文档:

Resolution and querying flag: if set, only filters that support the CATEGORY_DEFAULT will be considered for matching. This is a synonym for including the CATEGORY_DEFAULT in your supplied Intent.