I recently came across a new kind of app update flow which has 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.
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.
After this 90-day period, the latest available version of the app is automatically installed with the default update behavior (as outlined above). After the app is updated to the latest available version, a new 90-day postponement period will begin the next time the developer publishes a new version of the app.
Use internal app sharing to test in-app updates by performing the following steps: Make sure your test device has a version of your app installed that supports in-app updates and was installed using an internal app sharing URL. Follow the Play Console instructions to share your app internally.
You can use https://appupgrade.dev/ service to force update you mobile apps. You need to create new version for your app versions you want to update in the app upgrade service and select whether you want to force it or just want to let users know that new version is available. See the response has force update true.
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
Android officially announced the in-app updates to everyone today.
https://developer.android.com/guide/playcore/in-app-updates
Update: Handling both IMMEDIATE and FLEXIBLE updates in a single activity; Kotlin way.
import android.app.Activity import android.content.Intent import android.content.IntentSender import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import com.google.android.material.snackbar.Snackbar 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 import timber.log.Timber class BaseUpdateCheckActivity : AppCompatActivity() { private val appUpdateManager: AppUpdateManager by lazy { AppUpdateManagerFactory.create(this) } private val appUpdatedListener: InstallStateUpdatedListener by lazy { object : InstallStateUpdatedListener { override fun onStateUpdate(installState: InstallState) { when { installState.installStatus() == InstallStatus.DOWNLOADED -> popupSnackbarForCompleteUpdate() installState.installStatus() == InstallStatus.INSTALLED -> appUpdateManager.unregisterListener(this) else -> Timber.d("InstallStateUpdatedListener: state: %s", installState.installStatus()) } } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main_ad_view) checkForAppUpdate() } private fun checkForAppUpdate() { // Returns an intent object that you use to check for an update. val appUpdateInfoTask = appUpdateManager.appUpdateInfo // Checks that the platform will allow the specified type of update. appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) { // Request the update. try { val installType = when { appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) -> AppUpdateType.FLEXIBLE appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) -> AppUpdateType.IMMEDIATE else -> null } if (installType == AppUpdateType.FLEXIBLE) appUpdateManager.registerListener(appUpdatedListener) appUpdateManager.startUpdateFlowForResult( appUpdateInfo, installType!!, this, APP_UPDATE_REQUEST_CODE) } catch (e: IntentSender.SendIntentException) { e.printStackTrace() } } } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == APP_UPDATE_REQUEST_CODE) { if (resultCode != Activity.RESULT_OK) { Toast.makeText(this, "App Update failed, please try again on the next app launch.", Toast.LENGTH_SHORT) .show() } } } private fun popupSnackbarForCompleteUpdate() { val snackbar = Snackbar.make( findViewById(R.id.drawer_layout), "An update has just been downloaded.", Snackbar.LENGTH_INDEFINITE) snackbar.setAction("RESTART") { appUpdateManager.completeUpdate() } snackbar.setActionTextColor(ContextCompat.getColor(this, R.color.accent)) snackbar.show() } 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() } //Check if Immediate update is required try { if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) { // If an in-app update is already running, resume the update. appUpdateManager.startUpdateFlowForResult( appUpdateInfo, AppUpdateType.IMMEDIATE, this, APP_UPDATE_REQUEST_CODE) } } catch (e: IntentSender.SendIntentException) { e.printStackTrace() } } } companion object { private const val APP_UPDATE_REQUEST_CODE = 1991 } }
Source Gist: https://gist.github.com/saikiran91/6788ad4d00edca30dad3f51aa47a4c5c
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With