How to work with Android's in-app update API?
Asked Answered
C

7

55

I recently came across a new kind of app update flow which is 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.

  1. A card popped up from the bottom showing update is available
  2. When I clicked on "Update Hotstar" button, one dialog popped up (seems like it is provided by Google Play)

enter image description here

  1. Downloading was started in the background while the app was running
  2. After completion of the download, one SnackBar popped up showing app ready to install
  3. App restarted after the installation

enter image description here

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.

Chemaram answered 1/5, 2019 at 17:4 Comment(3)
It is called in-app Updates API and tested by selected partners. I am waiting for this too. Unfortunately, Google loves to announce features and products that take forever to release.Rowan
You may refer this answer as well #15213711Tact
you can find how to implement in app update on herePhotopia
C
73

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

Chemaram answered 9/5, 2019 at 13:24 Comment(26)
Apparently, the Google Documentation contains errors. The appUpdateInfo is declared as a Task, and therefore you can't access the member methods .updateAvailability() and .isUpdateTypeAllowed(). Someone recommended referencing the appUpdateInfo inside of a Listener, but the only examples I was able to find appear to be in Kotlin...Bradshaw
@MichaelDougan Yes, I noticed some errors in the documentation. I worked on those errors and implemented it in one of my apps. It is working fine.Chemaram
I'm running some tests but I can't seem to make it to work. I'm always getting the UpdateAvailability.UPDATE_NOT_AVAILABLE as a response. Is there anything that I've missed?Dehisce
@Dehisce I think your app should be published on Play store. For testing, decrease the version code of test app than published version code and try.Chemaram
@PratikED Bro I am unable to work with your code when I am just paste and run your code in my splash screen It always gives me error of SomeThing Else Can you help me to find out error on my app.Trim
@Trim Log the appUpdateInfo.installStatus() inside else statement. You might find the issue.Chemaram
Bro I did but cannot find any thing new can you give me sample app for this.Trim
@PratikED you are checking IMMEDIATE instead of FLEXIBLEDisconsider
@Dehisce can you found any solution for your issue?Disconsider
@vishalpatel yes, check my answer at https://mcmap.net/q/339052/-in-app-update-api-showing-update_not_available-while-testing-on-debugging-deviceDehisce
What if the Mobile Android Version is less than 5.0, whether we have to write separate implementation for this updates to handle ?Dinar
@Satyam This can be achieved by adding the version code of your app in a table on your server. This version code you increment manually when there is a need for force update of the app. You fetch the version code every time the user launches the app and compare it with the installed app's version code. To update the app, you redirect the user to the Play Store.Chemaram
How to know or set the update type- flexible/immediate?Arturo
Hi I'm also trying this but I can't seem to make it to work. I'm always getting the UpdateAvailability.UPDATE_NOT_AVAILABLE as a response. Is this already available to the public?Throughcomposed
@harminder-singh as per research, flexible/immediate is determine by user. you may use RemoteConfig to set whether you want it to be flexible or immediate.Throughcomposed
@Throughcomposed firebase is different approach where I need to update the version manually. I am asking about this inappupdate provided by android developers developer.android.com/guide/app-bundle/in-app-updatesArturo
you can try fakeUpdateManager to testDisorient
Why does a lot of people simply try/catch the startUpdateFlowResult and just print the stacktrace? Don't you need this to succeed or is this just a noisy function call?Petrifaction
I am new to android studio. Please help me with where these code segments should be added.Champion
@BumuthuDilshan I've updated the answer! Hope this will help you.Chemaram
any way to hide popup message when using flexible update? Can't we just start the flexible update without showing this update message? After all the meaning of flexible update is to don't disturb user.Poignant
@MuhammadSaqib The user can start the installation as per her convenience. That is why a button needs to show for commencing installation.Chemaram
@PratikED I handle installation part next time when user start the app. Currently when there is an update available, I start the flow with flexible open. You click update button and update downloaded in the background. (no UI no progress bar). When the user come back to app then I perform installation. That's why I'm asking if there is any way to start the update without this prompt popup.Poignant
@PratikED i have implemented the same code but not showing any popup. any help thank youMisleading
@PratikED My only concern with Flexible flow is what if the user will move to another screen while apk is being downloaded? Will snackbar appears in a new activity? or can we show alertdailog somehow on top of all activities? or in-app api has any other way to notify user after apk is dowloaded?Haile
Update downloads successfully, but mAppUpdateManager.completeUpdate(); not working. Can't install the update.Forth
R
30

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

Rowan answered 9/5, 2019 at 1:52 Comment(4)
how to decide if the available update is IMMEDIATE or FLEXIBLE can we set these flags in google play developer console so the client device will know if it has to update on immediate bases.Poignant
That what thought initially and created a gist, but it turned out we cannot set the flag in google play store; instead, I used firebase remote config to compleat the process. I will update the gist in some time. Thanks. @MuhammadSaqibRowan
I did a little research after commenting and I find out that they are actually planing to implement such feature. They will soon introduce update "Priority" feature in developer console and play core API would be able to read that priority. youtu.be/_o_q6hatcIs?t=577Poignant
For those wanting to implement this flag that Google hasn't released yet, seems like the next best option is Firebase Remote Config.Petrifaction
B
13

