How to get Category for each App on device on Android?

13,016

Solution 1

I know that this is an old post, but for anyone still looking for this, API level 26 (O) has added categories to android.content.pm.ApplicationInfo.

From the docs https://developer.android.com/reference/android/content/pm/ApplicationInfo#category:

public int category

The category of this app. Categories are used to cluster multiple apps together into meaningful groups, such as when summarizing battery, network, or disk usage. Apps should only define this value when they fit well into one of the specific categories.

Set from the R.attr.appCategory attribute in the manifest. If the manifest doesn't define a category, this value may have been provided by the installer via PackageManager#setApplicationCategoryHint(String, int). Value is CATEGORY_UNDEFINED, CATEGORY_GAME, CATEGORY_AUDIO, CATEGORY_VIDEO, CATEGORY_IMAGE, CATEGORY_SOCIAL, CATEGORY_NEWS, CATEGORY_MAPS, or CATEGORY_PRODUCTIVITY

One can now do something like:

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)
    // ...
}

Solution 2

if you get for each application its package name, you could ask directly to play store which category an app belongs, parsing html response page with this library:

org.jsoup.jsoup1.8.3

Here's a snippet to solve your problem:

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

You could make these queries in an AsyncTask, or in a service. Hope that you find it helpful.

Solution 3

Any suggestions on how to get the Category of the App?

Apps don't have categories. Some markets have categories.

From what I can tell there is nothing related to Category stored on the device itself.

Correct.

I was thinking of using the android market API to search for the application in the market and use the Category value returned by the search.

At the present time, there is no "android market API", unless you have a good legal team and a significant legal defense fund.

Moreover, not all apps are distributed via the "android market", particularly on devices like the Kindle Fire. Also, developers are welcome to change categories whenever they wish, at least on the Google Play Store.

Any suggestions on how best to do this?

Drop the feature and move on to other ways to add value.

Solution 4

I also faced the same issue. The solution for the above query is stated below.

Firstly, download the Jsoup library or download the jar file.

or Add this to your build.gradle(Module: app) implementation 'org.jsoup:jsoup:1.11.3'

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 = "https://play.google.com/store/apps/details?id=com.imo.android.imoim";  //GOOGLE_URL + packageInfo.packageName;
        Log.i(TAG, query_url);
        category = getCategory(query_url);
        Log.e("CATEGORY", category);

        // store category or do something else
        //}
        return null;
    }


    private String getCategory(String query_url) {

        try {
             Document doc = Jsoup.connect(query_url).get();
            Elements link = doc.select("a[class=\"hrTbp R8zArc\"]");
               return link.text();
        } catch (Exception e) {
            Log.e("DOc", e.toString());
        }
    }

}

In return, you will get Application Company Name and category of the application

Solution 5

I made a Kotlin solution based on the answer from @Ankit Kumar Singh.
This solution maps the category to an enum, in case you want to do other things than just show it.

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

And here is the enum with all the possible values at of this moment in time:

// 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
        }
    }
}
Share:
13,016
Mike Cooper
Author by

Mike Cooper

Mike Cooper is Founder and CEO at Revocent Inc (www.revocent.com). Revocent specializes in Certificate Automation software for enterprises of all sizes.

Updated on June 24, 2022

Comments

  • Mike Cooper
    Mike Cooper about 2 years

    I've got an Android app which scans for all Apps installed on the device and then reports this to a server (it's an MDM agent). Any suggestions on how to get the Category of the App? Everyone has a different list of Categories, but basically something like Game, Entertainment, Tools/Utilities, etc.

    From what I can tell there is nothing related to Category stored on the device itself. I was thinking of using the android market API to search for the application in the market and use the Category value returned by the search. Not sure how successful this will be finding a match. Any suggestions on how best to do this?

    Any suggestions on a different approach?

    Thanks in advance. mike

  • Bhanu Sharma
    Bhanu Sharma about 8 years
    hey Dr. booster do this. They categorized all game and show in grid view. Clean master also do same have u any idea how they did this and one thing i off internet but they did the same that means they do something which doesn't require internet or any API
  • Tomas Maksimavicius
    Tomas Maksimavicius over 6 years
    Hey, it works well, but what if google play is in other language? How to get only english version of category?
  • soufien karray
    soufien karray over 6 years
    try to add "&&hl=en" to query_url ;)
  • Jayesh M
    Jayesh M about 6 years
    I tried this code and it always returning error. Is there anything I want to do apart from this code ?
  • Makvin
    Makvin about 5 years
    from above solution i got both company name and category name but Is it possible to find its a game or app?
  • Rishabh Chandaliya Jain
    Rishabh Chandaliya Jain almost 5 years
    @Makvin yes you can find whether it is an game or application. Games also have sub-category list it down(eg. Action, Adventure, Arcade,etc). if my application category is same as Action or Adventure or Arcade I'll assign it as a GAME It's a work around but this might help you discover whether application is game or not Thansk
  • android developer
    android developer almost 5 years
    Will this work even today? Will it always be in English? Is there a way to get all possible categories? In case things change, how did you find what to put as parameter for doc.select() ?