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"/>
Related
I am trying to implement a recyclerview which will show all the music files in my android device with a 10 per page pagination and contains a search text. I am using content resolver for this. My application is targeting till android 12. So my existing code is like:
fun getAllMusicFilesInDevice(offset: Int, searchText: String){
val tempAudioList: MutableList<Album> = ArrayList()
val uri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
else
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(
MediaStore.Audio.AudioColumns.DATA,
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.AudioColumns.ALBUM,
MediaStore.Audio.ArtistColumns.ARTIST,
)
val selection = MediaStore.Audio.Media.DISPLAY_NAME + " LIKE ?"
val selectionArgs = arrayOf(searchText)
val sortOrder =
"${MediaStore.Audio.Media.DISPLAY_NAME} ASC LIMIT $PAGE_SIZE OFFSET $offset"
val c = mContext.contentResolver.query(
uri,
projection,
selection,
selectionArgs,
sortOrder
)
if (c != null) {
while (c.moveToNext()) {
try {
val path: String = c.getString(0)
val songName = c.getString(1)
val album: String = c.getString(2)
val artist: String = c.getString(3)
val extension = File(path).extension
if (isMusicFile(extension)) {
isMusicPresentInPlaylist(path, playlistDb) {
val audioModel =
Album(
name = songName,
path = path,
artist = artist,
album = album
)
tempAudioList.add(audioModel)
}
}
} catch (ex: Exception) {
}
}
c.close()
}}
This code does not give back any result and makes the app behave like its stuck. Please help me out on the mistakes I am making and anything I am doing wrong out here so that I get this function working.
UPDATE CURSOR CODE
val c = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val limit = Bundle().apply {
// Limit & Offset
putInt(ContentResolver.QUERY_ARG_LIMIT, AUDIO_PAGE_SIZE)
putInt(ContentResolver.QUERY_ARG_OFFSET, offset)
}
mContext.contentResolver.query(uri, projection, limit, null)
} else
mContext.contentResolver.query(
uri,
projection,
null,
null,
"${MediaStore.Audio.Media.DISPLAY_NAME} ASC LIMIT $AUDIO_PAGE_SIZE OFFSET $offset"
)
Note: this code is running in a background thread.
The following code is used to save files from the app to downloads:
Uri collection = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
ContentValues values = new ContentValues();
values.put(MediaStore.Downloads.DISPLAY_NAME, filename);
values.put(MediaStore.Downloads.MIME_TYPE, mimeType);
values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
ContentResolver contentResolver = context.getApplicationContext().getContentResolver();
Uri uri = contentResolver.insert(collection, values);
OutputStream outputStream = context.getApplicationContext().getContentResolver().openOutputStream(uri, "w");
Everything is saved, however, if you delete the file from downloads manually, and then try to download it again from the application, an error appears:
android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: files._data (code 2067 SQLITE_CONSTRAINT_UNIQUE[2067])
Is there a way to fix this or will I have to use unique names for each download?
This error happens because the machanism of OS: if we delete manually a file (media), its database will not be deleted immediately. Until we restart device.
Have a approach for this problem (still not be optimized - hope receiving sharing from people), such as:
Step 1: Get info of file via its name
Step 2: Ask OS to update its database via MediaScannerConnection.scanFile
Step 3: Use current code that has above problem
Codes for steps (collected on internet)
Step 1:
fun findByFileName(fileName: String): MutableList<FileInfo> {
val files = mutableListOf<FileInfo>()
val collection =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else {
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
}
val projection = arrayOf(
MediaStore.Video.Media._ID,
MediaStore.Video.Media.DISPLAY_NAME,
MediaStore.Video.Media.DURATION,
MediaStore.Video.Media.SIZE,
MediaStore.Video.Media.DATA
)
val selection = "${MediaStore.Video.Media.DISPLAY_NAME} LIKE ?"
val selectionArgs = arrayOf(
fileName
)
// Display videos in alphabetical order based on their display name.
val sortOrder = "${MediaStore.Video.Media.DISPLAY_NAME} ASC"
val query = context.contentResolver.query(
collection,
projection,
selection,
selectionArgs,
sortOrder
)
query?.use { cursor ->
// Cache column indices.
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)
val nameColumn =
cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME)
val durationColumn =
cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION)
val sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE)
val dataColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)
while (cursor.moveToNext()) {
// Get values of columns for a given video.
val id = cursor.getLong(idColumn)
val name = cursor.getString(nameColumn)
val duration = cursor.getInt(durationColumn)
val size = cursor.getInt(sizeColumn)
val data = cursor.getStringOrNull(dataColumn)
val contentUri: Uri = ContentUris.withAppendedId(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
id
)
// Stores column values and the contentUri in a local object
// that represents the media file.
files += FileInfo(contentUri, name, duration, size, data)
}
}
return files
}
data class FileInfo(
val uri: Uri,
val name: String,
val duration: Int,
val size: Int,
val data: String? = null
)
Step 2 + Step 3:
val exitedData = findByFileName(fileName = name)
if (exitedData != null) {
MediaScannerConnection.scanFile(context, arrayOf(exitedData.first().data.toString()), null, object: MediaScannerConnection.OnScanCompletedListener {
override fun onScanCompleted(path: String?, uri: Uri?) {
// Step 3
// Use current code have this problem
...
}
}
} else {
// Save file normally
// Use current code have this problem
...
}
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 am using media-store for listing pdf in recycler view.
Media store does not load non-media files on android 10 and above but works for media files
In build.gradle
compileSdkVersion 30
buildToolsVersion "30.0.2"
minSdkVersion 21
targetSdkVersion 30
FetchPdf.kt
class FetchPdf(private val applicationContext: Context) {
private val arrayList = ArrayList<Pdf>()
fun getPdf(): ArrayList<Pdf>{
val collection = MediaStore.Files.getContentUri("external")
val projection = arrayOf(
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DATE_MODIFIED,
MediaStore.Files.FileColumns.DISPLAY_NAME,
MediaStore.Files.FileColumns.SIZE,
MediaStore.Files.FileColumns.DATA
)
val mimeType = "application/pdf"
val whereClause = MediaStore.Files.FileColumns.MIME_TYPE + " IN ('" + mimeType + "')"
val orderBy = MediaStore.Files.FileColumns.DATE_MODIFIED + " DESC"
val cursor: Cursor? = applicationContext.contentResolver.query(
collection,
projection,
whereClause,
null,
orderBy
)
if (cursor != null) {
val idCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)
val nameCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)
val sizeCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.SIZE)
val dataCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA)
if (cursor.moveToFirst()) {
do {
val fileUri: Uri = Uri.withAppendedPath(
MediaStore.Files.getContentUri("external"),
cursor.getString(idCol)
)
val name = cursor.getString(nameCol)
val data = cursor.getString(dataCol)
val size = cursor.getLong(sizeCol)
arrayList.add(Pdf(name, fileUri.toString(), data))
} while (cursor.moveToNext())
}
cursor.close()
}
return arrayList
}
}
In Manifest:
I have only READ_EXTERNAL_STORAGE permission
What I have tried:
I tried changing MediaStore.Files.FileColumns.DATA to MediaStore.Files.FileColumns.RELATIVE_PATH not working
I tried traversing each and every folder recursively still not working
Any help would be appreciated.
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
}