Trying to implement this, the official Google Documentation quoted in the accepted answer is syntactically incorrect. It took some research, but I finally found the correct syntax:

Instead of:

// Creates an instance of the manager.
AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);

// Returns an intent object that you use to check for an update.
Task<AppUpdateInfo> appUpdateInfo = appUpdateManager.getAppUpdateInfo();

// Checks that the platform will allow the specified type of update.
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
  // For a flexible update, use AppUpdateType.FLEXIBLE
  && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
              // Request the update.

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

Do this:

    private AppUpdateManager appUpdateManager;
    ...
    // onCreate(){ 
    // Creates instance of the manager.
    appUpdateManager = AppUpdateManagerFactory.create(mainContext);

    // Don't need to do this here anymore
    // Returns an intent object that you use to check for an update.
    //Task<AppUpdateInfo> appUpdateInfo = appUpdateManager.getAppUpdateInfo();

    appUpdateManager
            .getAppUpdateInfo()
            .addOnSuccessListener(
                    appUpdateInfo -> {

                        // Checks that the platform will allow the specified type of update.
                        if ((appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE)
                                && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE))
                        {
                            // Request the update.
                            try {
                                appUpdateManager.startUpdateFlowForResult(
                                        appUpdateInfo,
                                        AppUpdateType.IMMEDIATE,
                                        this,
                                        REQUEST_APP_UPDATE);
                            } catch (IntentSender.SendIntentException e) {
                                e.printStackTrace();
                            }
                        }
                    });

Then, code a similar bit of code in the onResume() override in case an install got hung up along the way:

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

    appUpdateManager
            .getAppUpdateInfo()
            .addOnSuccessListener(
                    appUpdateInfo -> {

                        if (appUpdateInfo.updateAvailability()
                                == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
                            // If an in-app update is already running, resume the update.
                            try {
                                appUpdateManager.startUpdateFlowForResult(
                                        appUpdateInfo,
                                        AppUpdateType.IMMEDIATE,
                                        this,
                                        REQUEST_APP_UPDATE);
                            } catch (IntentSender.SendIntentException e) {
                                e.printStackTrace();
                            }
                        }
                    });
}
Bradshaw answered 15/5, 2019 at 17:29 Comment(2)
What is the value of REQUEST_APP_UPDATE ?Outpoint
@CoryRobinson it's anything =)Tufa
B
13

Please try this once. Official Document for reference

Step 1: In the build.gradle file add the below library (please check and update latest play code plugin version)

implementation 'com.google.android.play:core:1.6.4'

Step 2: Declare the following variables in class (Ex MainActivity.java)

    private AppUpdateManager mAppUpdateManager;
    private int RC_APP_UPDATE = 999;
    private int inAppUpdateType;
    private com.google.android.play.core.tasks.Task<AppUpdateInfo> appUpdateInfoTask;
    private InstallStateUpdatedListener installStateUpdatedListener;

Step 3: In onCreate() method add the below code (initializing variables)

        // Creates instance of the manager.
        mAppUpdateManager = AppUpdateManagerFactory.create(this);
        // Returns an intent object that you use to check for an update.
        appUpdateInfoTask = mAppUpdateManager.getAppUpdateInfo();
        //lambda operation used for below listener
        //For flexible update
        installStateUpdatedListener = installState -> {
            if (installState.installStatus() == InstallStatus.DOWNLOADED) {
                popupSnackbarForCompleteUpdate();
            }
        };
        mAppUpdateManager.registerListener(installStateUpdatedListener);

Step 4: In onDestroy() method of activity just unregister the listener

 @Override
    protected void onDestroy() {
        mAppUpdateManager.unregisterListener(installStateUpdatedListener);
        super.onDestroy();
    }

Step 5: In onResume() we need to listen to both Flexible and Immediate updates by the below code.

@Override
        protected void onResume() {
           try {   
  mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
        if (appUpdateInfo.updateAvailability() == 
           UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
           // If an in-app update is already running, resume the update.
                       try {
                            mAppUpdateManager.startUpdateFlowForResult(
                                    appUpdateInfo,
                                    inAppUpdateType,
                                    this,
                                    RC_APP_UPDATE);
                        } catch (IntentSender.SendIntentException e) {
                            e.printStackTrace();
                        }
                    }
                });
    
       
  mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
     //For flexible update            
       if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                        popupSnackbarForCompleteUpdate();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            super.onResume();
    }

Step 6: In onActivityResult() we need to handle user click actions(only for flexible update)

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_APP_UPDATE) {
            //when user clicks update button
            if (resultCode == RESULT_OK) {
                Toast.makeText(MainActivity.this, "App download starts...", Toast.LENGTH_LONG).show();
            } else if (resultCode != RESULT_CANCELED) {
                //if you want to request the update again just call checkUpdate()
                Toast.makeText(MainActivity.this, "App download canceled.", Toast.LENGTH_LONG).show();
            } else if (resultCode == RESULT_IN_APP_UPDATE_FAILED) {
                Toast.makeText(MainActivity.this, "App download failed.", Toast.LENGTH_LONG).show();
            }
        }
}

