Fetch Google Play Store Ratings

An Android tutorial to fetch Google Play Store ratings.

Introduction

TL;DR: Fetch Google Play Store Ratings in your Android app.

I like to stay on top of my Android apps and follow the store reviews & ratings closely. Visiting the store each time to check the same or opening the console isn’t that efficient or fast. Nowadays it’s common practice to have an admin portal – whether it’s an Android admin app or Admin web page/web app – there is one concentrated place to manage your app/service and keep track of performance.

I too have something of that sort for Snap Search – I’ve built an Android app that acts as an Admin version making it easy for me to follow the current store rating which is visible to the users, so I created a way to Fetch Google Play Store Ratings.

Existing Methods

You wouldn’t believe this, but there really aren’t any great libraries or tutorials to do the same for Android today. It really isn’t easy.

The Plan

Google hasn’t provided any API for this. Neither could I find any of the APIs exposed which I could take advantage of when I tried different ways. This left me with one choice – to crawl the page. Since that’s the way I was going with, why leave it to just the rating? There’s a lot of solid information which is visible to the user, like:

  • Last Updated
  • App Size
  • Installs
  • Current Version

I decided I’m going to crawl and display all this information now.

Requirements

For the purpose of this tutorial I am going to assume you already have an Android project setup. We’re only going to cover everything you need for this specific purpose.

GRADLE

  1. JSOUP

    implementation ‘org.jsoup:jsoup:1.12.1

APP PACKAGE CODE

  1. APP LIVE ON PLAY STORE

    For this tutorial, we’ll use Snap Search :
    https://play.google.com/store/apps/details?id=cybersky.snapsearch


Code

The Layout

This is the layout we’ll be creating:

Here’s the XML for the above:

<androidx.cardview.widget.CardView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="20dp"
    app:cardBackgroundColor="@color/fff"
    app:cardCornerRadius="5dp"
    app:cardElevation="5dp"
    app:contentPadding="20dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.3"
                android:layout_gravity="center_vertical"
                android:orientation="vertical">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:gravity="center"
                    android:layout_height="wrap_content">

                    <ImageView
                        android:layout_width="30dp"
                        android:layout_height="30dp"
                        android:layout_gravity="center_vertical"
                        android:src="@drawable/ic_rating" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_vertical"
                        android:gravity="center"
                        android:id="@+id/average_rating"
                        android:textSize="30sp"
                        android:text="-"
                        android:textColor="#333" />

                </LinearLayout>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:id="@+id/count_rating"
                    android:textSize="13sp"
                    android:textColor="#555"
                    android:layout_marginTop="0dp" />

            </LinearLayout>

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.7"
                android:paddingLeft="15dp"
                android:layout_gravity="center_vertical"
                android:orientation="vertical">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="5"
                        android:textSize="10sp"
                        android:layout_gravity="center_vertical"
                        android:textColor="#333" />

                    <ProgressBar
                        android:id="@+id/progress5"
                        style="?android:attr/progressBarStyleHorizontal"
                        android:layout_width="0dp"
                        android:layout_height="10dp"
                        android:layout_marginLeft="5dp"
                        android:layout_weight="1"
                        android:backgroundTint="@color/eee"
                        android:layout_gravity="center_vertical"
                        android:indeterminate="false"
                        android:max="100"
                        android:min="0"
                        android:progressBackgroundTint="@color/eee"
                        android:progressTint="#72C9A2"
                        android:scaleY="3" />

                </LinearLayout>


                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_marginTop="4dp"
                    android:layout_height="wrap_content">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="4"
                        android:textSize="10sp"
                        android:layout_gravity="center_vertical"
                        android:textColor="#333"/>

                    <ProgressBar
                        android:layout_width="match_parent"
                        android:layout_height="10dp"
                        android:id="@+id/progress4"
                        android:max="100"
                        android:min="0"
                        android:progressTint="#AED789"
                        android:backgroundTint="@color/eee"
                        android:progressBackgroundTint="@color/eee"
                        android:layout_gravity="center_vertical"
                        android:layout_marginLeft="5dp"
                        android:scaleY="3"
                        android:indeterminate="false"
                        style="?android:attr/progressBarStyleHorizontal" />

                </LinearLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_marginTop="4dp"
                    android:layout_height="wrap_content">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="3"
                        android:textSize="10sp"
                        android:layout_gravity="center_vertical"
                        android:textColor="#333"/>

                    <ProgressBar
                        android:layout_width="match_parent"
                        android:layout_height="10dp"
                        android:id="@+id/progress3"
                        android:max="100"
                        android:min="0"
                        android:progressTint="#F6D951"
                        android:backgroundTint="@color/eee"
                        android:progressBackgroundTint="@color/eee"
                        android:layout_gravity="center_vertical"
                        android:layout_marginLeft="5dp"
                        android:scaleY="3"
                        android:indeterminate="false"
                        style="?android:attr/progressBarStyleHorizontal" />

                </LinearLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_marginTop="4dp"
                    android:layout_height="wrap_content">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="2"
                        android:textSize="10sp"
                        android:layout_gravity="center_vertical"
                        android:textColor="#333"/>

                    <ProgressBar
                        android:layout_width="match_parent"
                        android:layout_height="10dp"
                        android:id="@+id/progress2"
                        android:max="100"
                        android:min="0"
                        android:progressTint="#F3B34B"
                        android:backgroundTint="@color/eee"
                        android:progressBackgroundTint="@color/eee"
                        android:layout_gravity="center_vertical"
                        android:layout_marginLeft="5dp"
                        android:scaleY="3"
                        android:indeterminate="false"
                        style="?android:attr/progressBarStyleHorizontal" />

                </LinearLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_marginTop="4dp"
                    android:layout_height="wrap_content">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="1"
                        android:textSize="10sp"
                        android:layout_gravity="center_vertical"
                        android:textColor="#333"/>

                    <ProgressBar
                        android:layout_width="match_parent"
                        android:layout_height="10dp"
                        android:id="@+id/progress1"
                        android:max="100"
                        android:min="0"
                        android:progressTint="#F08B5D"
                        android:backgroundTint="@color/eee"
                        android:progressBackgroundTint="@color/eee"
                        android:layout_gravity="center_vertical"
                        android:layout_marginLeft="5dp"
                        android:scaleY="3"
                        android:indeterminate="false"
                        style="?android:attr/progressBarStyleHorizontal" />

                </LinearLayout>

            </LinearLayout>


        </LinearLayout>


        <LinearLayout
            android:layout_marginTop="15dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:layout_weight="0.5">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="Updated"
                    android:textColor="#555"
                    android:textSize="10sp"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="1dp"
                    android:id="@+id/fetchUpdated"
                    android:text="..."
                    android:textColor="#111"
                    android:textSize="13sp"/>

            </LinearLayout>

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:layout_weight="0.5">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="Size"
                    android:textColor="#555"
                    android:textSize="10sp"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="1dp"
                    android:id="@+id/fetchSize"
                    android:text="..."
                    android:textColor="#111"
                    android:textSize="13sp"/>

            </LinearLayout>

        </LinearLayout>

        <LinearLayout
            android:layout_marginTop="5dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:layout_weight="0.5">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="Installs"
                    android:textColor="#555"
                    android:textSize="10sp"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="1dp"
                    android:id="@+id/fetchInstall"
                    android:text="..."
                    android:textColor="#111"
                    android:textSize="13sp"/>

            </LinearLayout>

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:layout_weight="0.5">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="Current Version"
                    android:textColor="#555"
                    android:textSize="10sp"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="1dp"
                    android:id="@+id/fetchVersion"
                    android:text="..."
                    android:textColor="#111"
                    android:textSize="13sp"/>

            </LinearLayout>

        </LinearLayout>

    </LinearLayout>

</androidx.cardview.widget.CardView>

The above layout works like a simple drop, except for just one drawable that needs to be arranged: @drawable/ic_rating – which is the star next to the average rating in the image.

