Search code examples
androidauto-updatein-app-updategoogle-play-core

How to work with Android's in-app update API?


I recently came across a new kind of app update flow which is provided by Google Play API. I liked this seamless flow to update an Android application. I observed the below mentioned steps in the Hotstar app.

  1. A card popped up from the bottom showing update is available
  2. When I clicked on "Update Hotstar" button, one dialog popped up (seems like it is provided by Google Play)

enter image description here

  1. Downloading was started in the background while the app was running
  2. After completion of the download, one SnackBar popped up showing app ready to install
  3. App restarted after the installation

enter image description here

How can I achieve this? There must be a way to communicate with Google Play. I went through many blogs. But didn't find any solution. This could be an awesome feature for a developer if the auto app update is disabled by the user.


Solution

  • Step 1: Add dependency (build.gradle (app)):

    dependencies {
    
        implementation 'com.google.android.play:core:1.7.3'
        ...
    }
    

    Step 2: Check for update availability and start if it's available

    private AppUpdateManager mAppUpdateManager;
    private static final int RC_APP_UPDATE = 11;
    

    In onStart() method:

    mAppUpdateManager = AppUpdateManagerFactory.create(this);
    
    mAppUpdateManager.registerListener(installStateUpdatedListener);
    
    mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
    
            if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
                    && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE /*AppUpdateType.IMMEDIATE*/)){
    
                try {    
                        mAppUpdateManager.startUpdateFlowForResult(
                                appUpdateInfo, AppUpdateType.FLEXIBLE /*AppUpdateType.IMMEDIATE*/, MainActivity.this, RC_APP_UPDATE);
    
                } catch (IntentSender.SendIntentException e) {
                    e.printStackTrace();
                }
    
            } else if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED){
                //CHECK THIS if AppUpdateType.FLEXIBLE, otherwise you can skip
                popupSnackbarForCompleteUpdate();
            } else {
                Log.e(TAG, "checkForAppUpdateAvailability: something else");
            }
        });
    

    Step 3: Listen to update state

    InstallStateUpdatedListener installStateUpdatedListener = new 
      InstallStateUpdatedListener() {
        @Override
        public void onStateUpdate(InstallState state) {
            if (state.installStatus() == InstallStatus.DOWNLOADED){
                //CHECK THIS if AppUpdateType.FLEXIBLE, otherwise you can skip
                popupSnackbarForCompleteUpdate();
            } else if (state.installStatus() == InstallStatus.INSTALLED){
                if (mAppUpdateManager != null){
              mAppUpdateManager.unregisterListener(installStateUpdatedListener);
                }
    
            } else {
                Log.i(TAG, "InstallStateUpdatedListener: state: " + state.installStatus());
            }
        }
    };
    

    Step 4: Get a callback for update status

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
    
        if (requestCode == RC_APP_UPDATE) {
            if (resultCode != RESULT_OK) {
                Log.e(TAG, "onActivityResult: app download failed");
            }
        }
    }
    

    Step 5: Flexible update

    private void popupSnackbarForCompleteUpdate() {
    
        Snackbar snackbar =
                Snackbar.make(
                        findViewById(R.id.coordinatorLayout_main),
                        "New app is ready!",
                        Snackbar.LENGTH_INDEFINITE);
    
        snackbar.setAction("Install", view -> {
            if (mAppUpdateManager != null){
                mAppUpdateManager.completeUpdate();
            }
        });
    
        
    snackbar.setActionTextColor(getResources().getColor(R.color.install_color));
        snackbar.show();
    }
    

    Step 6: Don't forget to unregister listener (in onStop method)

    if (mAppUpdateManager != null) {
         mAppUpdateManager.unregisterListener(installStateUpdatedListener);
    }
    

    Note: Add this listener in any one activity in your app preferably in MainActivity (Home page)

    For testing, you can use FakeAppUpdateManager

    https://developer.android.com/reference/com/google/android/play/core/appupdate/testing/FakeAppUpdateManager.html

    Constraint: In-app update works only with devices running Android 5.0 (API level 21) or higher

    Official Documentation: https://developer.android.com/guide/playcore/in-app-updates