New error panel with details dialog

This commit is contained in:
jarnedemeulemeester 2021-08-22 18:47:25 +02:00
parent f2ce030856
commit a067b6b13d
No known key found for this signature in database
GPG key ID: 60884A0C1EBA43E5
30 changed files with 377 additions and 298 deletions

View file

@ -42,6 +42,7 @@ android {
buildFeatures { buildFeatures {
dataBinding true dataBinding true
viewBinding true
} }
} }

View file

@ -110,7 +110,7 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {
}) })
binding.playerItemsErrorDetails.setOnClickListener { binding.playerItemsErrorDetails.setOnClickListener {
ErrorDialogFragment(viewModel.playerItemsError.value ?: "Unknown error").show(parentFragmentManager, "errordialog") ErrorDialogFragment(viewModel.playerItemsError.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
} }
viewModel.loadEpisode(args.episodeId) viewModel.loadEpisode(args.episodeId)

View file

@ -7,13 +7,13 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.R import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.adapters.FavoritesListAdapter import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
import dev.jdtech.jellyfin.databinding.FragmentFavoriteBinding import dev.jdtech.jellyfin.databinding.FragmentFavoriteBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.viewmodels.FavoriteViewModel import dev.jdtech.jellyfin.viewmodels.FavoriteViewModel
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
@ -29,16 +29,6 @@ class FavoriteFragment : Fragment() {
): View { ): View {
binding = FragmentFavoriteBinding.inflate(inflater, container, false) binding = FragmentFavoriteBinding.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 = viewLifecycleOwner binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel binding.viewModel = viewModel
binding.favoritesRecyclerView.adapter = FavoritesListAdapter( binding.favoritesRecyclerView.adapter = FavoritesListAdapter(
@ -53,11 +43,23 @@ class FavoriteFragment : Fragment() {
}) })
viewModel.error.observe(viewLifecycleOwner, { error -> viewModel.error.observe(viewLifecycleOwner, { error ->
if (error) { if (error != null) {
snackbar.show() binding.errorLayout.errorPanel.visibility = View.VISIBLE
binding.favoritesRecyclerView.visibility = View.GONE
} else {
binding.errorLayout.errorPanel.visibility = View.GONE
binding.favoritesRecyclerView.visibility = View.VISIBLE
} }
}) })
binding.errorLayout.errorRetryButton.setOnClickListener {
viewModel.loadData()
}
binding.errorLayout.errorDetailsButton.setOnClickListener {
ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
}
viewModel.favoriteSections.observe(viewLifecycleOwner, { sections -> viewModel.favoriteSections.observe(viewLifecycleOwner, { sections ->
if (sections.isEmpty()) { if (sections.isEmpty()) {
binding.noFavoritesText.visibility = View.VISIBLE binding.noFavoritesText.visibility = View.VISIBLE

View file

@ -5,13 +5,13 @@ import android.view.*
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.R import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
import dev.jdtech.jellyfin.adapters.ViewListAdapter import dev.jdtech.jellyfin.adapters.ViewListAdapter
import dev.jdtech.jellyfin.databinding.FragmentHomeBinding import dev.jdtech.jellyfin.databinding.FragmentHomeBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.viewmodels.HomeViewModel import dev.jdtech.jellyfin.viewmodels.HomeViewModel
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
@ -49,12 +49,6 @@ class HomeFragment : Fragment() {
): View { ): View {
binding = FragmentHomeBinding.inflate(inflater, container, false) binding = FragmentHomeBinding.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 = viewLifecycleOwner binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel binding.viewModel = viewModel
binding.viewsRecyclerView.adapter = ViewListAdapter(ViewListAdapter.OnClickListener { binding.viewsRecyclerView.adapter = ViewListAdapter(ViewListAdapter.OnClickListener {
@ -78,11 +72,23 @@ class HomeFragment : Fragment() {
}) })
viewModel.error.observe(viewLifecycleOwner, { error -> viewModel.error.observe(viewLifecycleOwner, { error ->
if (error) { if (error != null) {
snackbar.show() binding.errorLayout.errorPanel.visibility = View.VISIBLE
binding.viewsRecyclerView.visibility = View.GONE
} else {
binding.errorLayout.errorPanel.visibility = View.GONE
binding.viewsRecyclerView.visibility = View.VISIBLE
} }
}) })
binding.errorLayout.errorRetryButton.setOnClickListener {
viewModel.loadData()
}
binding.errorLayout.errorDetailsButton.setOnClickListener {
ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
}
return binding.root return binding.root
} }

View file

@ -8,12 +8,12 @@ import android.view.ViewGroup
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.R import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.viewmodels.LibraryViewModel import dev.jdtech.jellyfin.viewmodels.LibraryViewModel
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
import dev.jdtech.jellyfin.databinding.FragmentLibraryBinding import dev.jdtech.jellyfin.databinding.FragmentLibraryBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
@AndroidEntryPoint @AndroidEntryPoint
@ -39,21 +39,23 @@ class LibraryFragment : Fragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.viewModel = viewModel binding.viewModel = viewModel
val snackbar = viewModel.error.observe(viewLifecycleOwner, { error ->
Snackbar.make( if (error != null) {
binding.mainLayout, binding.errorLayout.errorPanel.visibility = View.VISIBLE
getString(R.string.error_loading_data), binding.itemsRecyclerView.visibility = View.GONE
Snackbar.LENGTH_INDEFINITE } else {
) binding.errorLayout.errorPanel.visibility = View.GONE
snackbar.setAction(getString(R.string.retry)) { binding.itemsRecyclerView.visibility = View.VISIBLE
}
})
binding.errorLayout.errorRetryButton.setOnClickListener {
viewModel.loadItems(args.libraryId) viewModel.loadItems(args.libraryId)
} }
viewModel.error.observe(viewLifecycleOwner, { error -> binding.errorLayout.errorDetailsButton.setOnClickListener {
if (error) { ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
snackbar.show()
} }
})
viewModel.finishedLoading.observe(viewLifecycleOwner, { viewModel.finishedLoading.observe(viewLifecycleOwner, {
binding.loadingIndicator.visibility = if (it) View.GONE else View.VISIBLE binding.loadingIndicator.visibility = if (it) View.GONE else View.VISIBLE

View file

@ -6,11 +6,11 @@ import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.R import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.adapters.CollectionListAdapter import dev.jdtech.jellyfin.adapters.CollectionListAdapter
import dev.jdtech.jellyfin.databinding.FragmentMediaBinding import dev.jdtech.jellyfin.databinding.FragmentMediaBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.viewmodels.MediaViewModel import dev.jdtech.jellyfin.viewmodels.MediaViewModel
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
@ -53,16 +53,6 @@ class MediaFragment : Fragment() {
): View { ): View {
binding = FragmentMediaBinding.inflate(inflater, container, false) 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 = viewLifecycleOwner binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel binding.viewModel = viewModel
binding.viewsRecyclerView.adapter = binding.viewsRecyclerView.adapter =
@ -75,11 +65,23 @@ class MediaFragment : Fragment() {
}) })
viewModel.error.observe(viewLifecycleOwner, { error -> viewModel.error.observe(viewLifecycleOwner, { error ->
if (error) { if (error != null) {
snackbar.show() binding.errorLayout.errorPanel.visibility = View.VISIBLE
binding.viewsRecyclerView.visibility = View.GONE
} else {
binding.errorLayout.errorPanel.visibility = View.GONE
binding.viewsRecyclerView.visibility = View.VISIBLE
} }
}) })
binding.errorLayout.errorRetryButton.setOnClickListener {
viewModel.loadData()
}
binding.errorLayout.errorDetailsButton.setOnClickListener {
ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
}
return binding.root return binding.root
} }

View file

@ -11,7 +11,6 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.R import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.adapters.PersonListAdapter import dev.jdtech.jellyfin.adapters.PersonListAdapter
@ -45,24 +44,26 @@ class MediaInfoFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) 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 binding.viewModel = viewModel
viewModel.error.observe(viewLifecycleOwner, { error -> viewModel.error.observe(viewLifecycleOwner, { error ->
if (error) { if (error != null) {
snackbar.show() binding.errorLayout.errorPanel.visibility = View.VISIBLE
binding.mediaInfoScrollview.visibility = View.GONE
} else {
binding.errorLayout.errorPanel.visibility = View.GONE
binding.mediaInfoScrollview.visibility = View.VISIBLE
} }
}) })
binding.errorLayout.errorRetryButton.setOnClickListener {
viewModel.loadData(args.itemId, args.itemType)
}
binding.errorLayout.errorDetailsButton.setOnClickListener {
ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
}
viewModel.item.observe(viewLifecycleOwner, { item -> viewModel.item.observe(viewLifecycleOwner, { item ->
if (item.originalTitle != item.name) { if (item.originalTitle != item.name) {
binding.originalTitle.visibility = View.VISIBLE binding.originalTitle.visibility = View.VISIBLE
@ -136,7 +137,7 @@ class MediaInfoFragment : Fragment() {
}) })
binding.playerItemsErrorDetails.setOnClickListener { binding.playerItemsErrorDetails.setOnClickListener {
ErrorDialogFragment(viewModel.playerItemsError.value ?: "Unknown error").show(parentFragmentManager, "errordialog") ErrorDialogFragment(viewModel.playerItemsError.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
} }
binding.trailerButton.setOnClickListener { binding.trailerButton.setOnClickListener {

View file

@ -8,13 +8,13 @@ import android.view.ViewGroup
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.R import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.adapters.FavoritesListAdapter import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
import dev.jdtech.jellyfin.databinding.FragmentSearchResultBinding import dev.jdtech.jellyfin.databinding.FragmentSearchResultBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.viewmodels.SearchResultViewModel import dev.jdtech.jellyfin.viewmodels.SearchResultViewModel
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
@ -32,16 +32,6 @@ class SearchResultFragment : Fragment() {
): View { ): View {
binding = FragmentSearchResultBinding.inflate(inflater, container, false) binding = FragmentSearchResultBinding.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(args.query)
}
binding.lifecycleOwner = viewLifecycleOwner binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel binding.viewModel = viewModel
binding.searchResultsRecyclerView.adapter = FavoritesListAdapter( binding.searchResultsRecyclerView.adapter = FavoritesListAdapter(
@ -56,11 +46,23 @@ class SearchResultFragment : Fragment() {
}) })
viewModel.error.observe(viewLifecycleOwner, { error -> viewModel.error.observe(viewLifecycleOwner, { error ->
if (error) { if (error != null) {
snackbar.show() binding.errorLayout.errorPanel.visibility = View.VISIBLE
binding.searchResultsRecyclerView.visibility = View.GONE
} else {
binding.errorLayout.errorPanel.visibility = View.GONE
binding.searchResultsRecyclerView.visibility = View.VISIBLE
} }
}) })
binding.errorLayout.errorRetryButton.setOnClickListener {
viewModel.loadData(args.query)
}
binding.errorLayout.errorDetailsButton.setOnClickListener {
ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
}
viewModel.sections.observe(viewLifecycleOwner, { sections -> viewModel.sections.observe(viewLifecycleOwner, { sections ->
if (sections.isEmpty()) { if (sections.isEmpty()) {
binding.noSearchResultsText.visibility = View.VISIBLE binding.noSearchResultsText.visibility = View.VISIBLE

View file

@ -8,11 +8,11 @@ import android.view.ViewGroup
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.R import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.adapters.EpisodeListAdapter import dev.jdtech.jellyfin.adapters.EpisodeListAdapter
import dev.jdtech.jellyfin.databinding.FragmentSeasonBinding import dev.jdtech.jellyfin.databinding.FragmentSeasonBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.viewmodels.SeasonViewModel import dev.jdtech.jellyfin.viewmodels.SeasonViewModel
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
@ -37,21 +37,23 @@ class SeasonFragment : Fragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.viewModel = viewModel binding.viewModel = viewModel
val snackbar = viewModel.error.observe(viewLifecycleOwner, { error ->
Snackbar.make( if (error != null) {
binding.mainLayout, binding.errorLayout.errorPanel.visibility = View.VISIBLE
getString(R.string.error_loading_data), binding.episodesRecyclerView.visibility = View.GONE
Snackbar.LENGTH_INDEFINITE } else {
) binding.errorLayout.errorPanel.visibility = View.GONE
snackbar.setAction(getString(R.string.retry)) { binding.episodesRecyclerView.visibility = View.VISIBLE
}
})
binding.errorLayout.errorRetryButton.setOnClickListener {
viewModel.loadEpisodes(args.seriesId, args.seasonId) viewModel.loadEpisodes(args.seriesId, args.seasonId)
} }
viewModel.error.observe(viewLifecycleOwner, { error -> binding.errorLayout.errorDetailsButton.setOnClickListener {
if (error) { ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
snackbar.show()
} }
})
viewModel.finishedLoading.observe(viewLifecycleOwner, { viewModel.finishedLoading.observe(viewLifecycleOwner, {
binding.loadingIndicator.visibility = if (it) View.GONE else View.VISIBLE binding.loadingIndicator.visibility = if (it) View.GONE else View.VISIBLE

View file

@ -8,7 +8,6 @@ import android.view.ViewGroup
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.databinding.FragmentServerSelectBinding import dev.jdtech.jellyfin.databinding.FragmentServerSelectBinding
import dev.jdtech.jellyfin.dialogs.DeleteServerDialogFragment import dev.jdtech.jellyfin.dialogs.DeleteServerDialogFragment
import dev.jdtech.jellyfin.adapters.ServerGridAdapter import dev.jdtech.jellyfin.adapters.ServerGridAdapter

View file

@ -26,15 +26,15 @@ constructor(
private val _finishedLoading = MutableLiveData<Boolean>() private val _finishedLoading = MutableLiveData<Boolean>()
val finishedLoading: LiveData<Boolean> = _finishedLoading val finishedLoading: LiveData<Boolean> = _finishedLoading
private val _error = MutableLiveData<Boolean>() private val _error = MutableLiveData<String>()
val error: LiveData<Boolean> = _error val error: LiveData<String> = _error
init { init {
loadData() loadData()
} }
fun loadData() { fun loadData() {
_error.value = false _error.value = null
_finishedLoading.value = false _finishedLoading.value = false
viewModelScope.launch { viewModelScope.launch {
try { try {
@ -78,7 +78,7 @@ constructor(
_favoriteSections.value = tempFavoriteSections _favoriteSections.value = tempFavoriteSections
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
_error.value = true _error.value = e.message
} }
_finishedLoading.value = true _finishedLoading.value = true
} }

View file

@ -38,15 +38,15 @@ constructor(
private val _finishedLoading = MutableLiveData<Boolean>() private val _finishedLoading = MutableLiveData<Boolean>()
val finishedLoading: LiveData<Boolean> = _finishedLoading val finishedLoading: LiveData<Boolean> = _finishedLoading
private val _error = MutableLiveData<Boolean>() private val _error = MutableLiveData<String>()
val error: LiveData<Boolean> = _error val error: LiveData<String> = _error
init { init {
loadData() loadData()
} }
fun loadData() { fun loadData() {
_error.value = false _error.value = null
_finishedLoading.value = false _finishedLoading.value = false
viewModelScope.launch { viewModelScope.launch {
try { try {
@ -87,7 +87,7 @@ constructor(
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
_error.value = true _error.value = e.message
} }
_finishedLoading.value = true _finishedLoading.value = true
} }

View file

@ -20,18 +20,18 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
private val _finishedLoading = MutableLiveData<Boolean>() private val _finishedLoading = MutableLiveData<Boolean>()
val finishedLoading: LiveData<Boolean> = _finishedLoading val finishedLoading: LiveData<Boolean> = _finishedLoading
private val _error = MutableLiveData<Boolean>() private val _error = MutableLiveData<String>()
val error: LiveData<Boolean> = _error val error: LiveData<String> = _error
fun loadItems(parentId: UUID) { fun loadItems(parentId: UUID) {
_error.value = false _error.value = null
_finishedLoading.value = false _finishedLoading.value = false
viewModelScope.launch { viewModelScope.launch {
try { try {
_items.value = jellyfinRepository.getItems(parentId) _items.value = jellyfinRepository.getItems(parentId)
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
_error.value = true _error.value = e.message
} }
_finishedLoading.value = true _finishedLoading.value = true
} }

View file

@ -1,12 +1,10 @@
package dev.jdtech.jellyfin.viewmodels package dev.jdtech.jellyfin.viewmodels
import android.app.Application
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.preference.PreferenceManager
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dev.jdtech.jellyfin.api.JellyfinApi import dev.jdtech.jellyfin.api.JellyfinApi
import dev.jdtech.jellyfin.database.Server import dev.jdtech.jellyfin.database.Server

View file

@ -64,8 +64,8 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
private val _favorite = MutableLiveData<Boolean>() private val _favorite = MutableLiveData<Boolean>()
val favorite: LiveData<Boolean> = _favorite val favorite: LiveData<Boolean> = _favorite
private val _error = MutableLiveData<Boolean>() private val _error = MutableLiveData<String>()
val error: LiveData<Boolean> = _error val error: LiveData<String> = _error
var playerItems: MutableList<PlayerItem> = mutableListOf() var playerItems: MutableList<PlayerItem> = mutableListOf()
@ -73,7 +73,7 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
val playerItemsError: LiveData<String> = _playerItemsError val playerItemsError: LiveData<String> = _playerItemsError
fun loadData(itemId: UUID, itemType: String) { fun loadData(itemId: UUID, itemType: String) {
_error.value = false _error.value = null
viewModelScope.launch { viewModelScope.launch {
try { try {
_item.value = jellyfinRepository.getItem(itemId) _item.value = jellyfinRepository.getItem(itemId)
@ -96,7 +96,7 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
} }
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
_error.value = true _error.value = e.message
} }
} }
} }

View file

@ -21,8 +21,8 @@ constructor(
private val _finishedLoading = MutableLiveData<Boolean>() private val _finishedLoading = MutableLiveData<Boolean>()
val finishedLoading: LiveData<Boolean> = _finishedLoading val finishedLoading: LiveData<Boolean> = _finishedLoading
private val _error = MutableLiveData<Boolean>() private val _error = MutableLiveData<String>()
val error: LiveData<Boolean> = _error val error: LiveData<String> = _error
init { init {
loadData() loadData()
@ -30,7 +30,7 @@ constructor(
fun loadData() { fun loadData() {
_finishedLoading.value = false _finishedLoading.value = false
_error.value = false _error.value = null
viewModelScope.launch { viewModelScope.launch {
try { try {
val items = jellyfinRepository.getItems() val items = jellyfinRepository.getItems()
@ -43,7 +43,7 @@ constructor(
} }
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
_error.value = true _error.value = e.message
} }
_finishedLoading.value = true _finishedLoading.value = true
} }

View file

@ -26,11 +26,11 @@ constructor(
private val _finishedLoading = MutableLiveData<Boolean>() private val _finishedLoading = MutableLiveData<Boolean>()
val finishedLoading: LiveData<Boolean> = _finishedLoading val finishedLoading: LiveData<Boolean> = _finishedLoading
private val _error = MutableLiveData<Boolean>() private val _error = MutableLiveData<String>()
val error: LiveData<Boolean> = _error val error: LiveData<String> = _error
fun loadData(query: String) { fun loadData(query: String) {
_error.value = false _error.value = null
_finishedLoading.value = false _finishedLoading.value = false
viewModelScope.launch { viewModelScope.launch {
try { try {
@ -74,7 +74,7 @@ constructor(
_sections.value = tempSections _sections.value = tempSections
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
_error.value = true _error.value = e.message
} }
_finishedLoading.value = true _finishedLoading.value = true
} }

View file

@ -24,18 +24,18 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
private val _finishedLoading = MutableLiveData<Boolean>() private val _finishedLoading = MutableLiveData<Boolean>()
val finishedLoading: LiveData<Boolean> = _finishedLoading val finishedLoading: LiveData<Boolean> = _finishedLoading
private val _error = MutableLiveData<Boolean>() private val _error = MutableLiveData<String>()
val error: LiveData<Boolean> = _error val error: LiveData<String> = _error
fun loadEpisodes(seriesId: UUID, seasonId: UUID) { fun loadEpisodes(seriesId: UUID, seasonId: UUID) {
_error.value = false _error.value = null
_finishedLoading.value = false _finishedLoading.value = false
viewModelScope.launch { viewModelScope.launch {
try { try {
_episodes.value = getEpisodes(seriesId, seasonId) _episodes.value = getEpisodes(seriesId, seasonId)
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
_error.value = true _error.value = e.message
} }
_finishedLoading.value = true _finishedLoading.value = true
} }

View file

@ -0,0 +1,28 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorOnBackground">
<path
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="@android:color/white"
android:strokeLineCap="round"/>
<path
android:pathData="M12,8L12,12"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="@android:color/white"
android:strokeLineCap="round"/>
<path
android:pathData="M12,16L12.01,16"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="@android:color/white"
android:strokeLineCap="round"/>
</vector>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -13,4 +13,4 @@
android:background="@color/black" android:background="@color/black"
app:show_subtitle_button="true" /> app:show_subtitle_button="true" />
</FrameLayout> </merge>

View file

@ -190,7 +190,7 @@
android:id="@+id/player_items_error_details" android:id="@+id/player_items_error_details"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/view_details" android:text="@string/view_details_underlined"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="@color/red" /> android:textColor="@color/red" />
</LinearLayout> </LinearLayout>

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:id="@+id/error_panel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="4dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_alert_circle" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/error_loading_data"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/error_details_button"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@string/view_details" />
<Button
android:id="@+id/error_retry_button"
style="@style/Widget.MaterialComponents.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/retry" />
</LinearLayout>
</LinearLayout>

View file

@ -9,12 +9,6 @@
type="dev.jdtech.jellyfin.viewmodels.FavoriteViewModel" /> type="dev.jdtech.jellyfin.viewmodels.FavoriteViewModel" />
</data> </data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.FavoriteFragment">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -30,6 +24,10 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:trackCornerRadius="10dp" /> app:trackCornerRadius="10dp" />
<include
android:id="@+id/error_layout"
layout="@layout/error_panel" />
<TextView <TextView
android:id="@+id/no_favorites_text" android:id="@+id/no_favorites_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -58,6 +56,4 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout> </layout>

View file

@ -10,11 +10,6 @@
type="dev.jdtech.jellyfin.viewmodels.HomeViewModel" /> type="dev.jdtech.jellyfin.viewmodels.HomeViewModel" />
</data> </data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -32,6 +27,8 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:trackCornerRadius="10dp" /> app:trackCornerRadius="10dp" />
<include android:id="@+id/error_layout" layout="@layout/error_panel" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/views_recycler_view" android:id="@+id/views_recycler_view"
android:layout_width="0dp" android:layout_width="0dp"
@ -47,6 +44,5 @@
tools:itemCount="4" tools:itemCount="4"
tools:listitem="@layout/view_item" /> tools:listitem="@layout/view_item" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout> </layout>

View file

@ -10,11 +10,6 @@
type="dev.jdtech.jellyfin.viewmodels.LibraryViewModel" /> type="dev.jdtech.jellyfin.viewmodels.LibraryViewModel" />
</data> </data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -31,6 +26,8 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:trackCornerRadius="10dp" /> app:trackCornerRadius="10dp" />
<include android:id="@+id/error_layout" layout="@layout/error_panel" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/items_recycler_view" android:id="@+id/items_recycler_view"
android:layout_width="0dp" android:layout_width="0dp"
@ -50,7 +47,6 @@
tools:listitem="@layout/base_item" /> tools:listitem="@layout/base_item" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout> </layout>

View file

@ -10,11 +10,6 @@
type="dev.jdtech.jellyfin.viewmodels.MediaViewModel" /> type="dev.jdtech.jellyfin.viewmodels.MediaViewModel" />
</data> </data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -32,6 +27,8 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:trackCornerRadius="10dp" /> app:trackCornerRadius="10dp" />
<include android:id="@+id/error_layout" layout="@layout/error_panel" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/views_recycler_view" android:id="@+id/views_recycler_view"
android:layout_width="0dp" android:layout_width="0dp"
@ -51,5 +48,4 @@
tools:listitem="@layout/collection_item" /> tools:listitem="@layout/collection_item" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout> </layout>

View file

@ -12,15 +12,22 @@
type="dev.jdtech.jellyfin.viewmodels.MediaInfoViewModel" /> type="dev.jdtech.jellyfin.viewmodels.MediaInfoViewModel" />
</data> </data>
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/main_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<include
android:id="@+id/error_layout"
layout="@layout/error_panel" />
<ScrollView <ScrollView
android:id="@+id/media_info_scrollview" android:id="@+id/media_info_scrollview"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="match_parent"> android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -209,7 +216,7 @@
android:id="@+id/player_items_error_details" android:id="@+id/player_items_error_details"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/view_details" android:text="@string/view_details_underlined"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="@color/red" /> android:textColor="@color/red" />
</LinearLayout> </LinearLayout>
@ -432,7 +439,6 @@
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout> </layout>

View file

@ -4,17 +4,12 @@
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<data> <data>
<variable <variable
name="viewModel" name="viewModel"
type="dev.jdtech.jellyfin.viewmodels.SearchResultViewModel" /> type="dev.jdtech.jellyfin.viewmodels.SearchResultViewModel" />
</data> </data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.FavoriteFragment">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -30,16 +25,18 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:trackCornerRadius="10dp" /> app:trackCornerRadius="10dp" />
<include android:id="@+id/error_layout" layout="@layout/error_panel" />
<TextView <TextView
android:id="@+id/no_search_results_text" android:id="@+id/no_search_results_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/no_search_results" android:text="@string/no_search_results"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent" />
android:visibility="gone"/>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/search_results_recycler_view" android:id="@+id/search_results_recycler_view"
@ -47,17 +44,15 @@
android:layout_height="0dp" android:layout_height="0dp"
android:clipToPadding="false" android:clipToPadding="false"
android:paddingTop="16dp" android:paddingTop="16dp"
app:favoriteSections="@{viewModel.sections}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:favoriteSections="@{viewModel.sections}"
tools:itemCount="4" tools:itemCount="4"
tools:listitem="@layout/favorite_section" /> tools:listitem="@layout/favorite_section" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout> </layout>

View file

@ -12,12 +12,6 @@
</data> </data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -33,6 +27,10 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:trackCornerRadius="10dp" /> app:trackCornerRadius="10dp" />
<include
android:id="@+id/error_layout"
layout="@layout/error_panel" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/episodes_recycler_view" android:id="@+id/episodes_recycler_view"
android:layout_width="0dp" android:layout_width="0dp"
@ -47,6 +45,4 @@
tools:itemCount="4" tools:itemCount="4"
tools:listitem="@layout/episode_item" /> tools:listitem="@layout/episode_item" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout> </layout>

View file

@ -50,8 +50,10 @@
<string name="settings_category_appearance">Appearance</string> <string name="settings_category_appearance">Appearance</string>
<string name="theme">Theme</string> <string name="theme">Theme</string>
<string name="error_preparing_player_items">Error preparing player items.</string> <string name="error_preparing_player_items">Error preparing player items.</string>
<string name="view_details"><u>View details</u></string> <string name="view_details">View details</string>
<string name="view_details_underlined"><u>@string/view_details</u></string>
<string name="about">About</string> <string name="about">About</string>
<string name="privacy_policy">Privacy policy</string> <string name="privacy_policy">Privacy policy</string>
<string name="app_info">App info</string> <string name="app_info">App info</string>
<string name="unknown_error">Unknown error</string>
</resources> </resources>