This is an update for my previous blog post on how to use the camera and photo gallery to get images. Since then, Android made some updates on how the file system works in version 7.0 so the previous approach will not work. I’ll be going over how to fix this issue for Android.

File Provider

First thing we need to do is to add a FileProvider to the AndroidManifest.xml file. Inside your application tag, add the provider and update the android:authorities value to include your own package name before .fileprovider.

You’ll notice a resource is getting referenced android:resource=”@xml/file_paths”, we need to create this file. In the Android project, create a new folder in the Resources folder called xml. Inside the newly created xml folder, create a new file called file_paths.xml. The contents of the file should look like this:

Again, make sure to update the com.yourcompany.appname to reflect what’s in your Manifest file.

You can find more information on this here.

Launching the camera and photo gallery

If you followed the previous blog, we are going to replace the two methods BringUpCamera() and BringUpPhotoGallery() with the following code. This new approach should work on newer Android OS and the older ones.

Handling the image result

After the picture has been taken with the camera or the image was selected from the gallery, the method OnActivityResult in your MainActivity.cs will get fired as usual. The way I have it set up, if the request code is 0 then we are expecting image data from the camera and if it’s a 1 then it came from the photo gallery; in my situation I wanted to save that image to the Documents Directory for later use. The variable App.selectedImageId is a static string defined in the Forms project, and this value represents the file name for the photo just taken, App.selectedImageId should be the same value as the parameter imageId in the method BringUpCamera(string imageId, FileFormatEnum fileType).

There is a lot going on here but it’s pretty straight forward. Once we get an image, it gets rotated to the orientation it was taken in, saved to the disk if a file name was supplied (only for images taken from the camera) and then using the MessagingCenter to send the bitmap data back up to your UI in the Forms project.

9 thoughts on “Xamarin Forms taking pictures on Android

  1. Many thanks for your time fixing this issue.

    But the method MessagingCenter.Subscribe(this, “ImageSelected”, (args) => .{ ..)

    is still not being invoked when I accept photo taken with the camera. I noticed this time it’s saved to storage but I need to get the byte[] for it like in your previous project. It’s not obvious to me how to do this (but then I only worked with Xamarin for a year).

    I can’t see many Use Cases where a photo needs to be stored (it’d quickly clog up storage and would need to be deleted by the app after being processed). In my case, I need to preview it in the page that took the photo and send it as bytes string via Web API (there is no need to store the photo!). Your previous project worked that way. Could you point me out in the right direction?

    Less important issue is that the interface signature for Android is now different to the iOS one from your previous projects, so the project will need some changes to integrate both projects.

  2. I might clean up the interface to match the previous implementation, a lot has changed since I wrote the last post a year ago.

    Can you confirm that MessagingCenter.Subscribe is getting invoked when you load an image from the photo gallery or if it works at all on iOS? Curious if it’s not working at all or if it’s only failing when taking a picture with the camera.

    The byte array for the image can be found in the method HandleBitmap on line 133. If you have no need to keep the image on the device, hardcode the file name in the first method BringUpCamera() so that every picture taken will just overwrite the previous, so at most you’ll only have one image being saved to the device.

  3. Many thanks for your reply Scott.

    I confirm MessagingCenter.Subscribe is getting invoked when selecting photo from Gallery.

    When I take photo with the camera, I get shown a preview of the photo with two icons – trashcan button and accept button (like in this screenshot: https://ibb.co/d8m5ZG). If I select trashcan, I can retake. When I select accept, I would expect the MessagingCenter.Subscribe to be invoked.

    I am on Nought (stock, moto G5) but this is not specific to my phone, it also happens on the Emulators in various older Android builds like Marshmellow etc.

    OnActivityResult() get invoked when camera photo accepted with reason code 0 as expected. but the Intent data parameter is null.

    Unfortunately I currently cannot test this on iOS.

  4. Extra information to the above: when the application is ran on a physical phone, same problem as above but the Intent data parameter is not null ( {Intent { act=inline-data (has extras) }} but its Data property is null (as expected with your solution, so not sure if this is helpful).

    It is as if the subscription signature is not the right type? Could this be the cause?

    MessagingCenter.Subscribe(this, “ImageSelected”, (args) =>
    {
    Device.BeginInvokeOnMainThread(() => //<< never gets invoked when camera photo accepted
    {
    //Set the source of the image view with the byte array
    (…)

  5. Very interesting, if it’s working for the photo gallery then I’d say you have the subscription set up correctly. At this point your best bet is to set break points or add a Console.WriteLine after every step and see where exactly the issue is at. It sounds like your not making it to the end where the MessagingCenter.Send is at. Make sure you have the permissions set in your AndroidManifest file for reading and writing to the external storage, if you haven’t already.

  6. Hi Scott, I managed to make it work. The screenshot below shows two small changes I had to do in OnActivityResult()

    https://ibb.co/j3YRbw

    Firstly, I had to comment out:

    var originalMetadata = new ExifInterface(pngFilename);

    as it was throwing a weird exception (no details provided).

    secondly, I had to comment out:

    if (File.Exists(pngFilename))

    You can ignore my App.selectedPetForPhoto = Guid.NewGuid().ToString().Replace(“-“, “”); it has had no effect on the second issue.

    With these changes, it works, so many thanks. It’d be good if you had the time to show a complete updated solution (iOS and Android like in your previous post). Also it would be ideal if you could provide all the key code used (I noticed you just mention changes needed to App.cs like selectedPetForPhoto but showing the code would help to less experienced Xamarin developers.It’s not clear how selectedPetForPhoto is generated, I presume a unique name each time photo taken?)

    Again, many thanks, appreciate your time providing this solution, too bad Xamarin Forms cannot provide such basic tasks out of the box, one wonders what’s the point of a supposed cross platform. It seems you need someone’s plug-ins to accomplish even the most basic of tasks!

  7. Good to hear! I’m not sure why that 1 line was throwing an exception. If your displaying the images in the app and notice that they are showing up sideways, you’ll need to uncomment the method that figures out what the correct orientation should be, and of course find out why it’s failing 🙂

  8. Scott, I’m adapting your code to a simple project that attempts to take a picture. I’ve copied the extra stuff into my manifest file and created file_paths.xml. When I try to call ” FileProvider.GetUriForFile()”, I’m getting a type not found error in visual studio. Is there a reference that I need to add to my project? I’ve done serious googling and haven’t been able to find anything.

    Thanks,
    –Adam

    1. Hi Adam,
      The FileProvider class appears to be part of the namespace Android.Support.V4.Content, so make sure your importing that at the top of your class
      using Android.Support.V4.Content;

      If that doesn’t work, check your Packages folder in your Android project and verify you have Xamarin.Android.Support.v4 installed (I think that’s the package you would need for that import?). If all else fails, open your Android SDK Manager and make sure you are not missing any critical packages.

Leave a Reply