Blog Infos
Author
Published
Topics
,
Published
keep your app users up to date on their devices

Users may test out new features, have access to speed enhancements, and take advantage of bug fixes when they keep your software updated on their devices. Although some users choose to enable background updates when using an unmetered connection, other users might need a reminder to do so.

Active users are prompted to upgrade your app using the in-app updates functionality in Google Play Core libraries.

Devices running Android 5.0 (API level 21) or higher are compatible with the in-app updates feature.

Here are two methods for displaying updates within your app:

Flexible

If the user wants to update the app, a popup window will ask them. Both acceptance and denial are options. If they agree, the update will start downloading behind the scenes. When your update offers a few modest UI tweaks or performance upgrades, utilize this.

Photo on In-app updates

Immediate

The user must update the app in order to use this full-screen UX indefinitely. You can utilize this if you have a crucial update, such as a security patch.

Photo on In-app updates

There are two signals that can start the update:

  • Priority: You specify the update’s importance in each release by providing an integer that ranges from 0 to 5. (5 being the highest priority). In order to update the app, this will start the appropriate update flow (Immediate or Flexible).
  • Staleness: Specifies the amount of time the device has been aware that an update is available. This aids in setting off the appropriate flow. For instance, the Flexible flow would be triggered if the user hadn’t updated the app in the previous 30 days following the release of the update, and the Immediate flow would be triggered if it had been longer than 90 days.

For a better user experience, you may also combine the two signals.

How to implement in-app updates in Android

Add the following dependencies to your module-level gradle.build file.

dependencies {
implementation 'com.google.android.play:core:1.7.0'
}
view raw gradle.build hosted with ❤ by GitHub

We’re going to put everything we need in a separate file to make implementation simpler, and then we’re going to call it from the Activity we want to check for updates.

The following code should be pasted into a new file called InAppUpdate.kt

import android.app.Activity
import android.content.Intent
import android.graphics.Color
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.snackbar.Snackbar
import com.google.android.play.core.appupdate.AppUpdateInfo
import com.google.android.play.core.appupdate.AppUpdateManager
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.install.InstallState
import com.google.android.play.core.install.InstallStateUpdatedListener
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.InstallStatus
import com.google.android.play.core.install.model.UpdateAvailability
class InAppUpdate(activity: Activity) : InstallStateUpdatedListener {
private var appUpdateManager: AppUpdateManager
private val MY_REQUEST_CODE = 500
private var parentActivity: Activity = activity
private var currentType = AppUpdateType.FLEXIBLE
init {
appUpdateManager = AppUpdateManagerFactory.create(parentActivity)
appUpdateManager.appUpdateInfo.addOnSuccessListener { info ->
// Check if update is available
if (info.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) { // UPDATE IS AVAILABLE
if (info.updatePriority() == 5) { // Priority: 5 (Immediate update flow)
if (info.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
startUpdate(info, AppUpdateType.IMMEDIATE)
}
} else if (info.updatePriority() == 4) { // Priority: 4
val clientVersionStalenessDays = info.clientVersionStalenessDays()
if (clientVersionStalenessDays != null && clientVersionStalenessDays >= 5 && info.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
// Trigger IMMEDIATE flow
startUpdate(info, AppUpdateType.IMMEDIATE)
} else if (clientVersionStalenessDays != null && clientVersionStalenessDays >= 3 && info.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
// Trigger FLEXIBLE flow
startUpdate(info, AppUpdateType.FLEXIBLE)
}
} else if (info.updatePriority() == 3) { // Priority: 3
val clientVersionStalenessDays = info.clientVersionStalenessDays()
if (clientVersionStalenessDays != null && clientVersionStalenessDays >= 30 && info.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
// Trigger IMMEDIATE flow
startUpdate(info, AppUpdateType.IMMEDIATE)
} else if (clientVersionStalenessDays != null && clientVersionStalenessDays >= 15 && info.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
// Trigger FLEXIBLE flow
startUpdate(info, AppUpdateType.FLEXIBLE)
}
} else if (info.updatePriority() == 2) { // Priority: 2
val clientVersionStalenessDays = info.clientVersionStalenessDays()
if (clientVersionStalenessDays != null && clientVersionStalenessDays >= 90 && info.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
// Trigger IMMEDIATE flow
startUpdate(info, AppUpdateType.IMMEDIATE)
} else if (clientVersionStalenessDays != null && clientVersionStalenessDays >= 30 && info.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
// Trigger FLEXIBLE flow
startUpdate(info, AppUpdateType.FLEXIBLE)
}
} else if (info.updatePriority() == 1) { // Priority: 1
// Trigger FLEXIBLE flow
startUpdate(info, AppUpdateType.FLEXIBLE)
} else { // Priority: 0
// Do not show in-app update
}
} else {
// UPDATE IS NOT AVAILABLE
}
}
appUpdateManager.registerListener(this)
}
private fun startUpdate(info: AppUpdateInfo, type: Int) {
appUpdateManager.startUpdateFlowForResult(info, type, parentActivity, MY_REQUEST_CODE)
currentType = type
}
fun onResume() {
appUpdateManager.appUpdateInfo.addOnSuccessListener { info ->
if (currentType == AppUpdateType.FLEXIBLE) {
// If the update is downloaded but not installed, notify the user to complete the update.
if (info.installStatus() == InstallStatus.DOWNLOADED)
flexibleUpdateDownloadCompleted()
} else if (currentType == AppUpdateType.IMMEDIATE) {
// for AppUpdateType.IMMEDIATE only, already executing updater
if (info.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
startUpdate(info, AppUpdateType.IMMEDIATE)
}
}
}
}
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == MY_REQUEST_CODE) {
if (resultCode != AppCompatActivity.RESULT_OK) {
// If the update is cancelled or fails, you can request to start the update again.
Log.e("ERROR", "Update flow failed! Result code: $resultCode")
}
}
}
private fun flexibleUpdateDownloadCompleted() {
Snackbar.make(
parentActivity.findViewById(R.id.activity_main_layout),
"An update has just been downloaded.",
Snackbar.LENGTH_INDEFINITE
).apply {
setAction("RESTART") { appUpdateManager.completeUpdate() }
setActionTextColor(Color.WHITE)
show()
}
}
fun onDestroy() {
appUpdateManager.unregisterListener(this)
}
override fun onStateUpdate(state: InstallState) {
if (state.installStatus() == InstallStatus.DOWNLOADED) {
flexibleUpdateDownloadCompleted()
}
}
}
view raw InAppUpdate.kt hosted with ❤ by GitHub

