Get list of photo galleries on Android
Groupings are defined by MediaStore.Images.Media.BUCKET_DISPLAY_NAME
. Here is the sample code to list the images and log their bucket name and date_taken:
// which image properties are we querying
String[] projection = new String[] {
MediaStore.Images.Media._ID,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
MediaStore.Images.Media.DATE_TAKEN
};
// content:// style URI for the "primary" external storage volume
Uri images = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
// Make the query.
Cursor cur = managedQuery(images,
projection, // Which columns to return
null, // Which rows to return (all rows)
null, // Selection arguments (none)
null // Ordering
);
Log.i("ListingImages"," query count=" + cur.getCount());
if (cur.moveToFirst()) {
String bucket;
String date;
int bucketColumn = cur.getColumnIndex(
MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
int dateColumn = cur.getColumnIndex(
MediaStore.Images.Media.DATE_TAKEN);
do {
// Get the field values
bucket = cur.getString(bucketColumn);
date = cur.getString(dateColumn);
// Do something with the values.
Log.i("ListingImages", " bucket=" + bucket
+ " date_taken=" + date);
} while (cur.moveToNext());
}
/**
* Getting All Images Path
*
* @param activity
* @return ArrayList with images Path
*/
public static ArrayList<String> getAllShownImagesPath(Activity activity) {
Uri uri;
Cursor cursor;
int column_index_data, column_index_folder_name;
ArrayList<String> listOfAllImages = new ArrayList<String>();
String absolutePathOfImage = null;
uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = { MediaColumns.DATA,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME };
cursor = activity.getContentResolver().query(uri, projection, null,
null, null);
column_index_data = cursor.getColumnIndexOrThrow(MediaColumns.DATA);
column_index_folder_name = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
while (cursor.moveToNext()) {
absolutePathOfImage = cursor.getString(column_index_data);
listOfAllImages.add(absolutePathOfImage);
}
return listOfAllImages;
}
I've found a way to get albums without iterating over every photo.
String[] projection = new String[]{
"COUNT(*) as count",
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATA,
"MAX (" + MediaStore.Images.ImageColumns.DATE_TAKEN + ") as max"};
Context context = ServiceProvider.getInstance().getApplicationContext();
Cursor cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
"1) GROUP BY (" + MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
null,
"max DESC");
cursor will contain as much elements, as distinct bucket name exists, and also you can get count inside every cursor position to get count of images inside album
here example:
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
//gets image path, it will always be a latest image because of sortOrdering by MAX date_taken
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
//gets count via alias ("as count" in projection)
int count = cursor.getInt(cursor.getColumnIndex("count"));
//do you logic here
...
} while (cursor.moveToNext());
}
cursor.close();
}
Some explanation about selection param:
contentResolver adds parentheses when compiling resulting query for sqlLite, so if we make selection like
"GROUP BY " + MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME
it will be compiled as "WHERE (GROUP BY bucket_display_name)" and will cause SQLiteException at runtime. Otherwise if we make selection like
"1) GROUP BY (" + MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME
it will be compiled as "WHERE (1) GROUP BY (bucket_display_name)", which is correct
Here is the full solution in few simple steps:
The next few steps will guid you how to create a Vector
that will hold the albums found on a given device. Every Album will hold a preview image as well an inner Vector
that will hold all the images of the Album.
Create an object that will hold the images once extracted from storage. We are going to call it
PhoneAlbum
. This is how it's going to look:public class PhoneAlbum { private int id; private String name; private String coverUri; private Vector<PhonePhoto> albumPhotos; public int getId() { return id; } public void setId( int id ) { this.id = id; } public String getName() { return name; } public void setName( String name ) { this.name = name; } public String getCoverUri() { return coverUri; } public void setCoverUri( String albumCoverUri ) { this.coverUri = albumCoverUri; } public Vector< PhonePhoto > getAlbumPhotos() { if ( albumPhotos == null ) { albumPhotos = new Vector<>(); } return albumPhotos; } public void setAlbumPhotos( Vector< PhonePhoto > albumPhotos ) { this.albumPhotos = albumPhotos; } }
Create an object that will hold the images within the album called:
PhonePhoto
public class PhonePhoto { private int id; private String albumName; private String photoUri; public int getId() { return id; } public void setId( int id ) { this.id = id; } public String getAlbumName() { return albumName; } public void setAlbumName( String name ) { this.albumName = name; } public String getPhotoUri() { return photoUri; } public void setPhotoUri( String photoUri ) { this.photoUri = photoUri; } }
Create an interface to handle the extracted images upon completion. We are going to call it
OnPhoneImagesObtained
. Here it is:public interface OnPhoneImagesObtained { void onComplete( Vector<PhoneAlbum> albums ); void onError(); }
Create a new class:
DeviceImageManager
public class DeviceImageManager { }
Once you created
DeviceImageManager
, add the following method:public static void getPhoneAlbums( Context context , OnPhoneImagesObtained listener ){ // Creating vectors to hold the final albums objects and albums names Vector< PhoneAlbum > phoneAlbums = new Vector<>(); Vector< String > albumsNames = new Vector<>(); // which image properties are we querying String[] projection = new String[] { MediaStore.Images.Media.BUCKET_DISPLAY_NAME, MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID }; // content: style URI for the "primary" external storage volume Uri images = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; // Make the query. Cursor cur = context.getContentResolver().query(images, projection, // Which columns to return null, // Which rows to return (all rows) null, // Selection arguments (none) null // Ordering ); if ( cur != null && cur.getCount() > 0 ) { Log.i("DeviceImageManager"," query count=" + cur.getCount()); if (cur.moveToFirst()) { String bucketName; String data; String imageId; int bucketNameColumn = cur.getColumnIndex( MediaStore.Images.Media.BUCKET_DISPLAY_NAME); int imageUriColumn = cur.getColumnIndex( MediaStore.Images.Media.DATA); int imageIdColumn = cur.getColumnIndex( MediaStore.Images.Media._ID ); do { // Get the field values bucketName = cur.getString( bucketNameColumn ); data = cur.getString( imageUriColumn ); imageId = cur.getString( imageIdColumn ); // Adding a new PhonePhoto object to phonePhotos vector PhonePhoto phonePhoto = new PhonePhoto(); phonePhoto.setAlbumName( bucketName ); phonePhoto.setPhotoUri( data ); phonePhoto.setId( Integer.valueOf( imageId ) ); if ( albumsNames.contains( bucketName ) ) { for ( PhoneAlbum album : phoneAlbums ) { if ( album.getName().equals( bucketName ) ) { album.getAlbumPhotos().add( phonePhoto ); Log.i( "DeviceImageManager", "A photo was added to album => " + bucketName ); break; } } } else { PhoneAlbum album = new PhoneAlbum(); Log.i( "DeviceImageManager", "A new album was created => " + bucketName ); album.setId( phonePhoto.getId() ); album.setName( bucketName ); album.setCoverUri( phonePhoto.getPhotoUri() ); album.getAlbumPhotos().add( phonePhoto ); Log.i( "DeviceImageManager", "A photo was added to album => " + bucketName ); phoneAlbums.add( album ); albumsNames.add( bucketName ); } } while (cur.moveToNext()); } cur.close(); listener.onComplete( phoneAlbums ); } else { listener.onError(); } }
Now all you have left is to render the images to screen. In my case I like to use
Picasso
. Here is how I do it:Picasso.with( getActivity() ) .load( "file:" + mPhoneAlbums.get( i ).getCoverUri() ) .centerCrop() .fit() .placeholder( R.drawable.temp_image ) .into( mAlbumPreview );
Don't forget to add a permission to read external storage in your manifest:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
That's it. You are good to go! Good luck!