Implement In-app Update In Android

October 27, 2021 Publish By : EXPERT APP DEVS 9 min read Viewed By : 79
in-app update in android

In-app update API was Introduced as a part of the Play Core Library, which is useful for the prompt to the user about newer versions of the app available in the Google Play store.

There are two ways of an In-app update Implementation.

  • Flexible Update
  • Immediate Update

Flexible Update:

  • In the case of a Flexible update, the dialog will prompt after the update, the user can perform other operations with the application.
  • Flexible Update is useful when there are no major changes in the core functionality of your app like some extra features that don't affect the core functionality of your app.
  • The latest version of the app starts downloading in the background in Flexible Update and after the completion of the downloading of the new app, the user will see the dialog of the complete downloading in that case the user needs to restart the app. The dialog will not show automatically, we need to check the download status and show it to the user and we will learn how we can do it later.

Immediate Update:

  • In the case of Immediate Update mode, the fullscreen UI is visible on screen and the user can’t do any other task within the application and after downloading completion the application restarts automatically and starts updating the application itself.
  • Immediate Update Mode is used when your app update affects the main (core) functionality of your app, When you have any critical and major update about the main features, in that case, we can use this mode for updating the app.

Now we have an idea and knowledge of all modes of In-app updates. Now we are going to start implementing it in our app.

The very first step is to Add the Google Play Core Library to your android build.gradle file, as per below.

implementation 'com.google.android.play:core: X.X.X' 

Now we need to check for the update-

Before requesting an update, we need to check if a new version of the app is available or not and also the type of update. To check for the availability of updates you can use the AppUpdateManager.

val appUpdateManager: AppUpdateManager = AppUpdateManagerFactory.create(baseContext)
val appUpdateInfo = appUpdateManager.appUpdateInfo
// Creates instance of the manager.
val appUpdateManager = AppUpdateManagerFactory.create(context)
// Returns an object of Updatemanager that you use to check for an update.
val appUpdateInfoTask = appUpdateManager.appUpdateInfo


appUpdateInfo?.addOnSuccessListener { appUpdateInfo ->
    if ((appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE ||
            appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) &&
    appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)
) {
    btn_update.visibility = View.VISIBLE
    setUpdateAction(appUpdateManager, appUpdateInfo)
  }
}

Here we have added a button to display some UX experience. Whenever there will be a flexible new update available will show the update button on screen. We have an update available of flexible types, and we will start the update on clicking the update button.

private fun setUpdateAction(manager: AppUpdateManager?, info: Task<AppUpdateInfo>) {
    // Before starting an update process , add a listener for updates.
btn_update.setOnClickListener {
        installStateUpdatedListener = InstallStateUpdatedListener {
            btn_update.visibility = View.GONE
            // tvStatus is a textview to display the update status      
            tvStatus.visibility = View.VISIBLE
            when (it.installStatus()) {
                InstallStatus.FAILED, InstallStatus.UNKNOWN -> {
                    tvStatus.text = getString(R.string.info_failed)
                    btn_update.visibility = View.VISIBLE
                }
                InstallStatus.PENDING -> {
                    tvStatus.text = getString(R.string.info_pending)
                }
                InstallStatus.CANCELED -> {
                    tvStatus.text = getString(R.string.info_canceled)
                }
                InstallStatus.DOWNLOADING -> {
                    tvStatus.text = getString(R.string.info_downloading)
                }
                InstallStatus.DOWNLOADED -> {
                    tvStatus.text = getString(R.string.info_downloaded)
                    popupSnackbarForCompleteUpdate(manager)
                }
                InstallStatus.INSTALLING -> {
                    tvStatus.text = getString(R.string.info_installing)
                }
                InstallStatus.INSTALLED -> {
                    tvStatus.text = getString(R.string.info_installed)
                    manager?.unregisterListener(installStateUpdatedListener)
                }
                else -> {
                    tvStatus.text = getString(R.string.info_restart)
                }
            }
        }
        manager?.registerListener(installStateUpdatedListener)
        manager?.startUpdateFlowForResult(            info.result, AppUpdateType.FLEXIBLE,
            this, IN_APP_UPDATE_REQUEST_CODE
        )
    }
}

