Category Archives: Mobile

Software development for mobile devices.

Automating Google Play Store reports with ‘gsutil’ and Windows PowerShell

Why (feel free to skip)

As Android developer I have various mobile devices (phones, tablets, watches) with all Android version starting 4.0 (Ice Cream Sandwich, API 14) up to Android 12 (L, API 31). Those devices accompanied by very detailed test scenarios help me ensure best possible user experience 🙂

Android test devices

As Android developer I have various mobile devices (phones, tablets, watches) with all Android version starting 4.0 (Ice Cream Sandwich, API 14) up to Android 12 (L, API 31).

When prepping release for an app, all test cases for that particular app are performed on each device. For each Android version I keep track of active installs and user rating. Applying that data in the test scenario. This helps me focus more on particular set of for ex. unhappy users running specific OS version.

Below is a screenshot of a test scenario for one of my apps: GSM Signal Monitor & SIM Card Info.

GSM Signal Monitor & SIM Card Info test scenario

GSM Signal Monitor & SIM Card Info test scenario

Each Android version has number of active installs and rating. Going to Google Play Store dev console and collecting this information by hand is somehow tedious job, so I decided to automate it using ‘Google Cloud Storage Util‘ and a bit of Windows PowerShell scripting.

How

Our task is to fetch Google Play store monthly reports for app installs and ratings by OS version, filter them and get data that can easily be copy pasted in the ‘Active Installs‘ tab of the test scenario.

1. Cloud Storage bucket ID

Google Play reports, beside dev console can also be accessed via Google Cloud Storage bucket associated with your developer account. You can go to “Download reports”, pick the report type you want and select “Copy Cloud Storage URI” next to it.

Google Play Cloud Storage URI

The url will look like this

gs://< bucket_id >/stats/installs/

where < bucket_id > will be your actual developer account cloud storage bucket. Remember this bucket id. We will be using it later.

2. Install gsutil

Google provides GSUtil for using cloud storage from the command line. Head over to https://cloud.google.com/storage/docs/gsutil_install#expandable-1 and follow the installation instructions.

We will be utilizing the gsutil.cmd assuming that the script is available in system PATH.

Next we need to authorize gsutil to access our cloud storage data. Follow the instructions on how to Authenticate stand-alone gsutil. For ‘project-id’ use the project ID of the app you want to get reports for. I’m interested in retrieving reports for GSM Signal Monitor & SIM Card Info app so I will be using it’s Cloud Storage project id.

Cloud Storage Project ID

If everything is setup correctly, opening command prompt and typing “gsutil help” should produce result similar to the screenshot below:

gsutil help command output

3. PowerShell magic 🙂

Script below will fetch installs and ratings reports from Google Play for specific app for the current month. Will get the last date available in the install report, filter both install and rating reports by ‘last available date’ and strip the reports from rows / columns we are not interested in. It will iterate over the active installs and will try to match installs with the ratings.

Replace $bucketId and $appPackageName with your cloud storage bucket id and your app package name.

Reports fetched from Google Play and the final output will be saved in the current working directory.

Upon execution you will get a report for your app active users/ratings by Android version, based on the data for the last date available in the install report nicely opened in your default text editor.

Script output: Active users and ratings.

P.S: Enable column selection mode in Notepad++ to get only the numbers. Use the arrow keys or your mouse while holding Shift + Alt.

P.S1: More info on how to retrieve various reports with gsutil here.

Feel free to modify the script to suit your reporting needs 🙂

Recompile with -Xlint in Android studio

Staying out of deprecated methods is useful, so your app won’t run in some compatibility mode on the device. Plus having clean build output is also nice 🙂

While building an app, Gradle may produces some warnings telling you that some input files are using unchecked or unsafe operations or they are overriding a deprecated API.

Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

As the message suggest, we should recompile with -Xlint to get more details about the warnings.

In the app level build.gradle file

app level build.gradle file

we should add a section labelled  allprojects{}(if not already present).

In the allprojects{} section we will instruct Gradle to apply custom compiler arguments for each task involving  Java code compilation.

allprojects {
    tasks.withType(JavaCompile) {
        options.compilerArgs &lt;&lt; "-Xlint:unchecked" &lt;&lt; "-Xlint:deprecation"
    }
}

Now each time we build our app we will get detailed output of the unchecked or unsafe operations and the deprecated API we are using.

Tip: if for some reason we want to continue using a deprecated API and just suppress the warnings, we could annotate the deprecated method with the

@SuppressWarnings("deprecation")

annotation.

Android, Smartphone, Android Developer, Android Studio

Auto ‘versionCode’ increment when building production apk’s

Since I adopted Fabric as a way to monitor vital app stats such as ‘Time in App per User‘ and it’s Beta distribution platform to distribute test builds, increasing APK’s versionCode numbers became a tedious task.

I decided to simplify the things by letting Gradle to do auto versionCode increments when producing release APK’s

Our implementation of build number increments will consist of a property file named version.properties located in the root folder of our project.

The property file will contain 2 variables, one defining the version name such as “2.3” and one defining the version code such as 15

VERSION_NAME=2.3
VERSION_CODE=19

In our app module build.gradle file

build.gradle

we will define a function which takes care of retrieving the a bough mentioned values from the properties file and increment the VERSION_CODE if needed.

/**
 * Get's value from 'version.properties' file
 * @param varName the name of the variable which value we wan't to get.
 * @return the variable value.
 */
