0

I'm updating a working app that targeted API 9 (Pie) to API 33 (Tiramisu) and the camera always returns Result.Cancelled to the OnActivityResult code. I'm using Visual Studio 2022 Version 17.3.6 on a Windows 11 pro machine. This is my camera code:

camerabutton.Click += (sender, evt) =>
{
    var cameraispresent = checkCameraHardware(this);
    if (cameraispresent)
    {
        try
        {
            CreateDirectoryForPictures();
            MySubjectInfo.Name = MySubjectInfo.Name.Replace(",", "");
            MySubjectInfo.Name = MySubjectInfo.Name.Replace(" ", "");
            MySubjectInfo.Name = MySubjectInfo.Name.Replace(".", "");
            var filename = MySubjectInfo.Name + ".jpg";
            Intent intent = new Intent(MediaStore.ActionImageCapture);
            App._file = new File(App._dir, String.Format(filename, Guid.NewGuid()));
            intent.PutExtra(MediaStore.ExtraOutput, Uri.FromFile(App._file));
            StartActivityForResult(intent, TakePictureRequestCode);
        }
        catch (Exception e)
        {
            var result = e.Message;
        };
    }
};

The create directory code, this code was also updated to reflect changes in API 33, however I can't find the folder on my test device via file explorer where the photos should be stored yet App._dir.Exists() returns true saying it's there:

    private void CreateDirectoryForPictures()
    {
        int version = (int)Android.OS.Build.VERSION.SdkInt;

        var root = "";

        if (Android.OS.Environment.IsExternalStorageEmulated)
        {
            root = Android.OS.Environment.ExternalStorageDirectory.ToString();
        }
        else
        {
            root = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyPictures);
        }

        if (version >= Convert.ToInt32(BuildVersionCodes.Q))
        {

            App._dir = new File(root + "/PhotoManager");
        }
        else
        {
            App._dir = new File(
                Environment.GetExternalStoragePublicDirectory(
                    Environment.DirectoryPictures), "PhotoManager");
        }

        if (!App._dir.Exists())
        {
            App._dir.Mkdirs();
        }
    }

This is the app class where I store the data:

public static class App
{
    public static File _file;
    public static File _dir;
    public static Bitmap bitmap;
}

This is the OnActivityResult1 code, I left my API 9 code in there commented out so you can see where I was and where I'm going. With API 9 I needed to resize the picture to scale it down to 160 X 100 as these photos are used for ID cards, I commented that code out until I can figure out why the camera intent is returning null:

    protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
    {
        base.OnActivityResult(requestCode, resultCode, data);

        if (requestCode == TakePictureRequestCode  && resultCode == Result.Ok)
        {
            //Intent mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
            //Uri contentUri = Uri.FromFile(App._file);
            //mediaScanIntent.SetData(contentUri);
            //SendBroadcast(mediaScanIntent);

            Bitmap photo = (Bitmap)data.GetByteArrayExtra("data");
            App.bitmap = photo;
            //int height = 160; //Resources.DisplayMetrics.HeightPixels;
            //int width = 100; //MyImageView.Height;
            //App.bitmap = App._file.Path.LoadAndResizeBitmap(width, height);
            if (App.bitmap != null)
            {
                MyImageView.SetImageBitmap(App.bitmap);

                
                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();

                bitmap = null;
                App.bitmap = null;
                PictureTaken = true;
            }

            // Dispose of the Java side bitmap.
            GC.Collect();

        }
    }

So what am I doing wrong?

