I'm trying to make a simple Android app for my own phone that looks at my Gallery, and for each image/video file:
creates a Bitmap thumbnail of the file
records the file's absolute path on the phone
Essentially, I'll have the following data model:
public class MediaFileModel {
private Bitmap thumbnail;
private String absPath;
// ctor, getters and setters omitted for brevity
}
And I need something that looks at all the files in my Gallery and yields a List<MediaFileModel>. My best attempt is here:
public List<MediaFileModel> getAllGalleryMedia() {
String[] projection = { MediaStore.MediaColumns.DATA };
List<MediaFileModel> galleryMedia = new ArrayList<>();
Cursor cursor = getActivity().getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
null);
// I believe here I'm iterating over all the gallery files (???)
while(cursor.moveToNext()) {
MediaFileModel next = new MediaFileModel();
// is this how I get the abs path of the current file?!?
String absPath = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA));
Bitmap thumbnail = null;
if (true /* ??? cursor is pointing to a media file that is an image/photo ??? */) {
thumbnail = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(absPath), 64, 64);
} else {
// else we have a video (I assume???)
thumbnail = ThumbnailUtils.createVideoThumbnail(absPath, MediaStore.Images.Thumbnails.MINI_KIND);
}
next.setThumbnail(thumbnail);
next.setAbsPath(absPath);
galleryMedia.add(next);
}
return galleryMedia;
}
However I'm not sure if my media query is setup correctly and I'm definitely not sure how to determine whether the file is an image/photo of a video, which I (believe I) need so that I can use the correct method for obtaining the thumbnail Bitmap.
Can anyone help nudge me over the finish line here?
Here is a code I was using for something similar, I hope this will help:
//The code has been removed
Note: Sorry I've removed the code because I am not sure if it has some portion copied from other open-sourced codes, I am using it in my apps but I can't remember if it is quoted from other source, so I've removed it, also it could be auto-completed using github copilot
Related
I want to detect and display most recent images and it should be either animated or static images on user choice.
Also cannot depend on extentions.Because
There would be absence of extention
webp images could be animated or static with same extention (.webp)
It could be wrong extentions
Is there way to identify whether images are animated (webp, gif, apng) or static (jpg, png, webp, etc)?
private static final String[] COLUMNS_OF_INTEREST = new String[]
{
MediaStore.Video.Media._ID,
MediaStore.Video.Media.DATA,
MediaStore.Video.Media.DISPLAY_NAME,
MediaStore.Video.Media.SIZE,
MediaStore.Video.Media.WIDTH,
MediaStore.Video.Media.HEIGHT,
MediaStore.Video.Media.DATE_ADDED
};
public void printGifUri(Context context)
{
ContentResolver cr = context.getContentResolver();
String selection = MediaStore.Images.Media.MIME_TYPE + "=?";
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("webp");
String[] selectionArgsPdf = new String[]{ mimeType };
Cursor gifCursor = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, COLUMNS_OF_INTEREST, selection,selectionArgsPdf,
MediaStore.Images.Media.DATE_ADDED + " DESC");
gifCursor.moveToFirst();
int columnIndexUri = gifCursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
for (int i = 0; i < gifCursor.getCount(); i++)
Log.d("gif file uri -> ", gifCursor.getString(columnIndexUri));
}
I want to also avoid the third party modules as they would increase the size of the app. You can suggest if there is any module available for this with low size.
I have Tried checking the existance of flags like ANIM, VP8, in image file and it works well but this explicit method is time consuming.
The major issue in this is webp as same extention would be static or animated. This makes difficult me to identify the animated and static type.
I think I maybe found a solution... try using
ImageDecoder
the documentation says
If the encoded image is an animated GIF or WEBP, decodeDrawable will
return an AnimatedImageDrawable.
So maybe checking that the result of ImageDecoder.decodeDrawable(source); is an instance of AnimatedImageDrawable should help you figure out if a file is animated or not
such as:
ImageDecoder.Source source = ImageDecoder.createSource(cr,uri);
Drawable drawable = ImageDecoder.decodeDrawable(source);
if (drawable instanceof AnimatedImageDrawable) {
//THE FILE IS ANIMATED
}else{
//THE FILE IS STATIC
}
or such as
private boolean isAnimated(ContentResolver cr,Uri uri){
ImageDecoder.Source source = ImageDecoder.createSource(cr,uri);
Drawable drawable = ImageDecoder.decodeDrawable(cr,source);
return drawable instanceof AnimatedImageDrawable;
}
EDIT:
for API 28 and before, we should use Movie class
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
ImageDecoder.Source source = ImageDecoder.createSource(file);
Drawable drawable = ImageDecoder.decodeDrawable(source);
return drawable instanceof AnimatedImageDrawable;
} else {
Movie movie = Movie.decodeStream(input);
return movie != null;
}
I hope that helps with your problem
I want to show the last image from user's gallery and show it in the ImageView. I used below code but got no success after all.
private void getLastGalleryImage() {
// Find the last picture
String[] projection = new String[]{
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.MIME_TYPE
};
final Cursor cursor = this.getContentResolver()
.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null,
null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
// Put it in the image view
if (cursor != null) {
if (cursor.moveToFirst()) {
String imageLocation = cursor.getString(1);
File imageFile = new File(imageLocation);
if (imageFile.exists()) {
Bitmap bm = BitmapFactory.decodeFile(imageLocation);
galleryImageView.setImageBitmap(bm);
}
}
}
}
What I want to achieve is shown in the image below:
Your code to obtain a bitmap instance is wrong (as it is useless for Android 10 and 11) where you use the File class.
Better use ._ID column to construct the uri for the file and then use the two code lines mentioned in: Android 10 MediaStore file permission
I want to create something like "PDF Viewer app". Application will search for all *.pdf files in location chosen by user. User can choose this folder by this function:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE);
Then I get DocumentFile (folder):
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == getActivity().RESULT_OK && requestCode == REQUEST_CODE) {
Uri uriTree = data.getData();
DocumentFile documentFile = DocumentFile.fromTreeUri(getActivity(), uriTree);
//rest of code here
}
}
Why I chose this method of selecting folder? Because I want to make possible to choose Secondary Storage (you know, in Android >= 5.0, you can't access Secondary Storage with Java.io.file).
Ok, so I get folder with all *.pdf as DocumentFile. Then I call:
for(DocumentFile file: documentFile.listFiles()){
String fileNameToDisplay = file.getName();
}
And this is VERY SLOW. It takes almost 30 seconds when there are ~600 files in chosen folder. To prove it, I chose directory from External Storage (not secondary storage), and then I tried two solutions: DocumentFile and File.
File version looks like it:
File f = new File(Environment.getExternalStorageDirectory()+"/pdffiles");
for(File file: f.listFiles()){
String fileNameToDisplay = file.getName();
}
}
Second version works about 500x faster. There is almost no time in displaying all files on List View.
Why is DocumentFile so slow?
If you read the source code of TreeDocumentFile, you will find that each call to listFiles() and getName() invokes ContentResolver#query() under the hood. Like CommonsWare said, this would perform hundreds of queries, which is very inefficient.
Here is the source code of listFiles():
#Override
public DocumentFile[] listFiles() {
final ContentResolver resolver = mContext.getContentResolver();
final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(mUri,
DocumentsContract.getDocumentId(mUri));
final ArrayList<Uri> results = new ArrayList<>();
Cursor c = null;
try {
c = resolver.query(childrenUri, new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID }, null, null, null);
while (c.moveToNext()) {
final String documentId = c.getString(0);
final Uri documentUri = DocumentsContract.buildDocumentUriUsingTree(mUri,
documentId);
results.add(documentUri);
}
} catch (Exception e) {
Log.w(TAG, "Failed query: " + e);
} finally {
closeQuietly(c);
}
final Uri[] result = results.toArray(new Uri[results.size()]);
final DocumentFile[] resultFiles = new DocumentFile[result.length];
for (int i = 0; i < result.length; i++) {
resultFiles[i] = new TreeDocumentFile(this, mContext, result[i]);
}
return resultFiles;
}
In this function call, listFiles() made a query that only selects the document ID column. However, in your case you also want the file name for each file. Therefore, you can add the column COLUMN_DISPLAY_NAME to the query. This would retrieve the filename and document ID (which later you will convert it into Uri) in a single query and is much more efficient. There are also many other columns available such as file type, file size, and last modified time, which you may want to retrieve them as well.
c = resolver.query(mUri, new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME
}, null, null, null);
Within the while loop, retrieve the filename by
final String filename = c.getString(1);
The above modified code is able to instantly retrieve the Uri and filename of a directory with 1000+ files.
In summary, my recommendation is to avoid using DocumentFile if you are working with more than just a few files. Instead use ContentResolver#query() to retrieve the Uri and other information by selecting multiple columns in the query. For file operations, use the static methods in the DocumentsContract class by passing the appropriate Uri's.
By the way, it seems that the sortOrder parameter of ContentResolver#query() gets completely ignored in the above code snippet when tested on Android 11 and Android 9. I would manually sort the results instead of relying on the query order.
Why is DocumentFile so slow?
For ~600 files you are performing ~600 requests of a ContentProvider to get the display name, which means ~600 IPC transactions.
Instead, use MediaStore to query for all indexed media with the application/pdf MIME type.
In case someone comes up here still looking for a solution,
I built a wrapper over this with some pretty good performance.
You can check the wrapper & performance info. here:
https://github.com/ItzNotABug/DocumentFileCompat
To use DocumentsContract to obtain children documents, see https://developer.android.com/reference/android/provider/DocumentsContract.html#buildChildDocumentsUriUsingTree(android.net.Uri, java.lang.String).
The Uri returned from ACTION_OPEN_DOCUMENT_TREE is a tree document URI. Use the above method to build the Uri to query all children documents.
The root document ID can be obtained using https://developer.android.com/reference/android/provider/DocumentsContract.html#getTreeDocumentId(android.net.Uri) with the Uri returned from ACTION_OPEN_TREE_DOCUMENT.
To obtain a speed little less than the one from File use DocumentsContract instead of DocumentFile to list the content of trees obtained with Intent.ACTION_OPEN_DOCUMENT_TREE.
I have an image processing app. In my code, I pass my service an ArrayList and the service does the rest. Now I want to extend the functionality of my app and let users be able to go to the gallery, pick one picture and, using the share button, send it to my app to be processed.
I want to reuse the most code as possible, so I decided that a good way to go would be converting those URIs returned by the send action to actual file paths.
My solution works as expected with QuickPic, but not with Google Photos. My code is as follows:
//MainFragment.onCreateView()
Intent intent = getActivity().getIntent();
if(Intent.ACTION_SEND.equals(intent.getAction()) {
handleSingleImage(intent);
}
private void handleSingleImage(Intent intent) {
Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
Log.d("MYURISTRING", uri.toString());
ArrayList<String> selectedPaths = new ArrayList<String>();
String path = Utils.getRealPathFromURI(getActivity(), uri);
selectedPaths.add(path);
Utils.startProcessPhotosService(getActivity(), MainFragment.this, selectedPaths);
}
//Utils
public static String getRealPathFromURI(Context context, Uri contentUri) {
/*String[] proj = {MediaStore.Images.Media.DATA};
CursorLoader cursorLoader = new CursorLoader(context, contentUri, proj, null, null, null);
Cursor cursor = cursorLoader.loadInBackground();
int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(columnIndex);*/
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(contentUri, null, null, null, null);
cursor.moveToFirst();
String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
cursor.close();
return path;
}
If I test this with a photo from QuickPic app, everything works as expected, and the URI on the log is as follows:
content://media/external/images/media/135695
But if I test this with Google Photos, my app crashes, and the URI is as follows:
content://com.google.android.apps.photos.contentprovider/-1/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F135669/ACTUAL
How can I do to support both styles of URI (and, possibly, more than these)?
Thank you
so I decided that a good way to go would be converting those URIs returned by the send action to actual file paths
That is so not a good way to go.
As I have already pointed out a couple of times today, and dozens upon dozens of times in the past months, a Uri is not a file. You cannot reliably get a local file path for a Uri. There may not even be a local path, let alone one that you can access.
If you wish to use the content represented by the Uri, use getContentResolver().openInputStream(), or things that in turn use it (e.g., Picasso for image loading).
I'm using OpenGL ES to make a game in Android. I got some code from a tutorial and I'm trying to change it to suit my app but I'm having a problem. I want to dynamically get an image resource using a string passed into a function as the resource name. I know usually you use getIdentifier() in this case, but that returns an int and I need an input stream. Is there any way of getting an input stream from a resource dynamically?
Alternatively, is there a better way of doing this?
Code below:
InputStream is = mContext.getResources().openRawResource(R.drawable.<imagename>);
Bitmap bitmap;
try {
bitmap = BitmapFactory.decodeStream(is);
}
finally {
try {
is.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
yes u can Suppose u have images stored in drawable with naming img1,img2,img3,img4,img5,img6,img7 than
first make an array like
String[] imgarray={"img1","img2","img3","img4","img5","img6","img7"};
public static String PACKAGE_NAME ;
PACKAGE_NAME=getApplicationContext().getPackageName();
Random r = new Random();
int n=r.nextInt(imgarray.length());
int resID = getResources().getIdentifier( PACKAGE_NAME+":drawable/" +imgarray[n] , null, null);
imageview.setImageResource(resID);
if want bitmap image than just add below line
Bitmap bm = BitmapFactory.decodeResource(getResources(),resID);
if u want other way with less coding than see accepted answer at Other Example