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:
parent
f8ae5d57a5
commit
160013bbe9
3 changed files with 105 additions and 39 deletions
|
@ -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"
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue