Prompt Android App User to Update App if current version <> market version
Asked Answered
Q

7

32

Lets say my Android App version 0.1 is installed currently on the User's phone. Everytime they launch my App I want to check if there is a different version available in the Android Market let's say this version is 0.2. If there is a mismatch between these two version I want to show a dialog box prompting the user to Upgrade the App.

I totally understand there exists a notification procedure from Android Market itself to the users but as far as my Analytics data is concerned it is not very effective in reminding users to upgrade to the new version of the App.

Any insight would be very helpful. Thanks StackOverflowers, you guys rock!

Quake answered 5/4, 2011 at 16:22 Comment(3)
Duplicates: #3058271 and #2176445Suzettesuzi
Neither of those posts answer this question.Styles
#19244805Henry
B
58

As of 2019 the best way for updating your app is to use In-app updates provided by Play Core library (1.5.0+). It works for Lollipop and newer, but let's be fair, Kit-Kat is less than 7% as of today and soon will be gone forever. You can safely run this code on Kit-Kat without version checks, it won't crash.

Official documentation: https://developer.android.com/guide/app-bundle/in-app-updates

There are two types of In-app updates: Flexible and Immediate

Flexible will ask you nicely in a dialog window: enter image description here

whereas Immediate will require you to update the app in order to continue using it with full-screen message (this page can be dismissed):

enter image description here

Important: for now, you can't choose which type of update to roll out in your App Release section on Developer Play Console. But apparently, they will give us that option soon. From what I've tested, currently, we're getting both types available in onSuccessListener.

So let's implement both types in our code.

In module build.gradle add the following dependency:

dependencies {
    implementation 'com.google.android.play:core:1.6.1'//for new version updater
}

In MainActivity.class:

private static final int REQ_CODE_VERSION_UPDATE = 530;
private AppUpdateManager appUpdateManager;
private InstallStateUpdatedListener installStateUpdatedListener;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

   checkForAppUpdate();
}

@Override
protected void onResume() {
    super.onResume();
    checkNewAppVersionState();
}

@Override
public void onActivityResult(int requestCode, final int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);

    switch (requestCode) {

        case REQ_CODE_VERSION_UPDATE:
            if (resultCode != RESULT_OK) { //RESULT_OK / RESULT_CANCELED / RESULT_IN_APP_UPDATE_FAILED
                L.d("Update flow failed! Result code: " + resultCode);
                // If the update is cancelled or fails,
                // you can request to start the update again.
                unregisterInstallStateUpdListener();
            }

            break;
    }
}

@Override
protected void onDestroy() {
    unregisterInstallStateUpdListener();
    super.onDestroy();
}


private void checkForAppUpdate() {
    // Creates instance of the manager.
    appUpdateManager = AppUpdateManagerFactory.create(AppCustom.getAppContext());

    // Returns an intent object that you use to check for an update.
    Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();

    // Create a listener to track request state updates.
    installStateUpdatedListener = new InstallStateUpdatedListener() {
        @Override
        public void onStateUpdate(InstallState installState) {
            // Show module progress, log state, or install the update.
            if (installState.installStatus() == InstallStatus.DOWNLOADED)
                // After the update is downloaded, show a notification
                // and request user confirmation to restart the app.
                popupSnackbarForCompleteUpdateAndUnregister();
        }
    };

    // Checks that the platform will allow the specified type of update.
    appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
        if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
            // Request the update.
            if (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {

                // Before starting an update, register a listener for updates.
                appUpdateManager.registerListener(installStateUpdatedListener);
                // Start an update.
                startAppUpdateFlexible(appUpdateInfo);
            } else if (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) ) {
                // Start an update.
                startAppUpdateImmediate(appUpdateInfo);
            }
        }
    });
}

