Add [Very hacky] genre support

Add a way to load genres! Except it creates duplicates. And requires creating like 20 music cursors. At least it works?
This commit is contained in:
OxygenCobalt 2020-08-21 09:43:22 -06:00
parent f8ae5d57a5
commit 160013bbe9
3 changed files with 105 additions and 39 deletions

View file

@ -52,12 +52,12 @@ dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version" implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version" implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
/*
// Room Database // Room Database
def room_version = "2.2.5" def room_version = "2.2.5"
kapt "androidx.room:room-compiler:$room_version" kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-runtime:$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 // Lifecycle
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"

View file

@ -4,6 +4,7 @@ import android.app.Application
import android.content.ContentResolver import android.content.ContentResolver
import android.database.Cursor import android.database.Cursor
import android.provider.MediaStore import android.provider.MediaStore
import android.provider.MediaStore.Audio.Genres
import android.provider.MediaStore.Audio.AudioColumns import android.provider.MediaStore.Audio.AudioColumns
import android.util.Log import android.util.Log
import org.oxycblt.auxio.music.models.Song import org.oxycblt.auxio.music.models.Song
@ -17,8 +18,10 @@ enum class MusicLoaderResponse {
class MusicLoader(private val app: Application) { class MusicLoader(private val app: Application) {
var songs = mutableListOf<Song>() var songs = mutableListOf<Song>()
var genres = mutableListOf<Pair<Long, String>>()
private var musicCursor: Cursor? = null private var musicCursor: Cursor? = null
private var genreCursor: Cursor? = null
val response: MusicLoaderResponse val response: MusicLoaderResponse
@ -27,14 +30,15 @@ class MusicLoader(private val app: Application) {
} }
private fun findMusic(): MusicLoaderResponse { private fun findMusic(): MusicLoaderResponse {
genreCursor = getGenreCursor(
app.contentResolver
)
useGenreCursor()
indexMusic()
try { try {
musicCursor = getCursor(
app.contentResolver
)
Log.i(this::class.simpleName, "Starting music search...")
useCursor()
} catch (error: Exception) { } catch (error: Exception) {
Log.e(this::class.simpleName, "Something went horribly wrong.") Log.e(this::class.simpleName, "Something went horribly wrong.")
error.printStackTrace() error.printStackTrace()
@ -63,14 +67,30 @@ class MusicLoader(private val app: Application) {
} }
} }
private fun getCursor(resolver: ContentResolver): Cursor? { private fun getGenreCursor(resolver: ContentResolver): Cursor? {
Log.i(this::class.simpleName, "Getting genre cursor.")
// Get every Genre indexed by the android system, for some reason
// you cant directly get this from the plain music cursor.
return resolver.query(
Genres.EXTERNAL_CONTENT_URI,
arrayOf(
Genres._ID, // 0
Genres.NAME // 1
),
null, null,
Genres.DEFAULT_SORT_ORDER
)
}
private fun getMusicCursor(resolver: ContentResolver, genreId: Long): Cursor? {
Log.i(this::class.simpleName, "Getting music cursor.") Log.i(this::class.simpleName, "Getting music cursor.")
// Get all the values that can be retrieved by the cursor. // Get all the values that can be retrieved by the cursor.
// The rest are retrieved using MediaMetadataExtractor and // The rest are retrieved using MediaMetadataExtractor and
// stored into a database. // stored into a database.
return resolver.query( return resolver.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, Genres.Members.getContentUri("external", genreId),
arrayOf( arrayOf(
AudioColumns._ID, // 0 AudioColumns._ID, // 0
AudioColumns.DISPLAY_NAME, // 1 AudioColumns.DISPLAY_NAME, // 1
@ -86,46 +106,91 @@ class MusicLoader(private val app: Application) {
) )
} }
// Use the cursor index music files from the shared storage, returns true if any were found. // Use the genre cursor to index all the genres the android system has indexed.
private fun useCursor() { private fun useGenreCursor() {
musicCursor?.use { cursor -> // TODO: Move genre to its own model, even if its just two values
// Don't run the more expensive file loading operations if there is no music to index. genreCursor?.use { cursor ->
// Don't even bother running if there's nothing to process.
if (cursor.count == 0) { if (cursor.count == 0) {
return return
} }
val idIndex = cursor.getColumnIndexOrThrow(AudioColumns._ID) val idIndex = cursor.getColumnIndexOrThrow(Genres._ID)
val displayIndex = cursor.getColumnIndexOrThrow(AudioColumns.DISPLAY_NAME) val nameIndex = cursor.getColumnIndexOrThrow(Genres.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()) { while (cursor.moveToNext()) {
val id = cursor.getLong(idIndex) genres.add(
Pair<Long, String>(
// Get the basic metadata from the cursor cursor.getLong(idIndex),
val title = cursor.getString(titleIndex) ?: cursor.getString(displayIndex) cursor.getString(nameIndex)
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)
// 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(
id, title, artist, album,
year, track, duration
) )
) )
Log.i(this::class.simpleName, cursor.getString(nameIndex))
} }
cursor.close() cursor.close()
} }
Log.i(this::class.simpleName, "Retrieved " + genres.size.toString() + " Genres.")
}
// Use the cursor index music files from the shared storage.
private fun indexMusic() {
Log.i(this::class.simpleName, "Starting music search...")
// So, android has a brain aneurysm if you try to get an audio genre through
// AudioColumns. As a result, I just index every genre's name & ID, create a cursor
// of music for that genres ID, and then load and iterate through them normally,
// creating using the genres name as that songs Genre.
// God why do I have to do this
for (genre in genres) {
val musicCursor = getMusicCursor(app.contentResolver, genre.first)
musicCursor?.use { cursor ->
// Don't run the more expensive file loading operations if there
// is no music to index.
if (cursor.count == 0) {
return
}
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)
val display = cursor.getString(displayIndex)
// Get the basic metadata from the cursor
val title = cursor.getString(titleIndex) ?: display
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)
// 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(
id, title, artist, album,
genre.second, year, track, duration
)
)
}
cursor.close()
}
}
} }
} }

View file

@ -6,6 +6,7 @@ data class Song(
val name: String?, val name: String?,
val artist: String?, val artist: String?,
val album: String?, val album: String?,
val genre: String?,
val year: Int, val year: Int,
val track: Int, val track: Int,
val duration: Long, val duration: Long,