Job Offers

Job Offers


    Kotlin Backend Developer and Mobile Enthusiast (m/f/d)

    Axel Springer National Media & Tech
    Berlin
    • Full Time
    apply now

    Mobile Developer Android (m/w/d) Firebase

    REWE digital
    Köln / Cologne
    • Full Time
    apply now

    (Senior) Android Software Developer (w/m/d)

    Paradox Cat GmbH
    Munich
    • Full Time
    apply now
Load more listings

OUR VIDEO RECOMMENDATION

,

Screenshot testing – The practical talk

I want to briefly present my journey while adding screenshot testing to our GetYourGuide app. Which libraries have I tried, what are the pros and cons, some pitfalls, and how to integrate screenshot testing on…
Watch Video

Screenshot testing – The practical talk

Benedict Pregler
Senior Mobile Engineer
GetYourGuide

Screenshot testing – The practical talk

Benedict Pregler
Senior Mobile Engine ...
GetYourGuide

Screenshot testing – The practical talk

Benedict Pregler
Senior Mobile Engineer
GetYourGuide

Jobs

Initialize the InAppUpdate.kt class and add the methods onResume and onActivityResults to your activity (often the MainActivity):

class MainActivity : AppCompatActivity() {
private lateinit var inAppUpdate: InAppUpdate
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
inAppUpdate = InAppUpdate(this)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
inAppUpdate.onActivityResult(requestCode,resultCode, data)
}
override fun onResume() {
super.onResume()
inAppUpdate.onResume()
}
override fun onDestroy() {
super.onDestroy()
inAppUpdate.onDestroy()
}
}
view raw MainActivity.kt hosted with ❤ by GitHub

When you use this code and set priority to:

5: Instantly displays Immediate (Recommended for critical updates)
4: Displays Immediacy after 5 days and Flexibility after 3 days.
3: Displays Immediate after 30 days and Flexible after 15 days (Recommended for performance updates)
2: Displays Immediacy after 90 days and Flexibility after 30 days (Recommended for minor updates)
1: Always Shows Flexibility
0: It has no effect on the update flow.

Of course, you are free to modify the code to suit your requirements.

There is no way to set the update’s priority through the Google Play Console; instead, you must use the Google Play Developer API.

Testing

You must upload your app twice to the internal (or alpha or beta) track in order to test your in-app update solution.

Thank you for taking the time to read this article. If you found this post to be useful and interesting, please clap and recommend it.

If I got something wrong, mention it in the comments. I would love to improve.

This article was originally published on proandroiddev.com on July 08, 2022

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
Automation is a key point of Software Testing once it make possible to reproduce…
READ MORE
blog
Every good Android application should be well tested to minimize the risk of error…
READ MORE
blog

Running Instrumented Tests in a Gradle task

During the latest Google I/O, a lot of great new technologies were shown. The…
READ MORE
blog
Notifications are a very important part of Android apps, showing relevant information, informing when…
READ MORE

Leave a Reply

Your email address will not be published.

Fill out this field
Fill out this field
Please enter a valid email address.

Menu