12

I want to retrieve only the name of the bucket (Albums). E.g. Camera, Download etc but not a list of Camera, Download etc from all the the photos so how do I retrieve one row each for each bucket name?

What I mean like in Gallery Application, you have albums first e.g. Camera. When you clicked on it, it show all the photos of the camera.

I can query the photos in a Camera Roll with the Where clause of the query. But what if I wanted only the name of each of the albums' name and not the photos, is it possible to query that? If I query all the photos and take only one row per set of photos, then it will be time consuming.

Please Help

LittleFunny
  • 8,155
  • 15
  • 87
  • 198

5 Answers5

25

I have the same problem and here is my solution (after tracing gallery source code) to get album name and the first image in it (can use as thumbnail for this album):

(Note that bucket repeat is removed by groupby & order technique)

    // which image properties are we querying
    String[] PROJECTION_BUCKET = {
            ImageColumns.BUCKET_ID,
            ImageColumns.BUCKET_DISPLAY_NAME,
            ImageColumns.DATE_TAKEN,
            ImageColumns.DATA};
    // We want to order the albums by reverse chronological order. We abuse the
    // "WHERE" parameter to insert a "GROUP BY" clause into the SQL statement.
    // The template for "WHERE" parameter is like:
    //    SELECT ... FROM ... WHERE (%s)
    // and we make it look like:
    //    SELECT ... FROM ... WHERE (1) GROUP BY 1,(2)
    // The "(1)" means true. The "1,(2)" means the first two columns specified
    // after SELECT. Note that because there is a ")" in the template, we use
    // "(2" to match it.
    String BUCKET_GROUP_BY =
            "1) GROUP BY 1,(2";
    String BUCKET_ORDER_BY = "MAX(datetaken) DESC";

    // Get the base URI for the People table in the Contacts content provider.
    Uri images = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

    Cursor cur = getContentResolver().query(
            images, PROJECTION_BUCKET, BUCKET_GROUP_BY, null, BUCKET_ORDER_BY);

    Log.i("ListingImages"," query count=" + cur.getCount());

    if (cur.moveToFirst()) {
        String bucket;
        String date;
        String data;
        int bucketColumn = cur.getColumnIndex(
                MediaStore.Images.Media.BUCKET_DISPLAY_NAME);

        int dateColumn = cur.getColumnIndex(
                MediaStore.Images.Media.DATE_TAKEN);
        int dataColumn = cur.getColumnIndex(
                MediaStore.Images.Media.DATA);

        do {
            // Get the field values
            bucket = cur.getString(bucketColumn);
            date = cur.getString(dateColumn);
            data = cur.getString(dataColumn);

            // Do something with the values.
            Log.i("ListingImages", " bucket=" + bucket 
                    + "  date_taken=" + date
                    + "  _data=" + data);
        } while (cur.moveToNext());
    }
