Search code examples
c#androidxamarinxamarin.androidandroid-install-apk

OnNewIntent is not called after PackageInstaller failure


I use this code for installing APK: https://stackoverflow.com/a/61889386/14152249

My activity launch mode is set to SingleTop. I call InstallPackageAndroidQAndAbove from OnActivityResult (I need to uninstall app before that and I check if it's uninstalled there). Also I modified that code to pass the PendingIntentFlags.UpdateCurrent flag during the creation of PendingIntent object. The problem is when APK is successfully installed or when installer is waiting for user action, OnNewIntent is always properly called and then I do some stuff. But when user aborts install, OnNewIntent is not called (occasionally though it DOES get called). I couldn't find anything about such a behavior. How can I fix this? Or maybe it has something to do with the device which I'm debugging on (Xiaomi Mi 9T Pro with MIUI 12 based on Android 10)?

I need to show an alert dialog when app failed to install and stop the process. As a workaround I use StartActivityForResult and then in OnActivityResult I wait for 5 seconds and check if app is installed. But this workaround is bad of course because if installation takes more than 5 seconds then alert dialog will be shown anyway though the app itself will be installed.


Solution

  • I made a lot better workaround than the one described in my question. It's still a workaround though as I couldn't find an explanation of such strange behavior.

    The only thing I need from this is actually notifying about installation fail. I declared a class which inherits PackageInstaller.SessionCallback. It looks like this:

    public class PackageInstallObserver : PackageInstaller.SessionCallback
    {
        private PackageInstaller PackageInstaller { get; }
        public event EventHandler InstallFailed;
    
        public PackageInstallObserver(PackageInstaller packageInstaller) => PackageInstaller = packageInstaller;
    
        public override void OnActiveChanged(int sessionId, bool active) { }
    
        public override void OnBadgingChanged(int sessionId) { }
    
        public override void OnCreated(int sessionId) { }
    
        public override void OnFinished(int sessionId, bool success)
        {
            PackageInstaller.UnregisterSessionCallback(this);
            PackageInstaller.Dispose();
            if (!success) InstallFailed?.Invoke(this, EventArgs.Empty);
        }
    
        public override void OnProgressChanged(int sessionId, float progress) { }
    }
    

    Then in my InstallPackageAndroidQAndAbove method I create an instance of that class and pass it to packageInstaller.RegisterSessionCallback method. So now InstallPackageAndroidQAndAbove looks like this:

    public void InstallPackageAndroidQAndAbove(Android.Net.Uri apkUri)
    {
       var packageInstaller = PackageManager.PackageInstaller;
       var sessionParams = new PackageInstaller.SessionParams(PackageInstallMode.FullInstall);
       int sessionId = packageInstaller.CreateSession(sessionParams);
       var session = packageInstaller.OpenSession(sessionId);
    
       AddApkToInstallSession(apkUri, session);
            
       // Create an install status receiver.
       var intent = new Intent(this, this.Class);
       intent.SetAction(PACKAGE_INSTALLED_ACTION);
       var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.UpdateCurrent);
       var statusReceiver = pendingIntent.IntentSender;
    
       // Create an observer and register it
       var observer = new PackageInstallObserver(packageInstaller);
       observer.InstallFailed += OnInstallFailed; // Subscribe to event
       packageInstaller.RegisterSessionCallback(observer);
    
       // Commit the session (this will start the installation workflow).
       session.Commit(statusReceiver);
    }