I'm writing a "gallery-type" app for Android. In the "master activity", I have a GridView which I want to load/fill with thumbnails from photos on the device. So I've written a ContentProvider, where the query method returns a cursor from MediaStore.Media.Thumbnails.
However, I also want to display some meta data from MediaStore.Media.Images. In order to do this, I ended up using a CursorJoiner to join c_thumbs and c_images on ID. Only problem with this, is that CursorJoiner requires the input cursors to already be sorted (ordered) on the join key (ID), in ascending order. What I want is newest photos on top, but is there way to sort an existing (already loaded) cursor?
Alternatively to CursorJoiner, maybe I could do the join in SQL? Is it possible to join MediaStore.Images.Media and MediaStore.Images.Thumbnails in one query (and how would that work)?
// split projection into MediaStore and MediaStore.Thumbs parts
ArrayList<String> MS_proj = new ArrayList<>();
ArrayList<String> MS_proj_thumbs = new ArrayList<>();
for (String f : projection) {
if (PhotoContract.ThumbEntry.COL_TYPES.get(f).equals(PhotoContract.TARGET_MEDIASTORE)) {
MS_proj.add(f);
} else {
MS_proj_thumbs.add(f);
}
}
String[] MS_proj_thumbs_array = new String[MS_proj_thumbs.size()];
MS_proj_thumbs_array = MS_proj_thumbs.toArray(MS_proj_thumbs_array);
// add _ID column to Images.Media projection, for use in JOIN
MS_proj.add("_ID");
String[] MS_proj_array = new String[MS_proj.size()];
MS_proj_array = MS_proj.toArray(MS_proj_array);
Uri baseUri;
// first, get cursor from MediaStore.Images.Thumbnails containing all thumbnails
baseUri = MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI;
Log.v("TEST", "Thumb: order by " + PhotoContract.ThumbEntry.COLUMN_DATE);
Cursor c_thumbs = getContext().getContentResolver().query(baseUri, MS_proj_thumbs_array, null, null, PhotoContract.ThumbEntry.COLUMN_IMAGE_ID);
if (c_thumbs == null || !c_thumbs.moveToFirst()) {
Log.v("TEST", "MediaStore.Thumbnails cursor contains no data...");
return null;
}
// then, get cursor from MediaStore.Images.Media (for TITLE and DESCRIPTION etc)
// add _ID column to query
baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Cursor c_images = getContext().getContentResolver().query(baseUri, MS_proj_array, null, null, PhotoContract.ThumbEntry.COLUMN_IMAGE_PK);
if (c_images == null || !c_images.moveToFirst()) {
Log.v("TEST", "MediaStore.Images cursor contains no data...");
return null;
}
// join these and return
// the join is on images._ID = thumbnails.IMAGE_ID
CursorJoiner joiner = new CursorJoiner(
c_thumbs, new String[] { PhotoContract.ThumbEntry.COLUMN_IMAGE_ID },
c_images, new String[] { PhotoContract.ThumbEntry.COLUMN_IMAGE_PK }
);
MatrixCursor retCursor = new MatrixCursor(projection);
/*
Does a join on two cursors using the specified columns.
The cursors must already be sorted on each of the specified columns in ascending order.
This joiner only supports the case where the tuple of key column values is unique.
*/
for (CursorJoiner.Result joinerResult : joiner) {
switch (joinerResult) {
case LEFT:
// handle case where a row in cursorA is unique
break;
case RIGHT:
// handle case where a row in cursorB is unique
break;
case BOTH:
// handle case where a row with the same key is in both cursors
retCursor.addRow(new Object[]{
c_thumbs.getLong(0), // ID
c_thumbs.getString(1), // data
c_thumbs.getLong(2), // image id
c_images.getString(0), // title
c_images.getString(1), // desc
c_images.getLong(2) // date
});
break;
}
}
// https://mcmap.net/q/1021318/-getcontentresolver-query-cause-cursorwrapperinner-warning
c_thumbs.close();
c_images.close();
return retCursor;