| 3 min read
It's been a long time since I posted the last article. To be frank, I am busy till neck in work and hardly get time to share my roller coaster ride with Mobility . However, I have a great plan for upcoming blog articles, more on that, later. (Xamarin stuff incoming)
Earlier, we had a requirement in one of our app where we had to pick image from gallery / file manager and use it for further processing. This being most implemented and straight forward task, doesn't look like a big task for Android developers out there. Like good guys, our team went straight to the Xamarin Developer Recipes (https://developer.xamarin.com/recipes/android/otherux/pickimage/) and found one article. As always, article was really easy and self explanatory. We implemented same code (with some optimizations, obviously). It started working flawlessly on my OnePlus One (no surprises here).
Here is Xamarin's implementation -
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) | |
{ | |
if ((requestCode == PickImageId) && (resultCode == Result.Ok) && (data != null)) | |
{ | |
Uri uri = data.Data; | |
_imageView.SetImageURI(uri); | |
string path = GetPathToImage(uri); | |
Toast.MakeText(this, path, ToastLength.Long); | |
} | |
} | |
private string GetPathToImage(Uri uri) | |
{ | |
string path = null; | |
// The projection contains the columns we want to return in our query. | |
string[] projection = new[] { Android.Provider.MediaStore.Images.Media.InterfaceConsts.Data }; | |
using (ICursor cursor = ManagedQuery(uri, projection, null, null, null)) | |
{ | |
if (cursor != null) | |
{ | |
int columnIndex = cursor.GetColumnIndexOrThrow(Android.Provider.MediaStore.Images.Media.InterfaceConsts.Data); | |
cursor.MoveToFirst(); | |
path = cursor.GetString(columnIndex); | |
} | |
} | |
return path; | |
} |
We were under impression, this code should work flawlessly on other devices as well. We borrowed Moto G and few other phones from colleagues. Started testing my app on those devices. Guess what, We could not get any image from Gallery. The reason why we get path as null might be because method ManageQuery is deprecated. We were surprised that Xamarin documentation still refers to method that was deprecated in API 11 (Honeycomb).
We came up with an alternative solution that worked for rest of the devices -
private string GetPathToImage(Android.Net.Uri uri) | |
{ | |
ICursor cursor = this.ContentResolver.Query(uri, null, null, null, null); | |
cursor.MoveToFirst(); | |
string document_id = cursor.GetString(0); | |
document_id = document_id.Split(':')[1]; | |
cursor.Close(); | |
cursor = ContentResolver.Query( | |
Android.Provider.MediaStore.Images.Media.ExternalContentUri, | |
null, MediaStore.Images.Media.InterfaceConsts.Id + " = ? ", new String[] { document_id }, null); | |
cursor.MoveToFirst(); | |
string path = cursor.GetString(cursor.GetColumnIndex(MediaStore.Images.Media.InterfaceConsts.Data)); | |
cursor.Close(); | |
return path; | |
} |
Instead of ManagedQuery, we now use ContentResolver's Query which queries the URI and returns cursor over result set. We move cursor to first row and try getting document ID, which we later use to resolve one more URI to obtain real path on file system.
Everything was looking fine and we decided to test the app on more complex and heavy Android skins like Touchwiz. This time, we could not split documentid because the value that was returning did not have any colon ( : ), resulting ArgumentOutOfRange exception. To handle that, we added a simple line to check whether documentid can be split with colon or not. Surprisingly, even without split, we could get cursor. Here's the working code -
private string GetPathToImage(Android.Net.Uri uri) | |
{ | |
ICursor cursor = this.ContentResolver.Query(uri, null, null, null, null); | |
cursor.MoveToFirst(); | |
string document_id = cursor.GetString(0); | |
if (document_id.Contains(":")) | |
document_id = document_id.Split(':')[1]; | |
cursor.Close(); | |
cursor = ContentResolver.Query( | |
Android.Provider.MediaStore.Images.Media.ExternalContentUri, | |
null, MediaStore.Images.Media.InterfaceConsts.Id + " = ? ", new String[] { document_id }, null); | |
cursor.MoveToFirst(); | |
string path = cursor.GetString(cursor.GetColumnIndex(MediaStore.Images.Media.InterfaceConsts.Data)); | |
cursor.Close(); | |
return path; | |
} |
While I have started loving Xamarin more and more, I have a suggestion for Xamarin team, please update the documentation. It really needs a quality check.
Hope, this article will help you to fix this issue if at all you get it.
Regards,
Rahul.