Move MusicRepository initialization to coroutine

Move the init call to MusicRepostiory to a coroutine so that it doesnt stop the UI thread.
This commit is contained in:
OxygenCobalt 2020-08-18 15:33:59 -06:00
parent 21a110edb0
commit 3134e313a3
3 changed files with 55 additions and 17 deletions

View file

@ -10,14 +10,18 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentLibraryBinding import org.oxycblt.auxio.databinding.FragmentLibraryBinding
import org.oxycblt.auxio.music.MusicRepository
class LibraryFragment : Fragment() { class LibraryFragment : Fragment() {
// Lazily initiate the ViewModel when its first referenced. // Lazily initiate the ViewModel when its first referenced.
// Not because this does anything, it just looks nicer. // Not because this does anything, it just looks nicer.
private val libraryModel: LibraryViewModel by lazy { private val libraryModel: LibraryViewModel by lazy {
ViewModelProvider(this).get(LibraryViewModel::class.java) ViewModelProvider(
this,
LibraryViewModel.Factory(
requireActivity().application
)
).get(LibraryViewModel::class.java)
} }
override fun onCreateView( override fun onCreateView(
@ -29,7 +33,7 @@ class LibraryFragment : Fragment() {
inflater, R.layout.fragment_library, container, false inflater, R.layout.fragment_library, container, false
) )
MusicRepository.getInstance().init(requireActivity().application) libraryModel
Log.d(this::class.simpleName, "Fragment created.") Log.d(this::class.simpleName, "Fragment created.")

View file

@ -1,13 +1,42 @@
package org.oxycblt.auxio.library package org.oxycblt.auxio.library
import android.app.Application
import android.util.Log import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.oxycblt.auxio.music.MusicRepository
class LibraryViewModel() : ViewModel() { class LibraryViewModel(private val app: Application) : ViewModel() {
// TODO: Implement music data in ViewModel private val viewModelJob = Job()
private val ioScope = CoroutineScope(
Dispatchers.IO
)
init { init {
startMusicRepo()
Log.d(this::class.simpleName, "ViewModel created.") Log.d(this::class.simpleName, "ViewModel created.")
} }
// TODO: Temp function, remove when LoadingFragment is added
private fun startMusicRepo() {
ioScope.launch {
MusicRepository.getInstance().init(app)
}
}
class Factory(private val application: Application) : ViewModelProvider.Factory {
@Suppress("unchecked_cast")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(LibraryViewModel::class.java)) {
return LibraryViewModel(application) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
} }

View file

@ -4,7 +4,6 @@ import android.app.Application
import android.content.ContentResolver import android.content.ContentResolver
import android.content.ContentUris import android.content.ContentUris
import android.database.Cursor import android.database.Cursor
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever import android.media.MediaMetadataRetriever
import android.provider.MediaStore import android.provider.MediaStore
import android.provider.MediaStore.Audio.AudioColumns import android.provider.MediaStore.Audio.AudioColumns
@ -47,13 +46,15 @@ class MusicRepository {
app.contentResolver app.contentResolver
) )
Log.i(this::class.simpleName, "Starting music search...")
// Index music files from shared storage // Index music files from shared storage
musicCursor?.use { cursor -> musicCursor?.use { cursor ->
val idIndex = cursor.getColumnIndexOrThrow(AudioColumns._ID) val idIndex = cursor.getColumnIndexOrThrow(AudioColumns._ID)
val displayIndex = cursor.getColumnIndexOrThrow(AudioColumns.DISPLAY_NAME) val displayIndex = cursor.getColumnIndexOrThrow(AudioColumns.DISPLAY_NAME)
var retriever = MediaMetadataRetriever() val retriever = MediaMetadataRetriever()
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
val id = cursor.getLong(idIndex) val id = cursor.getLong(idIndex)
@ -84,17 +85,22 @@ class MusicRepository {
MediaMetadataRetriever.METADATA_KEY_GENRE MediaMetadataRetriever.METADATA_KEY_GENRE
) )
val year = (retriever.extractMetadata( val year = (
retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_YEAR MediaMetadataRetriever.METADATA_KEY_YEAR
) ?: "0").toInt() ) ?: "0"
).toInt()
// Track is formatted as X/0, so trim off the /0 part to parse // Track is formatted as X/0, so trim off the /0 part to parse
// the track number correctly. // the track number correctly.
val track = (retriever.extractMetadata( val track = (
retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER
) ?: "0/0").split("/")[0].toInt() ) ?: "0/0"
).split("/")[0].toInt()
// Something has gone horribly wrong if a file has no duration. // Something has gone horribly wrong if a file has no duration,
// so assert it as such.
val duration = retriever.extractMetadata( val duration = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_DURATION MediaMetadataRetriever.METADATA_KEY_DURATION
)!!.toLong() )!!.toLong()
@ -117,7 +123,7 @@ class MusicRepository {
) )
} }
// Close the retriever when done so that it gets garbage collected // Close the retriever when done so that it gets garbage collected [I hope]
retriever.close() retriever.close()
} }
@ -127,7 +133,6 @@ class MusicRepository {
) )
return songList return songList
} catch (error: Exception) { } catch (error: Exception) {
// TODO: Add better error handling // TODO: Add better error handling