private void startAppUpdateImmediate(AppUpdateInfo appUpdateInfo) {
    try {
        appUpdateManager.startUpdateFlowForResult(
                appUpdateInfo,
                AppUpdateType.IMMEDIATE,
                // The current activity making the update request.
                this,
                // Include a request code to later monitor this update request.
                MainActivity.REQ_CODE_VERSION_UPDATE);
    } catch (IntentSender.SendIntentException e) {
        e.printStackTrace();
    }
}

private void startAppUpdateFlexible(AppUpdateInfo appUpdateInfo) {
    try {
        appUpdateManager.startUpdateFlowForResult(
                appUpdateInfo,
                AppUpdateType.FLEXIBLE,
                // The current activity making the update request.
                this,
                // Include a request code to later monitor this update request.
                MainActivity.REQ_CODE_VERSION_UPDATE);
    } catch (IntentSender.SendIntentException e) {
        e.printStackTrace();
        unregisterInstallStateUpdListener();
    }
}

/**
 * Displays the snackbar notification and call to action.
 * Needed only for Flexible app update
 */
private void popupSnackbarForCompleteUpdateAndUnregister() {
    Snackbar snackbar =
            Snackbar.make(drawerLayout, getString(R.string.update_downloaded), Snackbar.LENGTH_INDEFINITE);
    snackbar.setAction(R.string.restart, new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            appUpdateManager.completeUpdate();
        }
    });
    snackbar.setActionTextColor(getResources().getColor(R.color.action_color));
    snackbar.show();

    unregisterInstallStateUpdListener();
}

/**
 * Checks that the update is not stalled during 'onResume()'.
 * However, you should execute this check at all app entry points.
 */
private void checkNewAppVersionState() {
    appUpdateManager
            .getAppUpdateInfo()
            .addOnSuccessListener(
                    appUpdateInfo -> {
                        //FLEXIBLE:
                        // If the update is downloaded but not installed,
                        // notify the user to complete the update.
                        if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                            popupSnackbarForCompleteUpdateAndUnregister();
                        }

                        //IMMEDIATE:
                        if (appUpdateInfo.updateAvailability()
                                == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
                            // If an in-app update is already running, resume the update.
                            startAppUpdateImmediate(appUpdateInfo);
                        }
                    });

}

/**
 * Needed only for FLEXIBLE update
 */
private void unregisterInstallStateUpdListener() {
    if (appUpdateManager != null && installStateUpdatedListener != null)
        appUpdateManager.unregisterListener(installStateUpdatedListener);
}

And we're done!

Testing. Please read the docs so you will know how to test it properly with test tracks on Google Play.

