I am trying to store an image, which is located in my project's drawable folder to the gallery of my Samsung Android tablet. I have been able to successfully achieve this for an HTC phone using the exact same code; however, it appears when attempting this for a Samsung tablet, the URI I am using to insert the image is appearing not to exist. The error appears to occur when I attempt to open the ouput stream via the line...
OutputStream imageOut = getContentResolver().openOutputStream(url);
As mentioned before, this exact code worked for an HTC android phone, which leads me to believe something is different in the file system of the two devices as to where to store the image. The full code I am using (which is mainly copied from the MediaStore class) is found below.
Drawable drawable = getResources().getDrawable(R.drawable.sample);
Bitmap bitmapImage = ((BitmapDrawable)drawable).getBitmap();
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, "demo_image");
values.put(MediaStore.Images.Media.DESCRIPTION, "sample image");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
Uri url = null;
try {
url = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);
if (bitmapImage != null) {
OutputStream imageOut = getContentResolver().openOutputStream(url);
try {
bitmapImage.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
} finally {
imageOut.close();
}
long id = ContentUris.parseId(url);
// Wait until MINI_KIND thumbnail is generated.
Bitmap miniThumb = MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(), id,
MediaStore.Images.Thumbnails.MINI_KIND, null);
// This is for backward compatibility.
// Bitmap microThumb = MediaStore.Images.Thumbnail(getContentResolver(), miniThumb, id, 50F, 50F,
// MediaStore.Images.Thumbnails.MICRO_KIND);
} else {
//Log.e(TAG, "Failed to create thumbnail, removing original");
getContentResolver().delete(url, null, null);
url = null;
}
} catch (Exception e) {
//Log.e(TAG, "Failed to insert image", e);
if (url != null) {
getContentResolver().delete(url, null, null);
url = null;
}
}
Related
I'm reading thumbnails from the device by querying the MediaStore, using MediaStore.Images.Thumbnails.getThumbnail(). However, this has been deprecated in Android 10 (API 29), with a pointer to ContentResolver#loadThumbnail: https://developer.android.com/reference/android/provider/MediaStore.Images.Thumbnails
However, I can't get this to work (in an emulated device running API 29). I've copied some JPEGs onto the emulated device, which end up in the Downloads folder. They show up fine in the Photos app. The following code gives me a FileNotFoundException. What does "No content provider" actually tell me?
try {
Size thumbSize = new Size(100, 100);
Uri thumbUri = Uri.fromFile(new File(imgPath));
// imgPath: /storage/emulated/0/Download/pexels-photo-323490.jpeg
// thumbUri: file:///storage/emulated/0/Download/pexels-photo-323490.jpeg
Bitmap thumbBitmap;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
thumbBitmap = mContext.getContentResolver().loadThumbnail(thumbUri, thumbSize, null);
} else {
thumbBitmap = MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(),
imgId, MediaStore.Images.Thumbnails.MINI_KIND, null);
}
iconView.setImageBitmap(thumbBitmap);
} catch (Exception e) {
Log.e("err", e.toString());
}
Exception:
java.io.FileNotFoundException: No content provider: file:///storage/emulated/0/Download/pexels-photo-323490.jpeg
Please try this, hope it works for You:
int thumbColumn = cur.getColumnIndexOrThrow(MediaStore.Images.ImageColumns._ID);
int _thumpId = cur.getInt(thumbColumn);
Uri imageUri_t = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,_thumpId);
GGK
The best Answer for getting Thumbnail and All Android Versions is this:
val thumbnail: Bitmap = if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)) {
mContentResolver.loadThumbnail(contentUri, Size(width / 2, height / 2), null)
} else {
MediaStore.Images.Thumbnails.getThumbnail(mContentResolver, id, MediaStore.Images.Thumbnails.MINI_KIND, null)
}
The uri may not be good. I mean try using FileProvider to get uri of the file. If your legecyExternalStorage is false, then you can not access file like this. The same goes for android 12. You need to use MediaStore to get the contentUri
Recently Google added the Photos app for Google+ (plus) and it shows up when you launch an Intent to choose an image. However, if I select an image from Google+ Photos and try to use it in my application none of my current logic is able to return a usable URI or URL to actually get an image that I can download and manipulate. I'm currently using the "common" methods to try to manipulate the URI that can be found here on Stack Overflow and elsewhere. I can provide code if needed, but at this point I think it's kind of irrelevant since it works well for everything else except this new app. Any ideas on how to get a usable image?
The URI looks something like the following:
content://com.google.android.apps.photos.content/0/https%3A%2F%2Flh5.googleusercontent.com%<a bunch of letters and numbers here>
The MediaColumns.DATA info always returns null and the MediaColumns.DISPLAY_NAME always returns image.jpg no matter what I select from the Google Photos app. If I try to paste everything from https to the end in my browser, nothing comes up. Not sure how to get usable info from this.
When receiving the data intent, you should use the contentResolver to get the photos.
Here's what you should do:
String url = intent.getData().toString();
Bitmap bitmap = null;
InputStream is = null;
if (url.startsWith("content://com.google.android.apps.photos.content")){
is = getContentResolver().openInputStream(Uri.parse(url));
bitmap = BitmapFactory.decodeStream(is);
}
I did faced issues selecting images from new Google Photos app. I was able to resolve it by below code.
It works for me, basically what i did is i am checking if there is any authority is there or not in content URI. If it is there i am writing to temporary file and returning path of that temporary image. You can skip compression part while writing to temporary image
public static String getImageUrlWithAuthority(Context context, Uri uri) {
InputStream is = null;
if (uri.getAuthority() != null) {
try {
is = context.getContentResolver().openInputStream(uri);
Bitmap bmp = BitmapFactory.decodeStream(is);
return writeToTempImageAndGetPathUri(context, bmp).toString();
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
public static Uri writeToTempImageAndGetPathUri(Context inContext, Bitmap inImage) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
return Uri.parse(path);
}
P.S. : I have answered a similar question here
You have to use projection in order to get ImageColumns.DATA (or MediaColumns.DATA):
private String getRealPathFromURI(Uri contentURI) {
// Projection makes ContentResolver to get needed columns only
String[] medData = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(contentURI, medData, null, null, null);
// this is how you can simply get Bitmap
Bitmap bmp = MediaStore.Images.Media.getBitmap(getContentResolver(), contentURI);
// After using projection cursor will have needed DATA column
cursor.moveToFirst();
final int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
return cursor.getString(idx);
}
I am trying to pass back an image through a content provider in a separate app. I have two apps, one with the activity in (app a), the other with content provider (app b)
I have app a reading an image off my SD card via app b using the following code.
App a:
public void but_update(View view)
{
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://com.jash.cp_source_two.provider/note/1");
InputStream inStream = null;
try
{
inStream = resolver.openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(inStream);
image = (ImageView) findViewById(R.id.imageView1);
image.setImageBitmap(bitmap);
}
catch(FileNotFoundException e)
{
Toast.makeText(getBaseContext(), "error = "+e, Toast.LENGTH_LONG).show();
}
finally {
if (inStream != null) {
try {
inStream.close();
} catch (IOException e) {
Log.e("test", "could not close stream", e);
}
}
}
};
App b:
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
try
{
File path = new File(Environment.getExternalStorageDirectory().getAbsolutePath(),"pic2.png");
return ParcelFileDescriptor.open(path,ParcelFileDescriptor.MODE_READ_ONLY);
}
catch (FileNotFoundException e)
{
Log.i("r", "File not found");
throw new FileNotFoundException();
}
}
In app a I am able to display an image from app a's resources folder, using setImageURi and constructing a URI using the following code.
int id = R.drawable.a2;
Resources resources = getBaseContext().getResources();
Uri uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
resources.getResourcePackageName(id) + '/' +
resources.getResourceTypeName(id) + '/' +
resources.getResourceEntryName(id) );
image = (ImageView) findViewById(R.id.imageView1);
image.setImageURI(uri);
However, if I try to do the same in app b (read from app b's resources folder rather than the image on the SD card) it doesn't work, saying it can't find the file, even though I am creating the path of the file from the resource, so it is definitely there.
Any ideas? Does it restrict sending resources over the content provider somehow?
P.S. I also got an error when I tried to create the file with
File path = new File(uri); saying 'there is no applicable constructor to '(android.net.Uri)' though http://developer.android.com/reference/java/io/File.html#File(java.net.URI) Seems to think it's possible...unless java.net.URI is different to android.net.URI, in which case can I convert them?
Thanks
Russ
android.net.URI doesn't exist. There is an android.net.Uri (note the spelling). This is not the same as java.net.URI. You can probably convert a Uri to a String and then to a URI.
So, you can open a file on your SD card (or so you claim), and you can set an image in your main Activity (or so you claim). I don't see that your you're getting back an image or anything else from a content provider?
A content provider stores data. It can return files (that is, file descriptors), including file descriptors for asset files, but it has nothing to do with Resources in your application.
When I save an image to new location and then use MediaScanner to refresh the gallery then everything is fine - thumbnails and images are refreshed well.
But when I save an image to EXISTING location and then use MediaScanner - then only 'new' thumbnail isnt refreshed. (even though file is overwritten).
How to solve it?
Here is my code :
File file = new File(SDCARD_PATH, filename);
try {
FileOutputStream out = new FileOutputStream(file);
bmp.compress(format, BEST_IMAGE_QUALITY, out);
}catch (FileNotFoundException e) {
}
//refreshing single file using media scanner, no need to paste
This is a common and well know problem in Android. If you edit a media file, the thumbnail does not seem to update.
I have a fix for this, however, its still a fix and not a clean solution.
My fix is simple, and it basically deletes the stale thumbnail and then uses media scanner to update the thumbnails.
Here're the steps to be followed:
Step 1. Edit the file as you like. Say filename, "myVideoToBeEdited".
Step 2. Once you are done editing, delete its existing thumbnail.
First, get the video id using code like this:
final String[] columns = {
BaseColumns._ID, MediaColumns.DATA
};
ContentResolver cr = context.getContentResolver();
Cursor cursor = cr.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, columns, null, null, null);
boolean cancel = false;
if(null != cursor){
while(cursor.moveToNext() && !cancel){
String fileName = cursor.getString(cursor.getColumnIndex(MediaColumns.DATA));
int imageId = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID));
if(fileName.equals(myVideoToBeEdited)){
removeVideoThumbnail(getContentResolver(), imageId); // step 3
cancel = true;
}
}
}
There are other ways to get the id, and more optimised ones as well.
Step 3. Delete the thumbnail.
public void removeVideoThumbnail(ContentResolver contentResolver, long photoId) {
Cursor thumbnails = contentResolver.query(android.provider.MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI, null, android.provider.MediaStore.Video.Thumbnails.VIDEO_ID + "=?", new String[]{String.valueOf(photoId)}, null);
for (thumbnails.moveToFirst(); !thumbnails.isAfterLast(); thumbnails.moveToNext()) {
long thumbnailId = thumbnails.getLong(thumbnails.getColumnIndex(android.provider.MediaStore.Video.Thumbnails._ID));
String path = thumbnails.getString(thumbnails.getColumnIndex(android.provider.MediaStore.Video.Thumbnails.DATA));
File file = new File(path);
if (file.delete()) {
contentResolver.delete(android.provider.MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI, android.provider.MediaStore.Video.Thumbnails._ID + "=?", new String[]{String.valueOf(thumbnailId)});
}
}
}
Or, here's the method to delete image thumbnail
public void removeImageThumbnail(ContentResolver contentResolver, long photoId) {
Cursor thumbnails = contentResolver.query(android.provider.MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, null, android.provider.MediaStore.Images.Thumbnails.IMAGE_ID + "=?", new String[]{String.valueOf(photoId)}, null);
for (thumbnails.moveToFirst(); !thumbnails.isAfterLast(); thumbnails.moveToNext()) {
long thumbnailId = thumbnails.getLong(thumbnails.getColumnIndex(android.provider.MediaStore.Images.Thumbnails._ID));
String path = thumbnails.getString(thumbnails.getColumnIndex(android.provider.MediaStore.Images.Thumbnails.DATA));
File file = new File(path);
if (file.delete()) {
contentResolver.delete(android.provider.MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, android.provider.MediaStore.Images.Thumbnails._ID + "=?", new String[]{String.valueOf(thumbnailId)});
}
}
}
Step 4. And finally use media scanner connection to scan the file so that it updates the thumbnails.
MediaScannerConnection.scanFile(context,
new String[] { myVideoToBeEdited }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
// pass the mime type, else passing a null will enable file extension to dictate the mime type
// you are good to go
}
});
Have you tried to remove the "old" picture prior to saving the new one to file system? Like so:
File file = new File(SDCARD_PATH, filename);
try {
// Delete the "old" file.
if (file.exists()) {
file.delete();
}
FileOutputStream out = new FileOutputStream(file);
bmp.compress(format, BEST_IMAGE_QUALITY, out);
}catch (FileNotFoundException e) {
}catch (SecurityException e) {
}
I am building my own contact picker, because I needed multi-select support. Everything is working fine, except for one small problem with the contact images.
For contacts who don't have images I am showing a "no image" image. This works fine for contacts in the phone's address book. I am having a problem however when it comes to images from my google contacts.
Most of my google contacts do not have photos. However, when i query the Contacts database for photos, it still returns a URI for them of the form of content://com.android.contacts/contacts/657/photo (which is the same format as for contacts who do have a photo.
Then when I try to assign the photo to a QuickContactBadge, using bdg.setImageURI(pic); it sets it to essentially a blank picture, and logs a silent INFO message stating:
INFO/System.out(3968): resolveUri failed on bad bitmap uri:
content://com.android.contacts/contacts/657/photo
I need to know how I can either
a) validate the URI or
b) catch the INFO message above
c) query the imageview/badge to see if it found a valid image
so that i can assign these contacts my "no image" image.
How can I go about doing this?
EDIT 20110812.0044
I have tried adding this to my code as per Laurence's suggestion (which he's since removed):
// rv is my URI variable
if(rv != null) {
Drawable d = Drawable.createFromPath(rv.toString());
if (d == null) rv = null;
}
While the google contacts now get my "no image" image, ... so do all the other contacts, including ones that do in fact have images.
Okay, I figured out how to do this after poking through the ImageView source code. It is actually using the QuickContactBadge's own methods, but if necessary, one could always extract the relevant code from the Badge/ImageView control here.
After setting the QCB's image, I check to see if its drawable is null, instead of trying my own (as per Laurence's suggestion). This works better, because there is actually a whole slew of checking code the ImageView widget uses.
Here is my final code:
bdg.setImageURI(pic);
if(bdg.getDrawable() == null) bdg.setImageResource(R.drawable.contactg);
This works perfectly as I was hoping and expecting.
Just to answer the question on how to check the (data) value in the MediaStore:
ContentResolver cr = getContentResolver();
String[] projection = {MediaStore.MediaColumns.DATA}
Cursor cur = cr.query(Uri.parse(contentUri), projection, null, null, null);
if(cur != null) {
cur.moveToFirst();
String filePath = cur.getString(0);
if (filePath == null || filePath.isEmpty()) {
// data not set
} else if((new File(filePath)).exists()){
// do something if it exists
} else {
// File was not found
// this is binary data
}
} else {
// content Uri was invalid or some other error occurred
}
Inspiration taken from: https://stackoverflow.com/a/7649784/621690 and others.
There is also the column SIZE that might be checked: http://developer.android.com/reference/android/provider/MediaStore.MediaColumns.html#SIZE
It sounds like it should contain 0 if there is no data value. But I wouldn't know what it contains if data is a file path.
It could be that the images are not downloaded. I faced a similar problem with whatsapp images.
One way to go about this could be like below:
InputStream is = null;
try {
is = context.getContentResolver().openInputStream(myuri);
}catch (Exception e){
Log.d("TAG", "Exception " + e);
}
if(is==null)
//Assign to "no image"
Based on the code (http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.5_r4/android/widget/ImageView.java) my solution for checking Uri:
public static Uri checkUriExists (Context mContext,Uri mUri) {
Drawable d = null;
if (mUri != null) {
if ("content".equals(mUri.getScheme())) {
try {
d = Drawable.createFromStream(
mContext.getContentResolver().openInputStream(mUri),
null);
} catch (Exception e) {
Log.w("checkUriExists", "Unable to open content: " + mUri, e);
mUri = null;
}
} else {
d = Drawable.createFromPath(mUri.toString());
}
if (d == null) {
// Invalid uri
mUri = null;
}
}
return mUri;
}
I am using this code for Uri that has file:// authority
Uri resimUri = Uri.parse(path_str);
File imgFile = new File(resimUri.getPath());
if (imgFile.exists()) {
// file exists
}else {
// file is not there
}