While doing an update, you can handle the update related status like failure or cancellation by using the onActivityResult() as per the below code snippet:

/* This code snippet is required to handle the result of the manager.startConfirmationDialogForResult request */

override fun onActivityResult(requestId: Int, resultId: Int, details: Intent?) {
    if (requestId == IN_APP_UPDATE_REQUEST_CODE) {
        if (resultId != RESULT_OK) {
            toastAndLog("Update flow failed! Result code: $resultId")
            // If the update process is cancelled or fails,
            // you can request again to start the update.
        }
    } else {
        super.onActivityResult(requestId, resultId, details)
    }
}

The following points describe the different state and value you may receive from the onActivityResult() callback:

  • RESULT_OK: The user has accepted the update. For immediate updates, you might not receive any callback because the update should already be done by Google Play Library by the time the control is given back to your app.
  • RESULT_CANCELED: The user has denied or canceled the update.
  • ActivityResult.RESULT_IN_APP_UPDATE_FAILED: Some other error prevented either the user from providing consent or the update to proceed.

Install a Flexible Update

If you want to monitor the flexible update states and you find the InstallStatus.DOWNLOADED state, you need to restart the app to install the latest update.
Unlike an immediate update, Google Play does not perform an app restart operation for you. Because, during a flexible update process, the user expects to keep using the app until they decide that they want to install the update.
So, it’s recommended that you provide the user some notification or UI indication to notify that an update is ready for installation and request user confirmation to restart the app.

Here we will add a snack bar to request for the user confirmation for restarting the app.

/* Displays snackbar as a notification and sets calls on snackbar action button. */
private fun popupSnackbarForCompleteUpdate(appUpdateManager: AppUpdateManager?) {
    Snackbar.make(
        findViewById(R.id.mainLayoutParent),
        "Ready to INSTALL.", Snackbar.LENGTH_INDEFINITE
    ).apply {
        setAction("RESTART") { appUpdateManager?.completeUpdate() }
        setActionTextColor(ContextCompat.getColor(applicationContext, R.color.colorAccent))
        show()
    }
}

When trigger an appUpdateManager?.completeUpdate() method in the foreground, Platform displays a full-screen UI which restarts the app in the background. After the platform installs the update, the app restarts into its home activity.
If you call appUpdateManager?.completeUpdate() instead when the app is in the background, the update is installed silently without interrupting the user and the app UI.

// However, you need to execute this check at all app entry points.
override fun onResume() {
    super.onResume()

    appUpdateManager.appUpdateInfo
        .addOnSuccessListener {
            if (it.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE || UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
                updateManager.startUpdateFlowForResult(
                    it,
                    IMMEDIATE,
                    this,
                    IN_APP_UPDATE_REQUEST_CODE)
            }
        }
    }
}

Handle an immediate update

If you are using and performing an immediate update, and the user consents to install the update, Google Play displays the update progress and other details on top of your app’s Screen across the entire duration of the update process. In between the update process, if the user closes or terminates your app, the update should go in the background and continue to download and install in the background without any kind of user confirmation.

appUpdateInfo?.addOnSuccessListener { appUpdateInfo ->
    if ((appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE ||
            appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) &&
    appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
) {
    updateManager.startUpdateFlowForResult(it, IMMEDIATE, this,  IN_APP_UPDATE_REQUEST_CODE)
  }
}

To check how to test In-app updates please refer: https://developer.android.com/guide/playcore/in-app-updates

Conclusion

In this blog, we’ve learned about the new way of updating your app that is provided by the play core library from google. You can also check other features of the play core library like priority setting in your update process.

Happy Coding!!
 

Need a consultation?

Drop us a line! We are here to answer your questions 24/7.