question

AG-1964 avatar image
0 Votes"
AG-1964 asked JarvanZhang-MSFT edited

Save a picture from camera to device gallery using Xamarin.Essentials 1.7 Media Picker and Xamarin Forms?

Hi,

how to save a picture from camera to device gallery using Xamarin.Essentials 1.7 Media Picker and Xamarin Forms for Android 11 and iOS camera roll?

The only two options I have found are FileSystem.AppDataDirectory and FileSystem.CacheDirectory

Thanks,
AG

dotnet-xamarin
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Are there plans to add the option to save to gallery in Xamarin.Essentials Media Picker? I am currently using MediaPlugin and it works perfectly so why is it not in Xamarin.Essentials? So far I have avoided platform specific code.

Charles

0 Votes 0 ·
RobCaplan avatar image RobCaplan charlesyoung-8818 ·

There is an enhancement request at https://github.com/xamarin/Essentials/issues/1455

0 Votes 0 ·
JarvanZhang-MSFT avatar image
1 Vote"
JarvanZhang-MSFT answered JarvanZhang-MSFT edited

Hello @AG-1964 ,​

Welcome to our Microsoft Q&A platform!

how to save a picture from camera to device gallery using Xamarin.Essentials 1.7 Media Picker

Xamarin.Essentials.MediaPicker doesn't provide the function to save the captured phto to the gallery.

For this function, please try to save the file on each platform. Then call the function code in the shared project using DependencyService. Here is sample code, you could refer to:

private async void Button_Clicked(object sender, EventArgs e)
{
    var photo = await MediaPicker.CapturePhotoAsync();
    var stream = await photo.OpenReadAsync();

    MemoryStream ms = new MemoryStream();
    stream.CopyTo(ms);
    var bytes = ms.ToArray();

    DependencyService.Get<ITestInterface>().StorePhotoToGallery(bytes, "test.png");
}

//platform implementation
[assembly: Xamarin.Forms.Dependency(typeof(TestImplementation))]
namespace TestApplication_5.Droid
{
    public class TestImplementation : ITestInterface
    {
        public async void storePhotoToGallery(byte[] bytes, string fileName)
        {
            Context context = MainActivity.Instance;
            try
            {
                Java.IO.File storagePath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
                string path = System.IO.Path.Combine(storagePath.ToString(), fileName);
                System.IO.File.WriteAllBytes(path, bytes);
                var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
                mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(path)));
                context.SendBroadcast(mediaScanIntent);
            }
            catch (Exception ex)
            {
            }
        }
    }
}

For the iOS implementation, you could refer to: https://stackoverflow.com/a/60630182/11083277

You could also MediaPlugin instead. It provides the SaveToAlbum option when taking the new photo.

var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions
{
    SaveToAlbum = true
});

Best Regards,

Jarvan Zhang


If the response is helpful, please click "Accept Answer" and upvote it.

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.


· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi, @AG-1964
I have not heard from you for a couple of days. Please let me know if there is anything that I can help here.

0 Votes 0 ·
AG-1964 avatar image
0 Votes"
AG-1964 answered JarvanZhang-MSFT converted comment to answer

Hi @JarvanZhang-MSFT

I have created a test Solution name SaveImageToGallery and in SaveImageToGallery.Android I have created an interface

 public interface ISaveToGallery
 {
     void storePhotoToGallery(byte[] bytes, string fileName);
 }


I have created a SaveToGallery class that inherits the interface

 public class SaveToGallery : ISaveToGallery
 {
     public async void storePhotoToGallery(byte[] bytes, string fileName)
     {
         Context context = MainActivity.Instance;
         try
         {
             Java.IO.File storagePath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
             string path = System.IO.Path.Combine(storagePath.ToString(), fileName);
             System.IO.File.WriteAllBytes(path, bytes);
             var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
             mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(path)));
             context.SendBroadcast(mediaScanIntent);
         }
         catch (Exception ex)
         {
         }
     }
 }

I am receiving an error "'MainActivity' does not contain a definition for 'Instance'" for the line "Context context = MainActivity.Instance;"

For the line "Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures)" I am receiving the warning "'Environment.GetExternalStoragePublicDirectory(string?)' is obsolete: 'deprecated'

For the line Intent.ActionMediaScannerScanFile I am receiving the warning "'Intent.ActionMediaScannerScanFile' is obsolete: 'deprecated'"



For iOS, I would like to use Xamarin.Essentials 1.7 Media Picker and not CrossMedia from https://github.com/jamesmontemagno/MediaPlugin as it is obsolete and not maintain anymore.

Regards,
AG

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

JarvanZhang-MSFT avatar image
0 Votes"
JarvanZhang-MSFT answered KhaiFLPM-7217 commented

