Add error handling to all fragments
This commit is contained in:
parent
11793a423b
commit
fe7775329a
14 changed files with 600 additions and 414 deletions
|
@ -8,7 +8,9 @@ import android.view.ViewGroup
|
|||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.jdtech.jellyfin.R
|
||||
import dev.jdtech.jellyfin.viewmodels.LibraryViewModel
|
||||
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
||||
import dev.jdtech.jellyfin.databinding.FragmentLibraryBinding
|
||||
|
@ -36,6 +38,27 @@ class LibraryFragment : Fragment() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.viewModel = viewModel
|
||||
|
||||
val snackbar =
|
||||
Snackbar.make(
|
||||
binding.mainLayout,
|
||||
getString(R.string.error_loading_data),
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
)
|
||||
snackbar.setAction(getString(R.string.retry)) {
|
||||
viewModel.loadItems(args.libraryId)
|
||||
}
|
||||
|
||||
viewModel.error.observe(viewLifecycleOwner, { error ->
|
||||
if (error) {
|
||||
snackbar.show()
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.finishedLoading.observe(viewLifecycleOwner, {
|
||||
binding.loadingIndicator.visibility = if (it) View.GONE else View.VISIBLE
|
||||
})
|
||||
|
||||
binding.itemsRecyclerView.adapter =
|
||||
ViewItemListAdapter(ViewItemListAdapter.OnClickListener { item ->
|
||||
navigateToMediaInfoFragment(item)
|
||||
|
|
|
@ -7,7 +7,9 @@ import android.view.ViewGroup
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.jdtech.jellyfin.R
|
||||
import dev.jdtech.jellyfin.adapters.CollectionListAdapter
|
||||
import dev.jdtech.jellyfin.databinding.FragmentMediaBinding
|
||||
import dev.jdtech.jellyfin.viewmodels.MediaViewModel
|
||||
|
@ -26,6 +28,16 @@ class MediaFragment : Fragment() {
|
|||
): View {
|
||||
binding = FragmentMediaBinding.inflate(inflater, container, false)
|
||||
|
||||
val snackbar =
|
||||
Snackbar.make(
|
||||
binding.mainLayout,
|
||||
getString(R.string.error_loading_data),
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
)
|
||||
snackbar.setAction(getString(R.string.retry)) {
|
||||
viewModel.loadData()
|
||||
}
|
||||
|
||||
binding.lifecycleOwner = this
|
||||
binding.viewModel = viewModel
|
||||
binding.viewsRecyclerView.adapter =
|
||||
|
@ -34,8 +46,12 @@ class MediaFragment : Fragment() {
|
|||
})
|
||||
|
||||
viewModel.finishedLoading.observe(viewLifecycleOwner, {
|
||||
if (it) {
|
||||
binding.loadingIndicator.visibility = View.GONE
|
||||
binding.loadingIndicator.visibility = if (it) View.GONE else View.VISIBLE
|
||||
})
|
||||
|
||||
viewModel.error.observe(viewLifecycleOwner, { error ->
|
||||
if (error) {
|
||||
snackbar.show()
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.jdtech.jellyfin.R
|
||||
import dev.jdtech.jellyfin.adapters.PersonListAdapter
|
||||
|
@ -42,8 +43,24 @@ class MediaInfoFragment : Fragment() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val snackbar =
|
||||
Snackbar.make(
|
||||
binding.mainLayout,
|
||||
getString(R.string.error_loading_data),
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
)
|
||||
snackbar.setAction(getString(R.string.retry)) {
|
||||
viewModel.loadData(args.itemId, args.itemType)
|
||||
}
|
||||
|
||||
binding.viewModel = viewModel
|
||||
|
||||
viewModel.error.observe(viewLifecycleOwner, { error ->
|
||||
if (error) {
|
||||
snackbar.show()
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.item.observe(viewLifecycleOwner, { item ->
|
||||
if (item.originalTitle != item.name) {
|
||||
binding.originalTitle.visibility = View.VISIBLE
|
||||
|
|
|
@ -8,7 +8,9 @@ import android.view.ViewGroup
|
|||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.jdtech.jellyfin.R
|
||||
import dev.jdtech.jellyfin.adapters.EpisodeListAdapter
|
||||
import dev.jdtech.jellyfin.databinding.FragmentSeasonBinding
|
||||
import dev.jdtech.jellyfin.viewmodels.SeasonViewModel
|
||||
|
@ -34,6 +36,27 @@ class SeasonFragment : Fragment() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.viewModel = viewModel
|
||||
|
||||
val snackbar =
|
||||
Snackbar.make(
|
||||
binding.mainLayout,
|
||||
getString(R.string.error_loading_data),
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
)
|
||||
snackbar.setAction(getString(R.string.retry)) {
|
||||
viewModel.loadEpisodes(args.seriesId, args.seasonId)
|
||||
}
|
||||
|
||||
viewModel.error.observe(viewLifecycleOwner, { error ->
|
||||
if (error) {
|
||||
snackbar.show()
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.finishedLoading.observe(viewLifecycleOwner, {
|
||||
binding.loadingIndicator.visibility = if (it) View.GONE else View.VISIBLE
|
||||
})
|
||||
|
||||
binding.episodesRecyclerView.adapter =
|
||||
EpisodeListAdapter(EpisodeListAdapter.OnClickListener { episode ->
|
||||
navigateToEpisodeBottomSheetFragment(episode)
|
||||
|
|
|
@ -10,6 +10,7 @@ import dev.jdtech.jellyfin.repository.JellyfinRepository
|
|||
import kotlinx.coroutines.launch
|
||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||
import org.jellyfin.sdk.model.api.MediaSourceInfo
|
||||
import timber.log.Timber
|
||||
import java.text.DateFormat
|
||||
import java.time.ZoneOffset
|
||||
import java.util.*
|
||||
|
@ -42,12 +43,17 @@ constructor(
|
|||
|
||||
fun loadEpisode(episodeId: UUID) {
|
||||
viewModelScope.launch {
|
||||
_item.value = jellyfinRepository.getItem(episodeId)
|
||||
_runTime.value = "${_item.value?.runTimeTicks?.div(600000000)} min"
|
||||
_dateString.value = getDateString(_item.value!!)
|
||||
try {
|
||||
val item = jellyfinRepository.getItem(episodeId)
|
||||
_item.value = item
|
||||
_runTime.value = "${item.runTimeTicks?.div(600000000)} min"
|
||||
_dateString.value = getDateString(item)
|
||||
_mediaSources.value = jellyfinRepository.getMediaSources(episodeId)
|
||||
_played.value = _item.value?.userData?.played
|
||||
_favorite.value = _item.value?.userData?.isFavorite
|
||||
_played.value = item.userData?.played
|
||||
_favorite.value = item.userData?.isFavorite
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,12 +85,11 @@ constructor(
|
|||
|
||||
_views.value = items + views.map { HomeItem.ViewItem(it) }
|
||||
|
||||
_finishedLoading.value = true
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
_finishedLoading.value = true
|
||||
_error.value = true
|
||||
}
|
||||
_finishedLoading.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -19,9 +20,19 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
|
|||
private val _finishedLoading = MutableLiveData<Boolean>()
|
||||
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
||||
|
||||
private val _error = MutableLiveData<Boolean>()
|
||||
val error: LiveData<Boolean> = _error
|
||||
|
||||
fun loadItems(parentId: UUID) {
|
||||
_error.value = false
|
||||
_finishedLoading.value = false
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
_items.value = jellyfinRepository.getItems(parentId)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
_error.value = true
|
||||
}
|
||||
_finishedLoading.value = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import kotlinx.coroutines.withContext
|
|||
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||
import org.jellyfin.sdk.model.api.BaseItemPerson
|
||||
import org.jellyfin.sdk.model.api.MediaSourceInfo
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -62,8 +63,13 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
|
|||
private val _favorite = MutableLiveData<Boolean>()
|
||||
val favorite: LiveData<Boolean> = _favorite
|
||||
|
||||
private val _error = MutableLiveData<Boolean>()
|
||||
val error: LiveData<Boolean> = _error
|
||||
|
||||
fun loadData(itemId: UUID, itemType: String) {
|
||||
_error.value = false
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
_item.value = jellyfinRepository.getItem(itemId)
|
||||
_actors.value = getActors(_item.value!!)
|
||||
_director.value = getDirector(_item.value!!)
|
||||
|
@ -82,6 +88,10 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
|
|||
if (itemType == "Movie") {
|
||||
_mediaSources.value = jellyfinRepository.getMediaSources(itemId)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
_error.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
|
@ -20,8 +21,18 @@ constructor(
|
|||
private val _finishedLoading = MutableLiveData<Boolean>()
|
||||
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
||||
|
||||
private val _error = MutableLiveData<Boolean>()
|
||||
val error: LiveData<Boolean> = _error
|
||||
|
||||
init {
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun loadData() {
|
||||
_finishedLoading.value = false
|
||||
_error.value = false
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val items = jellyfinRepository.getItems()
|
||||
_collections.value =
|
||||
items.filter {
|
||||
|
@ -30,6 +41,10 @@ constructor(
|
|||
it.collectionType != "playlists" &&
|
||||
it.collectionType != "boxsets"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
_error.value = true
|
||||
}
|
||||
_finishedLoading.value = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import dev.jdtech.jellyfin.adapters.EpisodeItem
|
|||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jellyfin.sdk.model.api.ItemFields
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -20,9 +21,23 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
|
|||
private val _episodes = MutableLiveData<List<EpisodeItem>>()
|
||||
val episodes: LiveData<List<EpisodeItem>> = _episodes
|
||||
|
||||
private val _finishedLoading = MutableLiveData<Boolean>()
|
||||
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
||||
|
||||
private val _error = MutableLiveData<Boolean>()
|
||||
val error: LiveData<Boolean> = _error
|
||||
|
||||
fun loadEpisodes(seriesId: UUID, seasonId: UUID) {
|
||||
_error.value = false
|
||||
_finishedLoading.value = false
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
_episodes.value = getEpisodes(seriesId, seasonId)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
_error.value = true
|
||||
}
|
||||
_finishedLoading.value = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,17 +2,35 @@
|
|||
<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">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="dev.jdtech.jellyfin.viewmodels.LibraryViewModel" />
|
||||
</data>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/main_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".fragments.LibraryFragment">
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
android:id="@+id/loading_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:trackCornerRadius="10dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/items_recycler_view"
|
||||
android:layout_width="0dp"
|
||||
|
@ -31,5 +49,7 @@
|
|||
tools:listitem="@layout/base_item" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</layout>
|
||||
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
type="dev.jdtech.jellyfin.viewmodels.MediaViewModel" />
|
||||
</data>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/main_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -45,4 +50,5 @@
|
|||
tools:listitem="@layout/collection_item" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</layout>
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
type="dev.jdtech.jellyfin.viewmodels.MediaInfoViewModel" />
|
||||
</data>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/main_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
@ -374,4 +379,6 @@
|
|||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</layout>
|
||||
|
|
|
@ -12,10 +12,27 @@
|
|||
|
||||
</data>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/main_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
android:id="@+id/loading_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:trackCornerRadius="10dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/episodes_recycler_view"
|
||||
android:layout_width="0dp"
|
||||
|
@ -30,5 +47,6 @@
|
|||
tools:itemCount="4"
|
||||
tools:listitem="@layout/episode_item" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</layout>
|
Loading…
Reference in a new issue