Search code examples
androidxamarinxamarin.androidandroid-galleryxamarin.forms

Xamarin Choose Image From Gallery Path is Null


using this recipe to try and choose an image from the gallery and then upload it to s3 but my path always returns null.

private string _imgPath;
        public void InitializeMediaPicker()
        {
                Intent = new Intent();
                Intent.SetType("image/*");
                Intent.SetAction(Intent.ActionGetContent);
                StartActivityForResult(Intent.CreateChooser(Intent, "Select Picture"), 1000);
        }

        public string GetImage()
        {
            InitializeMediaPicker();

            return _imgPath;
        }

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            if ((requestCode != 1000) || (resultCode != Result.Ok) || (data == null)) return;
            var uri = data.Data;
            _imgPath = GetPathToImage(uri);
        }

        private string GetPathToImage(Android.Net.Uri uri)
        {
            string path = null;
            // The projection contains the columns we want to return in our query.
            var projection = new[] { Android.Provider.MediaStore.Images.Media.InterfaceConsts.Data };
            using (var cursor = ManagedQuery(uri, projection, null, null, null))
            {
                if (cursor == null) return path;
                var columnIndex = cursor.GetColumnIndexOrThrow(Android.Provider.MediaStore.Images.Media.InterfaceConsts.Data);
                cursor.MoveToFirst();
                path = cursor.GetString(columnIndex);
            }
            return path;
        }

Solution

  • Here is a working implementation ported from this question and specifically, this answer.

    Include the WRITE_EXTERNAL_STORAGE permission in your manifest for this code to work.

    public delegate void OnImageResultHandler(bool success, string imagePath);
    
    protected OnImageResultHandler _imagePickerCallback;
    public void GetImage(OnImageResultHandler callback)
    {
        if (callback == null) {
            throw new ArgumentException ("OnImageResultHandler callback cannot be null.");
        }
    
        _imagePickerCallback = callback;
        InitializeMediaPicker();
    }
    
    public void InitializeMediaPicker()
    {
        Intent = new Intent();
        Intent.SetType("image/*");
        Intent.SetAction(Intent.ActionGetContent);
        StartActivityForResult(Intent.CreateChooser(Intent, "Select Picture"), 1000);
    }
    
    protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
    {
        if ((requestCode != 1000) || (resultCode != Result.Ok) || (data == null)) {
            return;
        }
    
        string imagePath = null;
        var uri = data.Data;
        try {
            imagePath = GetPathToImage(uri);
        } catch (Exception ex) {
            // Failed for some reason.
        }
    
        _imagePickerCallback (imagePath != null, imagePath);
    }
    
    private string GetPathToImage(Android.Net.Uri uri)
    {
        string doc_id = "";
        using (var c1 = ContentResolver.Query (uri, null, null, null, null)) {
            c1.MoveToFirst ();
            String document_id = c1.GetString (0);
            doc_id = document_id.Substring (document_id.LastIndexOf (":") + 1);
        }
    
        string path = null;
    
        // The projection contains the columns we want to return in our query.
        string selection = Android.Provider.MediaStore.Images.Media.InterfaceConsts.Id + " =? ";
        using (var cursor = ManagedQuery(Android.Provider.MediaStore.Images.Media.ExternalContentUri, null, selection, new string[] {doc_id}, null))
        {
            if (cursor == null) return path;
            var columnIndex = cursor.GetColumnIndexOrThrow(Android.Provider.MediaStore.Images.Media.InterfaceConsts.Data);
            cursor.MoveToFirst();
            path = cursor.GetString(columnIndex);
        }
        return path;
    }
    

    Example Usage:

    Button button = FindViewById<Button> (Resource.Id.myButton);
    
    button.Click += delegate {
        GetImage(((b, p) => {
            Toast.MakeText(this, "Found path: " + p, ToastLength.Long).Show();
        }));
    };
    

    I've used a callback instead of returning the path for GetImage as the calling method would finish executing before OnActivityResult is called so the path returned would never be valid.