Forum Libraries, Components, and Plugins

In-App Update using Play Core Library

Hey you guys, need help!!

I have an app in Google Play that scan some QR Codes, and these Codes had a pattern, and we have to change it, because the read is very very slow. We will update the QRs and the App, but we have to force an update and keep the correct functionality. I was reading about an "in-app update" on https://developer.android.com/guide/app-bundle/in-app-updates and in there, they say that requires a library called Play Core. There is a Play Core Library for Xamarin? Considering that this site uses Android Studio (Java) for implementations references.

Answers

  • artfcartfc Member

    test

  • noteshipnoteship Member

    test

  • PatriceVBPatriceVB Member

    I'm also interested in any information about how to achieve in app update thanks this new Google Play capability.

    I do not found any information about how to use Play Core in Xamarin.

    Any ideas?

  • FreakyAliFreakyAli USMember ✭✭

    I guess this is unavailable for now, I am planning to make a Xamarin Binding for Play Core will get back to you guys as soon as it's done

  • Brand00wnBrand00wn Member ✭✭

    I'll be anxiously waiting

  • FreakyAliFreakyAli USMember ✭✭

    Hey, guys, I was able to complete the binding creation and I will be updating it on Nuget soon, for now, you guys can pick it up from my drive: https://drive.google.com/file/d/1r8QuqW25PKcQty0W_Q0eEgMcA8ynt_3J/view?usp=sharing

  • Brand00wnBrand00wn Member ✭✭

    Awesome!! I'll test and give you a feedback soon.

  • FreakyAliFreakyAli USMember ✭✭

    Sure let me know, I sort of gave it one round of test and it seems to be working great...

  • funklet1funklet1 Member ✭✭

    @G.Hakim
    The binding is very useful and works for the immediate update perfectly.
    I am having some difficulty getting the InstalledStateUpdateListener to work. The compiler has some issues with ThisInstalledStateListener.java
    error: ThisInstallStateUpdatedListener is not abstract and does not override abstract method onStateUpdate(InstallState) in StateUpdatedListener public class ThisInstallStateUpdatedListener
    error: name clash: onStateUpdate(Object) in ThisInstallStateUpdatedListener and onStateUpdate(StateT) in StateUpdatedListener have the same erasure, yet neither overrides the other public void onStateUpdate (java.lang.Object p0) where StateT is a type-variable: StateT extends Object declared in interface StateUpdatedListener

        class ThisInstallStateUpdatedListener : Java.Lang.Object, IInstallStateUpdatedListener
    {
        void IStateUpdatedListener.OnStateUpdate(Java.Lang.Object p0)
        {
            // Handles a Flexible AppUpdate
                if ((p0 as InstallState).InstallStatus() == InstallStatus.Downloaded)
                    ShowMessage("An update has just been downloaded and will install now.", "OK", (s, e) => appUpdateManager.CompleteUpdate(), string.Empty, null, false);
        }
    }
    

    Could you help and also is there any news on the Nuget?
    Thanks!

  • FreakyAliFreakyAli USMember ✭✭

    @funklet1 Hi happy to know it helps you out, secondly I would like to know how exactly are you using the ThisInstallStateUpdatedListener and what are you using it for. Also, note that I am a tad bit busy but I will be uploading it to Nuget really soon. Sorry for the delay everyone!

  • funklet1funklet1 Member ✭✭

    @FreakyAli,

    I appreciate that you are busy and the NuGet would be the icing on the cake but I like cake even if it doesn't have icing on it..

    I want to implement the Listener for the install state so that when the Flexible update is ready I can inform the user so they can update.

    From the developer.android.com section guide/app-bundle/in-app-updates#monitor_flexible

    // Create a listener to track request state updates.
    InstallStateUpdatedListener listener = state -> {
        // Show module progress, log state, or install the update.
      };
    
    // Before starting an update, register a listener for updates.
    appUpdateManager.registerListener(listener);
    
    // Start an update.
    
    // When status updates are no longer needed, unregister the listener.
    appUpdateManager.unregisterListener(listener);
    

    All help is greatly appreciated.

  • FreakyAliFreakyAli USMember ✭✭

    Well from the above code I understand that you are trying to get the flexi-update interface set up now I haven't used it yet, But this is how it should look from my understanding

            // Create a listener to track request state updates.
            IInstallStateUpdatedListener listener = new MyInstallStateUpdatedListener();
    
            // Before starting an update, register a listener for updates.
            mAppUpdateManager.RegisterListener(listener);
    
            // Start an update.
    
            // When status updates are no longer needed, unregister the listener.
            mAppUpdateManager.UnregisterListener(listener);
    

    And this is how the class would look

           public class MyInstallStateUpdatedListener : Java.Lang.Object, IInstallStateUpdatedListener
           {
                     void IStateUpdatedListener.OnStateUpdate(Java.Lang.Object p0)
                    {
                         // Handles a Flexible AppUpdate
                        if ((p0 as InstallState).InstallStatus() == InstallStatus.Downloaded)
                        { }
                    }
            }
    

    I hope this is what you were looking for, if not please feel free to get back at anytime

  • funklet1funklet1 Member ✭✭

    Yes, if you look at my code above you can see that my class is functionally identical to the one that you have provided.

    Unfortunately in VS2017 there is a compiler error.

    The compiler has some issues with ThisInstalledStateListener.java
    error: ThisInstallStateUpdatedListener is not abstract and does not override abstract method onStateUpdate(InstallState) in StateUpdatedListener public class ThisInstallStateUpdatedListener
    error: name clash: onStateUpdate(Object) in ThisInstallStateUpdatedListener and onStateUpdate(StateT) in StateUpdatedListener have the same erasure, yet neither overrides the other public void onStateUpdate (java.lang.Object p0) where StateT is a type-variable: StateT extends Object declared in interface StateUpdatedListener

    I don't know if the compiler error is due to an issue in the dll or Visual Studio so I am unable to troubleshoot further.

    Perhaps if you could provide the source to the binder project..

  • funklet1funklet1 Member ✭✭

    Please see the following, I believe that this is the issue I am facing, hopefully you can make modifications to your binding project to help out..

    www.andipalo.com/2017/07/16/java-seesharp-xamarin/

  • FreakyAliFreakyAli USMember ✭✭

    I will look into it ASAP, will let you know if I get somewhere also I plan on adding the binding to Github today will link it here, Also you will be free to contribute to it :#

  • AldyPutraAldyPutra USMember ✭✭

    guys is any update ? i need play core for xamarin android to use in app update feature, thanks for advance :)

  • Julien_PerezJulien_Perez Member ✭✭

    @FreakyAli Any updates about the nuget package ?

  • KastaKasta CZMember ✭✭
    edited April 29

    Any progress on this guys?

    @funklet1 Same errors like you. Have you made it work?

  • PatGetPatGet DEMember ✭✭

    Hi,

    seems like @funklet1 and @FreakyAli got at least immediate update working, but I don´t know how to do that. Any Idea how that works?

  • GabrielDwightGabrielDwight Member ✭✭

    Hi, @PatGet I got immediate update working though I had to bind the latest version of the play core library in my project. I guess there is an available xamarin play core library in nuget which is in preview.

    Tested the functionality in both debug (using the fake install app update manager to simulate the immediate update) and release configuration (able to check and detect updates from play store when the application is launched and update the app without any issues).

    Sample code snippet
    public class UpdateChecker
    {
    private Activity _activity;
    private int _Request_Update;
    public UpdateChecker(Activity activity, int Request_Update)
    {
    _activity = activity;
    _Request_Update = Request_Update;
    }

                public void CheckForUpdates()
                {
        #if DEBUG
                    var appUpdateManager = new FakeAppUpdateManager(_activity);
                    appUpdateManager.SetUpdateAvailable(versionxxxxx);
        #else
                    var appUpdateManager = GetManager();
        #endif
                    // Returns an intent object that you use to check for an update.
                    var appUpdateInfoTask = appUpdateManager.AppUpdateInfo;
                    appUpdateInfoTask.AddOnSuccessListener(new AppUpdateSuccessListener(appUpdateManager, _activity, _Request_Update));
                }
    
                private class AppUpdateSuccessListener : Java.Lang.Object, IOnSuccessListener
                {
                    private readonly IAppUpdateManager _appUpdateManager;
                    private readonly Activity _mainActivity;
                    private readonly int _update_request;
    
                    public AppUpdateSuccessListener(IAppUpdateManager appUpdateManager, Activity mainActivity, int update_request)
                    {
                        _appUpdateManager = appUpdateManager;
                        _mainActivity = mainActivity;
                        _update_request = update_request;
                    }
    
                    public void OnSuccess(Object p0)
                    {
                        if (!(p0 is AppUpdateInfo info))
                        {
                            return;
                        }
    
                        Log.Debug("AVAILABLE VERSION CODE", $"{info.AvailableVersionCode()}");
    
                        var availability = info.UpdateAvailability();
                        if((availability.Equals(UpdateAvailability.UpdateAvailable) || availability.Equals(UpdateAvailability.DeveloperTriggeredUpdateInProgress)) && info.IsUpdateTypeAllowed(AppUpdateType.Immediate))
                        {    
                            // Start an update
                            _appUpdateManager.StartUpdateFlowForResult(info, AppUpdateType.Immediate, _mainActivity, _update_request);
                        }
    
        #if DEBUG
                        var fakeAppUpdate = _appUpdateManager as FakeAppUpdateManager;
                        if (fakeAppUpdate.IsImmediateFlowVisible)
                        {
                            fakeAppUpdate.UserAcceptsUpdate();
                            fakeAppUpdate.DownloadStarts();
                            fakeAppUpdate.DownloadCompletes();
                            LaunchRestartDialog(_appUpdateManager);
                        }
        #endif
                    }
    
                    private void LaunchRestartDialog(IAppUpdateManager appUpdateManager)
                    {
                        AndroidX.AppCompat.App.AlertDialog.Builder dialog = new AndroidX.AppCompat.App.AlertDialog.Builder(_mainActivity);
                        AndroidX.AppCompat.App.AlertDialog alert = dialog.Create();
                        alert.SetMessage("Application successfully updated! You need to restart the app in order to use this new features");
                        alert.SetCancelable(false);
                        alert.SetButton((int)DialogButtonType.Positive, "Restart", (o, args) =>
                        {
                            appUpdateManager.CompleteUpdate();
                        });
                        alert.Show();
                    }
                }
    
                private IAppUpdateManager GetManager()
                {
                    // Creates instance of the manager.
                    return AppUpdateManagerFactory.Create(_activity);
                }
    
  • GabrielDwightGabrielDwight Member ✭✭

    Hi @PatGet I got immediate update working on both play store and test environment though I had to bind the latest version of play core library in my project.

    Sample code snippet below:
    `public class UpdateChecker
    {
    private Activity _activity;
    private int _Request_Update;
    public UpdateChecker(Activity activity, int Request_Update)
    {
    _activity = activity;
    _Request_Update = Request_Update;
    }

            public void CheckForUpdates()
            {
    #if DEBUG
                var appUpdateManager = new FakeAppUpdateManager(_activity);
                appUpdateManager.SetUpdateAvailable(#VersionCodeForYourApp#);
    #else
                var appUpdateManager = GetManager();
    #endif
                // Returns an intent object that you use to check for an update.
                var appUpdateInfoTask = appUpdateManager.AppUpdateInfo;
                appUpdateInfoTask.AddOnSuccessListener(new AppUpdateSuccessListener(appUpdateManager, _activity, _Request_Update));
            }
    
            private class AppUpdateSuccessListener : Java.Lang.Object, IOnSuccessListener
            {
                private readonly IAppUpdateManager _appUpdateManager;
                private readonly Activity _mainActivity;
                private readonly int _update_request;
    
                public AppUpdateSuccessListener(IAppUpdateManager appUpdateManager, Activity mainActivity, int update_request)
                {
                    _appUpdateManager = appUpdateManager;
                    _mainActivity = mainActivity;
                    _update_request = update_request;
                }
    
                public void OnSuccess(Object p0)
                {
                    if (!(p0 is AppUpdateInfo info))
                    {
                        return;
                    }
    
                    Log.Debug("AVAILABLE VERSION CODE", $"{info.AvailableVersionCode()}");
    
    
    
                    var availability = info.UpdateAvailability();
                    if((availability.Equals(UpdateAvailability.UpdateAvailable) || availability.Equals(UpdateAvailability.DeveloperTriggeredUpdateInProgress)) && info.IsUpdateTypeAllowed(AppUpdateType.Immediate))
                    {
    
                        // Start an update
                        _appUpdateManager.StartUpdateFlowForResult(info, AppUpdateType.Immediate, _mainActivity, _update_request);
                    }
    
    #if DEBUG
                    var fakeAppUpdate = _appUpdateManager as FakeAppUpdateManager;
                    if (fakeAppUpdate.IsImmediateFlowVisible)
                    {
                        fakeAppUpdate.UserAcceptsUpdate();
                        fakeAppUpdate.DownloadStarts();
                        fakeAppUpdate.DownloadCompletes();
                        LaunchRestartDialog(_appUpdateManager);
                    }
    #endif
                }
    
                private void LaunchRestartDialog(IAppUpdateManager appUpdateManager)
                {
                    AndroidX.AppCompat.App.AlertDialog.Builder dialog = new AndroidX.AppCompat.App.AlertDialog.Builder(_mainActivity);
                    AndroidX.AppCompat.App.AlertDialog alert = dialog.Create();
                    alert.SetMessage("Application successfully updated! You need to restart the app in order to use this new features");
                    alert.SetCancelable(false);
                    alert.SetButton((int)DialogButtonType.Positive, "Restart", (o, args) =>
                    {
                        appUpdateManager.CompleteUpdate();
                    });
                    alert.Show();
                }
            }
    
            private IAppUpdateManager GetManager()
            {
                // Creates instance of the manager.
                return AppUpdateManagerFactory.Create(_activity);
            }`
    
  • PatGetPatGet DEMember ✭✭

    Hey @GabrielDwight .
    many thanks! That looks good, at least it compiles with a minor change.

    Did you do anything special on the Binding? I had to add two " <remove-node" Entries:


    How do you call the UpdateChecker? I just call it in my MainActivity OnCreate, set _activity to this and _Request_Update to 1. Seems like that should work but my AppUpdateSuccessListener is never called. I´m using Closed Alpha Track. Should that work in Debug or is that the reason why you had the FakeAppUpdateManager implemented?

  • NaveenKumar.1636NaveenKumar.1636 USMember ✭✭

    Any one can help me how to get this AndroidX.AppCompat

  • PatGetPatGet DEMember ✭✭

    @NaveenKumar.1636 if you are not using Android X you can just replace "AndroidX.AppCompat.App" with "Android.App"

  • GabrielDwightGabrielDwight Member ✭✭

    Hey @PatGet

    For the bindings I had to solve some issues though I did it in some shady way because the latest version had some java generics assigned to Tasks. This the binding entries I had:
    <attr path="/api/package[@name='com.google.android.play.core.assetpacks']/ class[@name='NativeAssetPackStateUpdateListener']/ method[@name='onStateUpdate' and count(parameter)=1 and parameter[1][@type='com.google.android.play.core.assetpacks.AssetPackState']]/ parameter[1]" name="managedType"> Java.Lang.Object </attr> <attr path="/api/package[@name='com.google.android.play.core.assetpacks']/class[@name='AssetPackException']" name="extends">Java.Lang.Object</attr> <remove-node path="/api/package[@name='com.google.android.play.core.tasks']/class[@name='NativeOnCompleteListener']"/> <attr path="/api/package[@name='com.google.android.play.core.tasks']/class[@name='Tasks']" name="managedName">Tasks</attr> <attr path="/api/package[@name='com.google.android.play.core.splitcompat']/class[@name='SplitCompat']" name="managedName">SplitCompat</attr> <attr path="/api/package[@name='com.google.android.play.core.splitinstall']/class[@name='SplitInstallException']" name="extends">Java.Lang.Object</attr> <attr path="/api/package[@name='com.google.android.play.core.install']/class[@name='InstallException']" name="extends">Java.Lang.Object</attr> <attr path="/api/package[@name='com.google.android.play.core.tasks']/class[@name='RuntimeExecutionException']" name="extends">Java.Lang.Object</attr> <attr path="/api/package[@name='com.google.android.play.core.listener']/interface[@name='StateUpdatedListener']/method[@name='onStateUpdate' and count(parameter)=1 and parameter[1][@type='StateT']]/parameter[1]" name="type">Java.Lang.Object</attr>

    If you are going to use other features like splitinstaller it may require further testing.

    You can call the updatechecker either on the Mainactivity oncreate or onresume method depending on your preference. Let say you call on the oncreate method like this
    // Check for updates updateChecker = new UpdateChecker(this, REQUEST_UPDATE); updateChecker.CheckForUpdates();

    The reason I was using fakeupdatemanager in debug configuration is to simulate the update process though it won't show the immediate update play store window that's why I decided to show the Alert dialog in debug configuration. If you are using close alpha track in the play developer console. You may require to publish the application in release that will have the higher version code though with the same signing certificate then the application that you will use for testing may need to be in release configuration but with the lower version code number.

    The moment you run the application with the lower version code you should see the updatemanager firing up automatically to display the immediate update window. You can let me know if you have succeeded in your testing.

  • NAVEENKUMAR_GNAVEENKUMAR_G USMember ✭✭

    @All

    I have able to complete the mentioned without any error it compiled successfully.

    But when we click on restart for update nothing is happening while debugging am I missing anything?

Sign In or Register to comment.