Long story short:

  1. Sign your app with the release certificate and upload it to the one of publishing tracks in Developer Play Console under App Releases (alpha/beta/other custom closed track).

  2. In your release track page in the Manage Testers section create and add a list of testers and make sure you checked the checkbox! - this step is optional since your developer account email is also a testers account and you can use it for testing.

  3. Under the list of testers you will find "Opt-in URL" - copy this url and give it to your testers or open it yourself. Go to that page and accept proposition for testing. There will be a link to the app. (You won't be able to search for the app in Play Store so bookmark it)

  4. Install the app on your device by that link.

  5. In build.gradle increment the version of defaultConfig { versionCode k+1 } and build another signed apk Build > Generate Signed Bundle / APK... and upload it to your publishing track.

  6. Wait for... 1 hour? 2 hours? or more before it will be published on the track.

  7. CLEAR THE CACHE of Play Store app on your device. The problem is that Play app caches details about installed apps and their available updates so you need to clear the cache. In order to do that take two steps:

7.1. Go to Settings > App > Google PLay Store > Storage > Clear Cache.

7.2. Open the Play Store app > open main menu > My apps & games > and there you should see that your app has a new update.

If you don't see it make sure that your new update is already released on the track (go to your bookmarked page and use it to open your apps listing on the Play Store to see what version is shown there). Also, when your update will be live you'll see a notification on the top right of your Developer Play Console (a bell icon will have a red dot).

Hope it helps.

Bridge answered 28/6, 2019 at 14:12 Comment(10)
Yeah but this in app update feature too will have to be implemented as an update to an existing app. If the user has not updated the app earlier, why would he do it now? User will be prompted for update only when he first updates to the version that contains in app update API.Malodorous
@ShahoodulHassan "thanks" to new Google Policies for Google Play you would need to update all your apps anyway soon to target the latest versions. So just add this code in your next update. P.S. The only alternative would be to remotely shoot a notification with a reminder to update the app and do it manually on every update.Bridge
@KirillKarmazin Hi, how to achieve this in react native mainactivity.java file?Seminary
@Seminary sorry, can't help you with react nativeBridge
What if the user cicks the exit button in the immediate one ? I don't see any differente to the flexible one but the UI. I want to provide my users both types depending on their app versionGaillardia
@kirill or anyone else, do you know what determines the response to appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)? Does google decides that? Or can we configure that ourselves in the Play Store?Typhogenic
what is AppCustom ?Cucumber
@Kirill Karmazin, I had tried above solution, it is not working properly, want to implement Flexible update, above solution doesn't show any dialog box or ask user to update, i am using KotlinPrier
Is this solution works if the app published as .apk not as a bundle? Because in the official site this feature is a sub of app bundle. But they didn't mention any relation between app bundle and the new app update API.Valentine
it's it possible to display my custom dialog instead of default dialog?Deedee
I
15

The Android Market is a closed system and has only an unofficial api that might break at any point of time.

Your best bet is simply to host a file(xml, json or simple text) on a web server of yours in which you just have to update the current version of your app when you post it on the Market.

Your app will then only have to fetch that file at startup, checks wether currently installed app has a lower version number and displays a dialog to warn the user he is lagging.

Infantine answered 5/4, 2011 at 16:45 Comment(3)
this is pretty much what I thought about..just wanted to check if there is an API out there that Google already provides to check this when required.Quake
The problem here is with google play store cache. sometimes you might do a release and update the version in the json but the play store cache still points to the older apk. Until you clear cache in play store app settings its going to be unusableAggrieved
not to be regarded as a valid answer any moreStatolith
M
5

Another option you can use, if you want to avoid having your backend server to store your current app version like it's suggested in the accepted answer, is to use Google Tag Manager (GTM).

If you're already using the Google Analytics SDK, you have the GTM in it also.

In GTM you can define a value in the container for your app that specifies your latest released version. For example:

{
   "latestAppVersion": 14,
   ...
}

Then you can query that value when your app starts and show the user update dialog reminder if there's a newer version.

Container container = TagManager.getInstance(context).openContainer(myContainerId);
long latestVersionCode = container.getLong("latestAppVersion");

// get currently running app version code
PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
long versionCode = pInfo.versionCode;

// check if update is needed
if(versionCode < latestVersionCode) {
   // remind user to update his version
}
Mirandamire answered 28/2, 2014 at 22:47 Comment(2)
Did you use a tag or a macro? If macro did you use value-collections? Because I can't seem to make it work.Chrystal
I use macro value-collection, here is exactly copy-paste from the collection: { "latestAppVersion": 30 }Mirandamire
S
4

Take a look at this library that you can use to query the Android Market API

http://code.google.com/p/android-market-api/

Styles answered 5/4, 2011 at 16:39 Comment(1)
I see that this probably works but is not an "official" API from Google which means it could lose support or break anytime. Am I right in inferring that?Quake
I
4

You can use this Android Library: https://github.com/danielemaddaluno/Android-Update-Checker. It aims to provide a reusable instrument to check asynchronously if exists any newer released update of your app on the Store. It is based on the use of Jsoup (http://jsoup.org/) to test if a new update really exists parsing the app page on the Google Play Store:

