Android Find/Search Photos/Images in Album

April 3, 2020

If you need to list all albums, refer to Android Find Albums in Photo Gallery.

The following code show how to find photos in specific album, which you could modify based on your search criteria.

fun findImagesInAlbum(albumId: String) {
    val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI

    val projections = arrayOf(
        MediaStore.Images.ImageColumns._ID,
        // MediaStore.Images.ImageColumns.DATA,
        MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
        MediaStore.Images.ImageColumns.DATE_TAKEN,
        MediaStore.Images.ImageColumns.DISPLAY_NAME,
        MediaStore.Images.ImageColumns.ORIENTATION,
        MediaStore.Images.ImageColumns.WIDTH,
        MediaStore.Images.ImageColumns.HEIGHT,
        MediaStore.Images.ImageColumns.SIZE,
        @Suppress("DEPRECATION")
        MediaStore.Images.ImageColumns.LATITUDE,
        @Suppress("DEPRECATION")
        MediaStore.Images.ImageColumns.LONGITUDE
    )
    val selection = "${MediaStore.Images.ImageColumns.BUCKET_ID} == ?"
    val selectionArgs = arrayOf(
        albumId
    )

    contentResolver.query(contentUri, projections, selection, selectionArgs, "${MediaStore.Images.ImageColumns.DATE_TAKEN} ASC")?.use { cursor ->

        val totalCount = cursor.count
        if (cursor.moveToFirst()) {
            val idIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns._ID)
            val dateTakenIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DATE_TAKEN)
            val displayNameIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DISPLAY_NAME)

            val orientationIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.ORIENTATION)
            val widthIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.WIDTH)
            val heightIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.HEIGHT)
            val sizeIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.SIZE)
            @Suppress("DEPRECATION")
            val latIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.LATITUDE)
            @Suppress("DEPRECATION")
            val lonIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.LONGITUDE)

            do {
                val mediaId = cursor.getLong(idIndex)
                val filename = cursor.getString(displayNameIndex)
                val millis = cursor.getLong(dateTakenIndex)
                val orientation = cursor.getInt(orientationIndex)
                val width = cursor.getInt(widthIndex)
                val height = cursor.getInt(heightIndex)
                val size = cursor.getLong(sizeIndex)
                var lat = cursor.getDoubleOrNull(latIndex)
                var lon = cursor.getDoubleOrNull(lonIndex)

                // convert epoch millis to device timezone date
                val date = LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.systemDefault())
                // convert epoch millis to UTC date
                val dateUtc = LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneOffset.UTC)

                // scoped storage - access file via uri instead of filepath + filename
                var uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mediaId)

                // read file
                var exif: ExifInterface? = null

                // if you need to access lat/lng via scoped storage
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    // https://developer.android.com/reference/kotlin/android/provider/MediaStore#setrequireoriginal
                    uri = MediaStore.setRequireOriginal(uri)

                    if (lat == null || lon == null) {
                        // UnsupportedOperationException: Caller must hold ACCESS_MEDIA_LOCATION permission to access original
                        contentResolver.openInputStream(uri).use { stream ->
                            stream?.also {
                                exif = ExifInterface(stream)
                            }
                        }
                    }
                }
                else {
                    contentResolver.openInputStream(uri).use { stream ->
                        exif = ExifInterface(stream)
                    })
                }

                // get date via exif
                val exifDateFormatter = DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss")
                var exifDateString = exif.getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL)
                if (exifDateString == null) {
                    exifDateString = exif.getAttribute(ExifInterface.TAG_DATETIME)
                }
                val exifDate = if (exifDateString != null) {
                    LocalDateTime.parse(exifDateString, exifDateFormatter)
                }
            }
        }
    }
}

NOTE: Refer Get Photo Gps Location With Android 10 Scoped Storage

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.