def getVersionPropertiesValue(def varName)
{
    def propertiesFile = file('version.properties')
 
    if(!propertiesFile.canRead()) {
        throw new GradleException("Could not read " + propertiesFile.name)
    }
 
    Properties properties = new Properties();
    properties.load(new FileInputStream(propertiesFile))
 
    def propertyValue = properties[varName]
    if(varName == 'VERSION_CODE')
    {
        // If we are building release increment the version code
        List gradleTasksNames = gradle.startParameter.getTaskNames();
        for(String taskName : gradleTasksNames)
        {
            if(taskName.contains("Release"))
            {
                propertyValue = propertyValue.toInteger() + 1
                properties[varName] = propertyValue.toString()
                properties.store(propertiesFile.newWriter(), null)
                break
            }
        }
    }
 
    return propertyValue
}

In the defaultConfig section of the gradle build script we will call this function to retrieve values for the versionName and versionCode of our app.

android {
    compileSdkVersion 28
 
    defaultConfig {
        applicationId "com.example.foo"
        versionCode Integer.valueOf(getVersionPropertiesValue('VERSION_CODE'))
        versionName getVersionPropertiesValue('VERSION_NAME')
        minSdkVersion 14
        targetSdkVersion 28
    }
}

Now each time a release build is made, the version code will increment automatically. If we want to change the version name we can do so by changing the value of VERSION_NAME property.

Settings.canDrawOverlays() allays returns ‘false’ on Android O

I was updating one of my clients app, and testing it how it behaves on Android O (API 26). The app requires permissions to draw over system windows (android.permission.SYSTEM_ALERT_WINDOW). On Android 6 (API 23) and up, you are obligated to request ‘special’ permissions while the app is running.

The ‘SYSTEM_ALERT_WINDOW’ permission is a special permission that breaks the rules set by the new permission model available on Android 6 (API 23) and up. It’s request involves calling Settings.canDrawOverlays() and if it returns ‘false‘ starting ‘Settings‘ where the user can choose to grant your app the ability to draw overlays or not.

if(Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.M)
{
   // On API 23 and later ask the user to grant us permission to draw system overlay
   // windows.
   if (!Settings.canDrawOverlays(this))
   {
      Intent intent = new Intent(
             Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
             Uri.parse("package:" + getPackageName()));
 
      startActivityForResult(intent, REQUEST_PERMISSION_SYSTEM_OVERLAY_RESULT);
   }
}

There is a bug with Settings.canDrawOverlays() (only) on API 26 where it will always return ‘false’ disregarding the actual user decision. The workaround provided here is a bit ugly, but does not involves restarting the app (which will be quite annoying for the user) after the permission is granted.

The code below first checks the result of System.canDrawOverlays() if it returns ‘true’ it continues with the rest of the application flow. If it returns ‘false’ a check if we are running on Android O (API 26) is performed. If that’s the case, we are calling our ‘workaround’ method.

The ‘workaround’ method tries to add an invisible overlay window on the screen, and if that’s OK we assume that we have a permission to draw overlays, else an exception is thrown.

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        super.onActivityResult(requestCode, resultCode, data);
 
        if(Build.VERSION.SDK_INT &lt; Build.VERSION_CODES.M) return; if(requestCode == REQUEST_PERMISSION_SYSTEM_OVERLAY_RESULT) { if(Settings.canDrawOverlays(this)) { m_permissionSystemOverlayWindowGranted = true; if(m_permissionReadPhoneStateGranted &amp;&amp; m_permissionProcessOutgoingCallsGranted) { startService(new Intent(this, EstatePlusService.class)); m_layoutNoPermissions.setVisibility(View.INVISIBLE); m_progressBar.setVisibility(View.INVISIBLE); m_layoutLogin.setVisibility(View.VISIBLE); } } else if(Build.VERSION.SDK_INT == Build.VERSION_CODES.O) { // NOTE: This is a workaround to fix the bug in Android O where the // Settings.canDrawOverlays() will always return 'false' if(canDrawOverlays(this)) { m_permissionSystemOverlayWindowGranted = true; if(m_permissionReadPhoneStateGranted &amp;&amp; m_permissionProcessOutgoingCallsGranted) { startService(new Intent(this, EstatePlusService.class)); m_layoutNoPermissions.setVisibility(View.INVISIBLE); m_progressBar.setVisibility(View.INVISIBLE); m_layoutLogin.setVisibility(View.VISIBLE); } } } } } /** * Workaround for Android O */ public static boolean canDrawOverlays(Context context) { try { WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); if (windowManager == null) { return false; } final View viewToAdd = new View(context); WindowManager.LayoutParams params = new WindowManager.LayoutParams( 0, 0, android.os.Build.VERSION.SDK_INT &gt;= android.os.Build.VERSION_CODES.O ?
                                    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
            viewToAdd.setLayoutParams(params);
            windowManager.addView(viewToAdd, params);
            windowManager.removeView(viewToAdd);
            return true;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
 
        return false;
    }
Eclipse, IDE, Java

Eclipse Hotkeys Fix

Eclipse on GNU/Linux has an annoying bug with hotkeys for users with multiply keyboard layouts.

For example when the user is working with the English keyboard layout, hotkeys such as “Ctrl-C”, “Ctrl-V” will work as expected, but when switched to different layout (ex: Bulgarian) the keys will no longer work.

After a bit of searching I found a fix for this inconvenience at GitHub.

Thank you “amozzhuhin“!