Add Loading fallback states

Add fallback states for when there is no music & if the load fails, along with a retry button if that happens.
This commit is contained in:
OxygenCobalt 2020-08-19 09:45:15 -06:00
parent 1b21552576
commit 24452e8fa4
8 changed files with 116 additions and 26 deletions

View file

@ -17,8 +17,11 @@ import org.oxycblt.auxio.music.MusicLoadResponse
class LoadingFragment : Fragment() {
private val loadingModel: LoadingViewModel by lazy {
ViewModelProvider(this, LoadingViewModel.Factory(
requireActivity().application)
ViewModelProvider(
this,
LoadingViewModel.Factory(
requireActivity().application
)
).get(LoadingViewModel::class.java)
}
@ -34,21 +37,62 @@ class LoadingFragment : Fragment() {
)
binding.lifecycleOwner = this
binding.loadingModel = loadingModel
loadingModel.musicRepoResponse.observe(viewLifecycleOwner, Observer { response ->
onMusicLoadResponse(response)
})
loadingModel.musicRepoResponse.observe(
viewLifecycleOwner,
Observer { response ->
onMusicLoadResponse(response)
}
)
loadingModel.doRetry.observe(
viewLifecycleOwner,
Observer { retry ->
onRetry(retry)
}
)
Log.d(this::class.simpleName, "Fragment created.")
return binding.root
}
private fun onMusicLoadResponse(response: MusicLoadResponse) {
if (response == MusicLoadResponse.DONE) {
this.findNavController().navigate(
LoadingFragmentDirections.actionToLibrary()
)
private fun onMusicLoadResponse(repoResponse: MusicLoadResponse?) {
// Don't run this if the value is null, Which is what the value changes to after
// this is run.
repoResponse?.let { response ->
if (response == MusicLoadResponse.DONE) {
this.findNavController().navigate(
LoadingFragmentDirections.actionToLibrary()
)
} else {
// If the response wasn't a success, then show the specific error message
// depending on which error response was given, along with a retry button
binding.loadingBar.visibility = View.GONE
binding.statusText.visibility = View.VISIBLE
binding.resetButton.visibility = View.VISIBLE
if (response == MusicLoadResponse.NO_MUSIC) {
binding.statusText.text = getString(R.string.error_no_music)
} else {
binding.statusText.text = getString(R.string.error_music_load_failed)
}
}
loadingModel.doneWithResponse()
}
}
}
private fun onRetry(retry: Boolean) {
if (retry) {
binding.loadingBar.visibility = View.VISIBLE
binding.statusText.visibility = View.GONE
binding.resetButton.visibility = View.GONE
loadingModel.doneWithRetry()
}
}
}

View file

@ -6,12 +6,16 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.oxycblt.auxio.music.MusicLoadResponse
import org.oxycblt.auxio.music.MusicRepository
class LoadingViewModel(private val app: Application) : ViewModel() {
private val loadingJob = Job()
private val ioScope = CoroutineScope(
Dispatchers.IO
@ -20,6 +24,9 @@ class LoadingViewModel(private val app: Application) : ViewModel() {
private val mMusicRepoResponse = MutableLiveData<MusicLoadResponse>()
val musicRepoResponse: LiveData<MusicLoadResponse> get() = mMusicRepoResponse
private val mDoRetry = MutableLiveData<Boolean>()
val doRetry: LiveData<Boolean> get() = mDoRetry
init {
startMusicRepo()
@ -40,6 +47,20 @@ class LoadingViewModel(private val app: Application) : ViewModel() {
}
}
fun doneWithResponse() {
mMusicRepoResponse.value = null
}
fun retry() {
mDoRetry.value = true
startMusicRepo()
}
fun doneWithRetry() {
mDoRetry.value = false
}
override fun onCleared() {
super.onCleared()
@ -53,7 +74,8 @@ class LoadingViewModel(private val app: Application) : ViewModel() {
if (modelClass.isAssignableFrom(LoadingViewModel::class.java)) {
return LoadingViewModel(application) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
throw IllegalArgumentException("Unknown ViewModel class.")
}
}
}
}

View file

@ -149,7 +149,6 @@ class MusicRepository {
)
return songList
} catch (error: Exception) {
// TODO: Add better error handling

View file

@ -19,5 +19,4 @@
tools:ignore="FragmentTagUsage" />
</FrameLayout>
</layout>

View file

@ -1,6 +1,5 @@
<?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">

View file

@ -3,6 +3,13 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="loadingModel"
type="org.oxycblt.auxio.loading.LoadingViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -14,22 +21,37 @@
android:indeterminateTint="?attr/colorAccent"
android:indeterminateTintMode="src_in"
android:paddingBottom="@dimen/padding_small"
app:layout_constraintBottom_toTopOf="@+id/text_indexing_library"
app:layout_constraintBottom_toTopOf="@+id/status_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"/>
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/text_indexing_library"
android:id="@+id/status_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_loading_music"
app:layout_constraintBottom_toBottomOf="parent"
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" />
<Button
android:id="@+id/reset_button"
style="@style/Widget.AppCompat.Button.Borderless.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_retry"
android:onClick="@{() -> loadingModel.retry()}"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/status_text" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -9,7 +9,7 @@
android:id="@+id/loadingFragment"
android:name="org.oxycblt.auxio.loading.LoadingFragment"
android:label="LoadingFragment"
tools:layout="@layout/fragment_loading" >
tools:layout="@layout/fragment_loading">
<action
android:id="@+id/action_to_library"
app:destination="@id/libraryFragment" />

View file

@ -2,7 +2,12 @@
<resources>
<string name="app_name">Auxio</string>
<string name="label_loading_music">Scanning your music library for the first time...</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>
<string name="label_retry">Retry</string>
<string name="title_library_fragment">Library</string>
</resources>