Action.Picker returns invalid/wrong Uri (How to get path or byte[] from multiple picked gallery img)

6 views
Skip to first unread message

Sana Rajput

unread,
Sep 14, 2018, 3:18:32 AM9/14/18
to ReactiveUI mailing list

I have an forms app where i need to pick "1 to many" images from the phone storage.
For this i use the dependency injection system.

My problem is the somewhere i get an Android.netUri that resolves to a file that do not exist... and to a file name that i have never seen before.
The kicker is that if i pick pictures that was takes within the last couple of hours this code works...

Im am at the end of my hoap, i really hope someone can point me to something that i'm doing wrong.

i start the Picker activity with:

[assembly: Dependency(typeof(ImagePickerService))]
namespace MyApp.Droid
{
    public class ImagePickerService : Java.Lang.Object, IImagePickerService
    {
        public async Task OpenGallery()
        {
            try
            {
                var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Storage);
                if (status != PermissionStatus.Granted)
                {
                    if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Storage))
                    {
                        Toast.MakeText(CrossCurrentActivity.Current.Activity, "Need Storage permission to access to your photos.", ToastLength.Long).Show();
                    }

                    var results = await CrossPermissions.Current.RequestPermissionsAsync(new[] { Permission.Storage });
                    status = results[Permission.Storage];
                }

                if (status == PermissionStatus.Granted)
                {
                    Toast.MakeText(CrossCurrentActivity.Current.Activity, "Pick max 20 images", ToastLength.Long).Show();
                    var imageIntent = new Intent(Intent.ActionPick);
                    imageIntent.SetType("image/*");
                    imageIntent.PutExtra(Intent.ExtraAllowMultiple, true);
                    imageIntent.SetAction(Intent.ActionPick);
                    CrossCurrentActivity.Current.Activity.StartActivityForResult(Intent.CreateChooser(imageIntent, "Pick pictures"), 100);

                }
                else if (status != PermissionStatus.Unknown)
                {
                    Toast.MakeText(CrossCurrentActivity.Current.Activity, "Permission Denied. Can not continue, try again.", ToastLength.Long).Show();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                Toast.MakeText(CrossCurrentActivity.Current.Activity, "Error. Can not continue, try again.", ToastLength.Long).Show();
            }
        }

    }

then in my MainActivity.cs i have the OnActivityResult 
I have tried to use the ContentResolver.OpenInputStream to get the image bytes with no luck, so this is commented out atm.

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

            if (requestCode == OPENGALLERYCODE && resultCode == Result.Ok)
            {
                List<string> images = new List<string>();

                if (data != null)
                {
                    ClipData clipData = data.ClipData;
                    if (clipData != null)
                    {
                        for (int i = 0; i < clipData.ItemCount; i++)
                        {
                            ClipData.Item item = clipData.GetItemAt(i);
                            /*
                            var stream = ContentResolver.OpenInputStream(item.Uri); //This throws "FileNotFound"
                            byte[] byteArray;
                            using (var memoryStream = new MemoryStream())
                            {
                                stream.CopyTo(memoryStream);
                                byteArray = memoryStream.ToArray();
                                stream.Close();
                                stream = null;
                            }
                            stream = ContentResolver.OpenInputStream(item.Uri);
                            var exif = new ExifInterface(stream);
                            stream.Close();
                            */

                            Android.Net.Uri uri = item.Uri;
                            var path = GetActualPathFromFile(uri);

                            if (path != null)
                            {
                                var tmpImgPath = RotateToOriginalDimention(path);
                                images.Add(tmpImgPath);
                            }
                        }
                    }
                    else
                    {
                        Android.Net.Uri uri = data.Data;
                        var path = GetActualPathFromFile(uri);

                        if (path != null)
                        {
                            var tmpImgPath = RotateToOriginalDimention(path);
                            images.Add(tmpImgPath);
                        }
                    }
                    MessagingCenter.Send<App, List<string>>((App)Xamarin.Forms.Application.Current, "ImagesSelected", images);
                }
            }
        }

And the GetActualPathFromFile (also in my MainActivity.cs)
The hole func is below but i hit this part of the code and get at "FileNotFound"

(...)
                else if ("content".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase))
                {

                    var retval2 = getDataColumn(this, uri, null, null);
                    if (File.Exists(retval2)) //<----------------------- This returns "false"
                    {
                        return retval2;
                    }
                    else
                    {
                        throw new Exception("file not found " + retval2);
                    }
                }
(...)

