From 795647c2405c1a86067047d6af6accd70c86d065 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 8 Apr 2020 19:44:26 +0200 Subject: [PATCH] fix: handle recordings not yet loaded in event details VM --- .../common/mediadata/MediaRepository.kt | 32 ++++++--------- .../recording/persistence/EventDao.kt | 3 +- .../common/mediadata/network/ApiFactory.kt | 7 ++-- .../common/viewmodel/DetailsViewModel.kt | 41 +++++++++++++++---- .../leanback/detail/EventDetailsFragment.kt | 25 ++++++++--- .../events/EventsGridBrowseFragment.kt | 39 +++++++++--------- .../touch/browse/ConferenceGroupFragment.kt | 4 +- 7 files changed, 91 insertions(+), 60 deletions(-) diff --git a/common/src/main/java/de/nicidienase/chaosflix/common/mediadata/MediaRepository.kt b/common/src/main/java/de/nicidienase/chaosflix/common/mediadata/MediaRepository.kt index 068e3f47..7fc02b95 100644 --- a/common/src/main/java/de/nicidienase/chaosflix/common/mediadata/MediaRepository.kt +++ b/common/src/main/java/de/nicidienase/chaosflix/common/mediadata/MediaRepository.kt @@ -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.LiveEvent import de.nicidienase.chaosflix.common.util.SingleLiveEvent -import java.io.IOException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import retrofit2.Response +import java.io.IOException class MediaRepository( private val recordingApi: RecordingService, @@ -101,25 +101,19 @@ class MediaRepository( } } - fun updateRecordingsForEvent(event: Event): LiveData, String>> { - val updateState = SingleLiveEvent, String>>() - coroutineScope.launch(Dispatchers.IO) { - try { - val eventDto = recordingApi.getEventByGUIDSuspending(event.guid) - val recordingDtos = eventDto?.recordings - if (recordingDtos != null) { - val recordings: List = saveRecordings(event, recordingDtos) - updateState.postValue(LiveEvent(State.DONE, data = recordings)) - } else { - updateState.postValue(LiveEvent(State.DONE, error = "Error updating Recordings for ${event.title}")) - } - } 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() } + suspend fun updateRecordingsForEvent(event: Event): List? { + return try { + val eventDto = recordingApi.getEventByGUIDSuspending(event.guid) + val recordingDtos = eventDto?.recordings + if (recordingDtos != null) { + saveRecordings(event, recordingDtos) + } else { + null + } + } catch (e: Exception) { + e.printStackTrace() + null } - return updateState } suspend fun updateSingleEvent(guid: String): Event? = withContext(Dispatchers.IO) { diff --git a/common/src/main/java/de/nicidienase/chaosflix/common/mediadata/entities/recording/persistence/EventDao.kt b/common/src/main/java/de/nicidienase/chaosflix/common/mediadata/entities/recording/persistence/EventDao.kt index eb417b9d..aebfdd81 100644 --- a/common/src/main/java/de/nicidienase/chaosflix/common/mediadata/entities/recording/persistence/EventDao.kt +++ b/common/src/main/java/de/nicidienase/chaosflix/common/mediadata/entities/recording/persistence/EventDao.kt @@ -77,7 +77,8 @@ abstract class EventDao : BaseDao() { @Query("""SELECT event.*, conference.acronym as conference 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> override suspend fun updateOrInsertInternal(item: Event) { diff --git a/common/src/main/java/de/nicidienase/chaosflix/common/mediadata/network/ApiFactory.kt b/common/src/main/java/de/nicidienase/chaosflix/common/mediadata/network/ApiFactory.kt index ed66087d..d3d6182f 100644 --- a/common/src/main/java/de/nicidienase/chaosflix/common/mediadata/network/ApiFactory.kt +++ b/common/src/main/java/de/nicidienase/chaosflix/common/mediadata/network/ApiFactory.kt @@ -5,14 +5,14 @@ import android.util.Log import com.google.gson.Gson import de.nicidienase.chaosflix.BuildConfig import de.nicidienase.chaosflix.common.SingletonHolder2 -import java.io.File -import java.net.SocketTimeoutException -import java.util.concurrent.TimeUnit import okhttp3.Cache import okhttp3.Interceptor import okhttp3.OkHttpClient import retrofit2.Retrofit 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) { @@ -52,6 +52,7 @@ class ApiFactory private constructor(private val recordingUrl: String, cache: Fi return@Interceptor chain.proceed(requestWithUseragent) } catch (ex: SocketTimeoutException) { Log.e("UserAgentIntercepor", ex.message, ex) + return@Interceptor null } } diff --git a/common/src/main/java/de/nicidienase/chaosflix/common/viewmodel/DetailsViewModel.kt b/common/src/main/java/de/nicidienase/chaosflix/common/viewmodel/DetailsViewModel.kt index 1fe7dc27..d8162e50 100644 --- a/common/src/main/java/de/nicidienase/chaosflix/common/viewmodel/DetailsViewModel.kt +++ b/common/src/main/java/de/nicidienase/chaosflix/common/viewmodel/DetailsViewModel.kt @@ -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.util.LiveEvent import de.nicidienase.chaosflix.common.util.SingleLiveEvent -import java.io.File -import java.util.ArrayList import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import java.io.File +import java.util.* class DetailsViewModel( private val database: ChaosflixDatabase, @@ -30,16 +30,33 @@ class DetailsViewModel( val state: SingleLiveEvent> = SingleLiveEvent() + private var waitingForRecordings = false + val autoselectRecording: Boolean get() = preferencesManager.getAutoselectRecording() fun setEvent(event: Event): LiveData { - 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) } fun getRecordingForEvent(event: Event): LiveData> { - mediaRepository.updateRecordingsForEvent(event) + viewModelScope.launch { + mediaRepository.updateRecordingsForEvent(event) + } return database.recordingDao().findRecordingByEvent(event.id) } @@ -104,10 +121,15 @@ class DetailsViewModel( } else { // select quality then playEvent val items: List = database.recordingDao().findRecordingByEventSync(event.id) - val bundle = Bundle() - bundle.putParcelable(EVENT, event) - bundle.putParcelableArrayList(KEY_SELECT_RECORDINGS, ArrayList(items)) - state.postValue(LiveEvent(State.SelectRecording, data = bundle)) + if(items.isNotEmpty()){ + val bundle = Bundle() + bundle.putParcelable(EVENT, event) + 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, DisplayEvent, PlayExternal, - Error + Error, + Loading } companion object { diff --git a/leanback/src/main/java/de/nicidienase/chaosflix/leanback/detail/EventDetailsFragment.kt b/leanback/src/main/java/de/nicidienase/chaosflix/leanback/detail/EventDetailsFragment.kt index 55b96056..fd0c0bcf 100644 --- a/leanback/src/main/java/de/nicidienase/chaosflix/leanback/detail/EventDetailsFragment.kt +++ b/leanback/src/main/java/de/nicidienase/chaosflix/leanback/detail/EventDetailsFragment.kt @@ -72,6 +72,7 @@ import kotlinx.coroutines.launch class EventDetailsFragment : DetailsSupportFragment() { private var selectDialog: AlertDialog? = null + private var loadingDialog: AlertDialog? = null private val uiScope = CoroutineScope(Dispatchers.Main) @@ -81,8 +82,6 @@ class EventDetailsFragment : DetailsSupportFragment() { private var event: Event? = null private var room: Room? = null - private var currentRecordings: List? = null - private lateinit var rowsAdapter: ArrayObjectAdapter private var relatedEventsAdapter: ArrayObjectAdapter? = null @@ -169,6 +168,8 @@ class EventDetailsFragment : DetailsSupportFragment() { } } DetailsViewModel.State.SelectRecording -> { + loadingDialog?.dismiss() + loadingDialog = null val event: Event? = state.data?.getParcelable(DetailsViewModel.EVENT) val recordings: List? = state.data?.getParcelableArrayList(DetailsViewModel.KEY_SELECT_RECORDINGS) if (event != null && recordings != null && recordings.isNotEmpty()) { @@ -184,14 +185,26 @@ class EventDetailsFragment : DetailsSupportFragment() { DetailsViewModel.State.Error -> { showError(state.error) } - else -> { - // Download - Log.e(TAG, "Case not relevant for leanback UI, this should not happen") + DetailsViewModel.State.Loading -> { + showLoadingDialog() } + 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?) { if (errorMessage != null && errorMessage.isNotBlank()) { view?.let { @@ -315,7 +328,7 @@ class EventDetailsFragment : DetailsSupportFragment() { } override fun onLoadFailed(errorDrawable: Drawable?) { - detailsOverview.setImageDrawable(ContextCompat.getDrawable(requireContext(), DEFAULT_DRAWABLE)) + detailsOverview.imageDrawable = ContextCompat.getDrawable(requireContext(), DEFAULT_DRAWABLE) } }) } diff --git a/leanback/src/main/java/de/nicidienase/chaosflix/leanback/events/EventsGridBrowseFragment.kt b/leanback/src/main/java/de/nicidienase/chaosflix/leanback/events/EventsGridBrowseFragment.kt index 3e19864f..c7b85b06 100644 --- a/leanback/src/main/java/de/nicidienase/chaosflix/leanback/events/EventsGridBrowseFragment.kt +++ b/leanback/src/main/java/de/nicidienase/chaosflix/leanback/events/EventsGridBrowseFragment.kt @@ -30,11 +30,10 @@ import java.util.Timer import java.util.TimerTask class EventsGridBrowseFragment : VerticalGridSupportFragment(), EventsActivity.EventsFragment { - private val NUM_COLUMNS = 4 private val handler = Handler() private val rowsAdapter: ArrayObjectAdapter = - ArrayObjectAdapter(CardPresenter(R.style.EventGridCardStyle)) + ArrayObjectAdapter(CardPresenter(R.style.EventGridCardStyle)) private lateinit var defaultBackground: Drawable private var metrics: DisplayMetrics? = null private var backgroundTimer: Timer? = null @@ -49,10 +48,8 @@ class EventsGridBrowseFragment : VerticalGridSupportFragment(), EventsActivity.E defaultBackground = this } - val conference: Conference? = arguments?.getParcelable(EventsRowsBrowseFragment.CONFERENCE) - if (conference == null) { - throw IllegalStateException("No conference passed") - } + val conference: Conference = arguments?.getParcelable(EventsRowsBrowseFragment.CONFERENCE) + ?: throw IllegalStateException("No conference passed") loadImage(conference.logoUrl, this::setBadgeDrawable) title = conference.title @@ -91,16 +88,16 @@ class EventsGridBrowseFragment : VerticalGridSupportFragment(), EventsActivity.E options.centerCrop() Glide.with(this) - .load(url) - .apply(options) - .into(object : SimpleTarget() { - override fun onResourceReady( - resource: Drawable, - transition: Transition? - ) { - consumer.invoke(resource) - } - }) + .load(url) + .apply(options) + .into(object : SimpleTarget() { + override fun onResourceReady( + resource: Drawable, + transition: Transition? + ) { + consumer.invoke(resource) + } + }) } protected fun updateBackground(uri: String) { @@ -120,10 +117,10 @@ class EventsGridBrowseFragment : VerticalGridSupportFragment(), EventsActivity.E private inner class ItemViewSelectedListener : OnItemViewSelectedListener { override fun onItemSelected( - itemViewHolder: Presenter.ViewHolder, - item: Any, - rowViewHolder: RowPresenter.ViewHolder, - row: Row + itemViewHolder: Presenter.ViewHolder, + item: Any, + rowViewHolder: RowPresenter.ViewHolder, + row: Row ) { if (item is Event) { try { @@ -157,6 +154,8 @@ class EventsGridBrowseFragment : VerticalGridSupportFragment(), EventsActivity.E private const val BACKGROUND_UPDATE_DELAY = 300 private const val CONFERENCE = "conference" + private const val NUM_COLUMNS = 4 + fun create(conference: Conference): EventsGridBrowseFragment { return EventsGridBrowseFragment().apply { arguments = Bundle().apply { putParcelable(CONFERENCE, conference) } diff --git a/touch/src/main/java/de/nicidienase/chaosflix/touch/browse/ConferenceGroupFragment.kt b/touch/src/main/java/de/nicidienase/chaosflix/touch/browse/ConferenceGroupFragment.kt index 4953ff59..923256da 100644 --- a/touch/src/main/java/de/nicidienase/chaosflix/touch/browse/ConferenceGroupFragment.kt +++ b/touch/src/main/java/de/nicidienase/chaosflix/touch/browse/ConferenceGroupFragment.kt @@ -34,7 +34,7 @@ class ConferenceGroupFragment : BrowseFragment() { StaggeredGridLayoutManager(columnCount, StaggeredGridLayoutManager.VERTICAL) } view.layoutManager = layoutManager - val conferencesAdapter: ConferenceRecyclerViewAdapter = ConferenceRecyclerViewAdapter(listener) + val conferencesAdapter = ConferenceRecyclerViewAdapter(listener) conferencesAdapter.setHasStableIds(true) view.adapter = conferencesAdapter val groupId = conferenceGroup?.id @@ -45,7 +45,7 @@ class ConferenceGroupFragment : BrowseFragment() { setLoadingOverlayVisibility(false) } conferencesAdapter.conferences = conferenceList - val layoutState = arguments?.getParcelable(LAYOUTMANAGER_STATE)?.let { + arguments?.getParcelable(LAYOUTMANAGER_STATE)?.let { layoutManager?.onRestoreInstanceState(it) } }