mirror of
https://github.com/NiciDieNase/chaosflix
synced 2024-11-22 20:33:05 +00:00
fix: handle recordings not yet loaded in event details VM
This commit is contained in:
parent
c0b32e560a
commit
795647c240
7 changed files with 91 additions and 60 deletions
|
@ -23,13 +23,13 @@ import de.nicidienase.chaosflix.common.userdata.entities.watchlist.WatchlistItem
|
||||||
import de.nicidienase.chaosflix.common.util.ConferenceUtil
|
import de.nicidienase.chaosflix.common.util.ConferenceUtil
|
||||||
import de.nicidienase.chaosflix.common.util.LiveEvent
|
import de.nicidienase.chaosflix.common.util.LiveEvent
|
||||||
import de.nicidienase.chaosflix.common.util.SingleLiveEvent
|
import de.nicidienase.chaosflix.common.util.SingleLiveEvent
|
||||||
import java.io.IOException
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
class MediaRepository(
|
class MediaRepository(
|
||||||
private val recordingApi: RecordingService,
|
private val recordingApi: RecordingService,
|
||||||
|
@ -101,25 +101,19 @@ class MediaRepository(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateRecordingsForEvent(event: Event): LiveData<LiveEvent<State, List<Recording>, String>> {
|
suspend fun updateRecordingsForEvent(event: Event): List<Recording>? {
|
||||||
val updateState = SingleLiveEvent<LiveEvent<State, List<Recording>, String>>()
|
return try {
|
||||||
coroutineScope.launch(Dispatchers.IO) {
|
val eventDto = recordingApi.getEventByGUIDSuspending(event.guid)
|
||||||
try {
|
val recordingDtos = eventDto?.recordings
|
||||||
val eventDto = recordingApi.getEventByGUIDSuspending(event.guid)
|
if (recordingDtos != null) {
|
||||||
val recordingDtos = eventDto?.recordings
|
saveRecordings(event, recordingDtos)
|
||||||
if (recordingDtos != null) {
|
} else {
|
||||||
val recordings: List<Recording> = saveRecordings(event, recordingDtos)
|
null
|
||||||
updateState.postValue(LiveEvent(State.DONE, data = recordings))
|
}
|
||||||
} else {
|
} catch (e: Exception) {
|
||||||
updateState.postValue(LiveEvent(State.DONE, error = "Error updating Recordings for ${event.title}"))
|
e.printStackTrace()
|
||||||
}
|
null
|
||||||
} catch (e: IOException) {
|
|
||||||
updateState.postValue(LiveEvent(State.DONE, error = e.message))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
updateState.postValue(LiveEvent(State.DONE, error = "Error updating Recordings for ${event.title} (${e.cause})"))
|
|
||||||
e.printStackTrace() }
|
|
||||||
}
|
}
|
||||||
return updateState
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateSingleEvent(guid: String): Event? = withContext(Dispatchers.IO) {
|
suspend fun updateSingleEvent(guid: String): Event? = withContext(Dispatchers.IO) {
|
||||||
|
|
|
@ -77,7 +77,8 @@ abstract class EventDao : BaseDao<Event>() {
|
||||||
|
|
||||||
@Query("""SELECT event.*, conference.acronym as conference
|
@Query("""SELECT event.*, conference.acronym as conference
|
||||||
FROM event JOIN conference ON event.conferenceId=conference.id
|
FROM event JOIN conference ON event.conferenceId=conference.id
|
||||||
WHERE conference.id = :confernceId""")
|
WHERE conference.id = :confernceId
|
||||||
|
ORDER BY event.title""")
|
||||||
abstract fun getEventsWithConferenceForConfernce(confernceId: Long): LiveData<List<Event>>
|
abstract fun getEventsWithConferenceForConfernce(confernceId: Long): LiveData<List<Event>>
|
||||||
|
|
||||||
override suspend fun updateOrInsertInternal(item: Event) {
|
override suspend fun updateOrInsertInternal(item: Event) {
|
||||||
|
|
|
@ -5,14 +5,14 @@ import android.util.Log
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import de.nicidienase.chaosflix.BuildConfig
|
import de.nicidienase.chaosflix.BuildConfig
|
||||||
import de.nicidienase.chaosflix.common.SingletonHolder2
|
import de.nicidienase.chaosflix.common.SingletonHolder2
|
||||||
import java.io.File
|
|
||||||
import java.net.SocketTimeoutException
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import okhttp3.Cache
|
import okhttp3.Cache
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
import java.io.File
|
||||||
|
import java.net.SocketTimeoutException
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class ApiFactory private constructor(private val recordingUrl: String, cache: File? = null) {
|
class ApiFactory private constructor(private val recordingUrl: String, cache: File? = null) {
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ class ApiFactory private constructor(private val recordingUrl: String, cache: Fi
|
||||||
return@Interceptor chain.proceed(requestWithUseragent)
|
return@Interceptor chain.proceed(requestWithUseragent)
|
||||||
} catch (ex: SocketTimeoutException) {
|
} catch (ex: SocketTimeoutException) {
|
||||||
Log.e("UserAgentIntercepor", ex.message, ex)
|
Log.e("UserAgentIntercepor", ex.message, ex)
|
||||||
|
|
||||||
return@Interceptor null
|
return@Interceptor null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,10 @@ import de.nicidienase.chaosflix.common.mediadata.entities.recording.persistence.
|
||||||
import de.nicidienase.chaosflix.common.userdata.entities.watchlist.WatchlistItem
|
import de.nicidienase.chaosflix.common.userdata.entities.watchlist.WatchlistItem
|
||||||
import de.nicidienase.chaosflix.common.util.LiveEvent
|
import de.nicidienase.chaosflix.common.util.LiveEvent
|
||||||
import de.nicidienase.chaosflix.common.util.SingleLiveEvent
|
import de.nicidienase.chaosflix.common.util.SingleLiveEvent
|
||||||
import java.io.File
|
|
||||||
import java.util.ArrayList
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class DetailsViewModel(
|
class DetailsViewModel(
|
||||||
private val database: ChaosflixDatabase,
|
private val database: ChaosflixDatabase,
|
||||||
|
@ -30,16 +30,33 @@ class DetailsViewModel(
|
||||||
val state: SingleLiveEvent<LiveEvent<State, Bundle, String>> =
|
val state: SingleLiveEvent<LiveEvent<State, Bundle, String>> =
|
||||||
SingleLiveEvent()
|
SingleLiveEvent()
|
||||||
|
|
||||||
|
private var waitingForRecordings = false
|
||||||
|
|
||||||
val autoselectRecording: Boolean
|
val autoselectRecording: Boolean
|
||||||
get() = preferencesManager.getAutoselectRecording()
|
get() = preferencesManager.getAutoselectRecording()
|
||||||
|
|
||||||
fun setEvent(event: Event): LiveData<Event?> {
|
fun setEvent(event: Event): LiveData<Event?> {
|
||||||
mediaRepository.updateRecordingsForEvent(event)
|
viewModelScope.launch {
|
||||||
|
val recordings = mediaRepository.updateRecordingsForEvent(event)
|
||||||
|
if(waitingForRecordings){
|
||||||
|
if(recordings != null){
|
||||||
|
waitingForRecordings = false
|
||||||
|
val bundle = Bundle()
|
||||||
|
bundle.putParcelable(EVENT, event)
|
||||||
|
bundle.putParcelableArrayList(KEY_SELECT_RECORDINGS, ArrayList(recordings))
|
||||||
|
state.postValue(LiveEvent(State.SelectRecording, data = bundle))
|
||||||
|
} else {
|
||||||
|
state.postValue(LiveEvent(State.Error, error = "Could not load recordings."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return database.eventDao().findEventByGuid(event.guid)
|
return database.eventDao().findEventByGuid(event.guid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getRecordingForEvent(event: Event): LiveData<List<Recording>> {
|
fun getRecordingForEvent(event: Event): LiveData<List<Recording>> {
|
||||||
mediaRepository.updateRecordingsForEvent(event)
|
viewModelScope.launch {
|
||||||
|
mediaRepository.updateRecordingsForEvent(event)
|
||||||
|
}
|
||||||
return database.recordingDao().findRecordingByEvent(event.id)
|
return database.recordingDao().findRecordingByEvent(event.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,10 +121,15 @@ class DetailsViewModel(
|
||||||
} else {
|
} else {
|
||||||
// select quality then playEvent
|
// select quality then playEvent
|
||||||
val items: List<Recording> = database.recordingDao().findRecordingByEventSync(event.id)
|
val items: List<Recording> = database.recordingDao().findRecordingByEventSync(event.id)
|
||||||
val bundle = Bundle()
|
if(items.isNotEmpty()){
|
||||||
bundle.putParcelable(EVENT, event)
|
val bundle = Bundle()
|
||||||
bundle.putParcelableArrayList(KEY_SELECT_RECORDINGS, ArrayList(items))
|
bundle.putParcelable(EVENT, event)
|
||||||
state.postValue(LiveEvent(State.SelectRecording, data = bundle))
|
bundle.putParcelableArrayList(KEY_SELECT_RECORDINGS, ArrayList(items))
|
||||||
|
state.postValue(LiveEvent(State.SelectRecording, data = bundle))
|
||||||
|
} else {
|
||||||
|
state.postValue(LiveEvent(State.Loading))
|
||||||
|
waitingForRecordings = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +186,8 @@ class DetailsViewModel(
|
||||||
DownloadRecording,
|
DownloadRecording,
|
||||||
DisplayEvent,
|
DisplayEvent,
|
||||||
PlayExternal,
|
PlayExternal,
|
||||||
Error
|
Error,
|
||||||
|
Loading
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -72,6 +72,7 @@ import kotlinx.coroutines.launch
|
||||||
class EventDetailsFragment : DetailsSupportFragment() {
|
class EventDetailsFragment : DetailsSupportFragment() {
|
||||||
|
|
||||||
private var selectDialog: AlertDialog? = null
|
private var selectDialog: AlertDialog? = null
|
||||||
|
private var loadingDialog: AlertDialog? = null
|
||||||
|
|
||||||
private val uiScope = CoroutineScope(Dispatchers.Main)
|
private val uiScope = CoroutineScope(Dispatchers.Main)
|
||||||
|
|
||||||
|
@ -81,8 +82,6 @@ class EventDetailsFragment : DetailsSupportFragment() {
|
||||||
private var event: Event? = null
|
private var event: Event? = null
|
||||||
private var room: Room? = null
|
private var room: Room? = null
|
||||||
|
|
||||||
private var currentRecordings: List<Recording>? = null
|
|
||||||
|
|
||||||
private lateinit var rowsAdapter: ArrayObjectAdapter
|
private lateinit var rowsAdapter: ArrayObjectAdapter
|
||||||
|
|
||||||
private var relatedEventsAdapter: ArrayObjectAdapter? = null
|
private var relatedEventsAdapter: ArrayObjectAdapter? = null
|
||||||
|
@ -169,6 +168,8 @@ class EventDetailsFragment : DetailsSupportFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DetailsViewModel.State.SelectRecording -> {
|
DetailsViewModel.State.SelectRecording -> {
|
||||||
|
loadingDialog?.dismiss()
|
||||||
|
loadingDialog = null
|
||||||
val event: Event? = state.data?.getParcelable(DetailsViewModel.EVENT)
|
val event: Event? = state.data?.getParcelable(DetailsViewModel.EVENT)
|
||||||
val recordings: List<Recording>? = state.data?.getParcelableArrayList(DetailsViewModel.KEY_SELECT_RECORDINGS)
|
val recordings: List<Recording>? = state.data?.getParcelableArrayList(DetailsViewModel.KEY_SELECT_RECORDINGS)
|
||||||
if (event != null && recordings != null && recordings.isNotEmpty()) {
|
if (event != null && recordings != null && recordings.isNotEmpty()) {
|
||||||
|
@ -184,14 +185,26 @@ class EventDetailsFragment : DetailsSupportFragment() {
|
||||||
DetailsViewModel.State.Error -> {
|
DetailsViewModel.State.Error -> {
|
||||||
showError(state.error)
|
showError(state.error)
|
||||||
}
|
}
|
||||||
else -> {
|
DetailsViewModel.State.Loading -> {
|
||||||
// Download
|
showLoadingDialog()
|
||||||
Log.e(TAG, "Case not relevant for leanback UI, this should not happen")
|
|
||||||
}
|
}
|
||||||
|
DetailsViewModel.State.PlayOfflineItem -> irrelevantCase()
|
||||||
|
DetailsViewModel.State.DownloadRecording -> irrelevantCase()
|
||||||
|
DetailsViewModel.State.PlayExternal -> irrelevantCase()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showLoadingDialog() {
|
||||||
|
loadingDialog = AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("Loading Recordings")
|
||||||
|
.create().apply { show() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun irrelevantCase() {
|
||||||
|
Log.e(TAG, "Case not relevant for leanback UI, this should not happen")
|
||||||
|
}
|
||||||
|
|
||||||
private fun showError(errorMessage: String?) {
|
private fun showError(errorMessage: String?) {
|
||||||
if (errorMessage != null && errorMessage.isNotBlank()) {
|
if (errorMessage != null && errorMessage.isNotBlank()) {
|
||||||
view?.let {
|
view?.let {
|
||||||
|
@ -315,7 +328,7 @@ class EventDetailsFragment : DetailsSupportFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||||
detailsOverview.setImageDrawable(ContextCompat.getDrawable(requireContext(), DEFAULT_DRAWABLE))
|
detailsOverview.imageDrawable = ContextCompat.getDrawable(requireContext(), DEFAULT_DRAWABLE)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,10 @@ import java.util.Timer
|
||||||
import java.util.TimerTask
|
import java.util.TimerTask
|
||||||
|
|
||||||
class EventsGridBrowseFragment : VerticalGridSupportFragment(), EventsActivity.EventsFragment {
|
class EventsGridBrowseFragment : VerticalGridSupportFragment(), EventsActivity.EventsFragment {
|
||||||
private val NUM_COLUMNS = 4
|
|
||||||
|
|
||||||
private val handler = Handler()
|
private val handler = Handler()
|
||||||
private val rowsAdapter: ArrayObjectAdapter =
|
private val rowsAdapter: ArrayObjectAdapter =
|
||||||
ArrayObjectAdapter(CardPresenter(R.style.EventGridCardStyle))
|
ArrayObjectAdapter(CardPresenter(R.style.EventGridCardStyle))
|
||||||
private lateinit var defaultBackground: Drawable
|
private lateinit var defaultBackground: Drawable
|
||||||
private var metrics: DisplayMetrics? = null
|
private var metrics: DisplayMetrics? = null
|
||||||
private var backgroundTimer: Timer? = null
|
private var backgroundTimer: Timer? = null
|
||||||
|
@ -49,10 +48,8 @@ class EventsGridBrowseFragment : VerticalGridSupportFragment(), EventsActivity.E
|
||||||
defaultBackground = this
|
defaultBackground = this
|
||||||
}
|
}
|
||||||
|
|
||||||
val conference: Conference? = arguments?.getParcelable(EventsRowsBrowseFragment.CONFERENCE)
|
val conference: Conference = arguments?.getParcelable(EventsRowsBrowseFragment.CONFERENCE)
|
||||||
if (conference == null) {
|
?: throw IllegalStateException("No conference passed")
|
||||||
throw IllegalStateException("No conference passed")
|
|
||||||
}
|
|
||||||
|
|
||||||
loadImage(conference.logoUrl, this::setBadgeDrawable)
|
loadImage(conference.logoUrl, this::setBadgeDrawable)
|
||||||
title = conference.title
|
title = conference.title
|
||||||
|
@ -91,16 +88,16 @@ class EventsGridBrowseFragment : VerticalGridSupportFragment(), EventsActivity.E
|
||||||
options.centerCrop()
|
options.centerCrop()
|
||||||
|
|
||||||
Glide.with(this)
|
Glide.with(this)
|
||||||
.load(url)
|
.load(url)
|
||||||
.apply(options)
|
.apply(options)
|
||||||
.into(object : SimpleTarget<Drawable>() {
|
.into(object : SimpleTarget<Drawable>() {
|
||||||
override fun onResourceReady(
|
override fun onResourceReady(
|
||||||
resource: Drawable,
|
resource: Drawable,
|
||||||
transition: Transition<in Drawable>?
|
transition: Transition<in Drawable>?
|
||||||
) {
|
) {
|
||||||
consumer.invoke(resource)
|
consumer.invoke(resource)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun updateBackground(uri: String) {
|
protected fun updateBackground(uri: String) {
|
||||||
|
@ -120,10 +117,10 @@ class EventsGridBrowseFragment : VerticalGridSupportFragment(), EventsActivity.E
|
||||||
|
|
||||||
private inner class ItemViewSelectedListener : OnItemViewSelectedListener {
|
private inner class ItemViewSelectedListener : OnItemViewSelectedListener {
|
||||||
override fun onItemSelected(
|
override fun onItemSelected(
|
||||||
itemViewHolder: Presenter.ViewHolder,
|
itemViewHolder: Presenter.ViewHolder,
|
||||||
item: Any,
|
item: Any,
|
||||||
rowViewHolder: RowPresenter.ViewHolder,
|
rowViewHolder: RowPresenter.ViewHolder,
|
||||||
row: Row
|
row: Row
|
||||||
) {
|
) {
|
||||||
if (item is Event) {
|
if (item is Event) {
|
||||||
try {
|
try {
|
||||||
|
@ -157,6 +154,8 @@ class EventsGridBrowseFragment : VerticalGridSupportFragment(), EventsActivity.E
|
||||||
private const val BACKGROUND_UPDATE_DELAY = 300
|
private const val BACKGROUND_UPDATE_DELAY = 300
|
||||||
private const val CONFERENCE = "conference"
|
private const val CONFERENCE = "conference"
|
||||||
|
|
||||||
|
private const val NUM_COLUMNS = 4
|
||||||
|
|
||||||
fun create(conference: Conference): EventsGridBrowseFragment {
|
fun create(conference: Conference): EventsGridBrowseFragment {
|
||||||
return EventsGridBrowseFragment().apply {
|
return EventsGridBrowseFragment().apply {
|
||||||
arguments = Bundle().apply { putParcelable(CONFERENCE, conference) }
|
arguments = Bundle().apply { putParcelable(CONFERENCE, conference) }
|
||||||
|
|
|
@ -34,7 +34,7 @@ class ConferenceGroupFragment : BrowseFragment() {
|
||||||
StaggeredGridLayoutManager(columnCount, StaggeredGridLayoutManager.VERTICAL)
|
StaggeredGridLayoutManager(columnCount, StaggeredGridLayoutManager.VERTICAL)
|
||||||
}
|
}
|
||||||
view.layoutManager = layoutManager
|
view.layoutManager = layoutManager
|
||||||
val conferencesAdapter: ConferenceRecyclerViewAdapter = ConferenceRecyclerViewAdapter(listener)
|
val conferencesAdapter = ConferenceRecyclerViewAdapter(listener)
|
||||||
conferencesAdapter.setHasStableIds(true)
|
conferencesAdapter.setHasStableIds(true)
|
||||||
view.adapter = conferencesAdapter
|
view.adapter = conferencesAdapter
|
||||||
val groupId = conferenceGroup?.id
|
val groupId = conferenceGroup?.id
|
||||||
|
@ -45,7 +45,7 @@ class ConferenceGroupFragment : BrowseFragment() {
|
||||||
setLoadingOverlayVisibility(false)
|
setLoadingOverlayVisibility(false)
|
||||||
}
|
}
|
||||||
conferencesAdapter.conferences = conferenceList
|
conferencesAdapter.conferences = conferenceList
|
||||||
val layoutState = arguments?.getParcelable<Parcelable>(LAYOUTMANAGER_STATE)?.let {
|
arguments?.getParcelable<Parcelable>(LAYOUTMANAGER_STATE)?.let {
|
||||||
layoutManager?.onRestoreInstanceState(it)
|
layoutManager?.onRestoreInstanceState(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue