QtAndroidTools: ApkExpansionFiles

ApkExpansionFiles help in manage the apk expansion files download.

QtAndroidTools is a library that allows to simplify access to some native Android features from QML.

QtAndroidTools library source here. The project provide a demo app showing the use of all features.

Apk upload size limit is currently 100MB but in case your app require more data the Android Console allow to "attach" two files connected to your app. Official documentation about this feature is here, is strongly suggested to read it for allow a better comprension about how to use this tool. Usually the expansion files are downloaded by the Google Play together with the main app but in case the user delete it or some other problem occurs and the file will not be available anymore the app have to download again usually at startup. In this phase is required the java code of the two libraries explained in the documentation, the Google Play Licensing Library and the Google Play APK Expansion Library. However, at the time this post was written, there are a couple of problems. The official documentation suggest to get these libraries through the Android SDK Manager and then create two modules using the libraries just obtained. The problem here is the code of there libraries is very very old and use some deprecated dependencies making difficult the compilation using Qt Creator. It exist an updated version of these two libraries always developed by Google here and here. However also these two latest libraries are not supported by a long time and doesn't work very well in the latest versions of Android, from 8 and above. For this reasons this tool implement the two updated version community supported better-apk-expansion and better-licensing that make the same job but are updated enough for support the new notify feature of Android 8. I only made a small modify for support the Qt multi language engine. In the version inside the tool the java library ask to the C++ level for the info text to use in the Android notify label allowing the Qt language selector to select the correct language string.

Remember for have the download library working as expected you have to set the following permission in your app:

<manifest ...>
    <!-- Required to access Google Play Licensing -->
    <uses-permission android:name="com.android.vending.CHECK_LICENSE" />

    <!-- Required to download files from Google Play -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- Required to keep CPU alive while downloading files
        (NOT to keep screen awake) -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <!-- Required to poll the state of the network connection
        and respond to changes -->
        android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!-- Required to check whether Wi-Fi is enabled -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

    <!-- Required to read and write the expansion files on shared storage -->
        android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Also note, in case your Android app is target for version 23 or above you have to explicitly ask for the WRITE_EXTERNAL_STORAGE permission cause this is one of the "dangerous" permission. You can use the other AppPermissions tool available in this library. Also you have to correctly the various part of the library by addicting these lines inside your AndroidManifest.xml file:

<service android:name="com.google.android.vending.expansion.downloader.impl.DownloaderService" android:enabled="true"/>
<receiver android:name="com.google.android.vending.expansion.downloader.impl.DownloaderService$AlarmReceiver" android:enabled="true"/>

Please note from now it's assumed you have read the documentation and already know the type and the format of expansion files is possible to upload and how the licensing engine work. First initialization is the following:

QtAndroidApkExpansionFiles.base64PublicKey = "app_public_key_here";
QtAndroidApkExpansionFiles.salt = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
QtAndroidApkExpansionFiles.main.version = 1;
QtAndroidApkExpansionFiles.main.size = 123456789;
QtAndroidApkExpansionFiles.patch.version = 1;
QtAndroidApkExpansionFiles.patch.size = 123456789;

The base64PublicKey can be found in the Android Console app info and the salt is a random array of 20 bytes you have to generate by yourself. This is required for encrypt your data stored of the server. Version and size is required for identify your main and patch file. You can upload only the main file (and then you have to set only the main fields), these info are used to check if the current expansion files eventually already present in the device correspond in term of version number and file size.

Once configured the basic info you can check the operation to make by calling the following function:


If this function will return:


mean your expansion file are already available in the device than there is nothing to do, you can proceed with the app execution. In case of return:


mean the apk expansion files download is started than you have to manage the download progress info. A download notify will be showed in the system status bar but your app should also show a progress control to inform the user regarding the operation in progress and that he have to wait. All other return codes are basically error conditions, check the library sources to see all the possible error types.

During download phase the tool generate two signals updating you about current status as follow:

Connections {
    target: QtAndroidApkExpansionFiles
    onDownloadStateChanged: {
            case QtAndroidApkExpansionFiles.STATE_COMPLETED:
            case QtAndroidApkExpansionFiles.STATE_FAILED_UNLICENSED:
            case QtAndroidApkExpansionFiles.STATE_FAILED_FETCHING_URL:
            case QtAndroidApkExpansionFiles.STATE_FAILED_SDCARD_FULL:
            case QtAndroidApkExpansionFiles.STATE_FAILED_CANCELED:
            case QtAndroidApkExpansionFiles.STATE_FAILED:
    onDownloadProgress: {
        var time = new Date(timeRemaining);
        downloadProgressBar.to = overallTotal;
        downloadProgressBar.value = overallProgress;
        downloadSizeLabel.text = (overallProgress / (1024*1024)).toFixed(2) + "MB/" + (overallTotal / (1024*1024)).toFixed(2) + "MB";
        downloadTimeLabel.text = "Time left: ";
        if(timeRemaining > 1000 * 60 * 60)
            downloadTimeLabel.text += ("0" + time.getHours()).substr(-2) + ":" + ("0" + time.getMinutes()).substr(-2);
            downloadTimeLabel.text += ("0" + time.getMinutes()).substr(-2) + ":" + ("0" + time.getSeconds()).substr(-2);

onDownloadStateChanged have the newState param informing about the download state changed. Check the sources for all the possible values. onDownloadProgress report info about the download process like current size and time left. Once download completed or if your expansion files are already available is possible to retrieve the full path of both files by use the following functions:


During download phase always is possible to abort, stop and restart download by using the functions below:


A specific note regarding the function getString(). It return an info string connected with the current download status and some other info. It's used internally to pass correct language string to the java library but you can use also for the main interface as in demo example.


Popular posts from this blog

Access GPIO from Linux user space

Launch an app from Android shell terminal

Android: adb push and read-only file system error