Home » Android » google play – How to get Category for each App on device on Android?

google play – How to get Category for each App on device on Android?

Posted by: admin June 15, 2020 Leave a comment

Questions:

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

How to&Answers:

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.

Answer:

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.

Answer:

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

Answer:

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.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import net.nrask.appsanywhere.deviceSettings.installedApps.database.AppCategory
import org.jsoup.Jsoup

object GetAppCategoryService {
    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/"
    private const val CATEGORY_GAME_STRING = "GAME_" // All games start with this prefix
    private const val DEFAULT_VALUE = "OTHERS"

    fun fetchCategory(packageName: String, onResult: (AppCategory) -> Unit) {
        val url = "$APP_URL$packageName&hl=en" //https://play.google.com/store/apps/details?id=com.example.app&hl=en
        val categoryRaw = parseAndExtractCategory(url)

        launch(UI) {
            onResult(AppCategory.fromCategoryName(categoryRaw ?: DEFAULT_VALUE))
        }
    }

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

            if (href != null && href.length > 4 && href.contains(CATEGORY_STRING)) {
                getCategoryTypeByHref(href)
            } else {
                null
            }
        } catch (e: kotlin.Exception) {
            //TODO handle error
            null
        }
    }

    private fun getCategoryTypeByHref(href: String): String? {
        val appCategoryType = href.substring(href.indexOf(CATEGORY_STRING) + CAT_SIZE, href.length)

        return if (appCategoryType.contains(CATEGORY_GAME_STRING)) {
            AppCategory.GENERAL_GAMES_CATEGORY_NAME
        } else appCategoryType
    }
}

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

// Int value is equal to the category's name's hashvalue - For easy parsing
enum class AppCategory(val rawValue: Int) {
    OTHER(0),
    ART_AND_DESIGN(1798113474),
    AUTO_AND_VEHICLES(-201031457),
    BEAUTY(1955267708),
    BOOKS_AND_REFERENCE(652448174),
    BUSINESS(-364204096),
    COMICS(1993477496),
    COMMUNICATION(2080958390),
    DATING(2009386219),
    EDUCATION(-1799129208),
    ENTERTAINMENT(-678717592),
    EVENTS(2056967449),
    FINANCE(-135275590),
    FOOD_AND_DRINK(-267698865),
    HEALTH_AND_FITNESS(704829917),
    HOUSE_AND_HOME(-908401466),
    LIBRARIES_AND_DEMO(-1893543311),
    LIFESTYLE(-1796047851),
    MAPS_AND_NAVIGATION(1381037124),
    MEDICAL(1658758769),
    MUSIC_AND_AUDIO(702385524),
    NEWS_AND_MAGAZINES(1916976715),
    PARENTING(561964760),
    PERSONALIZATION(1779216900),
    PHOTOGRAPHY(-470332035),
    PRODUCTIVITY(-953829166),
    SHOPPING(438165864),
    SOCIAL(-1843721363),
    SPORTS(-1842431105),
    TOOLS(80007611),
    TRAVEL_AND_LOCAL(856995806),
    VIDEO_PLAYERS(289768878),
    WEATHER(1941423060),
    GAMES("GAMES".hashCode());


    companion object {
        private val map = AppCategory.values().associateBy(AppCategory::rawValue)
        const val GENERAL_GAMES_CATEGORY_NAME = "GAMES"

        fun fromCategoryName(name: String): AppCategory {
            return map[name.hashCode()] ?: AppCategory.OTHER
        }
    }
}

Answer:

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

Answer:

You can use below AsyncTask for extract Android app category from playStore by using app package id.

It’s requires jsoup library for parsing the html page. you can find it here org.jsoup.jsoup1.11.1

Answer:

Probably a bit late, but the problem is still here.
The OP has the advantage because of sending those results to the API (here I assume that the API is managed by the OP or his API colleagues at least).

So, for anyone with the similar problem I’d suggest following:

  1. Collect all the package names you’re interested in from device.
  2. Send that data to the your API
  3. API should extract package names and try to read results from its cache / db…
  4. For those packages that do not exist in cache / db make “market API” call and extract category – save it to the db / cache for reuse in this iteration.
  5. When all requests (to cache / db and market API) are completed do whatever you like with the results.

Things to consider:

When multiple users try to query your API for a same package name and you don’t have a category for that package in your cache / db…
Do 1 request to “market API” for packagex and update packagex in your cache / db to “waiting for results” state – next request should either get a “waiting for results” or a result that “market API” returned.

One should also consider a fallback for possible “market API” fails (market API not working, not a google play app, or something similar). This decision is basically tied to your domain and the business trend that you’re trying to catch will force a decision about this for you. If you’re really into getting this category stuff sorted out you could pipeline this fallback to human decision and update your API db / cache for packagex accordingly.

put up a nice API that would handle these and similar scenarios gracefully then one could probably even commercialize it up to a certain extent and “market API endpoint” – AKA play store package details page. That page would lose a big part of it’s fake users 🙂