Skip to main content

Support in-app updates

Keeping your app up-to-date on your users’ devices enables them to try new features, as well as benefit from performance improvements and bug fixes. Although some users enable background updates when their device is connected to an unmetered connection, other users may need to be reminded to update. In-app updates is a Play Core library feature that introduces a new request flow to prompt active users to update your app.
Add this in

dependencies{
   implementation 'com.google.android.play:core:1.6.4'
}
In-app updates work only with devices running Android 5.0 (API level 21) or higher, and requires you to use Play Core library 1.5.0 or higher. After meeting these requirements, your app can support the following UX for in-app updates:
  • Flexible
  • Immediate 

 Flexible

A user experience that provides background download and installation with graceful state monitoring. This UX is appropriate when it’s acceptable for the user to use the app while downloading the update. For example, you want to urge users to try a new feature that’s not critical to the core functionality of your app.


Immediate

A full-screen user experience that requires the user to update and restart the app in order to continue using the app. This UX is best for cases where an update is critical for continued use of the app. After a user accepts an immediate update, Google Play handles the update installation and app restart.


First, create an instance of AppUpdateManager and listener for update state: 

Check for update availability

Before requesting an update, you need to first check if one is available for your app. To check for an update, use AppUpdateManager, as shown below:
// Creates instance of the manager. 
private val appUpdateManager: AppUpdateManager = AppUpdateManagerFactory.create(this)
// Returns an intent object that you use to check for an update.
private val appUpdateInfoTask = appUpdateManager.appUpdateInfo
private val MY_REQUEST_CODE = 1000
private lateinit var listener: InstallStateUpdatedListener


override fun onResume() {
    super.onResume()
    checkAppUpdate()
}

Start an update

After checking that you are able to update the app, you can request an update using AppUpdateManager.startUpdateFlowForResult(), as shown below. However, you should be mindful how often you request updates to avoid annoying or tiring your users. That is, you should limit requesting in-app updates to only the changes that are important to the functionality of your app.

You can monitor the state of an update in progress by registering a listener to install status updates.


private fun checkAppUpdate() {
        // Create a listener to track request state updates.
        listener = InstallStateUpdatedListener {
            if (it.installStatus() == InstallStatus.DOWNLOADED) {
                // After the update is downloaded, show a notification
                // and request user confirmation to restart the app.
                popupSnackBarForCompleteUpdate()
            }
        }
        // Before starting an update, register a listener for updates.
        appUpdateManager.registerListener(listener)

        // Start an update.
        // Checks that the platform will allow the specified type of update.
        appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
            if ((appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE || appUpdateInfo.updateAvailability()
                            == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS)
                    // For a flexible update, use AppUpdateType.FLEXIBLE
                    && appUpdateInfo.isUpdateTypeAllowed(IMMEDIATE)) {
                // Request the update.
                requestUpdate(appUpdateInfo)
            }
        }
    }

The result contains the updated availability status. If an update is available and the update is allowed, the returned AppUpdateInfo also contains an intent to start the update. See the next section for how to start the update.
If an in-app update is already in progress, the result will also report the status of the in-progress update.

Each AppUpdateInfo an instance can be used to start an update only once. To retry the update in case of failure, you need to request a new AppUpdateInfo and check again that the update is available and allowed.
The type of update you request determines the next steps you need to take. To learn more, read the section about how to either Handle an immediate update or Handle a flexible update.

Install a flexible update

If you monitor the flexible update state and you detect the InstallStatus.DOWNLOADED state, you need to restart the app to install the update.
Unlike an immediate update, Google Play does not trigger an app restart for you. That’s because, during a flexible update, the user has an expectation to keep using the app until they decide that they want to install the update.

So, it’s recommended that you provide a notification (or some other UI indication) that informs the user that installation is ready and requests user confirmation to restart the app.
For example, you can implement a snackbar with Material Design requests confirmation from the user to restart the app, as shown in figure 1.
The following code sample shows a snackbar notification to the user after a flexible update is downloaded.
 
When you call appUpdateManager.completeUpdate() in the foreground, the platform displays a full-screen UI that restarts the app in the background. After the platform installs the update, the app restarts into its main activity.

If you instead call appUpdateManager.completeUpdate() in the background, the the update is installed silently without obscuring the device UI.
When the user brings your app to the foreground, it’s recommended that you check that your app doesn’t have an update waiting to be installed. If your app has an update in the DOWNLOADED state, show the notification to request that the user install the update, as shown below. Otherwise, the update data continues to occupy the user’s device storage.

// Checks that the update is not stalled during 'onResume()'.
// However, you should execute this check at all app entry points.
override fun onResume() {
    super.onResume()

    appUpdateManager
        .appUpdateInfo
        .addOnSuccessListener { appUpdateInfo ->
            ...
            // If the update is downloaded but not installed,
            // notify the user to complete the update.
            if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                popupSnackbarForCompleteUpdate()
            }
        }
}

Handle an immediate update

If you are performing an immediate update, and the user consents to install the update, Google Play displays the update progress on top of your app's UI across the entire duration of the update. During the update, if the user closes or terminates your app, the update should continue to download and install in the background without additional user confirmation.
However, when your app returns to the foreground, you should confirm that the an update is not stalled in the UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS state. If the update is stalled in this state, resume the update, as shown below:

