Sunday, June 19, 2016

Image uri doesn't display images on ImageView on some android device

Leave a Comment

I have an ImageView. When you click on a ImageView, it will open gallery and you pick up a image and show it on ImageView. I have a condition that when I close my app and then I open it, the image will retain there . So for that, I am storing image Uri on sharedprefrence. And on opening the application i am retrieving the same Uri and tries to display the image on imageView.

However in some phones - image appears perfect like Mi(Lollipop), Samsung(KitKat) but it doesn't appear in phones like - Motorola(Marshmallow) ,One Plus One (Marshmallow). Any idea why it's happening ? Here is my code

For picking up an image i am using

Intent intent=new Intent();intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(intent, "Select Picture"), Constants.SELECT_PICTURE); 

And on OnActivityResult() code is

 @Override  protected void onActivityResult(int requestCode, int resultCode, Intent data) {      super.onActivityResult(requestCode, resultCode, data);       if(requestCode==Constants.SELECT_PICTURE && resultCode==RESULT_OK && null!=data) {          Uri uri=data.getData();          Picasso.with(UsersProfileActivity.this)              .load(uri)              .centerCrop()              .resize(200,200)              .into(img_photo);           // This profile image i am storing into a sharedpreference                  profile_image=uri.toString();      } 

And while retrieving from sharedprefrence I convert String to uri by using Uri.parse(profile_image)

However if I notice, uri returns for different android phone is as follows

Mi(Lollipop)- Uri=content://media/external/images/media/12415 samsung(KitKat)-Uri=content://media/external/images/media/786 Motorola(Marshmallow)- Uri=content://com.android.providers.media.documents/document/image%3A30731 One Plus One (Marshmallow)- Uri=content://com.android.providers.media.documents/document/image%3A475 

Hence when uri content is -content://media/external/images/media/ is display image on ImageView Perfect and in other cases it is not

10 Answers

Answers 1

Try this

I developed and tested and working on

  1. Lenovo K3 Note(Marshmallow)
  2. Motorola(Lollipop)
  3. Samsung(KitKat)

In your MainActivity.java add

    profilePicture = (ImageView)findViewById(R.id.imgProfilePicture);     sharedPreferences = getSharedPreferences(getString(R.string.app_name)+"_ProfileDetails",MODE_PRIVATE);      // get uri from shared preference     String profilePicUri = sharedPreferences.getString("profilePicUrl","");      // if uri not found     if (profilePicUri.equalsIgnoreCase("")){         // code for open gallery to select image         Intent intent;         if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){             intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);             intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);             intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);         }else{             intent = new Intent(Intent.ACTION_GET_CONTENT);         }         intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);         intent.setType("image/*");          startActivityForResult(Intent.createChooser(intent, "Select Picture"), SELECT_PICTURE_CONSTANT);     }else{         try{             final int takeFlags =  (Intent.FLAG_GRANT_READ_URI_PERMISSION                     | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);             // Check for the freshest data.             getContentResolver().takePersistableUriPermission(Uri.parse(profilePicUri), takeFlags);             // convert uri to bitmap             Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), Uri.parse(profilePicUri));             // set bitmap to imageview             profilePicture.setImageBitmap(bitmap);         }         catch (Exception e){             //handle exception             e.printStackTrace();         }     } 

Now Handle onActivityResult.

@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {     super.onActivityResult(requestCode, resultCode, data);     if (requestCode == SELECT_PICTURE_CONSTANT && resultCode == RESULT_OK && null != data) {         Uri uri = data.getData();         // This profile image i am storing into a sharedpreference         SharedPreferences.Editor editor = sharedPreferences.edit();         // save uri to shared preference         editor.putString("profilePicUrl",uri.toString());         editor.commit();         profilePicture.setImageURI(uri);     } } 

In your Manifest file add permission

<uses-permission android:name="android.permission.MANAGE_DOCUMENTS"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> 

Note:

Assuming you added code for take permission for Marshmallow

Result Uri

lenovo K3 Note: content://com.android.externalstorage.documents/document/primary%3ADCIM%2FCamera%2FIMG_20160606_212815.jpg

samsung: content://com.android.providers.media.documents/document/image%3A2086

motorola: content://com.android.providers.media.documents/document/image%3A15828

Answers 2

You need to request the permission to read external storage to user at runtime for Android 6+

https://developer.android.com/training/permissions/requesting.html#perm-check

Hope this helps.

Answers 3

Maybe you can handle it by converting URI to real path first.

public String getRealPathFromURI(Context context, Uri contentUri) {     Cursor cursor = null;     try {         String[] proj = { MediaStore.Images.Media.DATA };         cursor = context.getContentResolver().query(contentUri,  proj, null, null, null);         int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);         cursor.moveToFirst();         return cursor.getString(column_index);     } finally {         if (cursor != null) {             cursor.close();         }     } } 

And your onActivityResult should looks like this

@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {     super.onActivityResult(requestCode, resultCode, data);      if(requestCode==Constants.SELECT_PICTURE && resultCode==RESULT_OK && null!=data)     {         Uri uri=data.getData();         String imagePath = getRealPathFromURI(getApplicationContext(), uril);  Picasso.with(UsersProfileActivity.this).load(imagePath).centerCrop().resize(200,200).into(img_photo);       profile_image = imagePath;        } 

Answers 4

I have managed to do this in the past. Instead of storing it in SharePreferences, I save it in the Bundle when onSaveInstanceState(Bundle) get called by: bundle.putParcelable(SOME_KEY, uri). You can do this because Uri implements Parcelable. After that, check for this uri in the Bundle passed in onCreate(). Code:

public void onCreate(Bundle savedInstanceState){     super.onCreate(savedInstanceState);     if (savedInstanceState != null){         loadImage((Uri)saveInstanceState.getParcelable(SOME_KEY))     } }  public void onSaveInstanceState(Bundle bundle){      bundle.putParcelable(SOME_KEY, uri) } 

P/S: I guess the problem of storing the uri in SharePreference is encoding/decoding Uri.

Answers 5

Quoting from 4.4 API overview:

'On previous versions of Android, if you want your app to retrieve a specific type of file from another app, it must invoke an intent with the ACTION_GET_CONTENT action. This action is still the appropriate way to request a file that you want to import into your app. However, Android 4.4 introduces the ACTION_OPEN_DOCUMENT action, which allows the user to select a file of a specific type and grant your app long-term read access to that file (possibly with write access) without importing the file to your app.'(emphasis added)

To allow your app to retrieve previously-selected images, just change the action from ACTION_GET_CONTENT to ACTION_OPEN_DOCUMENT (solution confirmed working on Nexus 7 2013).

    Intent intent = new Intent();     intent.setType("image/*");     String action = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_GET_CONTENT;     intent.setAction(action);     startActivityForResult(Intent.createChooser(intent, "Select Picture"), Constants.SELECT_PICTURE); 

Answers 6

You can Easily convert URI to Bitmap:

ParcelFileDescriptor parcelFileDescriptor =                 context.getContentResolver().openFileDescriptor(selectedFileUri, "r");         FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();         Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);           parcelFileDescriptor.close(); 

And then set in to Imageview: imgview.setImageBitmap(image);

Hope this will help you.

Answers 7

Could be a Picasso versioning issue. They fixed something with loading large images in version 2.5.0 (2.4.0...?), which isn't available via the repositories yet.

You'll need to manually include the nightly snapshot of Picasso, and that will likely fix your issue. It did for me.

Answers 8

Pass your URI in this method it will return a String and than convert it into Bitmap

  String path = getPath(getApplicationContext(), uri);   Bitmap bitmap = BitmapFactory.decodeFile(path);   imageView.setImageBitmap(bitmap);       public static String getPath(final Context context, final Uri uri) {      // DocumentProvider     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, uri)) {          if (isExternalStorageDocument(uri)) {// ExternalStorageProvider             final String docId = DocumentsContract.getDocumentId(uri);             final String[] split = docId.split(":");             final String type = split[0];             String storageDefinition;               if ("primary".equalsIgnoreCase(type)) {                  return Environment.getExternalStorageDirectory() + "/" + split[1];              } else {                  if (Environment.isExternalStorageRemovable()) {                     storageDefinition = "EXTERNAL_STORAGE";                  } else {                     storageDefinition = "SECONDARY_STORAGE";                 }                  return System.getenv(storageDefinition) + "/" + split[1];             }          } else if (isDownloadsDocument(uri)) {// DownloadsProvider              final String id = DocumentsContract.getDocumentId(uri);             final Uri contentUri = ContentUris.withAppendedId(                     Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));              return getDataColumn(context, contentUri, null, null);          } else if (isMediaDocument(uri)) {// MediaProvider             final String docId = DocumentsContract.getDocumentId(uri);             final String[] split = docId.split(":");             final String type = split[0];              Uri contentUri = null;             if ("image".equals(type)) {                 contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;             } else if ("video".equals(type)) {                 contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;             } else if ("audio".equals(type)) {                 contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;             }              final String selection = "_id=?";             final String[] selectionArgs = new String[]{                     split[1]             };              return getDataColumn(context, contentUri, selection, selectionArgs);         }      } else if ("content".equalsIgnoreCase(uri.getScheme())) {// MediaStore (and general)          // Return the remote address         if (isGooglePhotosUri(uri))             return uri.getLastPathSegment();          return getDataColumn(context, uri, null, null);      } else if ("file".equalsIgnoreCase(uri.getScheme())) {// File         return uri.getPath();     }      return null; } 

Answers 9

I also run into something like this earlier and found a useful helper class which gives you the file's exact location. Here is the link.

You can use the methods like FileUtils.getPath(context, uri) or FileUtils.getFile(context, uri) to get the file. Then just use it like this:

 Picasso.with(UsersProfileActivity.this)          .load(Uri.fromFile(file))          .centerCrop()          .resize(200,200)          .into(img_photo); 

Hope this helps.

Answers 10

Just not 100% sure if I get the question. If certain images cannot be shown in the imageView (even before closing and restarting the app) then the issue is with Picasso is being able to handle the contents from certain providers. I am not getting the sense that this is the case, but if it is, then you might want to consider filing a bug/RFE for that library. On the other hand, if Picasso is correctly showing the image for a given Uri, but not after Uri_org -> String -> Uri_regen conversion then it means that Uri_org is not the same as Uri_regen. You can (temporarily) add a couple of lines your code to convert the String back to Uri and put a breakpoint just after and compare the two Uri's. That should give you a clue as to how things are going wrong.

profile_image=uri.toString(); //add the next few lines for debugging purposes only; remove them later Uri uri2 = Uri.parse(profile_image) if (!uri.equals(uri2)    //add a breakpoint on the next line    Log.d("Uri mismatch), "Ouch"); //remove the lines after you are done with debugging 

If you are not hitting the breakpoint, then move the breakpoint back to the "if" statement and visually check the two Uri's (which almost certainly will be the same, otherwise you would have had hit the breakpoint, but this would help checking the debug code itself). If the Uri's are the same then somehow the String preference is not being preserved correctly for some reason. In that case make a note of the original Uri and add a breakpoint on the line that converts the String back to Uri:

Uri uri = Uri.parse(profile_image) //profile_image is retrieved from sharedpreferences 

If the previous experiment worked, it should be working again here unless the profile_image is different. Either way you should get pointers as to where the issue is.

Good luck!

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment