How to work with Android's in-app update API?
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
Where to put in-app update implementation in Android
THIS ANSWER IS A HACK!
I have had quite a bit of trouble with in app updates. the issue being that this librery was made for users that stay in the same activity where startUpdateFlowForResult
is called from and then whenever the user does something unexpected like checking whatsapp or going to the next activity everything breaks down and the update doesnt get completed. and you can try to fix it with endless boilerplate but since calling a snackbar that is activity agnostic is super complex that will cost you at least some gray hairs and leave you with delicate code that probably still is somewhat buggy.
THE ALTERNATIVE
I finally gave upand decided to redirect my users directly to GooglePlayStore:
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE ) { //maybe you could add some additional checks like priority level here
makeUpdateDialog(mContext).show();
}
I use the In-app Updates library exclusively to check for availability and then I show a dialog with the following code in the action button:
try {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(ownGooglePlayLink)));
} catch (android.content.ActivityNotFoundException anfe) {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(ownWebLink)));
}
public static final String ownGooglePlayLink="market://details?id=com.my.package.name";
public static final String ownWebLink="https://play.google.com/store/apps/details?id=com.my.package.name";
This is admitedly very hacky but its not as bad as it seems. I find that the user experience remmains good and the user can either skip your update or return to the app after initializing the update.
The android in-app update API is not working as I expected
This actually just randomly worked about 36 hours later when I opened the app again.
There must be a 24 hour delay or so from when update goes live til when the API will pick it up.
Related Topics
How to Find the Android Version Name Programmatically
How to Configure Launcher Activity Programmatically in Android
Android 'Debuggable' Default Value
Pass Arraylist Data into Soap Web Service in Android
How to Create/Write File in the Root of the Android Device
Android: How to Change the Datepicker View Date Format from Mm/Dd/Yyyy to Dd/Mm/Yyyy
Http Post Method Passing Null Values to the Server
How to Implement a Gcm Hello World for Android Using Android Studio
Handling Keepsynced() While on Background on Android and with Fcm
How to Get Meid and Imei Information Using Adb Commands on Android 5.0
How to Change Images on Imageview After Some Interval
Why Is My Field 'Null' After Injection? How to Inject My Object
Why There's a Separate Mutablelivedata Subclass of Livedata
Writing/Reading Files To/From Android Phone's Internal Memory