如何在Android上的设备上获取每个应用程序的类别?

Mik*_*per 6 android google-play

我有一个Android应用程序,它扫描设备上安装的所有应用程序,然后将其报告给服务器(它是MDM代理).有关如何获得应用类别的任何建议?每个人都有不同的类别列表,但基本上类似于游戏,娱乐,工具/实用程序等.

据我所知,与设备本身存储的类别无关.我正在考虑使用Android市场API来搜索市场中的应用程序并使用搜索返回的Category值.不确定这会找到一场比赛有多成功.有关如何最好地做到这一点的任何建议?

有关不同方法的任何建议吗?

提前致谢.麦克风

小智 13

我知道这是一篇旧文章,但对于仍在寻找此内容的人来说,API 级别 26 (O) 已将类别添加到android.content.pm.ApplicationInfo.

来自文档https://developer.android.com/reference/android/content/pm/ApplicationInfo#category

public int category

该应用程序的类别。类别用于将多个应用程序聚集到有意义的组中,例如在汇总电池、网络或磁盘使用情况时。应用程序仅应在非常适合某一特定类别时才定义此值。

从清单中的R.attr.appCategory属性设置。如果清单未定义类别,则安装程序可能已通过PackageManager#setApplicationCategoryHint(String, int)提供此值。值为CATEGORY_UNDEFINED, CATEGORY_GAME, CATEGORY_AUDIO, CATEGORY_VIDEO, CATEGORY_IMAGE, CATEGORY_SOCIAL, CATEGORY_NEWS, CATEGORY_MAPS, 或CATEGORY_PRODUCTIVITY

现在可以做类似的事情:

PackageManager pm = context.getPackageManager();
ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, 0);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    int appCategory = applicationInfo.category;
    String categoryTitle = (String) ApplicationInfo.getCategoryTitle(context, appCategory)
    // ...
}
Run Code Online (Sandbox Code Playgroud)


joh*_*_kb 6

如果为每个应用程序获取其程序包名称,则可以直接要求商店播放应用程序所属的类别,并使用以下库解析html响应页面:

org.jsoup.jsoup1.8.3

以下是解决您的问题的代码段:

public class MainActivity extends AppCompatActivity {

public final static String GOOGLE_URL = "https://play.google.com/store/apps/details?id=";
public static final String ERROR = "error";

...


   private class FetchCategoryTask extends AsyncTask<Void, Void, Void> {

       private final String TAG = FetchCategoryTask.class.getSimpleName();
       private PackageManager pm;
       private ActivityUtil mActivityUtil;

       @Override
       protected Void doInBackground(Void... errors) {
          String category;
           pm = getPackageManager();
           List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
           Iterator<ApplicationInfo> iterator = packages.iterator();
           while (iterator.hasNext()) {
               ApplicationInfo packageInfo = iterator.next();
               String query_url = GOOGLE_URL + packageInfo.packageName;
               Log.i(TAG, query_url);
               category = getCategory(query_url);
               // store category or do something else
           }
           return null;
        }


        private String getCategory(String query_url) {
           boolean network = mActivityUtil.isNetworkAvailable();
           if (!network) {
               //manage connectivity lost
               return ERROR;
           } else {
               try {
                   Document doc = Jsoup.connect(query_url).get();
                   Element link = doc.select("span[itemprop=genre]").first();
                   return link.text();
               } catch (Exception e) {
                   return ERROR;
               }
           }
        }
    }  
}
Run Code Online (Sandbox Code Playgroud)

您可以在AsyncTask或服务中进行这些查询。希望对您有所帮助。


Com*_*are 5

有关如何获得应用类别的任何建议?

应用没有类别.一些市场有类别.

据我所知,与设备本身存储的类别无关.

正确.

我正在考虑使用Android市场API来搜索市场中的应用程序并使用搜索返回的Category值.

目前,没有"Android市场API",除非你有一个良好的法律团队和重要的法律辩护基金.

此外,并非所有应用程序都通过"Android市场"分发,特别是在Kindle Fire等设备上.此外,欢迎开发人员随时更改类别,至少在Google Play商店中如此.

有关如何最好地做到这一点的任何建议?

删除该功能并继续使用其他方式来增加价值.


Seb*_*Jeg 5

我根据 @Ankit Kumar Singh 的答案制作了一个 Kotlin 解决方案。
此解决方案将类别映射到枚举,以防您想做其他事情而不仅仅是显示它。

import kotlinx.coroutines.*
import org.jsoup.Jsoup
import javax.inject.Inject
import javax.inject.Singleton

class AppCategoryService {
    companion object {
        private const val APP_URL = "https://play.google.com/store/apps/details?id="
        private const val CAT_SIZE = 9
        private const val CATEGORY_STRING = "category/"
    }

    suspend fun fetchCategory(packageName: String): AppCategory {
        val url = "$APP_URL$packageName&hl=en" //https://play.google.com/store/apps/details?id=com.example.app&hl=en
        val categoryRaw = parseAndExtractCategory(url) ?: return AppCategory.OTHER
        return AppCategory.fromCategoryName(categoryRaw)
    }

    @Suppress("BlockingMethodInNonBlockingContext")
    private suspend fun parseAndExtractCategory(url: String): String? = withContext(Dispatchers.IO) {
        return@withContext try {
            val text = Jsoup.connect(url).get()?.select("a[itemprop=genre]") ?: return@withContext null
            val href = text.attr("abs:href")

            if (href != null && href.length > 4 && href.contains(CATEGORY_STRING)) {
                getCategoryTypeByHref(href)
            } else {
                null
            }
        } catch (e: Throwable) {
            null
        }
    }

    private fun getCategoryTypeByHref(href: String) = href.substring(href.indexOf(CATEGORY_STRING) + CAT_SIZE, href.length)
}
Run Code Online (Sandbox Code Playgroud)

这是此时此刻所有可能值的枚举:

// Note: Enum name matches API value and should not be changed
enum class AppCategory {
    OTHER,
    ART_AND_DESIGN,
    AUTO_AND_VEHICLES,
    BEAUTY,
    BOOKS_AND_REFERENCE,
    BUSINESS,
    COMICS,
    COMMUNICATION,
    DATING,
    EDUCATION,
    ENTERTAINMENT,
    EVENTS,
    FINANCE,
    FOOD_AND_DRINK,
    HEALTH_AND_FITNESS,
    HOUSE_AND_HOME,
    LIBRARIES_AND_DEMO,
    LIFESTYLE,
    MAPS_AND_NAVIGATION,
    MEDICAL,
    MUSIC_AND_AUDIO,
    NEWS_AND_MAGAZINES,
    PARENTING,
    PERSONALIZATION,
    PHOTOGRAPHY,
    PRODUCTIVITY,
    SHOPPING,
    SOCIAL,
    SPORTS,
    TOOLS,
    TRAVEL_AND_LOCAL,
    VIDEO_PLAYERS,
    WEATHER,
    GAMES;

    companion object {
        private val map = values().associateBy(AppCategory::name)
        private const val CATEGORY_GAME_STRING = "GAME_" // All games start with this prefix

        fun fromCategoryName(name: String): AppCategory {
            if (name.contains(CATEGORY_GAME_STRING)) return GAMES
            return map[name.toUpperCase(Locale.ROOT)] ?: OTHER
        }
    }
}
Run Code Online (Sandbox Code Playgroud)