How to activate front camera in MAUI using Media Picker?

Jaime Stuardo 66 Reputation points
2024-06-06T20:21:19.8266667+00:00

Hello...

I need to allow my MAUI app users to take a selfie, so that, I need to activate front camera. When using MediaPicker.Default.CapturePhotoAsync rear camera is used.

Any help, please?

Thanks.

.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
3,614 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Leon Lu (Shanghai Wicresoft Co,.Ltd.) 76,556 Reputation points Microsoft Vendor
    2024-06-10T00:56:42.48+00:00

    Hello,

    . it is Android....do you mean I should download the Media Picker source code and add it to my project and then, changing that line?

    Please copy the source code of MediaPicker for android platform. Then add captureIntent.PutExtra("android.intent.extras.CAMERA_FACING", 1); you can invoke this android platform code in your MAUI project.

    For Android, you can copy the CaptureAsync method in your MAUI project, then wrap it by Conditional compilation.

    #if ANDROID
        public async Task<FileResult> CaptureAsync(MediaPickerOptions options, bool photo)
        {
           
    
            await Permissions.CheckStatusAsync<Permissions.Camera>();
            // StorageWrite no longer exists starting from Android API 33
            if (!OperatingSystem.IsAndroidVersionAtLeast(33))
                await Permissions.CheckStatusAsync<Permissions.StorageWrite>();
    
            var captureIntent = new Intent(photo ? MediaStore.ActionImageCapture : MediaStore.ActionVideoCapture);
    
            if (!PlatformUtils.IsIntentSupported(captureIntent))
                throw new FeatureNotSupportedException($"Either there was no camera on the device or '{captureIntent.Action}' was not added to the <queries> element in the app's manifest file. See more: https://developer.android.com/about/versions/11/privacy/package-visibility");
    
    //add following line to open the front camera here
            captureIntent.PutExtra("android.intent.extras.CAMERA_FACING", 1);
    
            captureIntent.AddFlags(ActivityFlags.GrantReadUriPermission);
            captureIntent.AddFlags(ActivityFlags.GrantWriteUriPermission);
    
            try
            {
                var activity = ActivityStateManager.Default.GetCurrentActivity();
    
                string captureResult = null;
    
                if (photo)
                    captureResult = await CapturePhotoAsync(captureIntent);
               
    
                // Return the file that we just captured
                return new FileResult(captureResult);
            }
            catch (OperationCanceledException)
            {
                return null;
            }
        }
    
        internal static string Authority => Platform.AppContext.PackageName + ".fileProvider";
    
        async Task<string> CapturePhotoAsync(Intent captureIntent)
        {
            // Create the temporary file
            var fileName = Guid.NewGuid().ToString("N") + ".jpg";
            var tmpFile = FileSystemUtils.GetTemporaryFile(Platform.CurrentActivity.CacheDir, fileName);
    
            // Set up the content:// uri
            Android.Net.Uri outputUri = null;
    
            void OnCreate(Intent intent)
            {
                // Android requires that using a file provider to get a content:// uri for a file to be called
                // from within the context of the actual activity which may share that uri with another intent
                // it launches.
                outputUri ??= FileProvider.GetUriForFile(Platform.AppContext, Authority, tmpFile);
    
                intent.PutExtra(MediaStore.ExtraOutput, outputUri);
            }
    
            await MyIntermediateActivity.StartAsync(captureIntent, PlatformUtils.requestCodeMediaCapture, OnCreate);
    
            return tmpFile.AbsolutePath;
        }
    
    #endif
    
    

    If you use above method, you need to add FileSystemUtils.cs, PlatformUtils.cs and create a custom IntermediateActivity in yourProject.Platforms.Android folder.

    Here is code about FileSystemUtils.cs, PlatformUtils.cs. I made some changes about context and actitivty,

    #if ANDROID
    
    using Android.App;
    using Android.Content;
    using Android.Content.PM;
    using Android.Provider;
    
    using yourProject.Platforms.Android;
    #endif
    
    
    
    #if ANDROID
    
    static class FileSystemUtils
    {
        internal const string EssentialsFolderHash = "2203693cc04e0be7f4f024d5f9499e13";
        public static Java.IO.File GetTemporaryFile(Java.IO.File root, string fileName)
        {
            // create the directory for all Essentials files
            var rootDir = new Java.IO.File(root, EssentialsFolderHash);
            rootDir.Mkdirs();
            rootDir.DeleteOnExit();
    
            // create a unique directory just in case there are multiple file with the same name
            var tmpDir = new Java.IO.File(rootDir, Guid.NewGuid().ToString("N"));
            tmpDir.Mkdirs();
            tmpDir.DeleteOnExit();
    
            // create the new temporary file
            var tmpFile = new Java.IO.File(tmpDir, fileName);
            tmpFile.DeleteOnExit();
    
            return tmpFile;
        }
    
    }
    
    static class PlatformUtils
    {
        internal const int requestCodeFilePicker = 11001;
        internal const int requestCodeMediaPicker = 11002;
        internal const int requestCodeMediaCapture = 11003;
        internal const int requestCodePickContact = 11004;
    
        internal const int requestCodeStart = 12000;
    
        static int requestCode = requestCodeStart;
    
        internal static int NextRequestCode()
        {
            if (++requestCode >= 12999)
                requestCode = requestCodeStart;
    
            return requestCode;
        }
    
        internal static Intent? RegisterBroadcastReceiver(BroadcastReceiver? receiver, IntentFilter filter)
        {
    #if ANDROID34_0_OR_GREATER
            if (OperatingSystem.IsAndroidVersionAtLeast(34))
            {
                return Platform.AppContext.RegisterReceiver(receiver, filter, ReceiverFlags.NotExported);
            }
    #endif
            return Platform.AppContext.RegisterReceiver(receiver, filter);
        }
    
        internal static bool HasSystemFeature(string systemFeature)
        {
            var packageManager = Platform.AppContext.PackageManager;
            if (packageManager is not null)
            {
                foreach (var feature in packageManager.GetSystemAvailableFeatures())
                {
                    if (feature?.Name?.Equals(systemFeature, StringComparison.OrdinalIgnoreCase) ?? false)
                        return true;
                }
            }
            return false;
        }
    
        internal static bool IsIntentSupported(Intent intent)
        {
            if (Platform.AppContext is not Context ctx || ctx.PackageManager is not PackageManager pm)
                return false;
    
            return intent.ResolveActivity(pm) is not null;
        }
    
        internal static bool IsIntentSupported(Intent intent, string expectedPackageName)
        {
            if (Platform.AppContext is not Context ctx || ctx.PackageManager is not PackageManager pm)
                return false;
    
            return intent.ResolveActivity(pm) is ComponentName c && c.PackageName == expectedPackageName;
        }
    }
    #endif
    
    

    Then open your yourProject.Platforms.Android folder, add following MyIntermediateActivity.cs.

    using Android.App;
    using Android.Content;
    using Android.Content.PM;
    using Android.OS;
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace yourProject.Platforms.Android
    {
        [Activity(ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, Exported = false)]
        internal class MyIntermediateActivity : Activity
        {
            const string launchedExtra = "launched";
            const string actualIntentExtra = "actual_intent";
            const string guidExtra = "guid";
            const string requestCodeExtra = "request_code";
    
            static readonly ConcurrentDictionary<string, IntermediateTask> pendingTasks = new();
    
            bool launched;
            Intent? actualIntent;
            string? guid;
            int requestCode;
    
            protected override void OnCreate(Bundle? savedInstanceState)
            {
                base.OnCreate(savedInstanceState);
    
                var extras = savedInstanceState ?? Intent?.Extras;
    
                // read the values
                launched = extras?.GetBoolean(launchedExtra, false) ?? false;
    #pragma warning disable 618 // TODO: one day use the API 33+ version: https://developer.android.com/reference/android/os/Bundle#getParcelable(java.lang.String,%20java.lang.Class%3CT%3E)
    #pragma warning disable CA1422 // Validate platform compatibility
    #pragma warning disable CA1416 // Validate platform compatibility
                actualIntent = extras?.GetParcelable(actualIntentExtra) as Intent;
    #pragma warning restore CA1422 // Validate platform compatibility
    #pragma warning restore CA1416 // Validate platform compatibility
    #pragma warning restore 618
                guid = extras?.GetString(guidExtra);
                requestCode = extras?.GetInt(requestCodeExtra, -1) ?? -1;
    
                if (GetIntermediateTask(guid) is IntermediateTask task)
                {
                    task.OnCreate?.Invoke(actualIntent!);
                }
    
                // if this is the first time, lauch the real activity
                if (!launched)
                    StartActivityForResult(actualIntent, requestCode);
            }
    
            protected override void OnSaveInstanceState(Bundle outState)
            {
                // make sure we mark this activity as launched
                outState.PutBoolean(launchedExtra, true);
    
                // save the values
                outState.PutParcelable(actualIntentExtra, actualIntent);
                outState.PutString(guidExtra, guid);
                outState.PutInt(requestCodeExtra, requestCode);
    
                base.OnSaveInstanceState(outState);
            }
    
            protected override void OnActivityResult(int requestCode, Result resultCode, Intent? data)
            {
                base.OnActivityResult(requestCode, resultCode, data);
    
                // we have a valid GUID, so handle the task
                if (GetIntermediateTask(guid, true) is IntermediateTask task)
                {
                    if (resultCode == Result.Canceled)
                    {
                        task.TaskCompletionSource.TrySetCanceled();
                    }
                    else
                    {
                        try
                        {
                            data ??= new Intent();
    
                            task.OnResult?.Invoke(data);
    
                            task.TaskCompletionSource.TrySetResult(data);
                        }
                        catch (Exception ex)
                        {
                            task.TaskCompletionSource.TrySetException(ex);
                        }
                    }
                }
    
                // close the intermediate activity
                Finish();
            }
    
            public static Task<Intent> StartAsync(Intent intent, int requestCode, Action<Intent>? onCreate = null, Action<Intent>? onResult = null)
            {
                // make sure we have the activity
                var activity = ActivityStateManager.Default.GetCurrentActivity()!;
    
                // create a new task
                var data = new IntermediateTask(onCreate, onResult);
                pendingTasks[data.Id] = data;
    
                // create the intermediate intent, and add the real intent to it
                var intermediateIntent = new Intent(activity, typeof(MyIntermediateActivity));
                intermediateIntent.PutExtra(actualIntentExtra, intent);
                intermediateIntent.PutExtra(guidExtra, data.Id);
                intermediateIntent.PutExtra(requestCodeExtra, requestCode);
    
                // start the intermediate activity
                activity.StartActivityForResult(intermediateIntent, requestCode);
    
                return data.TaskCompletionSource.Task;
            }
    
            static IntermediateTask? GetIntermediateTask(string? guid, bool remove = false)
            {
                if (string.IsNullOrEmpty(guid))
                    return null;
    
                if (remove)
                {
                    pendingTasks.TryRemove(guid, out var removedTask);
                    return removedTask;
                }
    
                pendingTasks.TryGetValue(guid, out var task);
                return task;
            }
    
            class IntermediateTask
            {
                public IntermediateTask(Action<Intent>? onCreate, Action<Intent>? onResult)
                {
                    Id = Guid.NewGuid().ToString();
                    TaskCompletionSource = new TaskCompletionSource<Intent>();
    
                    OnCreate = onCreate;
                    OnResult = onResult;
                }
    
                public string Id { get; }
    
                public TaskCompletionSource<Intent> TaskCompletionSource { get; }
    
                public Action<Intent>? OnCreate { get; }
    
                public Action<Intent>? OnResult { get; }
            }
        }
    }
    
    
    

    In the end, you can take a photo with font camera directly.

    #if ANDROID
            FileResult photo=  await CaptureAsync(new MediaPickerOptions(),true);
           
    #endif
    
    

    Best Regards,

    Leon Lu


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.