As far as I can understand the right way of working with images in Android is to store images Uri in the database, not the image actual path.
Thats how we can get thumbnails for free. And we don't need to prepare them by ourself and hide somewere.
The code below takes thumbnails by Uri.
Code works on android 4.4 and below. But to get image id it doesn't query content provider. It takes ID from Uri itself. The question is: Is it a stable solution, can I rely on it?
#TargetApi(Build.VERSION_CODES.KITKAT)
public static Bitmap getImageThumbnail(Context context, Uri uri){
final ContentResolver resolver = context.getContentResolver();
long id;
try {
if (UIUtils.hasKitKat() && DocumentsContract.isDocumentUri(context, uri)) {
String wholeID = DocumentsContract.getDocumentId(uri);
// Split at colon, use second item in the array
id = Long.parseLong(wholeID.split(":")[1]);
}
else if (isMediaUri(uri)){
id = ContentUris.parseId(uri);
}
else return null;
return MediaStore.Images.Thumbnails.getThumbnail(
resolver,
id,
MediaStore.Images.Thumbnails.MINI_KIND,
null);
}
catch (Exception e) {
if (DEBUG) Log.e(TAG, "getThumbnail", e);
return null;
}
}
public static boolean isMediaUri(Uri uri) {
return "media".equalsIgnoreCase(uri.getAuthority());
}
you should be using DocumentsContract.getDocumentThumbnail(). this is guaranteed to work with any document uri, not just the builtin media provider.
Related
I have a file picker implemented in my app like this:
Intent intent = new Intent();
intent.setType("*/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Title"), FILE_PICK);
It's a known issue that you can't easily get the actual file location this way because Intent will return some weird Uri that you can't really use to create a File object.
I'm using this method to get the actual File path:
/**
* Get a file path from a Uri. This will get the the path for Storage Access
* Framework Documents, as well as the _data field for the MediaStore and
* other file-based ContentProviders.
*
* #param context The context.
* #param uri The Uri to query.
* #author paulburke
*/
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
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);
}
// MediaProvider
else if (isMediaDocument(uri)) {
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);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
This works for some files, and for some not. Right now I noticed that it doesn't work when I pick a file from "Downloads" folder. It skips all if statements above and returns null.
What would be the correct way, that would be more reliable to get the actual selected File path?
I have a file picker implemented in my app like this
That is not a "file picker". It is a content picker.
I need a way to be able to pick any file on Android device so that I can upload it to my backend.
Um, well, that depends a fair bit on how literal you are in that sentence.
ACTION_GET_CONTENT is not a problem in general. However, what you get are not necessarily files. However, you can still upload them, assuming that whatever you are using for the uploading allows you to upload from an InputStream. If so, use openInputStream() on a ContentResolver, passing in the Uri, to get the InputStream to use.
If you really need it to be actual files, you can implement a file-picker UI directly in your app. There are libraries for this. However, you will not be able to access all files — in particular, you cannot use this to upload files from removable storage on Android 4.4+.
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 have registered my app to receive files (of any type, not just images) from other apps following this post.
I have implemented the solution that was answered but I cannot find a way to retrieve the "file name" of the data stream.
As an example from an Uri like:
content://downloads/all_downloads/5
I can get the stream out but I don't know anything about the name of the original file generating it.
Is there a way to retrieve it?
In MOST cases this will solve your problem:
Uri intentData = intent.getData();
if (intentData != null) {
String filePath;
if("content".equals(intent.getScheme()))
{
filePath = getFilePathFromContentUri(intentData);
}
else
{
filePath = intentData.getPath();
}
}
private String getFilePathFromContentUri(Uri selectedUri) {
String filePath;
String[] filePathColumn = {MediaColumns.DATA};
Cursor cursor = getContentResolver().query(selectedUri, filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
filePath = cursor.getString(columnIndex);
cursor.close();
return filePath;
}
Is there a way to retrieve it?
Generally, no, because there may not be a name, in part because there may not be a file. You may be able to get an InputStream on the contents, but that does not mean that there is a file behind the InputStream.
There may be some specific hacks for some specific providers (e.g., MediaStore) to try to determine the file name associated with some data Uri, though such hacks may not be reliable.
onCreate()
Intent intent1 = getIntent();
String action = intent1.getAction();
String type = intent1.getType();
if (Intent.ACTION_SEND.equals(action) && type != null) {
this.handleSend(intent1);
}
void handleSend(Intent intent) {
try {
Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
imageShare.setImageURI(imageUri);
} catch (Exception e) {
e.printStackTrace();
}
}
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
}
Is it possible to get the thumbnail image from a video Url? I need to thumbnails of videos in a list view.
Yes its possible to get the thumbnail of a video using ThumbnailUtils.
FileOutputStream out;
File land=new File(Environment.getExternalStorageDirectory().getAbsoluteFile()
+"/portland.jpg");// image file use to create image u can give any path.
Bitmap bitmap=ThumbnailUtils.createVideoThumbnail(filePath, MediaStore.Video.Thumbnails.FULL_SCREEN_KIND);//filePath is your video file path.
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArray = stream.toByteArray();
out=new FileOutputStream(land.getPath());
out.write(byteArray);
out.close();
If you are specifically asking about youtube videos then there are automatically 4 generated images by Youtube.
http://img.youtube.com/vi/video_url_here/0.jpg
http://img.youtube.com/vi/video_url_here/1.jpg
http://img.youtube.com/vi/video_url_here/2.jpg
http://img.youtube.com/vi/video_url_here/3.jpg
Standard Image sizes
Player Background Thumbnail (480x360 pixels)
http://i1.ytimg.com/vi/G0wGs3useV8/0.jpg
Start Thumbnail (120x90 pixels)
http://i1.ytimg.com/vi/G0wGs3useV8/1.jpg
Middle Thumbnail (120x90 pixels)
http://i1.ytimg.com/vi/G0wGs3useV8/2.jpg
End Thumbnail (120x90 pixels)
http://i1.ytimg.com/vi/G0wGs3useV8/3.jpg
High Quality Thumbnail (480x360 pixels)
http://i1.ytimg.com/vi/G0wGs3useV8/hqdefault.jpg
Medium Quality Thumbnail (320x180 pixels)
http://i1.ytimg.com/vi/G0wGs3useV8/mqdefault.jpg
Normal Quality Thumbnail (120x90 pixels)
http://i1.ytimg.com/vi/G0wGs3useV8/default.jpg
Standard Definition Thumbnail (640x480 pixels)
http://i1.ytimg.com/vi/G0wGs3useV8/sddefault.jpg
Maximum Resolution Thumbnail (1920x1080 pixels)
http://i1.ytimg.com/vi/G0wGs3useV8/maxresdefault.jpg
It is working in my case
Uri videoUri = data.getData();
String selectedPathVideo="";
selectedPathVideo = ImageFilePath.getPath(getApplicationContext(), videoUri);
Log.i("Image File Path", ""+selectedPathVideo);
try {
Bitmap thumb = ThumbnailUtils.createVideoThumbnail(selectedPathVideo, MediaStore.Video.Thumbnails.MICRO_KIND);
imgFarmerVideo.setImageBitmap(thumb);
} catch (Exception e) {
e.printStackTrace();
}
The Support File
public class ImageFilePath {
/**
* Method for return file path of Gallery image
*
* #param context
* #param uri
* #return path of the selected image file from gallery
*/
public static String getPath(final Context context, final Uri uri)
{
//check here to KITKAT or new version
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
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);
}
// MediaProvider
else if (isMediaDocument(uri)) {
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);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* #param context The context.
* #param uri The Uri to query.
* #param selection (Optional) Filter used in the query.
* #param selectionArgs (Optional) Selection arguments used in the query.
* #return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
}
Try ThumbnailUtils, to get video bitmap from file path
ThumbnailUtils.createVideoThumbnail(filePath,MediaStore.Video.Thumbnails.MINI_KIND);
It is possible to get thumbnail image of video from video url.
If your video type is Vimeo follow below steps:
1)Suppose your Video Url is:
http://player.vimeo.com/video/17314292
2)Get Video Id From Url : eg. 17314292
3)Call WS with Video id and XMl format
http://vimeo.com/api/v2/video/17314292.xml
You will get "thumbnail_small","thumbnail_medium","thumbnail_large" in xml format for video thumb from video id
If your video type is YouTube follow below steps:
1)Video Url :
http://www.youtube.com/embed/Nl2iMF0yKW8
2)Get Video Id From Url : eg. Nl2iMF0yKW8
3)Replace video id in this url
http://i3.ytimg.com/vi/Nl2iMF0yKW8/default.jpg
NOTE:Just replace your id here.
It's very important to identify the components of a problem. I'd actually say you have two separate problems:
Downloading a file from an URL.
Creating a thumbnail image from an incomplete video file.
Look into those two separately, you'll find plenty of information.
To get thumbnail from the URL, i only got one solution till now,
You have to use This library
It Supports file, http, https, mms, mmsh and rtmp protocols
and
It Supports aac, acc+, avi, flac, mp2, mp3, mp4, ogg, 3gp and more! formats (audio and video):
It is possible to get a thumbnail from a video file or url using FFMPEG.
FFMPEG must be built using the NDK (or you can find some Android built FFMPEG binaries). Here is a GitHub project to build FFMPEG:
https://github.com/guardianproject/android-ffmpeg
You can then include the FFMPEG binaries with your app and execute FFMPEG from code in your app to generate an image from the video (local or URL) using a command like:
ffmpeg -i videosite.com/video.flv -ss 00:00:15.000 -vframes 1 thumbnail_out.png
One thing to consider is at what seek time (option -ss) you grab the thumbnail for it to be a meaningful image that represents the video.