diff --git a/app/src/main/java/org/oxycblt/auxio/music/Album.kt b/app/src/main/java/org/oxycblt/auxio/music/Album.kt index 9bb7a53b4..3f2bf740b 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Album.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Album.kt @@ -1,46 +1,43 @@ package org.oxycblt.auxio.music +import android.graphics.Bitmap + // Basic Abstraction for Song -data class Album ( - var mSongs: List +data class Album( + var songs: List ) { - 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 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 } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/oxycblt/auxio/music/Artist.kt b/app/src/main/java/org/oxycblt/auxio/music/Artist.kt index fe270a15d..d049c3d8f 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Artist.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Artist.kt @@ -2,33 +2,25 @@ package org.oxycblt.auxio.music // Abstraction for mAlbums data class Artist( - private var mAlbums: List + private var albums: List ) { - 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 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 } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt index d7bb3f2a1..c88ef4557 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt @@ -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() 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 { diff --git a/app/src/main/java/org/oxycblt/auxio/music/Song.kt b/app/src/main/java/org/oxycblt/auxio/music/Song.kt index 30663c157..ae1fe5bb6 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Song.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Song.kt @@ -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 + ) + } + } +}