The Hole GetActualPathFromFile

        private string GetActualPathFromFile(Android.Net.Uri uri)
        {
            bool isKitKat = Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Kitkat;

            if (isKitKat && DocumentsContract.IsDocumentUri(this, uri))
            {
                // ExternalStorageProvider
                if (isExternalStorageDocument(uri))
                {
                    string docId = DocumentsContract.GetDocumentId(uri);

                    char[] chars = { ':' };
                    string[] split = docId.Split(chars);
                    string type = split[0];

                    if ("primary".Equals(type, StringComparison.OrdinalIgnoreCase))
                    {
                        var retval = Android.OS.Environment.ExternalStorageDirectory + "/" + split[1];
                        if (File.Exists(retval))
                        {
                            return retval;
                        }
                        else
                        {
                            throw new Exception("file not found " + retval);
                        }
                    }
                }
                // DownloadsProvider
                else if (isDownloadsDocument(uri))
                {
                    string id = DocumentsContract.GetDocumentId(uri);

                    Android.Net.Uri contentUri = ContentUris.WithAppendedId(
                                    Android.Net.Uri.Parse("content://downloads/public_downloads"), long.Parse(id));

                    //System.Diagnostics.Debug.WriteLine(contentUri.ToString());

                    var retval = getDataColumn(this, contentUri, null, null);
                    if (File.Exists(retval))
                    {
                        return retval;
                    }
                    else
                    {
                        throw new Exception("file not found " + retval);
                    }
                }
                // MediaProvider
                else if (isMediaDocument(uri))
                {
                    String docId = DocumentsContract.GetDocumentId(uri);

                    char[] chars = { ':' };
                    String[] split = docId.Split(chars);

                    String type = split[0];

                    Android.Net.Uri contentUri = null;
                    if ("image".Equals(type))
                    {
                        contentUri = MediaStore.Images.Media.ExternalContentUri;
                    }
                    else if ("video".Equals(type))
                    {
                        contentUri = MediaStore.Video.Media.ExternalContentUri;
                    }
                    else if ("audio".Equals(type))
                    {
                        contentUri = MediaStore.Audio.Media.ExternalContentUri;
                    }

                    String selection = "_id=?";
                    String[] selectionArgs = new String[]
                    {
                        split[1]
                    };

                    var retval = getDataColumn(this, contentUri, selection, selectionArgs);
                    if (File.Exists(retval))
                    {
                        return retval;
                    }
                    else
                    {
                        throw new Exception("file not found " + retval);
                    }
                }
            }
            // MediaStore (and general)
            else if ("content".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase))
            {

                // Return the remote address
                if (isGooglePhotosUri(uri))
                {
                    var retval = uri.LastPathSegment;
                    if (File.Exists(retval))
                    {
                        return retval;
                    }
                    else
                    {
                        throw new Exception("file not found " + retval);
                    }
                }


                var retval2 = getDataColumn(this, uri, null, null);
                if (File.Exists(retval2))
                {
                    return retval2;
                }
                else
                {
                    throw new Exception("file not found " + retval2);
                }
            }
            // File
            else if ("file".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase))
            {
                var retval = uri.Path;
                if (File.Exists(retval))
                {
                    return retval;
                }
                else
                {
                    throw new Exception("file not found " + retval);
                }
            }

            throw new Exception("file not found ");
        }

        public static String getDataColumn(Context context, Android.Net.Uri uri, String selection, String[] selectionArgs)
        {
            ICursor cursor = null;
            String column = "_data";
            String[] projection =
            {
                column
            };

            try
            {
                cursor = context.ContentResolver.Query(uri, projection, selection, selectionArgs, null);
                if (cursor != null && cursor.MoveToFirst())
                {
                    int index = cursor.GetColumnIndexOrThrow(column);
                    return cursor.GetString(index);
                }
            }
            finally
            {
                if (cursor != null)
                    cursor.Close();
            }
            return null;
        }

        //Whether the Uri authority is ExternalStorageProvider.
        public static bool isExternalStorageDocument(Android.Net.Uri uri)
        {
            return "com.android.externalstorage.documents".Equals(uri.Authority);
        }

        //Whether the Uri authority is DownloadsProvider.
        public static bool isDownloadsDocument(Android.Net.Uri uri)
        {
            return "com.android.providers.downloads.documents".Equals(uri.Authority);
        }

        //Whether the Uri authority is MediaProvider.
        public static bool isMediaDocument(Android.Net.Uri uri)
        {
            return "com.android.providers.media.documents".Equals(uri.Authority);
        }

        //Whether the Uri authority is Google Photos.
        public static bool isGooglePhotosUri(Android.Net.Uri uri)
        {
            return "com.google.android.apps.photos.content".Equals(uri.Authority);
        }

akshita...@gmail.com

unread,
Sep 18, 2018, 2:01:26 AM9/18/18
to ReactiveUI mailing list
Hi Sana, I have tested your code on both real device and emulator from API 21 to 27. They all work fine.
But I can't make it work with CrossCurrentActivity.Current.Activity, so I used Android.App.Application.Context and a static Activity in MainActivity instead. 
What's the environment are you using? Could you share your complete project?
If you still need help regarding Xamarin Development feel free to contact me.
Reply all
Reply to author
Forward
0 new messages