I am trying to write a music player app by using Jetpack Compose. I have a MusicCardModel like below
data class MusicCardModel(
val contentUri: Uri?,
val songId: Long?,
val cover: Bitmap?,
val songTitle: String?,
val artist: String?,
val duration: String?
)
When I start the app I am scanning all the music files by using MediaStore below function
@SuppressLint("Recycle")
fun Context.musicList(): MutableList<MusicCardModel> {
val list = mutableListOf<MusicCardModel>()
val collection =
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.Media._ID,
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ALBUM_ID,
MediaStore.Audio.Media.ARTIST
)
val selection = MediaStore.Audio.Media.IS_MUSIC + "!= 0"
val sortOrder = "${MediaStore.Audio.Media.DISPLAY_NAME} ASC"
val query = this.contentResolver.query(
collection,
projection,
selection,
null,
sortOrder
)
query?.use { cursor ->
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)
val durationColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)
val titleColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)
val artistColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)
val albumIdColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID)
while (cursor.moveToNext()) {
val id = cursor.getLong(idColumn)
val duration = cursor.getInt(durationColumn)
val title = cursor.getString(titleColumn)
val artist = cursor.getString(artistColumn)
val albumId = cursor.getLong(albumIdColumn)
val contentUri: Uri = ContentUris.withAppendedId(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
id
)
val bitmap = getAlbumArt(this, contentUri)
val durationString = convertMili(duration)
list.add(MusicCardModel(contentUri, id, bitmap, title, artist, durationString))
}
}
return list
}
fun getAlbumArt(context: Context, uri: Uri): Bitmap{
val mmr = MediaMetadataRetriever()
mmr.setDataSource(context, uri)
val data = mmr.embeddedPicture
return if(data != null){
BitmapFactory.decodeByteArray(data, 0, data.size)
}else{
BitmapFactory.decodeResource(context.resources, R.drawable.note)
}
}
These part of code showing that list
Box(modifier = Modifier
.padding(bottom = if (isPlaying.value) 80.dp else 0.dp)){
LazyColumn {
items(list) { index ->
MusicCard(
uri = index.contentUri!!,
songId = index.songId,
artist = index.artist!!,
name = index.songTitle!!,
duration = index.duration!!,
isPlaying = isPlaying,
playingSong = playingSong
)
}
}
}
@Composable
fun MusicCard(
uri: Uri,
artist: String,
name: String,
cover: Bitmap?,
duration: String,
isPlaying: MutableState<Boolean>,
playingSong: MutableState<MusicCardModel>,
songId: Long?,
playState: MutableState<Boolean>
) {
val context = LocalContext.current
Card(modifier = Modifier
.fillMaxWidth()
.clickable {
playMusic(context, uri)
playState.value = true
isPlaying.value = true
val playingSongModel = MusicCardModel(uri, songId,
null, name, artist, duration)
playingSong.value = playingSongModel
}
) {
Row(
modifier = Modifier.padding(10.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(
modifier = Modifier.weight(1f),
verticalAlignment = Alignment.CenterVertically
) {
Image(
modifier = Modifier.size(70.dp),
bitmap = cover!!.asImageBitmap(),
contentDescription = "Cover Photo",
)
Column(
modifier = Modifier
.padding(horizontal = 10.dp)
) {
Text(
modifier = Modifier.padding(vertical = 5.dp),
text = artist
)
Text (name, maxLines = 1)
}
}
Text(text = duration)
}
}
}
There are 2 problems. Number one is creating this list with album arts taking too much time and it is waiting so long to get that list on the screen. Without album art its very fast but I want to show album arts. How can I lazily load the metadatas of music files? Second problem is loading all this list data to the memory gives a TransactionTooLargeException when I switching to another app and the app stops. How can I solve these problems?