Home > OS >  class ActivityResultCallback : Java.Lang.Object, IActivityResultCallback result is always null
class ActivityResultCallback : Java.Lang.Object, IActivityResultCallback result is always null

Time:01-05

I have converted my old StartActivityForResult code to the new RegisterForActivityResult as StartActivityForResult is depreciated in the android API 29 and higher. My new code works perfectly, except that result in class ActivityResultCallback is always null. I need to be able to know when the user cancels the taking of the picture when they hit the back button, otherwise the app crashes (if a previous picture doesn't already exist) or a previously taken picture is processed again. My code (showing only the relevant code):

public class Photo : AppCompatActivity
{
    public ImageView MyImageView;
    public ImageButton camerabutton;
    public bool PictureTaken = false;
    private CurrentSubjectInfo MySubjectInfo;
    private ActivityResultCallback _activityResultCallback;
    private ActivityResultLauncher _activityResultLauncher;
    private Uri uri;

    protected override void OnCreate(Bundle bundle)
    {
        try 
        {
            _activityResultCallback = new ActivityResultCallback();
            _activityResultCallback.OnActivityResultCalled  = ActivityResultCallback_ActivityResultCalled;
            _activityResultLauncher = RegisterForActivityResult(new ActivityResultContracts.TakePicture(), _activityResultCallback);
            RequestedOrientation = Android.Content.PM.ScreenOrientation.Portrait;
            base.OnCreate(bundle);
            SetContentView(Resource.Layout.Photo);
            PictureTaken = false;
            MySubjectInfo = new CurrentSubjectInfo("", "", "", "", "");
            // retrieve subject information from previous activity
            MySubjectInfo = Mybundle.GetParcelable("MySubjectInfo", Java.Lang.Class.FromType(typeof(CurrentSubjectInfo))) as CurrentSubjectInfo;
            ImageButton camerabutton = FindViewById<ImageButton>(Resource.Id.button1);
            MyImageView = FindViewById<ImageView>(Resource.Id.imageView1);

            camerabutton.Click  = (sender, evt) =>
            {

                var cameraispresent = CheckCameraHardware();
                if (cameraispresent)
                {
                    try
                    {
                        // get directory where pictures are stored
                        App._dir = Environment.GetExternalStoragePublicDirectory(Environment.DirectoryPictures);
                        // build file name
                        MySubjectInfo.Name = MySubjectInfo.Name.Replace(",", "");
                        MySubjectInfo.Name = MySubjectInfo.Name.Replace(" ", "");
                        MySubjectInfo.Name = MySubjectInfo.Name.Replace(".", "");
                        var filename = MySubjectInfo.Name   ".jpg";
                        App._file = new File(App._dir, String.Format(filename, Guid.NewGuid()));
                        uri = FileProvider.GetUriForFile(this, this.ApplicationContext.PackageName   ".provider",App._file);
                        // launch camera activity
                        _activityResultLauncher.Launch(uri);

                    }
                    catch (Exception e)
                    {
                        // trap error and log it
                    };

                }
            };
        }
    }
    Boolean CheckCameraHardware()
    {
        Android.Content.PM.PackageManager pm = PackageManager;
        if (pm.HasSystemFeature(Android.Content.PM.PackageManager.FeatureCamera))
        {
            // this device has a camera
            return true;
        }
        else
        {
            // no camera on this device
            return false;
        }
    }

    private void ActivityResultCallback_ActivityResultCalled(object sender, ActivityResult result)
    {
        // result is always null, so I don't check it
        try
        {
            // Because the resulting bitmap is rotated and too large, I set original orientation and resize
            // suffice it to say it works perfectly so I won't post the code here
            int height = 260;
            int width = 200;
            App.bitmap = App._file.Path.LoadAndResizeBitmap(width, height);

            Bitmap bitmap = App.bitmap;
            var filePath = App._file.AbsolutePath;
            // save the resized bitmap, overwriting original file
            var stream = new System.IO.FileStream(filePath, System.IO.FileMode.Create);
            bitmap.Compress(Bitmap.CompressFormat.Jpeg, 100, stream);
            stream.Close();
            // set the imageview to the resulting bitmap
            MyImageView.SetImageBitmap (App.bitmap);
            // cleanup
            bitmap = null;
            App.bitmap = null;
            PictureTaken = true;
        }
        catch (Exception ex)
        {
            PictureTaken = false;
            // trap and log error
        }
    }
}

This is the ActivityResultCallback class:

public class ActivityResultCallback : Java.Lang.Object, IActivityResultCallback
{
    public EventHandler<ActivityResult> OnActivityResultCalled;

    public void OnActivityResult(Java.Lang.Object result)
    {
        ActivityResult activityResult = result as ActivityResult;
        OnActivityResultCalled?.Invoke(this, activityResult);
    }
}

As stated all this code executes perfectly with no errors except that Java.Lang.Object result is always null. I need to know when the user cancels the camera activity, I would assume that Java.Lang.Object result would indicate cancelled but I get nothing. What am I missing?

CodePudding user response:

Ok, after some playing around I noticed that the parameter Java.Lang.Object result in the ActivityResultCallback class was either true or false depending on what the user did with the camera. So I changed the class to:

public class ActivityResultCallback : Java.Lang.Object, IActivityResultCallback
{
    public EventHandler<ActivityResult> OnActivityResultCalled;
    
    public void OnActivityResult(Java.Lang.Object result)
    {

        ActivityResult activityResult;
        if ((bool)result)
        {
            activityResult = new ActivityResult((int)Result.Ok, null);
        } else
        {
            activityResult = new ActivityResult((int)Result.Canceled, null);
        }
        OnActivityResultCalled?.Invoke(this, activityResult);
    }
}

In the ActivityResultCallback_ActivityResultCalled function, I modified it to this:

    private void ActivityResultCallback_ActivityResultCalled(object sender, ActivityResult result)
    {

        try
        {
            if (result.ResultCode == (int)Result.Ok)
            {
                int height = 260;
                int width = 200;
                App.bitmap = App._file.Path.LoadAndResizeBitmap(width, height);

                Bitmap bitmap = App.bitmap;
                var filePath = App._file.AbsolutePath;
                var stream = new System.IO.FileStream(filePath, System.IO.FileMode.Create);
                bitmap.Compress(Bitmap.CompressFormat.Jpeg, 100, stream);
                stream.Close();

                MyImageView.SetImageBitmap(App.bitmap);

                bitmap = null;
                App.bitmap = null;
                PictureTaken = true;
            }
        }
        catch (Exception ex)
        {
            // trap and log error
        }
    }

activityResult apparently has, at a minimum, two parameters, the result and data. I already had what I needed for data so I set that to null and cast Result.Ok or Result.Cancelled to int depending on whether result was true or false. I still don't totally understand how to use the new ActivityForResult API, but this works for me and I'm running with it.

  • Related