Remove cover/genre loading

Remove the cover loading [For now] and permanently remove the genre loading, as both take too long to do in the loading screen. Will re-add album loading once more UI is added.
This commit is contained in:
OxygenCobalt 2020-08-19 16:38:43 -06:00
parent 6508280900
commit f8ae5d57a5
9 changed files with 60 additions and 137 deletions

View file

@ -47,10 +47,18 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-rc1'
// Navigation
def navigation_version = "2.3.0"
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
// Room Database
def room_version = "2.2.5"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-runtime:$room_version"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
// Lifecycle
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"

View file

@ -72,13 +72,13 @@ class LoadingFragment : Fragment() {
// depending on which error response was given, along with a retry button
binding.loadingBar.visibility = View.GONE
binding.statusText.visibility = View.VISIBLE
binding.errorText.visibility = View.VISIBLE
binding.resetButton.visibility = View.VISIBLE
if (response == MusicLoaderResponse.NO_MUSIC) {
binding.statusText.text = getString(R.string.error_no_music)
binding.errorText.text = getString(R.string.error_no_music)
} else {
binding.statusText.text = getString(R.string.error_music_load_failed)
binding.errorText.text = getString(R.string.error_music_load_failed)
}
}
@ -89,7 +89,7 @@ class LoadingFragment : Fragment() {
private fun onRetry(retry: Boolean) {
if (retry) {
binding.loadingBar.visibility = View.VISIBLE
binding.statusText.visibility = View.GONE
binding.errorText.visibility = View.GONE
binding.resetButton.visibility = View.GONE
loadingModel.doneWithRetry()

View file

@ -2,9 +2,7 @@ package org.oxycblt.auxio.music
import android.app.Application
import android.content.ContentResolver
import android.content.ContentUris
import android.database.Cursor
import android.media.MediaMetadataRetriever
import android.provider.MediaStore
import android.provider.MediaStore.Audio.AudioColumns
import android.util.Log
@ -15,12 +13,11 @@ enum class MusicLoaderResponse {
}
// Class that loads music from the FileSystem.
// This thing is probably full of memory leaks.
// FIXME: This thing probably has some memory leaks *somewhere*
class MusicLoader(private val app: Application) {
var songs = mutableListOf<Song>()
private val retriever: MediaMetadataRetriever = MediaMetadataRetriever()
private var musicCursor: Cursor? = null
val response: MusicLoaderResponse
@ -29,7 +26,7 @@ class MusicLoader(private val app: Application) {
response = findMusic()
}
private fun findMusic() : MusicLoaderResponse {
private fun findMusic(): MusicLoaderResponse {
try {
musicCursor = getCursor(
app.contentResolver
@ -38,14 +35,11 @@ class MusicLoader(private val app: Application) {
Log.i(this::class.simpleName, "Starting music search...")
useCursor()
} catch (error: Exception) {
// TODO: Add better error handling
Log.e(this::class.simpleName, "Something went horribly wrong.")
error.printStackTrace()
finalize()
musicCursor?.close()
return MusicLoaderResponse.FAILURE
}
@ -72,11 +66,20 @@ class MusicLoader(private val app: Application) {
private fun getCursor(resolver: ContentResolver): Cursor? {
Log.i(this::class.simpleName, "Getting music cursor.")
// Get all the values that can be retrieved by the cursor.
// The rest are retrieved using MediaMetadataExtractor and
// stored into a database.
return resolver.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
arrayOf(
AudioColumns._ID,
AudioColumns.DISPLAY_NAME
AudioColumns._ID, // 0
AudioColumns.DISPLAY_NAME, // 1
AudioColumns.TITLE, // 2
AudioColumns.ARTIST, // 3
AudioColumns.ALBUM, // 4
AudioColumns.YEAR, // 5
AudioColumns.TRACK, // 6
AudioColumns.DURATION // 7
),
AudioColumns.IS_MUSIC + "=1", null,
MediaStore.Audio.Media.DEFAULT_SORT_ORDER
@ -94,81 +97,35 @@ class MusicLoader(private val app: Application) {
val idIndex = cursor.getColumnIndexOrThrow(AudioColumns._ID)
val displayIndex = cursor.getColumnIndexOrThrow(AudioColumns.DISPLAY_NAME)
val titleIndex = 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)
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 basic metadata from the cursor
val title = cursor.getString(titleIndex) ?: cursor.getString(displayIndex)
val artist = cursor.getString(artistIndex)
val album = cursor.getString(albumIndex)
val year = cursor.getInt(yearIndex)
val track = cursor.getInt(trackIndex)
val duration = cursor.getLong(durationIndex)
// 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,
// so assert it as such.
val duration = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_DURATION
)!!.toLong()
// TODO: Add int-based genre compatibility
// TODO: Add album art [But its loaded separately, as that will take a bit]
// TODO: Add genres whenever android hasn't borked it
songs.add(
Song(
title,
artist,
album,
genre,
year,
track,
duration,
retriever.embeddedPicture,
id
id, title, artist, album,
year, track, duration
)
)
}
}
}
// Free the metadata retriever & the musicCursor, and make the song list immutable.
private fun finalize() {
retriever.close()
musicCursor?.use{
it.close()
cursor.close()
}
}
}
}

View file

@ -1,13 +1,11 @@
package org.oxycblt.auxio.music
import android.util.Log
import org.oxycblt.auxio.music.models.Album
import org.oxycblt.auxio.music.models.Artist
import org.oxycblt.auxio.music.models.Song
// Sort a list of Song objects into lists of songs, albums, and artists.
fun processSongs(songs: MutableList<Song>) : MutableList<Song> {
fun processSongs(songs: MutableList<Song>): MutableList<Song> {
// Eliminate all duplicates from the list
// excluding the ID, as that's guaranteed to be unique [I think]
return songs.distinctBy {
@ -16,7 +14,7 @@ fun processSongs(songs: MutableList<Song>) : MutableList<Song> {
}
// Sort a list of song objects into albums
fun sortIntoAlbums(songs: MutableList<Song>) : MutableList<Album> {
fun sortIntoAlbums(songs: MutableList<Song>): MutableList<Album> {
val songsByAlbum = songs.groupBy { it.album }
val albumList = mutableListOf<Album>()
@ -33,7 +31,7 @@ fun sortIntoAlbums(songs: MutableList<Song>) : MutableList<Album> {
}
// Sort a list of album objects into artists
fun sortIntoArtists(albums: MutableList<Album>) : MutableList<Artist> {
fun sortIntoArtists(albums: MutableList<Album>): MutableList<Artist> {
val albumsByArtist = albums.groupBy { it.artist }
val artistList = mutableListOf<Artist>()
@ -48,4 +46,4 @@ fun sortIntoArtists(albums: MutableList<Album>) : MutableList<Artist> {
}
return artistList
}
}

View file

@ -1,20 +1,16 @@
package org.oxycblt.auxio.music.models
import android.graphics.Bitmap
// Abstraction for Song
data class Album(
var songs: List<Song>
) {
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, Artist, Genre, Year, and Cover
// for the Album Name, Artist, and Year
for (song in songs) {
if (song.album != null) {
title = song.album
@ -24,14 +20,6 @@ data class Album(
artist = song.artist
}
if (song.genre != null) {
genre = song.genre
}
if (song.cover != null) {
cover = song.cover
}
if (song.year != 0) {
year = song.year
}

View file

@ -5,21 +5,13 @@ data class Artist(
private var albums: List<Album>
) {
var name: String? = null
var genre: String? = null
// TODO: Artist photos
init {
// Like Album, iterate through the child albums and pick out the first valid
// tag for Album/Genre
// Like Album, iterate through the child albums and pick out the first valid for artist
for (album in albums) {
if (album.artist != null) {
name = album.artist
}
if (album.genre != null) {
genre = album.genre
}
}
// Also sort the mAlbums by year

View file

@ -1,32 +1,13 @@
package org.oxycblt.auxio.music.models
import android.graphics.Bitmap
import android.graphics.BitmapFactory
// Class containing all relevant values for a song.
data class Song(
val id: Long,
val name: String?,
val artist: String?,
val album: String?,
val genre: String?,
val year: Int,
val track: Int,
val duration: 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
)
}
}
}
val coverData: ByteArray? = null
)

View file

@ -21,7 +21,7 @@
android:indeterminateTint="?attr/colorAccent"
android:indeterminateTintMode="src_in"
android:paddingBottom="@dimen/padding_small"
app:layout_constraintBottom_toTopOf="@+id/status_text"
app:layout_constraintBottom_toTopOf="@+id/error_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
@ -29,15 +29,15 @@
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/status_text"
android:id="@+id/error_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/status_loading_music"
app:layout_constraintBottom_toTopOf="@+id/reset_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loading_bar" />
app:layout_constraintTop_toBottomOf="@+id/loading_bar"
tools:text="@string/error_music_load_failed" />
<Button
android:id="@+id/reset_button"
@ -51,7 +51,8 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/status_text" />
app:layout_constraintTop_toBottomOf="@+id/error_text"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -2,8 +2,6 @@
<resources>
<string name="app_name">Auxio</string>
<string name="status_loading_music">Scanning your music library for the first time...</string>
<string name="error_no_music">No music found.</string>
<string name="error_music_load_failed">Music loading failed.</string>