I am receiving an error "'MainActivity' does not contain a definition for 'Instance'" for the line "Context context = MainActivity.Instance

I created a static instance in the MainActivity class like below.

public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
    public static MainActivity Instance { get; set; }
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        Instance = this;
        ...
        LoadApplication(new App());
    }
}

I am receiving the warning "'Intent.ActionMediaScannerScanFile' is obsolete: 'deprecated'"

Java.IO.File storagePath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
string path = System.IO.Path.Combine(storagePath.ToString(), fileName);
System.IO.File.WriteAllBytes(path, bytes);
MediaScannerConnection.ScanFile(Android.App.Application.Context, new string[] { path }, null, null);

I am receiving the warning "'Environment.GetExternalStoragePublicDirectory(string?)' is obsolete: 'deprecated

This method is deprecated on Android 10, try using ContentResolver to store the image on Android 10 instead. Here is a similar thread using native code, you could refer to:
https://stackoverflow.com/a/63777157/11083277

· 4
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

The code does not working for me on Android as I am receiving the error

ex = {System.NullReferenceException: Object reference not set to an instance of an object.
at SaveImageToGallery.Droid.SaveToGallery.storePhotoToGallery (System.Byte[] bytes, System.String fileName) [0x0007e] in C:\XamarinProjects\SaveImageToGallery\SaveImageT...

Does this error apply because my Android device is running Android 11?
Also the link you have provided 11083277 is Java code not C# for Xamarin.

Here is my code:


 using Android.Content;
 using SaveImageToGallery.Droid;
 using System;
    
 [assembly: Xamarin.Forms.Dependency(typeof(SaveToGallery))]
 namespace SaveImageToGallery.Droid
 {
     public class SaveToGallery : ISaveToGallery
     {
         public async void storePhotoToGallery(byte[] bytes, string fileName)
         {
             Context context = MainActivity.Instance;
             try
             {
                 Java.IO.File storagePath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
                 string path = System.IO.Path.Combine(storagePath.ToString(), fileName);
                 System.IO.File.WriteAllBytes(path, bytes);
                 var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
                 mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(path)));
                 context.SendBroadcast(mediaScanIntent);
             }
             catch (Exception ex)
             {
             }
         }
     }
 }



Regards,
AG

0 Votes 0 ·

Hi, AG-1964. Which object is null? Though the code is deprecated in Android 11, but it still supports to save the file and should not cause an error. Please add a breakpoint to check which object caused the error.

Update

Here is the converted in c# of the link I posted.

public void storePhotoToGallery(System.IO.Stream stream, string fileName)
{
    Context context = MainActivity.Instance;
    try
    {
        if (Android.OS.Build.VERSION.SdkInt > BuildVersionCodes.Q)
        {
            ContentResolver resolver = context.ContentResolver;
            ContentValues contentValues = new ContentValues();
            contentValues.Put(MediaStore.MediaColumns.DisplayName, fileName);
            contentValues.Put(MediaStore.MediaColumns.MimeType, "image/png");
            contentValues.Put(MediaStore.MediaColumns.RelativePath, "DCIM/" + "test");
            Android.Net.Uri imageUri = resolver.Insert(MediaStore.Images.Media.ExternalContentUri, contentValues);

            var os = resolver.OpenOutputStream(imageUri);

            BitmapFactory.Options options = new BitmapFactory.Options();
            options.InJustDecodeBounds = true;
            var bitmap = BitmapFactory.DecodeStream(stream);
            bitmap.Compress(Bitmap.CompressFormat.Png, 100, os);

            os.Flush();
            os.Close();
        }
        else
        {
            //use the above code I posted
        }
    }
    catch (Exception ex)
    {

    }
}

0 Votes 0 ·

Hi,

Ok, the code is working now but I wonder why on Android, taking a picture with the camera is with delay also the saving to device where in iOS is doing so instantly and smoothly.

I am also looking for the same solution for iOS using Xamarin.Essentials 1.7 Media Picker and not CrossMedia from [https://github.com/jamesmontemagno/MediaPlugin][1] as it is obsolete and not maintain anymore

0 Votes 0 ·

on Android, taking a picture with the camera is with delay

Will this occur on all the Android devices? This may due to the Android system platform, this kind of freeze may be inevitable (this depends on many hardware and firmware design choices, made by the manufacturer).

I am also looking for the same solution for iOS using Xamarin.Essentials 1.7 Media Picker

To save the photo to gallery, try to use UIImage.SaveToPhotosAlbum command and call the using DependencyService in the shared project.

public void SaveImageFromByte(byte[] bytes, string fileName)
{
    var image = new UIImage(NSData.FromArray(bytes));
    image.SaveToPhotosAlbum((image, error) =>
    {

    });
}
0 Votes 0 ·