Prescott Chartier
  • 1,519
  • 3
  • 17
  • 34
  • OnActivityResult is deprecated. You have to use the [ActivityResultLauncher](https://stackoverflow.com/questions/62671106/onactivityresult-method-is-deprecated-what-is-the-alternative) – user496854 Nov 02 '22 at 22:40
  • I reviewed that article and incorporated the code into my project, but I cannot resolve `RegisterForActivityResult`. The error I get is: `The name 'RegisterForActivityResult' does not exist in the current context`. Not sure how to proceed. I cannot find any information on `RegisterForActivityResult` anywhere. – Prescott Chartier Nov 03 '22 at 03:13
  • You can check this [link](https://stackoverflow.com/questions/66541013/how-to-use-activityresultcontract-in-xamarin-android) and [this](https://stackoverflow.com/questions/38117079/how-to-await-onactivityresult-in-xamarin-c-sharp-on-android), it tells how to use `OnActivityResult` on Xamarin.Android. By the way, there is an [issue](https://github.com/xamarin/AndroidX/issues/289) about it... – Jianwei Sun - MSFT Nov 03 '22 at 07:37
  • Yes, I've seen all of that but it doesn't address the fact that `RegisterForActivityResult` does not resolve in Visual Studio 2022 C#. I'm still researching, but so far haven't gotten anywhere. – Prescott Chartier Nov 03 '22 at 12:27
  • @Prescott Chartier RegisterForActivityResult does not resolve in Visual Studio 2022 C#. What is your using statement. You will require using AndroidX.Activity.Result; – user2153142 Nov 03 '22 at 23:12
  • Yes, I have that statement in my project. `using AndroidX.Activity.Result;`. – Prescott Chartier Nov 04 '22 at 02:22
  • I have working code posted here https://stackoverflow.com/questions/74800669/class-activityresultcallback-java-lang-object-iactivityresultcallback-result .... now, of course, another problem ... – Prescott Chartier Dec 14 '22 at 16:10

2 Answers2

0

You cannot use Uri.FromFile anymore since Android 7.

Use FileProvider to serve a writable uri for the camera app.

blackapps
  • 8,011
  • 2
  • 11
  • 25
  • Ok, looked at that and I followed the code example at https://stackoverflow.com/questions/59107681/xamarin-forms-fileprovider-geturiforfile-the-name-context-does-not-exist-in and I get an error on the FileProvider Authority, that error is: Couldn't find meta-data for provider with authority MyApp.fileprovider. Changed "fileprovider" to "FileProvider" and got the same result. What ami I missing? – Prescott Chartier Nov 03 '22 at 15:49
  • How would i know without seeing code. Please make another stackoverflow post with only your FileProvider problem. – blackapps Nov 03 '22 at 16:57
0

Example: EnsureBluetoothEnabled called from a permission request

Setup

activityResultCallback = new ActivityResultCallback();
activityResultCallback.OnActivityResultCalled += ActivityResultCallback_ActivityResultCalled;
activityResultLauncher = RegisterForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResultCallback);


private void EnsureBluetoothEnabled()
{
            if ((bluetoothAdapter != null) && (!bluetoothAdapter.IsEnabled))
                activityResultLauncher.Launch(new Intent(BluetoothAdapter.ActionRequestEnable));
}

Handler:

private void ActivityResultCallback_ActivityResultCalled(object sender, ActivityResult result)
        {
            if (result.ResultCode == (int)Result.Ok)
            {
                if (bluetoothAdapter.State == State.On)
                    ShowBluetoothConfirmationDialog(true);
            }
            else if (result.ResultCode == (int)Result.Canceled)
                ShowBluetoothConfirmationDialog(false);

        }

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);
        }
    }
user2153142
  • 431
  • 1
  • 4
  • 7
  • I can't resolve `RegisterForActivityResult`, VS 2022 IDE reports "The name 'RegisterForActivityResult' does not exist in the current context". – Prescott Chartier Nov 04 '22 at 11:58
  • I created a case with Microsoft concerning this issue. https://developercommunity.visualstudio.com/t/Unable-to-resolve-RegisterForActivityRes/10189797?port=1025&fsid=5865f6fc-5a95-4ab7-ae8e-94a72ff710a4&ref=native&refTime=1667585704422&refUserId=ebd9983e-b3e0-6b4f-9a94-60b4174c2f34 – Prescott Chartier Nov 04 '22 at 18:15
  • Can you provide a sample demonstrating "does not exist in the current context"? Doubt you'll get far reporting it at developercommunity - controlled by bots. You would be better off reporting it at https://github.com/xamarin/xamarin-android – user2153142 Nov 04 '22 at 23:28
  • I was able to resolve this by setting `public class MyActivity : Activity` to `public class MyActivity : AppCompatActivity`. Got working code here https://stackoverflow.com/questions/74800669/class-activityresultcallback-java-lang-object-iactivityresultcallback-result .... and of course, another probelm. – Prescott Chartier Dec 14 '22 at 16:13