0

I am implementing a grid gallery in my Android app in which photos are sorted according to the photo's Exif metadata. All photos are read from a specific folder stored in Shared Storage at /DCIM or /Pictures. Since the photos will be sorted by their Exif metadata, the Exif metadata must be known before displaying the photos in a RecyclerView.

Currently, I read the files using a content provider, as recommended in this link. My code works well and is fast, but I also need to read the Exif metadata for each photo. My current implementation uses an InputStream to extract metadata via ExifInterface, which slows down the code to about 20ms per photo, compared to under 1ms per photo when excluding the metadata. Due to this considerable slowdown, displaying hundreds of photos is not acceptable in terms of the user experience.

Here is my current implementation:

private fun getMediaWithExif(folderName: String): List<Photo> {
   val photoList = mutableListOf<Photo>()
   val imagesUri: Uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
   val projection = arrayOf(
       MediaStore.Images.Media._ID,
       MediaStore.Images.Media.DATE_MODIFIED,
       MediaStore.Images.Media.DISPLAY_NAME,
   )

   val bucketNameSelection = MediaStore.Images.Media.BUCKET_DISPLAY_NAME + " = ? "
   val selection = bucketNameSelection
   val selectionArgs: Array<String> = arrayOf(folderName)
   val sortOrder = MediaStore.Images.ImageColumns.DATE_ADDED + " DESC"

   appContext.contentResolver.query(
       imagesUri,
       projection,
       selection,
       selectionArgs,
       sortOrder
   )?.use { cursor ->
       val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
       val dateColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_MODIFIED)
       val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)

       while (cursor.moveToNext()) {
           val id = cursor.getLong(idColumn)
           val date = cursor.getLong(dateColumn)
           val name = cursor.getString(nameColumn)

           val contentUri: Uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)

           appContext.contentResolver.openInputStream(contentUri)?.use { stream ->
               ExifInterface(stream).run {
                   photoList.add(
                       Photo(
                           uri = contentUri,
                           name = name,
                           date = Date(date * 1000),
                           latitude = this.latLong?.get(0),
                           longitude = this.latLong?.get(1),
                           accuracy = this.getAttribute(ExifInterface.TAG_GPS_H_POSITIONING_ERROR)
                               ?.toFloatOrNull(),
                           altitude = this.getAltitude(0.0),
                       )
                   )
               }
           }
       }
   }

   return photoList
}

I am seeking suggestions on how to efficiently read photos, including Exif metadata. Is there a better approach to extracting Exif metadata, or should I periodically check Shared Storage for modifications and cache any photo data into a ROOM database for readily available photo data?

Note that I have tried initialising the ExifInterface with an absolute path String, File, and a FileDescriptor, but it did not speed up the code. Thank you for your time and help, it is greatly appreciated!

Mike
  • 125
  • 1
  • 12
  • What dies this have to do with scoped storage? You confuse me. – blackapps Jun 16 '23 at 12:26
  • Since the introduction of Scoped Storage, files must be accessed and read with a contentResolver, and not simply with the File API, e.g. File (pathName). I have noticed that when files are accessed with a contentResolver, reading the Exif metadata of a photo significantly slows down. This is not the case when accessing files and reading them with the File API on older Android versions. Thus, my question is specifically about reading Exif data when using the Scoped Storage model. Hope this makes sense. – Mike Jun 18 '23 at 10:09
  • No does not make sense as using a content resolver is not needed for any file. Nothing has to do with scoped storage. Please dont confuse people. – blackapps Jun 18 '23 at 18:06
  • With ''using a content resolver' you probably mean 'read a file via a content provider instead directly via file api'. – blackapps Jun 18 '23 at 18:08
  • I updated the text. Do you maybe have a suggestion on how to fix the issue? – Mike Jun 19 '23 at 08:15
  • You have still not told what you consider to be 'Load photos'. – blackapps Jun 19 '23 at 10:34
  • If you load images in a grid then at which moment would you use latlon,? And for displaying images in a grid you do not need lat,lon values. – blackapps Jun 19 '23 at 10:36
  • Hi @blackapps, thanks for your comment. I have further updated the text for clarification. Basically, the grid gallery should be sorted according to the photos' exif metadata, e.g. by location, note,... Thus, the Exif metadata of all photos must be known before displaying the photos in the RecyclerView. – Mike Jun 19 '23 at 14:35

0 Answers0