Google In-App Update: Keep your users up-to-date

Gaurang Goda
Yudiz Solutions
Published in
8 min readMay 4, 2020

Overview :

Hello visitors, greetings for the day! We, Android developers, use lengthy workarounds to enable force-updates in our app. For instance, we use Firebase’s remote config to keep track of the latest version on the Play Store. This induces extra burden on the developers as they have to manually update the version on the firebase whenever they update the app. This is error-prone and to be honest, is a bad UX for users.
To make this process easy and user-engaging, Google has introduced the In-App Update which will help keep the app up-to-date, providing great control to developers. Today, we are going to discuss it. Let’s begin, It’s going to be a cakewalk, trust me!

Topics:

Synopsis

Prerequisite & Limitation

Types Of In-App Updates

Let’s Dive Into Practical

Important Links

TL;DR

Synopsis

Each fresh update concerning the application comes with a new feature or bug fixes or any vital improvements. To give the best user experience, it is a duty upon the developer to assist all the users to stay up to date. Some of the users have enabled background update option which auto-download the latest update of the applications in the device but for others which are still using an old application that users need to get notified manually while they are using the application. The play core library comes with new In-app updates that give the user a prompt if the update of the app is available.

Prerequisite & Limitation

There are some pre-requisite and limitations of Google-In-App Play core Library which we should keep in mind before starting the project:

  • Google-In-App update will work on the device which is running Android 5.0 (API level 21) or above.
  • Google-In-App update will require the use of Play Core library version 1.5.0 or higher.
  • Google-In-App update isn’t compatible with the apps that use the APK expansion files(.obb files).
  • If the application has been uploaded as an Android App Bundle(.aab) file format then the maximum allowed compressed size is 150 MB for In-App update support.

Types Of In-App Updates

The Google In-App update can be categorized into Two Types :
1. Flexible:

As the name suggests, this type will be flexible with the user. The user has to decide whether to update the app or to postpone it. In this feature, the file will be downloaded in the background so the user can continue to use the app. The whole download process is managed internally by the Play Core library.
We can listen when the files have been downloaded and notify the user to install the downloaded app. After the update of the application is downloaded we can install and restart the app which is gracefully managed by In-App update.

2. Immediate:

This can be used as a force-update process. There will be a full-screen UI that will inform the user about the update and on clicking update, it will manage the complete process all by itself which includes downloading, installing, and restarting the app. The close dialog or update canceled event can be captured by the developer and he can perform the actions as required.

Let’s Dive Into Practical

Including Library To Your Existing Project:

Open build.gradle file inside the application module and add the latest version of Google Play Core Library.

dependencies {
// play core lib for app update
implementation ‘com.google.android.play:core:1.6.4’
}

1. Flexible Update

credits: google

Check For Update Availability:

We need to initialize the values and check for the update if available. If the update is available we should prompt the flexible update dialog.

private var inAppUpdateManager: AppUpdateManager? = null
private var FLEXIBLE_UPDATE_REQUEST = 1001
private fun setUpInAppUpdate() {
// Creates an instance of the manager.
inAppUpdateManager = AppUpdateManagerFactory.create(this)
// Returns an intent object that you use to check for an update.
val appUpdateInfoIntent = inAppUpdateManager?.appUpdateInfo
// Checks that the platform will allow the specified type of update.
appUpdateInfoIntent?.addOnSuccessListener { appUpdateInfo ->
// checks if the update is available or not
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)
) {
inAppUpdateManager?.startUpdateFlowForResult(
appUpdateInfo, AppUpdateType.FLEXIBLE,
this, FLEXIBLE_UPDATE_REQUEST)
}
}
inAppUpdateManager?.registerListener(this)
}
  • inAppUpdateManager is the instance of AppUpdateManager.
  • appUpdateInfoIntent is the instance appUpdateInfo that helps us to check if an update is available.
  • addOnSuccessListener will be called if the update is available.
  • After checking the condition for Flexible update allowed we can call startUpdateFlowForResult for Update prompt.
  • AppUpdateType.FLEXIBLE is used to open a Flexible In-App update type of Prompt.

The update of the app is handled by the Library in the background. We just need to handle the callback inside the OnActivityResult Method.

Handle OnActivity Result

After opening the FLEXIBLE Prompt we need to handle and notify as per their actions. It can be handled inside the OnActivityResult method.

We can handle responses with the help of requestCode, responseCode and Intent data.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == FLEXIBLE_UPDATE_REQUEST) {
if (resultCode == RESULT_OK) {
Toast.makeText(
applicationContext,
“The update is started in background”,
Toast.LENGTH_SHORT
).show()
} else {
// canceled by the user or failed, It should be handled as per as requirement
}
}
}
//this event should be handled as per as requirement
}
}
}
  • requestCode == FLEXIBLE_UPDATE_REQUEST helps us to identify the requestcode.
  • We’ll receive RESULT_OK if the user has clicked the update button.
  • We’ll receive RESULT_CANCELED if the user cancels the update.