I’ve used LinearLayouts to arrange the views but ConstraintLayout could be a better option too. However, the purpose of the tutorial is mostly in the code to fetch the values rather than display. So, let’s move on …

MainActivity.java

Scraping the play store needs to be done using an AsyncTask because it’s best to not reserve the main thread for a network call.

Requirements:

  • All views in the above layout with an id have been initialised and respective view objects have been created for use.

We’re going to parse the text on the play store page live, as seen by users and you. I’ve identified the correct labels required, the classes/ids of those fields and you’ll be seeing them referenced in the code below.

DISCLAIMER
private class AsyncTaskRunner extends AsyncTask<Void, Integer, Void> {

    String currentRating, strUpdated, strSize, strInstalls, strCurrentVersion;
    int totalRatings;
    int[] stars = new int[5];

    @Override
    protected void onPreExecute() {
        // you can use this for any clearing purposes
    }


    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        averageRating.setText(currentRating);
        ratingCount.setText("" + totalRatings + " Ratings");
        fetchInstalls.setText(strInstalls);
        fetchSize.setText(strSize);
        fetchUpdated.setText(strUpdated);
        fetchVersion.setText(strCurrentVersion);
        progress1.setProgress(stars[4]);
        progress2.setProgress(stars[3]);
        progress3.setProgress(stars[2]);
        progress4.setProgress(stars[1]);
        progress5.setProgress(stars[0]);
    }

    @Override
    protected Void doInBackground(Void... ignore) {


        try {
            // JSOUP THINGS
            String URL = "https://play.google.com/store/apps/details?id=cybersky.snapsearch";
            Document document = Jsoup.connect(URL).timeout(0).get();

            Element ratingElement = document.select(".BHMmbe").first();
            currentRating = ratingElement.text();

            Element countElement = document.selectFirst(".EymY4b");
            for (Element span: countElement.getElementsByTag("span")) {
                if (!span.hasClass("O3QoBc hzfjkd")) {
                    totalRatings = Integer.parseInt(span.text().substring(0, span.text().indexOf(" ")));
                    break;
                }
            }

            Element starsElement = document.select(".VEF2C").first();
            int pos = 0;
            for (Element div: starsElement.getElementsByTag("div")) {
                if (div.hasClass("mMF0fd")) {
                    for (Element widths: div.getElementsByTag("span")) {
                        if (!widths.hasClass("Gn2mNd")) {
                            String style = widths.attr("style");
                            String percent = style.substring(style.indexOf(" ") + 1, style.indexOf("%"));
                            stars[pos++] = Integer.parseInt(percent);
                        }
                    }
                }
            }

            Element infoElement = document.selectFirst(".IxB2fe");
            for (Element info: infoElement.getElementsByTag("div")) {
                if (info.hasClass("hAyfc")) {
                    for (Element nestedInfo: info.getElementsByTag("div")) {
                        if (nestedInfo.hasClass("BgcNfc")) {
                            if (nestedInfo.text().equalsIgnoreCase("Updated")) {
                                strUpdated = info.getElementsByClass("htlgb").first().text();
                            }
                            if (nestedInfo.text().equalsIgnoreCase("Size")) {
                                strSize = info.getElementsByClass("htlgb").first().text();
                            }
                            if (nestedInfo.text().equalsIgnoreCase("Installs")) {
                                strInstalls = info.getElementsByClass("htlgb").first().text();
                            }
                            if (nestedInfo.text().equalsIgnoreCase("Current Version")) {
                                strCurrentVersion = info.getElementsByClass("htlgb").first().text();
                            }
                        }
                    }
                }
            }
        }catch (Exception e) {
            // exception occured
            Log.e(TAG, "Parsing error from store");
        }

        return null;

    }
}

As simple as that! In those many lines of code, we’re actually parsing an apps Play store listing and retrieving all this crucial information! I hope this helps you in your purpose to Fetch Google Play Store Ratings!

PS: If you have 2 minutes, it would be great if you could leave a good rating for Snap Search! ????

Leave a Reply

Your email address will not be published. Required fields are marked *