Step 7: Create a method to check update available or not and start the update (Immediate update)

private void inAppUpdate() {

        try {
            // Checks that the platform will allow the specified type of update.
            appUpdateInfoTask.addOnSuccessListener(new OnSuccessListener<AppUpdateInfo>() {
                @Override
                public void onSuccess(AppUpdateInfo appUpdateInfo) {
                    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
                            // For a flexible update, use AppUpdateType.FLEXIBLE
                            && appUpdateInfo.isUpdateTypeAllowed(inAppUpdateType)) {
                        // Request the update.

                        try {
                            mAppUpdateManager.startUpdateFlowForResult(
                                    // Pass the intent that is returned by 'getAppUpdateInfo()'.
                                    appUpdateInfo,
                                    // Or 'AppUpdateType.FLEXIBLE' for flexible updates.
                                    inAppUpdateType,
                                    // The current activity making the update request.
                                    MainActivity.this,
                                    // Include a request code to later monitor this update request.
                                    RC_APP_UPDATE);
                        } catch (IntentSender.SendIntentException ignored) {

                        }
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

Step 8: Finely create a snack dialog or any alert to show the user that flexible update is downloaded and ready to update(need used action to start update - only for flexible update)

private void popupSnackbarForCompleteUpdate() {
        try {
        Snackbar snackbar =
            Snackbar.make(
                    findViewById(R.id.id_of_root_loyout),
                    "An update has just been downloaded.\nRestart to update",
                    Snackbar.LENGTH_INDEFINITE);

    snackbar.setAction("INSTALL", view -> {
        if (mAppUpdateManager != null){
            mAppUpdateManager.completeUpdate();
        }
    });
snackbar.setActionTextColor(getResources().getColor(R.color.install_color));
    snackbar.show();

        } catch (Resources.NotFoundException e) {
            e.printStackTrace();
        }
    } 

Step 9: Now call the method with the in-app update type(Flexible or Immediate) wherever you want to start to check updates.

//For Immediate 
inAppUpdateType = AppUpdateType.IMMEDIATE; //1
inAppUpdate();

//For Flexible 
inAppUpdateType = AppUpdateType.FLEXIBLE; //0
inAppUpdate();

Points to Remember:

  1. The flexible update will download first then it will notify the user that download completed then the user has to start the update(options given above step 8).

  2. There is an option in google play console to test in-app sharing, just we can upload normal apk(no need signed apk)to test. https://support.google.com/googleplay/android-developer/answer/9303479?hl=en

  3. Need to enable in-app sharing option in your test device play store app. How to Enable Internal App Sharing for Android?

  4. Still, any issue in the play store, just clear cache and clear data then restart the device once and try.

Bagger answered 5/6, 2020 at 6:37 Comment(1)
you get always false for 3. condition (resultCode == RESULT_IN_APP_UPDATE_FAILED) in onActivityResult(), because of wrong logic in 2.condition. Your 2. condition should be as following (resultCode == RESULT_CANCELED)Canopus
B
2

My guess is that it is controlled by the app itself, rather than Google Play. I've developed apps that make an API call on startup to read the 'latest' version number and whether that version is a 'mandatory' update or not, and compares it to the running app version. If a new version is available, the user is presented with a dialog like the one you displayed (though their's is much nicer) alerting the user that an update is available. If the update is 'mandatory', then the message tells them that they must update the app before continuing. If they click Update, then they are taken to the App Store page where they initiate the download of the update as usual and the app exits. If they click Close, the app just exits. If the update is not mandatory, they are asked if they would like to update now, or continue. If they click Update, then they are taken to the App Store page where they initiate the download of the update as usual and the app exits. If they click Continue, then they are just taken into the existing version of the app.

I'm not sure how they managed the background download then kicked off the app update before exiting the app. That would be very nice, but our method above was also very easy and gives a lot of capability to the developer.

Bradshaw answered 1/5, 2019 at 17:19 Comment(2)
Thanks for your time. We can develop this easily within the app. But you have to take the user to Play Store. However, if you can guess it, there are some callbacks from Google Play. That's why they are able to show SnackBar on completion on download.Chemaram
Cool! It appears Google Play is testing an in-app Update API with select partners now. Would like to check it out when it becomes available... android-developers.googleblog.com/2018/11/…Bradshaw
K
2

Try these libraries, where you can implement in few lines of code.

https://github.com/SanojPunchihewa/InAppUpdater

https://github.com/dnKaratzas/android-inapp-update

Kimbra answered 12/10, 2020 at 21:34 Comment(0)
C
0

Google is testing an early version of an In-apps update API as described at this blog post.

It's only available for some early testing partners right now, but it should be available for all developers eventually. Keep your eye out on the Android Developers Blog and for announcements in the Play console.

Cost answered 3/5, 2019 at 6:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.