Thuong
  • 602
  • 6
  • 13
  • 2
    How to get no of photos in bucket? – Krunal Shah May 27 '15 at 11:07
  • Dont know but I am receiving duplicate enteries on 5.0 any ideas? – ingsaurabh Nov 15 '15 at 09:18
  • @Krunal Shah I would try to run another query for each bucket that filters on the BUCKET_ID in the where parameter. – stwienert May 16 '16 at 01:28
  • in case of querying all the `video` albums change `uri` to : `uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI` – Nilesh Deokar Mar 04 '19 at 14:43
  • `The "1,(2)" means the group by first two columns`. Which are `ImageColumns.BUCKET_ID, ImageColumns.BUCKET_DISPLAY_NAME` specified in `PROJECTION_BUCKET` – Nilesh Deokar Mar 06 '19 at 15:04
  • better way of writing `GROUP BY` clause would be : `val bucketGroupBy = "1) GROUP BY ${ImageColumns.BUCKET_ID}, (${ImageColumns.BUCKET_DISPLAY_NAME}" `. [Why do we use Group by 1 and Group by 1,2,3 in SQL query?](https://dba.stackexchange.com/a/86612) – Nilesh Deokar Mar 06 '19 at 15:12
  • 2
    this code doesn't work with version above android pie (android 10) its shows syntax error in "1) GROUP BY 1,(2" keyword. – Bhavik Nathani Nov 07 '19 at 06:46
  • 1
    As @BhavikNathani wrote, it doesn't work in Android 10. Anyone has an idea how to replace this? – grebulon Dec 26 '19 at 13:17
7

Here it is. it works for me:

Uri images = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    String[] projection = new String[]{   
            MediaStore.Images.Media.BUCKET_ID,
            MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
            MediaStore.Images.Media.DATE_TAKEN,
            MediaStore.Images.Media.DATA
    };

    String BUCKET_ORDER_BY = MediaStore.Images.Media.DATE_MODIFIED + " DESC";
    String BUCKET_GROUP_BY = "1) GROUP BY 1,(2";
    Cursor imagecursor = managedQuery(images,
            projection, // Which columns to return
            BUCKET_GROUP_BY,       // Which rows to return (all rows)
            null,       // Selection arguments (none)
            BUCKET_ORDER_BY        // Ordering
            );

    this.imageUrls = new ArrayList<String>();
    this.imageBuckets  = new ArrayList<String>();
    for (int i = 0; i < imagecursor.getCount(); i++)
    {
        imagecursor.moveToPosition(i);
        int bucketColumnIndex = imagecursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
        String bucketDisplayName = imagecursor.getString(bucketColumnIndex);
        imageBuckets.add(bucketDisplayName);
        int dataColumnIndex = imagecursor.getColumnIndex(MediaStore.Images.Media.DATA);
        imageUrls.add(imagecursor.getString(dataColumnIndex));

    }

imageBuckets Arraylist is containing album names

imageUrls Arraylist is containing path of last modifided image of album you can use it as thumbnail

farhad.kargaran
  • 2,233
  • 1
  • 24
  • 30
  • How to get no of photos in bucket? – Krunal Shah May 27 '15 at 11:06
  • better way of writing `GROUP BY` clause would be : `val bucketGroupBy = "1) GROUP BY ${ImageColumns.BUCKET_ID}, (${ImageColumns.BUCKET_DISPLAY_NAME}" `. [Why do we use Group by 1 and Group by 1,2,3 in SQL query?](https://dba.stackexchange.com/a/86612) – Nilesh Deokar Mar 06 '19 at 15:09
2

Use following function to get albums of Video or Images :

   /*
    *
    *   Author : @nieldeokar
    *   mediaType could be one of
    *
    *   public static final int MEDIA_TYPE_IMAGE = 1;
    *
    *   public static final int MEDIA_TYPE_VIDEO = 3;
    *
    *   from android.provider.MediaStore class
    *
    */
fun getAlbumList(mediaType: Int, contentResolver: ContentResolver) {
    val countColumnName = "count"
    var contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    if (mediaType == MEDIA_TYPE_VIDEO) {
        contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
    }

    val projection = arrayOf(ImageColumns.BUCKET_ID, ImageColumns.BUCKET_DISPLAY_NAME, ImageColumns.DATE_TAKEN, ImageColumns.DATA)
    val bucketGroupBy = "1) GROUP BY ${ImageColumns.BUCKET_ID}, (${ImageColumns.BUCKET_DISPLAY_NAME}"
    val bucketOrderBy = MediaStore.Images.Media.DATE_MODIFIED + " DESC"

    val cursor = contentResolver.query(contentUri, projection, bucketGroupBy, null, bucketOrderBy)


    if (cursor != null) {
        while (cursor.moveToNext()) {
            val bucketId = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.BUCKET_ID))
            val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME))
            val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)) // Thumb image path

            val selection = MediaStore.Images.Media.BUCKET_ID + "='" + bucketId + "'"

            val countCursor = contentResolver.query(contentUri, arrayOf( "count(" + MediaStore.Images.ImageColumns._ID + ")"), selection, null, null)

            var count = 0
            if (countCursor != null) {
                countCursor.moveToFirst()
                count = countCursor.getInt(0)
                countCursor.close()
            }

            Log.d("AlbumScanner", "bucketId : $bucketId | name : $name | count : $count | path : $path")
        }
        cursor.close()

    }

}

