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:
OxygenCobalt 2020-08-18 15:22:12 -06:00
parent a8b368b577
commit 21a110edb0
4 changed files with 126 additions and 74 deletions

View file

@ -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 }
}
}
}

View file

@ -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 }
}
}
}

View file

@ -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 {

View file

@ -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
)
}
}
}