// Checks that the update is not stalled during 'onResume()'.
// However, you should execute this check at all entry points into the app.
override fun onResume() {
    super.onResume()

    appUpdateManager
        .appUpdateInfo
        .addOnSuccessListener { appUpdateInfo ->
            ...
            if (appUpdateInfo.updateAvailability()
                == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
            ) {
                // If an in-app update is already running, resume the update.
                manager.startUpdateFlowForResult(
                    appUpdateInfo,
                    IMMEDIATE,
                    this,
                    MY_REQUEST_CODE
                );
            }
        }
}

Request for App Update 


private fun requestUpdate(appUpdateInfo: AppUpdateInfo) {
        appUpdateManager.startUpdateFlowForResult(
                // Pass the intent that is returned by 'getAppUpdateInfo()'.
                appUpdateInfo,
                // Or 'AppUpdateType.FLEXIBLE' for flexible updates.
                IMMEDIATE,
                // The current activity making the update request.
                this,
                // Include a request code to later monitor this update request.
                MY_REQUEST_CODE)
    }

SnackBar for showing the completion of update download and restart the app action


/* Displays the snackBar notification and call to action. */
    private fun popupSnackBarForCompleteUpdate() {
        Snackbar.make(findViewById(R.id.frmFragmentContainer),
                "An update has just been downloaded.",
                Snackbar.LENGTH_INDEFINITE
        ).apply {
            setAction("RESTART") { appUpdateManager.completeUpdate() }
            setActionTextColor(ContextCompat.getColor(this@LockerActivity, R.color.orange_bg))
            show()
        }
    }

Get a callback for update status

After starting an update, you can use an onActivityResult() callback to handle an update failure or cancellation, as shown below.


override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == MY_REQUEST_CODE) {
            if (resultCode != RESULT_OK) {
                log("Update flow failed! Result code: $resultCode")
                // If the update is cancelled or fails,
                // you can request to start the update again.
            }
        }
    }

The following describes the different values you may receive from the onActivityResult() callback:
  • RESULT_OK: The user has accepted the update. For immediate updates, you might not receive this callback because the update should already be completed by Google Play 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.

onDestroy() unregister the app update progress listener


override fun onDestroy() {
        super.onDestroy()
        // When status updates are no longer needed, unregister the listener.
        appUpdateManager.unregisterListener(listener)
    }

Troubleshoot

This section describes some possible solutions to situations where in-app updates might not work as expected during testing.
  • In-app updates are available only to user accounts that own the app. So, make sure the account you’re using has downloaded your app from Google Play at least once before using the account to test in-app updates.
  • Make sure that the app that you are testing in-app updates with has the same application ID and is signed with the same signing key as the one available from Google Play.
  • Because Google Play can only update an app to a higher version code, make sure the app you are testing as a lower version code than the update version code.
  • Make sure the account is eligible and the Google Play cache is up to date. To do so, while logged into the Google Play Store account on the test device, proceed as follows:
    1. Make sure you completely close the Google Play Store App.
    2. Open the Google Play Store app and go to the My Apps & Games tab.
    3. If the app you are testing doesn’t appear with an available update, check that you’ve properly set up your testing tracks.

Comments

Popular posts from this blog

Vertical AutoScrolling TextView in Android

In android by default we can scroll the text in horizontal using marquee in layout, but if we want to scroll the text in vertical its not possible by default. So here we will learn to create a custom TextView which will auto-scroll in vertical direction. Source Code:  VerticalScrollingTextView-Android Create a AutoScrollingTextView.class which extends TextView: @SuppressLint ( "AppCompatCustomView" ) public class AutoScrollingTextView extends TextView { private static final float DEFAULT_SPEED = 65.0f ; public Scroller scroller ; public float speed = DEFAULT_SPEED ; public boolean continuousScrolling = true; public AutoScrollingTextView (Context context) { super (context) ; init( null, 0 ) ; scrollerInstance(context) ; } public AutoScrollingTextView (Context context , AttributeSet attrs) { super (context , attrs) ; init(attrs , 0 ) ; scr

Flexbox inside the RecyclerView as a LayoutManager (FlexboxLayoutManager).

Currently google has release the Flexbox which can be used for building flexible layouts using FlexboxLayout, it can be interpreted as an advanced LinearLayout because both layouts align their child views sequentially. For more detail on this flexbox-layout But here we are gonna work on Flexbox with RecyclerView. Flexbox with a large number of items in a scrollable container! Let's first see what are the Supported attributes / features comparison Due to some characteristics of the RecyclerView, some Flexbox attributes are not available/not implemented to the FlexboxLayoutManager. Here is a quick overview of the attributes/features comparison between the two containers. Attribute / Feature FlexboxLayout                FlexboxLayoutManager (RecyclerView) flexDirection flexWrap (except wrap_reverse ) justifyContent alignItems alignContent - layout_order - layout_flexGrow layout_flexShrink layout_alignSelf layout_fl

Android RecyclerView and StaggeredGridLayoutManager with Picasso/Glide

This project is there in GitHub https://github.com/yuvaraj119/Picasso-RecyclerView-StaggeredGridLayoutManager You can download and start customizing it for your project also. How to use with Picasso Picasso + RecyclerView + StaggeredGridLayoutManager Its the enhanced version of this project https://github.com/pohh/slotmachinepicasso were there was a problem with Picasso + RecyclerView + StaggeredGridLayoutManager shuffles resizable recycler views infinitely issue posted on github https://github.com/square/picasso/issues/918 I have made some changes now it works with Picasso and Glide without any shuffles and position change Currently this project is done with Picasso If you want to use it with Glide How to use with Glide Glide + RecyclerView + StaggeredGridLayoutManager Add dependencies for Glide https://github.com/bumptech/glide Remove Picasso library from dependency and remove all the codes of Picasso from MyGridAdapter.java and also from other p