AquaStream/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt

784 lines
30 KiB
Kotlin
Raw Normal View History

2022-08-04 22:26:33 +00:00
package com.lagradost.cloudstream3.ui.result
import android.animation.Animator
2023-07-18 20:18:14 +00:00
import android.annotation.SuppressLint
2022-08-06 18:51:32 +00:00
import android.app.Dialog
2022-08-05 23:41:35 +00:00
import android.os.Bundle
2023-07-18 01:55:00 +00:00
import android.view.LayoutInflater
2022-08-05 23:41:35 +00:00
import android.view.View
2023-07-18 01:55:00 +00:00
import android.view.ViewGroup
import android.view.animation.DecelerateInterpolator
2023-07-18 20:18:14 +00:00
import androidx.appcompat.app.AlertDialog
2022-08-05 23:41:35 +00:00
import androidx.core.view.isGone
import androidx.core.view.isVisible
2023-07-27 19:47:42 +00:00
import androidx.core.widget.NestedScrollView
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
2022-08-05 23:41:35 +00:00
import androidx.recyclerview.widget.RecyclerView
2022-08-06 18:51:32 +00:00
import com.google.android.material.bottomsheet.BottomSheetDialog
2022-12-23 21:53:51 +00:00
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
2022-08-05 23:41:35 +00:00
import com.lagradost.cloudstream3.DubStatus
2022-12-23 21:53:51 +00:00
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
2022-08-04 22:26:33 +00:00
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchResponse
2023-07-18 01:55:00 +00:00
import com.lagradost.cloudstream3.databinding.FragmentResultTvBinding
import com.lagradost.cloudstream3.mvvm.Resource
2022-08-05 23:41:35 +00:00
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
2023-07-18 01:55:00 +00:00
import com.lagradost.cloudstream3.ui.WatchType
2023-07-18 20:18:14 +00:00
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup
2022-12-23 21:53:51 +00:00
import com.lagradost.cloudstream3.ui.player.ExtractorLinkGenerator
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
import com.lagradost.cloudstream3.ui.result.ResultFragment.getStoredData
import com.lagradost.cloudstream3.ui.result.ResultFragment.updateUIEvent
2023-07-28 02:18:28 +00:00
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_FOCUSED
2022-08-05 23:41:35 +00:00
import com.lagradost.cloudstream3.ui.search.SearchAdapter
import com.lagradost.cloudstream3.ui.search.SearchHelper
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
2023-07-18 20:18:14 +00:00
import com.lagradost.cloudstream3.utils.AppUtils.html
2023-07-27 19:47:42 +00:00
import com.lagradost.cloudstream3.utils.AppUtils.isRtl
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
2023-07-18 01:55:00 +00:00
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
2022-08-06 18:51:32 +00:00
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
2023-07-18 20:18:14 +00:00
import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
2022-08-06 18:51:32 +00:00
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
2022-12-23 21:53:51 +00:00
import com.lagradost.cloudstream3.utils.UIHelper.navigate
2023-07-18 20:18:14 +00:00
import com.lagradost.cloudstream3.utils.UIHelper.setImage
2022-08-04 22:26:33 +00:00
class ResultFragmentTv : Fragment() {
protected lateinit var viewModel: ResultViewModel2
2023-07-18 01:55:00 +00:00
private var binding: FragmentResultTvBinding? = null
override fun onDestroyView() {
binding = null
updateUIEvent -= ::updateUI
2023-07-18 01:55:00 +00:00
super.onDestroyView()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel =
ViewModelProvider(this)[ResultViewModel2::class.java]
viewModel.EPISODE_RANGE_SIZE = 50
updateUIEvent += ::updateUI
val localBinding = FragmentResultTvBinding.inflate(inflater, container, false)
binding = localBinding
return localBinding.root
}
2023-07-18 01:55:00 +00:00
2023-07-19 18:37:57 +00:00
private fun updateUI(id: Int?) {
viewModel.reloadEpisodes()
2023-07-18 01:55:00 +00:00
}
2022-08-06 16:08:20 +00:00
private var currentRecommendations: List<SearchResponse> = emptyList()
2022-08-05 23:41:35 +00:00
private fun handleSelection(data: Any) {
when (data) {
is EpisodeRange -> {
viewModel.changeRange(data)
}
2023-07-18 01:55:00 +00:00
2022-08-05 23:41:35 +00:00
is Int -> {
viewModel.changeSeason(data)
}
2023-07-18 01:55:00 +00:00
2022-08-05 23:41:35 +00:00
is DubStatus -> {
viewModel.changeDubStatus(data)
}
2023-07-18 01:55:00 +00:00
2022-08-06 16:08:20 +00:00
is String -> {
setRecommendations(currentRecommendations, data)
}
2022-08-05 23:41:35 +00:00
}
}
private fun RecyclerView?.select(index: Int) {
(this?.adapter as? SelectAdaptor?)?.select(index, this)
}
private fun RecyclerView?.update(data: List<SelectData>) {
(this?.adapter as? SelectAdaptor?)?.updateSelectionList(data)
this?.isVisible = data.size > 1
}
private fun RecyclerView?.setAdapter() {
this?.adapter = SelectAdaptor { data ->
handleSelection(data)
}
}
private fun hasNoFocus(): Boolean {
val focus = activity?.currentFocus
if (focus == null || !focus.isVisible) return true
2023-07-18 01:55:00 +00:00
return focus == binding?.resultRoot
2022-08-05 23:41:35 +00:00
}
2023-07-18 20:18:14 +00:00
private fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
2022-08-06 16:08:20 +00:00
currentRecommendations = rec ?: emptyList()
2022-08-05 23:41:35 +00:00
val isInvalid = rec.isNullOrEmpty()
2023-07-18 01:55:00 +00:00
binding?.apply {
resultRecommendationsList.isGone = isInvalid
resultRecommendationsHolder.isGone = isInvalid
val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName
(resultRecommendationsList.adapter as? SearchAdapter)?.updateList(rec?.filter { it.apiName == matchAgainst }
?: emptyList())
rec?.map { it.apiName }?.distinct()?.let { apiNames ->
// very dirty selection
resultRecommendationsFilterSelection.isVisible = apiNames.size > 1
resultRecommendationsFilterSelection.update(apiNames.map { txt(it) to it })
resultRecommendationsFilterSelection.select(apiNames.indexOf(matchAgainst))
} ?: run {
resultRecommendationsFilterSelection.isVisible = false
}
2022-08-05 23:41:35 +00:00
}
}
2022-08-06 18:51:32 +00:00
var loadingDialog: Dialog? = null
var popupDialog: Dialog? = null
2023-07-18 20:18:14 +00:00
2023-07-19 18:37:57 +00:00
private fun reloadViewModel(forceReload: Boolean) {
if (!viewModel.hasLoaded() || forceReload) {
val storedData = getStoredData() ?: return
viewModel.load(
activity,
storedData.url,
storedData.apiName,
storedData.showFillers,
storedData.dubStatus,
storedData.start
)
}
}
override fun onResume() {
activity?.let {
it.window?.navigationBarColor =
it.colorFromAttribute(R.attr.primaryBlackBackground)
}
afterPluginsLoadedEvent += ::reloadViewModel
super.onResume()
}
override fun onStop() {
afterPluginsLoadedEvent -= ::reloadViewModel
super.onStop()
}
private fun View.fade(turnVisible: Boolean) {
if (turnVisible) {
isVisible = true
}
this.animate().alpha(if (turnVisible) 1.0f else 0.0f).apply {
duration = 200
interpolator = DecelerateInterpolator()
setListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator) {
}
override fun onAnimationEnd(animation: Animator) {
this@fade.isVisible = turnVisible
}
override fun onAnimationCancel(animation: Animator) {
}
override fun onAnimationRepeat(animation: Animator) {
}
})
}
2023-08-02 00:13:30 +00:00
this.animate().translationX(if (turnVisible) 0f else if (isRtl()) -100.0f else 100f).apply {
duration = 200
interpolator = DecelerateInterpolator()
}
}
2023-07-23 18:10:21 +00:00
private fun toggleEpisodes(show: Boolean) {
binding?.apply {
episodesShadow.fade(show)
episodeHolderTv.fade(show)
2023-08-02 00:13:30 +00:00
if (episodesShadow.isRtl()) {
2023-07-28 02:18:28 +00:00
episodesShadow.scaleX = -1.0f
episodesShadow.scaleY = -1.0f
} else {
episodesShadow.scaleX = 1.0f
episodesShadow.scaleY = 1.0f
}
2023-07-23 18:10:21 +00:00
}
}
2023-07-18 20:18:14 +00:00
@SuppressLint("SetTextI18n")
2022-08-05 23:41:35 +00:00
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// ===== setup =====
val storedData = getStoredData() ?: return
activity?.window?.decorView?.clearFocus()
activity?.loadCache()
hideKeyboard()
if (storedData.restart || !viewModel.hasLoaded())
viewModel.load(
activity,
storedData.url,
storedData.apiName,
storedData.showFillers,
storedData.dubStatus,
storedData.start
)
// ===== ===== =====
2023-07-18 01:55:00 +00:00
binding?.apply {
2023-07-28 02:18:28 +00:00
//episodesShadow.rotationX = 180.0f//if(episodesShadow.isRtl()) 180.0f else 0.0f
2023-07-23 18:10:21 +00:00
val leftListener: View.OnFocusChangeListener =
View.OnFocusChangeListener { _, hasFocus ->
if (!hasFocus) return@OnFocusChangeListener
toggleEpisodes(false)
}
val rightListener: View.OnFocusChangeListener =
View.OnFocusChangeListener { _, hasFocus ->
if (!hasFocus) return@OnFocusChangeListener
toggleEpisodes(true)
}
resultPlayMovie.onFocusChangeListener = leftListener
resultPlaySeries.onFocusChangeListener = leftListener
resultResumeSeries.onFocusChangeListener = leftListener
resultPlayTrailer.onFocusChangeListener = leftListener
resultEpisodesShow.onFocusChangeListener = rightListener
resultDescription.onFocusChangeListener = leftListener
resultBookmarkButton.onFocusChangeListener = leftListener
2023-07-27 19:47:42 +00:00
resultEpisodesShow.setOnClickListener {
// toggle, to make it more touch accessable just in case someone thinks that a
// tv layout is better but is using a touch device
toggleEpisodes(!episodeHolderTv.isVisible)
}
2023-08-02 00:13:30 +00:00
// resultEpisodes.onFocusChangeListener = leftListener
2023-07-28 02:18:28 +00:00
2023-07-23 18:10:21 +00:00
redirectToPlay.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) return@setOnFocusChangeListener
toggleEpisodes(false)
binding?.apply {
val views = listOf(
resultPlayMovie,
resultPlaySeries,
resultResumeSeries,
resultPlayTrailer,
resultBookmarkButton
)
for (requestView in views) {
if (!requestView.isVisible) continue
if (requestView.requestFocus()) break
}
}
}
2023-07-27 19:47:42 +00:00
// parallax on background
resultFinishLoading.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
backgroundPosterHolder.translationY = -scrollY.toFloat() * 0.8f
})
2023-07-23 18:10:21 +00:00
redirectToEpisodes.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) return@setOnFocusChangeListener
toggleEpisodes(true)
binding?.apply {
val views = listOf(
resultSeasonSelection,
resultRangeSelection,
resultDubSelection,
2023-08-02 00:13:30 +00:00
resultEpisodes,
2023-07-23 18:10:21 +00:00
resultPlayTrailer,
)
for (requestView in views) {
2023-07-31 13:35:42 +00:00
if (!requestView.isShown) continue
if (requestView.requestFocus()) break // View.FOCUS_RIGHT
2023-07-23 18:10:21 +00:00
}
}
}
resultEpisodes.setLinearListLayout(
isHorizontal = false,
nextUp = FOCUS_SELF,
nextDown = FOCUS_SELF,
nextRight = FOCUS_SELF,
)
resultDubSelection.setLinearListLayout(
isHorizontal = false,
nextUp = FOCUS_SELF,
nextDown = FOCUS_SELF,
)
resultRangeSelection.setLinearListLayout(
isHorizontal = false,
nextUp = FOCUS_SELF,
nextDown = FOCUS_SELF,
)
resultSeasonSelection.setLinearListLayout(
isHorizontal = false,
nextUp = FOCUS_SELF,
nextDown = FOCUS_SELF,
)
/*.layoutManager =
2023-07-27 19:47:42 +00:00
LinearListLayout(resultEpisodes.context, resultEpisodes.isRtl()).apply {
2023-07-23 01:07:24 +00:00
setVertical()
2023-07-27 19:47:42 +00:00
}*/
resultReloadConnectionerror.setOnClickListener {
2023-07-19 18:37:57 +00:00
viewModel.load(
activity,
storedData.url,
storedData.apiName,
storedData.showFillers,
storedData.dubStatus,
storedData.start
)
}
resultMetaSite.isFocusable = false
//resultReloadConnectionOpenInBrowser.setOnClickListener {view ->
// view.context?.openBrowser(storedData?.url ?: return@setOnClickListener, fallbackWebview = true)
//}
2023-07-18 01:55:00 +00:00
resultSeasonSelection.setAdapter()
resultRangeSelection.setAdapter()
resultDubSelection.setAdapter()
resultRecommendationsFilterSelection.setAdapter()
2023-07-18 20:18:14 +00:00
resultCastItems.setOnFocusChangeListener { _, hasFocus ->
// Always escape focus
if (hasFocus) binding?.resultBookmarkButton?.requestFocus()
}
2023-07-23 01:07:24 +00:00
//resultBack.setOnClickListener {
// activity?.popCurrentPage()
//}
2023-07-18 20:18:14 +00:00
resultRecommendationsList.spanCount = 8
resultRecommendationsList.adapter =
SearchAdapter(
ArrayList(),
resultRecommendationsList,
) { callback ->
2023-08-02 00:13:30 +00:00
if (callback.action == SEARCH_ACTION_FOCUSED)
2023-07-28 02:18:28 +00:00
toggleEpisodes(false)
else
SearchHelper.handleSearchClickCallback(callback)
2023-07-18 20:18:14 +00:00
}
resultEpisodes.adapter =
EpisodeAdapter(
false,
{ episodeClick ->
viewModel.handleAction(episodeClick)
2023-07-18 20:18:14 +00:00
},
{ downloadClickEvent ->
DownloadButtonSetup.handleDownloadClick(downloadClickEvent)
}
)
resultCastItems.layoutManager = object : LinearListLayout(view.context) {
override fun onInterceptFocusSearch(focused: View, direction: Int): View? {
return super.onInterceptFocusSearch(focused, direction)
}
override fun onRequestChildFocus(
parent: RecyclerView,
state: RecyclerView.State,
child: View,
focused: View?
): Boolean {
// Make the cast always focus the first visible item when focused
// from somewhere else. Otherwise it jumps to the last item.
return if (parent.focusedChild == null) {
scrollToPosition(this.findFirstCompletelyVisibleItemPosition())
true
} else {
super.onRequestChildFocus(parent, state, child, focused)
}
}
}.apply {
setHorizontal()
}
2023-07-28 02:18:28 +00:00
resultCastItems.adapter = ActorAdaptor {
toggleEpisodes(false)
}
}
observeNullable(viewModel.resumeWatching) { resume ->
binding?.apply {
2023-07-19 18:37:57 +00:00
// show progress no matter if series or movie
resume?.progress?.let { progress ->
resultResumeSeriesProgressText.setText(progress.progressLeft)
resultResumeSeriesProgress.apply {
isVisible = true
this.max = progress.maxProgress
this.progress = progress.progress
}
resultResumeProgressHolder.isVisible = true
} ?: run {
resultResumeProgressHolder.isVisible = false
}
2023-07-19 18:37:57 +00:00
// if movie then hide both as movie button is
// always visible on movies, this is done in movie observe
2023-07-23 18:10:21 +00:00
if (resume?.isMovie == true) {
2023-07-19 18:37:57 +00:00
resultPlaySeries.isVisible = false
resultResumeSeries.isVisible = false
return@observeNullable
}
// if series then
// > resultPlaySeries is visible when null
// > resultResumeSeries is visible when not null
if (resume == null) {
resultPlaySeries.isVisible = true
resultResumeSeries.isVisible = false
return@observeNullable
}
resultPlaySeries.isVisible = false
resultResumeSeries.isVisible = true
if (hasNoFocus()) {
resultResumeSeries.requestFocus()
}
resultResumeSeries.text =
if (resume.isMovie) context?.getString(R.string.play_movie_button) else context?.getNameFull(
null, // resume.result.name, we don't want episode title
resume.result.episode,
resume.result.season
)
resultResumeSeries.setOnClickListener {
viewModel.handleAction(
EpisodeClickEvent(
storedData.playerAction, //?: ACTION_PLAY_EPISODE_IN_PLAYER,
resume.result
)
)
}
2023-07-19 18:37:57 +00:00
resultResumeSeries.setOnLongClickListener {
viewModel.handleAction(
EpisodeClickEvent(ACTION_SHOW_OPTIONS, resume.result)
)
return@setOnLongClickListener true
}
}
}
observe(viewModel.trailers) { trailersLinks ->
context?.updateHasTrailers()
if (!LoadResponse.isTrailersEnabled) return@observe
val trailers = trailersLinks.flatMap { it.mirros }
binding?.resultPlayTrailer?.apply {
isGone = trailers.isEmpty()
setOnClickListener {
if (trailers.isEmpty()) return@setOnClickListener
activity.navigate(
R.id.global_to_navigation_player, GeneratorPlayer.newInstance(
ExtractorLinkGenerator(
trailers,
emptyList()
)
)
)
}
}
2023-07-18 01:55:00 +00:00
}
observe(viewModel.watchStatus) { watchType ->
binding?.resultBookmarkButton?.apply {
setText(watchType.stringRes)
setOnClickListener { view ->
activity?.showBottomDialog(
WatchType.values().map { view.context.getString(it.stringRes) }.toList(),
watchType.ordinal,
view.context.getString(R.string.action_add_to_bookmarks),
showApply = false,
{}) {
viewModel.updateWatchStatus(WatchType.values()[it])
}
}
}
2023-07-18 01:55:00 +00:00
}
2022-08-05 23:41:35 +00:00
2023-07-18 20:18:14 +00:00
observeNullable(viewModel.movie) { data ->
binding?.apply {
resultPlayMovie.isVisible = data is Resource.Success
2023-07-19 18:37:57 +00:00
seriesHolder.isVisible = data == null
2023-07-23 18:10:21 +00:00
resultEpisodesShow.isVisible = data == null
2023-07-19 18:37:57 +00:00
2023-07-18 20:18:14 +00:00
(data as? Resource.Success)?.value?.let { (text, ep) ->
resultPlayMovie.setText(text)
resultPlayMovie.setOnClickListener {
viewModel.handleAction(
EpisodeClickEvent(ACTION_CLICK_DEFAULT, ep)
)
}
resultPlayMovie.setOnLongClickListener {
viewModel.handleAction(
EpisodeClickEvent(ACTION_SHOW_OPTIONS, ep)
)
return@setOnLongClickListener true
}
if (hasNoFocus()) {
resultPlayMovie.requestFocus()
}
}
}
}
2022-08-05 23:41:35 +00:00
observeNullable(viewModel.selectPopup) { popup ->
2023-07-18 01:55:00 +00:00
if (popup == null) {
popupDialog?.dismissSafe(activity)
popupDialog = null
return@observeNullable
}
popupDialog?.dismissSafe(activity)
popupDialog = activity?.let { act ->
val options = popup.getOptions(act)
val title = popup.getTitle(act)
act.showBottomDialogInstant(
options, title, {
popupDialog = null
popup.callback(null)
}, {
popupDialog = null
popup.callback(it)
2022-08-06 18:51:32 +00:00
}
)
2022-08-06 18:51:32 +00:00
}
}
observeNullable(viewModel.loadedLinks) { load ->
2023-07-18 01:55:00 +00:00
if (load == null) {
loadingDialog?.dismissSafe(activity)
loadingDialog = null
return@observeNullable
}
2023-07-18 01:55:00 +00:00
if (loadingDialog?.isShowing != true) {
loadingDialog?.dismissSafe(activity)
loadingDialog = null
}
loadingDialog = loadingDialog ?: context?.let { ctx ->
val builder = BottomSheetDialog(ctx)
builder.setContentView(R.layout.bottom_loading)
builder.setOnDismissListener {
loadingDialog = null
viewModel.cancelLinks()
}
//builder.setOnCancelListener {
// it?.dismiss()
//}
builder.setCanceledOnTouchOutside(true)
builder.show()
builder
}
2022-08-06 18:51:32 +00:00
}
2022-08-06 18:36:45 +00:00
observeNullable(viewModel.episodesCountText) { count ->
2023-07-18 01:55:00 +00:00
binding?.resultEpisodesText.setText(count)
2022-08-06 18:36:45 +00:00
}
2022-08-05 23:41:35 +00:00
observe(viewModel.selectedRangeIndex) { selected ->
2023-07-18 01:55:00 +00:00
binding?.resultRangeSelection.select(selected)
2022-08-05 23:41:35 +00:00
}
observe(viewModel.selectedSeasonIndex) { selected ->
2023-07-18 01:55:00 +00:00
binding?.resultSeasonSelection.select(selected)
2022-08-05 23:41:35 +00:00
}
observe(viewModel.selectedDubStatusIndex) { selected ->
2023-07-18 01:55:00 +00:00
binding?.resultDubSelection.select(selected)
2022-08-05 23:41:35 +00:00
}
observe(viewModel.rangeSelections) {
2023-07-18 01:55:00 +00:00
binding?.resultRangeSelection.update(it)
2022-08-05 23:41:35 +00:00
}
observe(viewModel.dubSubSelections) {
2023-07-18 01:55:00 +00:00
binding?.resultDubSelection.update(it)
2022-08-05 23:41:35 +00:00
}
observe(viewModel.seasonSelections) {
2023-07-18 01:55:00 +00:00
binding?.resultSeasonSelection.update(it)
2022-08-05 23:41:35 +00:00
}
2023-07-18 20:18:14 +00:00
observe(viewModel.recommendations) { recommendations ->
setRecommendations(recommendations, null)
}
observe(viewModel.episodeSynopsis) { description ->
view.context?.let { ctx ->
val builder: AlertDialog.Builder =
AlertDialog.Builder(ctx, R.style.AlertDialogCustom)
builder.setMessage(description.html())
.setTitle(R.string.synopsis)
.setOnDismissListener {
viewModel.releaseEpisodeSynopsis()
}
.show()
2023-07-18 01:55:00 +00:00
}
2023-07-18 20:18:14 +00:00
}
observeNullable(viewModel.episodes) { episodes ->
binding?.apply {
resultEpisodes.isVisible = episodes is Resource.Success
2023-07-23 18:10:21 +00:00
// resultEpisodeLoading.isVisible = episodes is Resource.Loading
2023-07-18 20:18:14 +00:00
if (episodes is Resource.Success) {
2023-07-19 18:37:57 +00:00
val first = episodes.value.firstOrNull()
if (first != null) {
resultPlaySeries.text = context?.getNameFull(
null, // resume.result.name, we don't want episode title
first.episode,
first.season
)
resultPlaySeries.setOnClickListener {
viewModel.handleAction(
EpisodeClickEvent(
2023-08-01 01:12:32 +00:00
ACTION_CLICK_DEFAULT,
2023-07-19 18:37:57 +00:00
first
)
)
}
resultPlaySeries.setOnLongClickListener {
viewModel.handleAction(
EpisodeClickEvent(ACTION_SHOW_OPTIONS, first)
)
return@setOnLongClickListener true
}
}
2023-07-18 20:18:14 +00:00
/*
* Okay so what is this fuckery?
* Basically Android TV will crash if you request a new focus while
* the adapter gets updated.
*
* This means that if you load thumbnails and request a next focus at the same time
* the app will crash without any way to catch it!
*
* How to bypass this?
* This code basically steals the focus for 500ms and puts it in an inescapable view
* then lets out the focus by requesting focus to result_episodes
*/
val hasEpisodes =
!(resultEpisodes.adapter as? EpisodeAdapter?)?.cardList.isNullOrEmpty()
2023-07-23 18:10:21 +00:00
/*val focus = activity?.currentFocus
2023-07-18 20:18:14 +00:00
if (hasEpisodes) {
// Make it impossible to focus anywhere else!
temporaryNoFocus.isFocusable = true
temporaryNoFocus.requestFocus()
2023-07-23 18:10:21 +00:00
}*/
2023-07-18 01:55:00 +00:00
2023-07-18 20:18:14 +00:00
(resultEpisodes.adapter as? EpisodeAdapter)?.updateList(episodes.value)
2023-07-23 18:10:21 +00:00
/* if (hasEpisodes) main {
2023-07-18 20:18:14 +00:00
2023-07-23 18:10:21 +00:00
delay(500)
// This might make some people sad as it changes the focus when leaving an episode :(
if(focus?.requestFocus() == true) {
temporaryNoFocus.isFocusable = false
return@main
}
temporaryNoFocus.isFocusable = false
temporaryNoFocus.requestFocus()
}
if (hasNoFocus())
binding?.resultEpisodes?.requestFocus()*/
2023-07-18 01:55:00 +00:00
}
2023-07-18 20:18:14 +00:00
}
2022-08-06 16:08:20 +00:00
}
2023-07-18 20:18:14 +00:00
observeNullable(viewModel.page) { data ->
if (data == null) return@observeNullable
binding?.apply {
when (data) {
is Resource.Success -> {
val d = data.value
resultVpn.setText(d.vpnText)
resultInfo.setText(d.metaText)
resultNoEpisodes.setText(d.noEpisodesFoundText)
resultTitle.setText(d.titleText)
resultMetaSite.setText(d.apiName)
resultMetaType.setText(d.typeText)
resultMetaYear.setText(d.yearText)
resultMetaDuration.setText(d.durationText)
resultMetaRating.setText(d.ratingText)
resultCastText.setText(d.actorsText)
resultNextAiring.setText(d.nextAiringEpisode)
resultNextAiringTime.setText(d.nextAiringDate)
resultPoster.setImage(d.posterImage)
resultDescription.setTextHtml(d.plotText)
2023-07-19 18:37:57 +00:00
resultDescription.setOnClickListener { view ->
view.context?.let { ctx ->
val builder: AlertDialog.Builder =
AlertDialog.Builder(ctx, R.style.AlertDialogCustom)
builder.setMessage(d.plotText.asString(ctx).html())
.setTitle(d.plotHeaderText.asString(ctx))
.show()
}
}
2023-07-18 20:18:14 +00:00
2023-07-23 18:10:21 +00:00
val error = listOf(
R.drawable.profile_bg_dark_blue,
R.drawable.profile_bg_blue,
R.drawable.profile_bg_orange,
R.drawable.profile_bg_pink,
R.drawable.profile_bg_purple,
R.drawable.profile_bg_red,
R.drawable.profile_bg_teal
).random()
backgroundPoster.setImage(
d.posterBackgroundImage ?: UiImage.Drawable(error),
radius = 0,
errorImageDrawable = error
)
2023-07-23 01:07:24 +00:00
2023-07-18 20:18:14 +00:00
resultComingSoon.isVisible = d.comingSoon
resultDataHolder.isGone = d.comingSoon
UIHelper.populateChips(resultTag, d.tags)
resultCastItems.isGone = d.actors.isNullOrEmpty()
(resultCastItems.adapter as? ActorAdaptor)?.updateList(
d.actors ?: emptyList()
)
}
is Resource.Loading -> {
}
is Resource.Failure -> {
resultErrorText.text =
storedData.url.plus("\n") + data.errorString
2023-07-18 20:18:14 +00:00
}
}
resultFinishLoading.isVisible = data is Resource.Success
resultLoading.isVisible = data is Resource.Loading
resultLoadingError.isVisible = data is Resource.Failure
//resultReloadConnectionOpenInBrowser.isVisible = data is Resource.Failure
2023-07-18 20:18:14 +00:00
}
}
2022-08-04 22:26:33 +00:00
}
}