I am currently using the following code to load all images from the Android contentProvider in my repository:
override suspend fun getLocalImagePaths() = SuspendableResult.of<List<String>, Exception> {
val result = mutableListOf<String>()
val uri: Uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.MediaColumns.DATA)
contentResolver.query(uri, projection, null, null, null)?.use {
val dataIndex = it.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)
while (it.moveToNext()) {
result.add(it.getString(dataIndex))
}
}
result
}
This gets the absolute paths to all available images and it seems to work in Android 9, allthough some images can't be loaded (I am using Glide), but in Android 10 I can't load any of the image paths that are returned from the mentioned method. How could I do this?
override suspend fun getLocalImagePaths() = SuspendableResult.of<List<Uri>, Exception> {
val result = mutableListOf<Uri>()
val uri: Uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Images.Media._ID)
contentResolver.query(uri, projection, null, null, null)?.use {
while (it.moveToNext()) {
result.add(
ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
it.getLong(0)
)
)
}
}
result
}
Related
I use this function to show images from specific album in gallery:
fun findImagesInAlbum(albumId: String): List<Image> {
val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projections = arrayOf(
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.DISPLAY_NAME
)
val selection = "${MediaStore.Images.ImageColumns.BUCKET_ID} == ?"
val selectionArgs = arrayOf(albumId)
val findImages = HashMap<String, Image>()
contentResolver.query(contentUri, projections, selection, selectionArgs,
"${MediaStore.Images.ImageColumns.DATE_TAKEN} ASC")?.use { cursor ->
if (cursor.moveToFirst()) {
val idIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns._ID)
val displayNameIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DISPLAY_NAME)
do {
val mediaId = cursor.getLong(idIndex)
val filename = cursor.getString(displayNameIndex)
val uri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
mediaId
)
val image = Image(
id = mediaId.toString(),
name = filename,
uri = uri,
count = 0,
)
findImages[mediaId.toString()] = image
image.count++
} while (cursor.moveToNext())
}
}
return findImages.values.toList().sortedByDescending { it.name }
}
I show the List<Image> at the RecyclerView and it works fine. After clicking on image it opens on fullscreen. What functions can i use to rename an opened image and save its name in the phone storage?
I tried to solve this problem with DocumentsContract.renameDocument, but it throws error: java.lang.UnsupportedOperationException: Unsupported call: android:renameDocument.
I want to get GPS coordinates of all the images in the user gallery. The solution below works, but it takes 15 seconds for 1500 photos in the gallery. Is there a more efficient way? The result shall be an array of all user photos and their locations.
Loading all images (600ms for 1500 photos):
resolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
selectionArgs,
sortOrder
)?.use { cursor ->
Getting Exif data for each (15s for 1500 photos):
fun readExif(resolver: ContentResolver, id: Long): String? {
val contentUri: Uri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id,
)
var photoUri = MediaStore.setRequireOriginal(contentUri)
var a: String? = ""
var b: String? = ""
var c: String? = ""
resolver.openInputStream(photoUri)?.use { stream ->
val exifInterface = ExifInterface(stream);
a = exifInterface.getAttribute(ExifInterface.TAG_DATETIME)
b = exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE)
c = exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE)
}
return a;
}
I added the request premissions for reading files tried this method inside my activity but nothing shows,like there is no images in real devices and emulator,what am i doing wrong?
fun storaheread() {
val imageProjection = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.DATE_TAKEN,
MediaStore.Images.Media._ID
)
val imageSortOrder = "${MediaStore.Images.Media.DATE_TAKEN} DESC"
val cursor = contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
imageProjection,
null,
null,
imageSortOrder
)
cursor.use {
it?.let {
val idColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
val nameColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
val sizeColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE)
val dateColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_TAKEN)
while (it.moveToNext()) {
val id = it.getLong(idColumn)
val name = it.getString(nameColumn)
val size = it.getString(sizeColumn)
val date = it.getString(dateColumn)
val contentUri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
)
// add the URI to the list
// generate the thumbnail
// val thumbnail = (this as Context).contentResolver.loadThumbnail(contentUri, Size(480, 480), null)
Log.d("image name",name)
}
} ?: kotlin.run {
Log.e("TAG", "Cursor is null!")
}
}
}
you can check out this similar question, it may help you achieve this situation.
Loading all the images from gallery into the Application in android
I keep getting an empty cursor with this method used for loading external images, no idea why. It also worked for me on one emulator configuration before, but now when I try to Log the contents of ID it says:
android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 0. There's pictures installed on the emulator in 3 different external directories.
private suspend fun loadImages(): List<Image>
{
return withContext(Dispatchers.IO) {
val uri = if(Build.VERSION.SDK_INT >= 29) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else MediaStore.Images.Media.EXTERNAL_CONTENT_URI
requireActivity().contentResolver.query(uri, arrayOf(MediaStore.Images.Media._ID),
null, null, "${MediaStore.Images.Media.DATE_ADDED} ASC"
)?.use { cursor ->
val photos = mutableListOf<Image>()
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
while(cursor.moveToNext()) {
val id = cursor.getLong(idColumn)
val contentUri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
photos.add(Image(id, contentUri))
}
photos
} ?: listOf()
}
}
pic of working image gallery
As usual with my issues, it was something entirely different. The emulator bugged out, I reset it and uploaded images again and it works now. The code is a bit changed without the dispatcher now.
private fun loadImages(): List<Image>
{
val photos = mutableListOf<Image>()
val uri = if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else MediaStore.Images.Media.EXTERNAL_CONTENT_URI
requireActivity().contentResolver.query(uri, arrayOf(MediaStore.Images.Media._ID),
null, null, null
)?.use { cursor ->
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
while(cursor.moveToNext()) {
photos.add(Image(ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getLong(idColumn))))
}
return photos.toList()
} ?: return listOf()
}
this is the code now, it works fine
I develop image gallery app for Android (min sdk 23, target sdk 29). And I trying to get list of photos from the phone. Here the code:
val list: MutableList<Photo> = mutableListOf()
val projection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DATE_TAKEN,
MediaStore.Images.Media.ORIENTATION,
MediaStore.Images.Media.SIZE
)
val query = contentResolver?.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
MediaStore.Images.Media.DATE_TAKEN + " DESC"
)
query?.use { cursor ->
if (cursor.count != 0) {
val idIndex = cursor.getColumnIndex(MediaStore.Images.Media._ID)
val sizeIndex = cursor.getColumnIndex(MediaStore.Images.Media.SIZE)
while (cursor.moveToNext()) {
val id = cursor.getLong(idIndex)
val size = cursor.getInt(sizeIndex)
val contentUri: Uri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
)
val photo = Photo(contentUri, size)
list.add(photo)
}
}
}
But Android Studio says to me that some MediaStore fields requires API level 29+. (e.g. MediaStore.Images.Media.DATE_TAKEN, MediaStore.Images.Media.ORIENTATION, MediaStore.Images.Media.DATA, etc.).
Screen from AS.
So my question is how do I should get images list on APIs below 29?
Also studio says same about contentResolver.loadThumbnail() method.
And adjacent question: is it possible in Android to retrieve list of photo albums? Or I should get list of all images and then run over it and pick out albums?
Try this, it's work even with API 29 :
private fun getAllImages(activity: Activity): ArrayList<Photo>? {
var cursor: Cursor? = null
var i = 0
val arrayList: ArrayList<Photo> = ArrayList()
if (cursor == null) {
val resolver = activity.contentResolver
cursor = resolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null,
null,
null,
null
)
if (cursor != null) {
while (i < cursor.count) {
cursor.moveToPosition(i)
val fieldIndex: Int = cursor.getColumnIndex(MediaStore.Images.Media._ID)
val id: Long = cursor.getLong(fieldIndex)
val imageUri =
ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
arrayList.add(Photo("", imageUri))
i++
}
cursor.close()
}
}
return arrayList
}
data class Photo(var name: String, var uri: Uri)
Use any image loader library such as Glide or Picasso :
Glide.with(this).load(getAllImages(this)!![0].uri).into(imgView)
Add this permission to your manifest, and make sure it granted :
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>