private boolean web_update(){
    try {       
        String curVersion = applicationContext.getPackageManager().getPackageInfo(package_name, 0).versionName; 
        String newVersion = curVersion;
        newVersion = Jsoup.connect("https://play.google.com/store/apps/details?id=" + package_name + "&hl=en")
                .timeout(30000)
                .userAgent("Mozilla/5.0 (Windows; U; WindowsNT 5.1; en-US; rv1.8.1.6) Gecko/20070725 Firefox/2.0.0.6")
                .referrer("http://www.google.com")
                .get()
                .select("div[itemprop=softwareVersion]")
                .first()
                .ownText();
        return (value(curVersion) < value(newVersion)) ? true : false;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

And as "value" function the following (works if values are beetween 0-99):

private long value(String string) {
    string = string.trim();
    if( string.contains( "." )){ 
        final int index = string.lastIndexOf( "." );
        return value( string.substring( 0, index ))* 100 + value( string.substring( index + 1 )); 
    }
    else {
        return Long.valueOf( string ); 
    }
}

If you want only to verify a mismatch beetween versions, you can change:

"value(curVersion) < value(newVersion)" with "value(curVersion) != value(newVersion)"

Interlace answered 19/1, 2014 at 17:41 Comment(2)
Isn't parsing the value from App store an overkill here? I would suggest one of the methods listed above -- simply store the key on the server and fetch it through an API call.Unload
the goal is to check the latest version that is currently available on the store (as publishing an app may take a while to appears). however, the whole html is some 350KB or more (that's why @Nitin said "overkill"). If anyone prefer this as solution, make sure your app check only when WiFi connection is available.Paniculate
S
0

For prompting Android App User to Update App if current version is not equal to market version, you should first check the app version on the market and compare it with the version of the app on the device. If they are different, it may be an update available. In this post I wrote down the code for getting the current version of market and current version on the device and compare them together. I also showed how to show the update dialog and redirect the user to the update page. Please visit this link: https://mcmap.net/q/151735/-android-in-app-version-check

Saddlebag answered 13/2, 2016 at 19:21 Comment(0)
A
0

My working Kotlin code for force App update:

const val FLEXIABLE_UPADTE: Int = 101
const val FORCE_UPDATE: Int = 102
const val APP_UPDATE_CODE: Int = 500

override fun onCreate {
    // Get updateType from Webservice.
    updateApp(updateType)
}

private fun updateApp(statusCode: Int) {
    appUpdateManager = AppUpdateManagerFactory.create(this @MainActivity)

    val appUpdateInfoTask = appUpdateManager ? .appUpdateInfo

    appUpdateInfoTask ? .addOnSuccessListener {
        appUpdateInfo - >
            if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
                if ((statusCode == Constants.FORCE_UPDATE))
                    appUpdateManager ? .startUpdateFlowForResult(
                        appUpdateInfo, AppUpdateType.IMMEDIATE, this, Constants.APP_UPDATE_CODE
                    )
                else if (statusCode == Constants.FLEXIABLE_UPADTE)
                    appUpdateManager ? .startUpdateFlowForResult(
                        appUpdateInfo, AppUpdateType.FLEXIBLE, this, Constants.FLEXIABLE_UPADTE
                    )
            }
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent ? ) {

    try {
        if (requestCode == Constants.APP_UPDATE_CODE && resultCode == Activity.RESULT_OK) {
            if (resultCode != RESULT_OK) {
                appUpdateCompleted()
            }
        }
    } catch (e: java.lang.Exception) {

    }
}

private fun appUpdateCompleted() {
    Snackbar.make(
        findViewById(R.id.activity_main_layout),
        "An update has just been downloaded.",
        Snackbar.LENGTH_INDEFINITE
    ).apply {
        setAction("RESTART") {
            appUpdateManager.completeUpdate()
        }
        setActionTextColor(resources.getColor(R.color.snackbar_action_text_color))
        show()
    }

}
Amazed answered 4/2, 2020 at 12:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.