I have been retrieving images from MediaStore in the following way...
Uri uriExternal = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = {
MediaStore.MediaColumns._ID,
MediaStore.MediaColumns.DATE_ADDED
};
Cursor cursor = getContentResolver()
.query(uriExternal, projection,
MediaStore.MediaColumns.DATA + " IS NOT NULL",
null,
MediaStore.MediaColumns.DATE_ADDED + " DESC");
if(cursor != null) {
while (cursor.moveToNext()) {
String _id = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
paths.add(uriExternal.toString() + "/" + _id);
}
cursor.close();
}
Basically, I'm simply appending the file id to the external content provider uri. This makes a uri that I can use with content providers...
content://media/external/images/media/{id}
It all works perfectly fine, all external images are displayed and loaded flawlessly. However, since I've failed to find proper documentation, I'm a little concerned I'm not doing things the proper way. Especially because of the way I'm constructing the uri...kind of hard-coding it...
The questions are...
Is this the correct way to construct a content uri for an external image?
Is there a more reliable way to achieve this?
Personally, I use ContentUris.withAppendedId(). That way, I don't have to worry about whether I am starting with a Uri that ends in / or not. :-)
In general, MediaStore adheres to the original ContentProvider vision of using the content ID as the last path segment of a Uri pointing to the content. However, that is not a general rule, and it will not work for all providers.
Related
I am querying the Images table to get all pictures in the MediaStore.Images.Media.EXTERNAL_CONTENT_URI directory.
See the following query:
String[] what = new String[]{ MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.MIME_TYPE,
MediaStore.Images.ImageColumns.DATA };
String where = MediaStore.Images.Media.MIME_TYPE + "='image/jpeg'" +
" OR " + MediaStore.Images.Media.MIME_TYPE + "='image/png’";
Cursor cursor = getContext().getContentResolver()
.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
what,
where,
null,
MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC”);
Now, I’d like to have an Uri pointing to each of the result.
This is what I am doing right now:
int dataIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
String path = cursor.getString(dataIndex);
final Uri uri = Uri.fromFile(new File(path));
E.g., I take the path from the DATA column, create a file and use Uri.fromFile. I have two questions.
Is this guaranteed to work? Is the query above guaranteed to return paths in the data column? It works for all the pictures in my phone: path is always a path, like /storage/0/whatever.jpg, and uri.toString() is the same but with the file scheme. Still, pictures can very well be defined by content:// uris, but I fail to see how (and if) these are represented in the images table.
If not, what should I expect in the DATA column, and how to get an Uri from it?
Is the query above guaranteed to return paths in the data column?
It should return something. That "something" may not be usable. For example, the image might be on removable storage, and you cannot access it directly.
how to get an Uri from it?
You don't. You construct a Uri from the _ID:
Uri imageUri=
ContentUris
.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
cursor.getInt(cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID)));
I have an app which uses cursors to load thumbnails (into a gridview) from the mediastore:
CursorLoader cursorLoader = null;
if (Integer.valueOf(android.os.Build.VERSION.SDK_INT) >= android.os.Build.VERSION_CODES.HONEYCOMB) {
String[] from = { MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.DATE_ADDED,
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.Files.FileColumns.MIME_TYPE,
MediaStore.Files.FileColumns.TITLE };
projection = from;
String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE + " OR "
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO;
Uri queryUri = MediaStore.Files.getContentUri("external");
cursorLoader = new CursorLoader(this, queryUri, projection,
selection, null, MediaStore.Files.FileColumns.DATE_ADDED
+ " DESC");
} else {
String[] from = { MediaStore.MediaColumns.DATE_ADDED };
projection = from;
Uri sourceImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
cursorLoader = new CursorLoader(this, sourceImageUri, null, null,
null, MediaStore.MediaColumns.DATE_ADDED + " DESC");
}
Cursor cursor = null;
if (cursorLoader != null) {
cursor = cursorLoader.loadInBackground();
mGalleryAdapter = new GalleryAdapter(this,
android.R.layout.simple_list_item_1, cursor, projection,
to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
gallery.setAdapter(mGalleryAdapter);
gallery.setOnItemClickListener(myOnItemClickListener);
}
However, some newer images are loaded without thumbnails, which is rather silly. The thumbnails are only generated when I open the gallery app (Photos) on my phone.
How can I refresh all the thumbnails programatically on the device so there are no images left blank without a thumbnail?
I would like this to work on all Android version, including 4.4. Thanks in advance :)
You should be able to trigger a rescan with this:
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://"
+ Environment.getExternalStorageDirectory())));
You may need to alter the path to suit your needs.
Found here
And if you wanted to wait for the scan to complete before moving forward you will need to register a receiver for the ACTION_MEDIA_SCANNER_FINISHED intent.
Details and discussion on that here/RdvA7y4MqfE
kaiwid23 suggests this doesn't work for 4.4+. In that case you can manually control the scanner with a MediaScannerConnection.
You will want to use the MediaConnection.scanFile() method. I don't know if it works recursively if supplied a directory or whether it just accepts single paths though.
In this case you pass an OnScanCompletedListener in the constructor so monitoring for the scan to complete is actually much easier.
Edit:
Use MediaStore.Images.Thumbnails.getThumbnail() or MediaStore.Video.Thumbnails.getThumbnail() - it's a blocking class and generates a new bitmap if it doesn't find one. Much simpler.
You simply need the MediaStore ID of the file, which I assume you have, and you can use the standard getContentResolver() in your Activity or through a context. This method also allows for you to get a Bitmap directly in MINI or MICRO sizes.
More on this here.
Old answer:
As indivisible says, you can use
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://"
+ Environment.getExternalStorageDirectory())));
However, that will not work on Android 4.4 (I think), so consider using the MediaScanner class for API 19+.
I have videoplayer app with filebrowser listing all videos on SD card
Code inspired by i want get audio files in sd card
Using ContentResolver, works as expected, but it does not update if the files on card change. I do not mean automatically, but after view/app restart. Not even reinstalling the application helped, still shows the same files. The deleted video file is not visible via PC nor it is possible to play it (This video cannot be played (translation)).
I dumped the data and the problem is not in view caching or elsewhere. I do not implement any caching of my own and failed to find anything on the matter. Thank you
Code:
// acquisition
String[] projection = {
MediaStore.Video.Media._ID,
MediaStore.Video.Media.DISPLAY_NAME,
MediaStore.Video.Media.DURATION,
MediaStore.Video.Media.DATA
};
ContentResolver resolver = getActivity().getContentResolver();
Cursor videoCursor = resolver.query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
null
);
// extraction
while(cursor.moveToNext()) {
cursorIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
filepath = cursor.getString(cursorIndex);
cursorIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME);
filename = cursor.getString(cursorIndex);
cursorIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION);
duration = cursor.getString(cursorIndex);
result[ index++ ] = new VideoFileMetadata(filename, duration, filepath);
}
Edit 1 [14-03-2013]:
I tried adding number + " = " + number to ORDER or WHERE clause to act as a potential query caching buster, but it had no effect (although it's possible it was removed by an optimizer as a useless clause). This time I had reinstalled the application from a different machine using different certificate, but the query result remained the same, listing currently non-existing files.
You should first call cursor.moveToFirst() .
So, your cursor iteration loop should look like
if (cursor.moveToFirst()) {
do {
// cursorIndex = cursor.getColumnIndexOrThrow, etc...
} while (cursor.moveToNext());
}
In the class MediaStore.Files class, its mentioned that,
Media provider table containing an index of all files in the media storage, including non-media files.
I'm interested in querying for non-media files like PDF.
I'm using CursorLoader to query the database. The second parameter for the constructor requires an Uri argument which is easy to get for the media types Audio, Images and Video as each of them have a EXTERNAL_CONTENT_URI and INTERNAL_CONTENT_URI constant defined for them.
For MediaStore.Files there is no such defined constant. I tried using the getContentUri() method but couldn't figure out the argument value for volumeName. I tried giving "/mnt/sdcard" and also the volume name that appears when I connect the device to my system but in vain.
I saw a similar question on Google Groups but that is not resolved.
EDIT: I also tried using Uri.fromFile(new File("/mnt/sdcard/")) and Uri.parse(new File("/mnt/sdcard").toString()) but that didn't work out either.
It is "external" or "internal" although internal (system files) is probably not useful here.
ContentResolver cr = context.getContentResolver();
Uri uri = MediaStore.Files.getContentUri("external");
// every column, although that is huge waste, you probably need
// BaseColumns.DATA (the path) only.
String[] projection = null;
// exclude media files, they would be here also.
String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_NONE;
String[] selectionArgs = null; // there is no ? in selection so null here
String sortOrder = null; // unordered
Cursor allNonMediaFiles = cr.query(uri, projection, selection, selectionArgs, sortOrder);
If you want .pdf only you could check the mimetype
// only pdf
String selectionMimeType = MediaStore.Files.FileColumns.MIME_TYPE + "=?";
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("pdf");
String[] selectionArgsPdf = new String[]{ mimeType };
Cursor allPdfFiles = cr.query(uri, projection, selectionMimeType, selectionArgsPdf, sortOrder);
Is there a way to limit the result retrieved from mediastore using managedQuery function on Android. Since I currently have a grid that displaying all photos found on the sd card but it is too intensive of fetching it so I decide to limit the result retrieved from the media store but could not find a limit function that can reduce the resulting set of data.
Please help
use order in contentresolver's query method to implement your function,
such as 'columnname asc limit number'
in my case:
cursor = resolver.query(STORAGE_URI, projection,
Media.BUCKET_DISPLAY_NAME + "=?",
new String[] { folderName },
" _id asc limit " + num);
You can limit the result using the sortOrder parameter in query method. Something like this
ContentResolver contentResolver = getContentResolver();
Cursor androidCursor = null;
String sortOrder = String.format("%s limit 100",BaseColumns._ID);
androidCursor = contentResolver.query(IMAGE_URI,PROJECTION, null, null, sortOrder);
This will order the result set by id and limit the result.
When targeting Android 11 the suggested answer will cause an exception, with java.lang.IllegalArgumentException: Invalid token LIMIT
Instead you should provide a "limit" query parameter on the URI that the MediaProvider will read.
val queryUri = IMAGE_URI.buildUpon().appendQueryParameter("limit", limit.toString()).build()
I haven't found documentation for this, but it's present in the sources