Implementing Status Update Listeners
After clicking the update button, Google play will begin to download an update in the background. In Flexible update, Google Play does not restart the app for you after it’s downloaded. We will need to monitor the update states so that we can install the update when it is downloaded.

It is recommended to give notification to the user informing the update is ready to install. The installation and restart process should be executed at the request of the user.

The inAppUpdateManager?.registerListener(this) is being called to register onStateUpdare listener. It is declared above in the setUpInAppUpdate method.
We need to implement InstallStateUpdatedListener in the class.

override fun onStateUpdate(state: InstallState?) {
if (state?.installStatus() == InstallStatus.DOWNLOADED) {
// invokes when the app has been downloaded and still the app is in foreground.
updateCompletedNotificationSnack()
}
}
/* Displays the snackbar notification and call to action. */
private fun updateCompletedNotificationSnack() {
Snackbar.make(
main,
getString(R.string.update_complete_message),
Snackbar.LENGTH_INDEFINITE
).apply {
setAction(getString(R.string.install_update)) {
inAppUpdateManager?.completeUpdate() //it will install downloaded build
}
setActionTextColor(Color.WHITE)
show()
}
}
override fun onDestroy() {
super.onDestroy()
//unregister the listener when stateUpdate is no longer needed,
inAppUpdateManager?.unregisterListener(this)
}

We can monitor different states as per our requirement. Currently, we have InstallStatus.DOWNLOADED to observe the downloaded state of the update.

When we call inAppUpdateManager?.completeUpdate() in the foreground, the platform will display a Full-Screen UI. After the installation is completed the app restarts.

When we call inAppUpdateManager?.completeUpdate() in the background, the update is installed silently without blocking the device UI.

Every time the app comes into the foreground state, it is recommended to check your app doesn’t have an update waiting to be installed. If the app has an update in the InstallStatus.DOWNLOADED state, show the notification to request the user for installing the update otherwise the data continues to occupy the user’s device storage. The install status needs to be checked at every possible entry point.

override fun onResume() {
super.onResume()
inAppUpdateManager?.appUpdateInfo?.addOnSuccessListener
{ appUpdateInfo ->
// If the update is downloaded but not installed, notify the user to complete the update.
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED)
updateCompletedNotificationSnack()
}
}

1. Immediate Update

credits: google

Check For Update Availability
This step will be nearly the same as Flexible Update. There is only a change in the request type of AppUpdateType. AppUpdateType.IMMEDIATE.

private fun setInAppUpdateListeners() {
inAppUpdateManager = AppUpdateManagerFactory.create(this)

// Returns an intent object that you use to check for an update.
val appUpdateInfoIntent = inAppUpdateManager?.appUpdateInfo
// Checks that the platform will allow the specified type of update.
appUpdateInfoIntent?.addOnSuccessListener { appUpdateInfo ->

// checks if the update is available or not
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.isUpdateTypeAllowed(IMMEDIATE))
{
inAppUpdateManager?.startUpdateFlowForResult(
appUpdateInfo, IMMEDIATE,
this, IN_APP_UPDATE_REQUEST)
}
}
}

Handling Immediate Update:
onActivityResult
method is used to handle the cancel event of the user.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == IN_APP_UPDATE_REQUEST) {
if (resultCode != RESULT_OK) {
finish()
// update dialog is cancelled by user or fails, it should be handled as per as requirement, you can close the app or you can request to start the update again.
}
}
}

During the immediate update, if the user closes or terminates your app, the update should be downloaded in the background without any additional user confirmation. However, whenever the app returns to the foreground state, you need to confirm that the update is not held off in the UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS state. If the update is holding off in this state, the update should be resumed, as shown below:

override fun onResume() {
super.onResume()
inAppUpdateManager?.appUpdateInfo?.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS)
{
// If an in-app update is already running, resume the update.

inAppUpdateManager?.startUpdateFlowForResult(
appUpdateInfo, IMMEDIATE, this, IN_APP_UPDATE_REQUEST)
}
}
}

That’s it. We have successfully implemented Flexible and Immediate updates on our app. Currently, we can only use any one type of Update in an application. If you want to use both at the same time you need to create an API for Immediate or Flexible.

Important Link:

Here are some useful links that you might want to look into for more information

TL;DR

Google provides us a very handy feature to manipulate app-updates and ask/force users to update the app based on the requirements. Along with providing a user-friendly UX, this saves us from workarounds and boilerplate code. Few OS-legacy limitations should be kept in mind while implementing it.

That’s all for now. I hope you enjoyed reading the article. Have a great, updated day ahead! ;)

--

--