This forms a SQL Statement as :

SELECT bucket_id, bucket_display_name, datetaken, _data FROM images WHERE (1) GROUP BY bucket_id,(bucket_display_name) ORDER BY date_modified DESC

UPDATE : As @PerracoLabs pointed out, This code needs to be improved for targeting Android Q.

Nilesh Deokar
  • 2,975
  • 30
  • 53
  • 3
    Take into account that the old grouping trick "1) GROUP BY " no longer works in Android Q – PerracoLabs Mar 19 '19 at 12:32
  • I'm still testing it on Q. Grouping query works but `count(*) AS count` doesn't work. @PerracoLabs your input would be great help. – Nilesh Deokar Mar 20 '19 at 06:49
  • 1
    Did you set your Gradle to target Q? The test was done in the Emulator. When doing a query against the MediaStore using a ContentResolver I found that the the framework includes automatically at the beginning of the generated query in the Where condition the next string: (IS_TRASHED=0) AND (IS_PENDING=0). These are added regardless what you do, and the problem is that they make the bracket of the "1) GROUP " to be unbalanced, and therefore creates and invalid syntax query. – PerracoLabs Mar 20 '19 at 08:53
  • In addition checking your code I can see you are using the DATA field. Unfortunately this has also been deprecated: https://developer.android.com/reference/android/provider/MediaStore.MediaColumns.html#DATA – PerracoLabs Mar 20 '19 at 15:51
  • 1
    Thank you for pointing out corrections. As of now I have updated ans with Warning for **Android Q**. I would test against target Q and then will improve code asap. – Nilesh Deokar Mar 21 '19 at 06:46
  • Any solutions for `1) GROUP BY` in android Q? – Mateen Chaudhry Jun 30 '19 at 07:03
  • Please be advised, need to test queries with target sdk 29 + android Q – Efi G Mar 23 '20 at 07:22
-1

You can query MediaStore.Images.Media.BUCKET_DISPLAY_NAME for it. Peter Knego sent a good example. It's a good example and you can update it using query() from a ContentResolver object rather than managedQuery() that is deprecated.

// 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
};

// Get the base URI for the People table in the Contacts content provider.
Uri images = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

// Make the query.
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(images,
        projection, // Which columns to return
        "",         // Which rows to return (all rows)
        null,       // Selection arguments (none)
        ""          // 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());

}
Community
  • 1
  • 1
Pedro Alves
  • 1,667
  • 4
  • 17
  • 37
  • Good but i only want to query unique bucket display name instead of a list of repeated bucket display name. E.g. I want to have something like Camera and Competition album. But for ur sample code it will display something like Camera, Camera, Camera, Competition, Competition etc. – LittleFunny Aug 15 '12 at 05:38
  • 1
    Yes! I solved this by adding each element to a HashMap as key. It's a bit annoying but it works. – Pedro Alves Aug 17 '12 at 20:55
  • 1
    Still doesn't solve the issue correctly - you can have two Unique folders that have the same display name and have different photos. The BUCKET_IT is what needs to be the key for a hash - not display name. – jt-gilkeson May 23 '17 at 23:45
-1

Do projection in query as below:

String bucketProjection[] = {"Distinct "+ MediaStore.Images.Media.BUCKET_DISPLAY_NAME};
Tom
  • 4,257
  • 6
  • 33
  • 49
kiran
  • 1
  • 1
  • 1
    The Display Name is not a unique identifier (you can have two different folders with the same name) - the BUCKET_ID is unique. If you want a list of unique "folders", you treat two the the same name as one - since there are different images in the two identically named buckets. – jt-gilkeson May 23 '17 at 23:42