Heavily rewrite/refactor MusicRepository
Rewrite MusicRepository to perform all metadata calculations by themselves, also adding support for Genre loading & Album Art.
This commit is contained in:
parent
a8b368b577
commit
21a110edb0
4 changed files with 126 additions and 74 deletions
|
@ -1,46 +1,43 @@
|
|||
package org.oxycblt.auxio.music
|
||||
|
||||
import android.graphics.Bitmap
|
||||
|
||||
// Basic Abstraction for Song
|
||||
data class Album (
|
||||
var mSongs: List<Song>
|
||||
data class Album(
|
||||
var songs: List<Song>
|
||||
) {
|
||||
private var mTitle: String? = null
|
||||
private var mArtist: String? = null
|
||||
//private var mGenre: String? = null
|
||||
private var mYear: Int = 0
|
||||
|
||||
// Immutable backings as the member variables are mutable
|
||||
val title: String? get() = mTitle
|
||||
val artist: String? get() = mArtist
|
||||
//val genre: String? get() = genre
|
||||
val year: Int get() = mYear
|
||||
|
||||
val songs: List<Song> get() = mSongs
|
||||
var title: String? = null
|
||||
var artist: String? = null
|
||||
var genre: String? = null
|
||||
var cover: Bitmap? = null
|
||||
var year: Int = 0
|
||||
|
||||
init {
|
||||
// Iterate through the child songs and inherit the first valid value
|
||||
// for the Album name & year, otherwise it will revert to its defaults
|
||||
for (song in mSongs) {
|
||||
// for the Album Name, Artist, Genre, Year, and Cover
|
||||
for (song in songs) {
|
||||
if (song.album != null) {
|
||||
mTitle = song.album
|
||||
title = song.album
|
||||
}
|
||||
|
||||
if (song.artist != null) {
|
||||
mArtist = song.artist
|
||||
artist = song.artist
|
||||
}
|
||||
|
||||
/*
|
||||
if (song.genre != null) {
|
||||
mGenre = song.genre
|
||||
genre = song.genre
|
||||
}
|
||||
|
||||
if (song.cover != null) {
|
||||
cover = song.cover
|
||||
}
|
||||
*/
|
||||
|
||||
if (song.year != 0) {
|
||||
mYear = song.year
|
||||
year = song.year
|
||||
}
|
||||
}
|
||||
|
||||
// Also sort the songs by track
|
||||
mSongs = songs.sortedBy { it.track }
|
||||
songs = songs.sortedBy { it.track }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,33 +2,25 @@ package org.oxycblt.auxio.music
|
|||
|
||||
// Abstraction for mAlbums
|
||||
data class Artist(
|
||||
private var mAlbums: List<Album>
|
||||
private var albums: List<Album>
|
||||
) {
|
||||
private var mName: String? = null
|
||||
//private var mGenre: String? = null
|
||||
|
||||
// Immutable backings as the member variables are mutable
|
||||
val name: String? get() = mName
|
||||
//val genre: String? get() = mGenre
|
||||
|
||||
val albums: List<Album> get() = mAlbums
|
||||
var name: String? = null
|
||||
var genre: String? = null
|
||||
|
||||
init {
|
||||
// Like album, iterate through the child albums and pick out the first valid
|
||||
// Like Album, iterate through the child albums and pick out the first valid
|
||||
// tag for Album/Genre
|
||||
for (album in mAlbums) {
|
||||
for (album in albums) {
|
||||
if (album.artist != null) {
|
||||
mName = album.artist
|
||||
name = album.artist
|
||||
}
|
||||
|
||||
/*
|
||||
if (album.genre != null) {
|
||||
mGenre = album.genre
|
||||
genre = album.genre
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Also sort the mAlbums by year
|
||||
mAlbums = mAlbums.sortedBy { it.year }
|
||||
albums = albums.sortedBy { it.year }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,10 @@ package org.oxycblt.auxio.music
|
|||
|
||||
import android.app.Application
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentUris
|
||||
import android.database.Cursor
|
||||
import android.graphics.Bitmap
|
||||
import android.media.MediaMetadataRetriever
|
||||
import android.provider.MediaStore
|
||||
import android.provider.MediaStore.Audio.AudioColumns
|
||||
import android.util.Log
|
||||
|
@ -39,6 +42,7 @@ class MusicRepository {
|
|||
val songList = mutableListOf<Song>()
|
||||
|
||||
try {
|
||||
|
||||
val musicCursor = getCursor(
|
||||
app.contentResolver
|
||||
)
|
||||
|
@ -46,27 +50,75 @@ class MusicRepository {
|
|||
// Index music files from shared storage
|
||||
musicCursor?.use { cursor ->
|
||||
|
||||
val nameIndex = cursor.getColumnIndexOrThrow(AudioColumns.TITLE)
|
||||
val artistIndex = cursor.getColumnIndexOrThrow(AudioColumns.ARTIST)
|
||||
val albumIndex = cursor.getColumnIndexOrThrow(AudioColumns.ALBUM)
|
||||
val yearIndex = cursor.getColumnIndexOrThrow(AudioColumns.YEAR)
|
||||
val trackIndex = cursor.getColumnIndexOrThrow(AudioColumns.TRACK)
|
||||
val durationIndex = cursor.getColumnIndexOrThrow(AudioColumns.DURATION)
|
||||
val idIndex = cursor.getColumnIndexOrThrow(AudioColumns._ID)
|
||||
val displayIndex = cursor.getColumnIndexOrThrow(AudioColumns.DISPLAY_NAME)
|
||||
|
||||
var retriever = MediaMetadataRetriever()
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
val id = cursor.getLong(idIndex)
|
||||
|
||||
// Read the current file from the ID
|
||||
retriever.setDataSource(
|
||||
app.applicationContext,
|
||||
ContentUris.withAppendedId(
|
||||
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||
id
|
||||
)
|
||||
)
|
||||
|
||||
// Get the metadata attributes
|
||||
val title = retriever.extractMetadata(
|
||||
MediaMetadataRetriever.METADATA_KEY_TITLE
|
||||
) ?: cursor.getString(displayIndex)
|
||||
|
||||
val artist = retriever.extractMetadata(
|
||||
MediaMetadataRetriever.METADATA_KEY_ARTIST
|
||||
)
|
||||
|
||||
val album = retriever.extractMetadata(
|
||||
MediaMetadataRetriever.METADATA_KEY_ALBUM
|
||||
)
|
||||
|
||||
val genre = retriever.extractMetadata(
|
||||
MediaMetadataRetriever.METADATA_KEY_GENRE
|
||||
)
|
||||
|
||||
val year = (retriever.extractMetadata(
|
||||
MediaMetadataRetriever.METADATA_KEY_YEAR
|
||||
) ?: "0").toInt()
|
||||
|
||||
// Track is formatted as X/0, so trim off the /0 part to parse
|
||||
// the track number correctly.
|
||||
val track = (retriever.extractMetadata(
|
||||
MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER
|
||||
) ?: "0/0").split("/")[0].toInt()
|
||||
|
||||
// Something has gone horribly wrong if a file has no duration.
|
||||
val duration = retriever.extractMetadata(
|
||||
MediaMetadataRetriever.METADATA_KEY_DURATION
|
||||
)!!.toLong()
|
||||
|
||||
// TODO: Add int-based genre compatibility
|
||||
songList.add(
|
||||
Song(
|
||||
cursor.getString(nameIndex),
|
||||
cursor.getString(artistIndex),
|
||||
cursor.getString(albumIndex),
|
||||
cursor.getInt(yearIndex),
|
||||
cursor.getInt(trackIndex),
|
||||
cursor.getLong(durationIndex),
|
||||
cursor.getLong(idIndex)
|
||||
title,
|
||||
artist,
|
||||
album,
|
||||
genre,
|
||||
year,
|
||||
track,
|
||||
duration,
|
||||
|
||||
retriever.embeddedPicture,
|
||||
id
|
||||
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Close the retriever when done so that it gets garbage collected
|
||||
retriever.close()
|
||||
}
|
||||
|
||||
Log.d(
|
||||
|
@ -75,6 +127,7 @@ class MusicRepository {
|
|||
)
|
||||
|
||||
return songList
|
||||
|
||||
} catch (error: Exception) {
|
||||
// TODO: Add better error handling
|
||||
|
||||
|
@ -91,20 +144,12 @@ class MusicRepository {
|
|||
return resolver.query(
|
||||
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||
arrayOf(
|
||||
AudioColumns.TITLE,
|
||||
AudioColumns.ARTIST,
|
||||
AudioColumns.ALBUM,
|
||||
AudioColumns.YEAR,
|
||||
AudioColumns.TRACK,
|
||||
AudioColumns.DURATION,
|
||||
AudioColumns._ID
|
||||
AudioColumns._ID,
|
||||
AudioColumns.DISPLAY_NAME
|
||||
),
|
||||
AudioColumns.IS_MUSIC + "=1", null,
|
||||
MediaStore.Audio.Media.DEFAULT_SORT_ORDER
|
||||
)
|
||||
|
||||
// TODO: Art Loading, since android cant do it on its own
|
||||
// TODO: Genre Loading?
|
||||
}
|
||||
|
||||
// Sort the list of Song objects into an abstracted lis
|
||||
|
@ -145,18 +190,18 @@ class MusicRepository {
|
|||
}
|
||||
}
|
||||
|
||||
Log.i(this::class.simpleName,
|
||||
"Successfully sorted songs into "
|
||||
+ artistList.size.toString()
|
||||
+ " Artists and "
|
||||
+ albumList.size.toString()
|
||||
+ " Albums."
|
||||
Log.i(
|
||||
this::class.simpleName,
|
||||
"Successfully sorted songs into " +
|
||||
artistList.size.toString() +
|
||||
" Artists and " +
|
||||
albumList.size.toString() +
|
||||
" Albums."
|
||||
)
|
||||
|
||||
mArtists = artistList
|
||||
mAlbums = albumList
|
||||
mSongs = distinctSongs
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -1,14 +1,32 @@
|
|||
package org.oxycblt.auxio.music
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
|
||||
// Class containing all relevant values for a song.
|
||||
data class Song(
|
||||
val name: String?,
|
||||
val artist: String?,
|
||||
val album: String?,
|
||||
//val genre: String?,
|
||||
val genre: String?,
|
||||
val year: Int,
|
||||
val track: Int,
|
||||
val duration: Long,
|
||||
val id: Long?
|
||||
)
|
||||
|
||||
private val coverData: ByteArray?,
|
||||
val id: Long
|
||||
) {
|
||||
var cover: Bitmap? = null
|
||||
|
||||
init {
|
||||
coverData?.let { data ->
|
||||
// Decode the Album Cover ByteArray if not null.
|
||||
val options = BitmapFactory.Options()
|
||||
options.inMutable = true
|
||||
|
||||
cover = BitmapFactory.decodeByteArray(
|
||||
data, 0, data.size, options
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue