Add Music Loading

Add a Music Repository/Loader, along with a boilerplate ViewModel for LibraryFragment.
This commit is contained in:
OxygenCobalt 2020-08-17 15:21:14 -06:00
parent 9961b87f47
commit 22e8049d6b
10 changed files with 160 additions and 12 deletions

View file

@ -8,7 +8,7 @@ android {
defaultConfig {
applicationId "org.oxycblt.auxio"
minSdkVersion 23
minSdkVersion 24
targetSdkVersion 30
versionCode 1
versionName "1.0"

View file

@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.oxycblt.auxio">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"

View file

@ -7,17 +7,23 @@ import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentLibraryBinding
class LibraryFragment : Fragment() {
// Lazily initiate the ViewModel when its first referenced.
// Not because this does anything, it just looks nicer.
private val libraryModel: LibraryViewModel by lazy {
ViewModelProvider(this).get(LibraryViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = DataBindingUtil.inflate<FragmentLibraryBinding>(
inflater, R.layout.fragment_library, container, false
)

View file

@ -0,0 +1,14 @@
package org.oxycblt.auxio.library
import android.util.Log
import androidx.lifecycle.ViewModel
class LibraryViewModel() : ViewModel() {
// TODO: Implement music data in ViewModel
init {
Log.d(this::class.simpleName, "ViewModel created.")
}
}

View file

@ -0,0 +1,118 @@
package org.oxycblt.auxio.music
import android.app.Application
import android.content.ContentResolver
import android.database.Cursor
import android.provider.MediaStore
import android.provider.MediaStore.Audio.AudioColumns
import android.util.Log
// Storage for music data. Design largely adapted from Music Player GO:
// https://github.com/enricocid/Music-Player-GO
class MusicRepository() {
var rawMusicList = mutableListOf<RawMusic>()
fun getMusic(app: Application): MutableList<RawMusic> {
findMusic(app)?.let { rm ->
// TODO: Sort the raw music
rawMusicList = rm
}
return rawMusicList
}
private fun findMusic(app: Application): MutableList<RawMusic>? {
try {
val musicCursor = getCursor(
app.contentResolver
)
// 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)
while (cursor.moveToNext()) {
rawMusicList.add(
RawMusic(
cursor.getString(nameIndex),
cursor.getString(artistIndex),
cursor.getString(albumIndex),
cursor.getInt(yearIndex),
cursor.getInt(trackIndex),
cursor.getLong(durationIndex),
cursor.getLong(idIndex)
)
)
}
}
Log.d(
this::class.simpleName,
"Music search ended with " + rawMusicList.size.toString() + " Songs found."
)
return rawMusicList
} catch (error: Exception) {
// TODO: Add better error handling
Log.e(this::class.simpleName, "Something went horribly wrong.")
error.printStackTrace()
return null
}
}
private fun getCursor(resolver: ContentResolver): Cursor? {
Log.i(this::class.simpleName, "Getting music cursor.")
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.IS_MUSIC + "=1", null,
MediaStore.Audio.Media.DEFAULT_SORT_ORDER
)
}
companion object {
@Volatile
private var INSTANCE: MusicRepository? = null
fun getInstance(): MusicRepository {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val newInstance = MusicRepository()
INSTANCE = newInstance
Log.d(
MusicRepository::class.simpleName,
"Created an instance of MusicRepository."
)
return newInstance
}
}
}
}

View file

@ -0,0 +1,13 @@
package org.oxycblt.auxio.music
// Class containing all relevant values for a song
// TODO: Is broken into Artist, Album, and Song classes
data class RawMusic(
val name: String?,
val artist: String?,
val album: String?,
val year: Int,
val track: Int,
val duration: Long,
val id: Long?
)

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">

View file

@ -1,13 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:app="http://schemas.android.com/apk/res-auto"
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/background">
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
@ -18,7 +16,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:title="@string/fragment_library_title"
tools:titleTextColor="@color/primaryTextColor"/>
tools:titleTextColor="@color/primaryTextColor" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_main"
app:startDestination="@id/playerFragment">

View file

@ -5,6 +5,5 @@
<item name="colorPrimary">@color/primaryColor</item>
<item name="colorPrimaryDark">@color/primaryColor</item>
<item name="colorAccent">@color/primaryColor</item>
<item name="background">@color/secondaryColor</item>
</style>
</resources>