removed synthetic by removing ResultFragment

This commit is contained in:
LagradOst 2023-07-19 01:51:17 +02:00
parent 4fcf396591
commit ed0d374721
7 changed files with 459 additions and 355 deletions

View file

@ -7,7 +7,6 @@ plugins {
id("com.android.application")
id("kotlin-android")
id("kotlin-kapt")
id("kotlin-android-extensions")
id("org.jetbrains.dokka")
}

View file

@ -1,94 +1,18 @@
package com.lagradost.cloudstream3.ui.result
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.Intent.*
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipDrawable
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
import com.lagradost.cloudstream3.ui.player.FullScreenPlayer
import com.lagradost.cloudstream3.ui.result.EpisodeAdapter.Companion.getPlayerAction
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.DataStoreHelper.getVideoWatchState
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import kotlinx.android.synthetic.main.fragment_result.download_button
import kotlinx.android.synthetic.main.fragment_result.result_cast_items
import kotlinx.android.synthetic.main.fragment_result.result_cast_text
import kotlinx.android.synthetic.main.fragment_result.result_coming_soon
import kotlinx.android.synthetic.main.fragment_result.result_data_holder
import kotlinx.android.synthetic.main.fragment_result.result_description
import kotlinx.android.synthetic.main.fragment_result.result_dub_select
import kotlinx.android.synthetic.main.fragment_result.result_episode_loading
import kotlinx.android.synthetic.main.fragment_result.result_episode_select
import kotlinx.android.synthetic.main.fragment_result.result_episodes
import kotlinx.android.synthetic.main.fragment_result.result_error_text
import kotlinx.android.synthetic.main.fragment_result.result_finish_loading
import kotlinx.android.synthetic.main.fragment_result.result_info
import kotlinx.android.synthetic.main.fragment_result.result_loading
import kotlinx.android.synthetic.main.fragment_result.result_loading_error
import kotlinx.android.synthetic.main.fragment_result.result_meta_duration
import kotlinx.android.synthetic.main.fragment_result.result_meta_rating
import kotlinx.android.synthetic.main.fragment_result.result_meta_site
import kotlinx.android.synthetic.main.fragment_result.result_meta_type
import kotlinx.android.synthetic.main.fragment_result.result_meta_year
import kotlinx.android.synthetic.main.fragment_result.result_next_airing
import kotlinx.android.synthetic.main.fragment_result.result_next_airing_time
import kotlinx.android.synthetic.main.fragment_result.result_no_episodes
import kotlinx.android.synthetic.main.fragment_result.result_play_movie
import kotlinx.android.synthetic.main.fragment_result.result_poster
import kotlinx.android.synthetic.main.fragment_result.result_poster_background
import kotlinx.android.synthetic.main.fragment_result.result_poster_holder
import kotlinx.android.synthetic.main.fragment_result.result_reload_connection_open_in_browser
import kotlinx.android.synthetic.main.fragment_result.result_reload_connectionerror
import kotlinx.android.synthetic.main.fragment_result.result_resume_parent
import kotlinx.android.synthetic.main.fragment_result.result_resume_progress_holder
import kotlinx.android.synthetic.main.fragment_result.result_resume_series_button
import kotlinx.android.synthetic.main.fragment_result.result_resume_series_progress
import kotlinx.android.synthetic.main.fragment_result.result_resume_series_progress_text
import kotlinx.android.synthetic.main.fragment_result.result_resume_series_title
import kotlinx.android.synthetic.main.fragment_result.result_season_button
import kotlinx.android.synthetic.main.fragment_result.result_tag
import kotlinx.android.synthetic.main.fragment_result.result_tag_holder
import kotlinx.android.synthetic.main.fragment_result.result_title
import kotlinx.android.synthetic.main.fragment_result.result_vpn
import kotlinx.android.synthetic.main.fragment_result_tv.result_resume_series_button_play
import kotlinx.android.synthetic.main.fragment_result_tv.temporary_no_focus
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import com.lagradost.cloudstream3.utils.Event
const val START_ACTION_RESUME_LATEST = 1
const val START_ACTION_LOAD_EP = 2
@ -188,15 +112,14 @@ fun ResultEpisode.getWatchProgress(): Float {
return (getDisplayPosition() / 1000).toFloat() / (duration / 1000).toFloat()
}
open class ResultFragment : FullScreenPlayer() {
companion object {
const val URL_BUNDLE = "url"
const val API_NAME_BUNDLE = "apiName"
const val SEASON_BUNDLE = "season"
const val EPISODE_BUNDLE = "episode"
const val START_ACTION_BUNDLE = "startAction"
const val START_VALUE_BUNDLE = "startValue"
const val RESTART_BUNDLE = "restart"
object ResultFragment {
private const val URL_BUNDLE = "url"
private const val API_NAME_BUNDLE = "apiName"
private const val SEASON_BUNDLE = "season"
private const val EPISODE_BUNDLE = "episode"
private const val START_ACTION_BUNDLE = "startAction"
private const val START_VALUE_BUNDLE = "startValue"
private const val RESTART_BUNDLE = "restart"
fun newInstance(
card: SearchResponse, startAction: Int = 0, startValue: Int? = null
@ -234,30 +157,24 @@ open class ResultFragment : FullScreenPlayer() {
}
}
fun updateUI() {
updateUIListener?.invoke()
fun updateUI(id: Int? = null) {
// updateUIListener?.invoke()
updateUIEvent.invoke(id)
}
val updateUIEvent = Event<Int?>()
private var updateUIListener: (() -> Unit)? = null
}
//private var updateUIListener: (() -> Unit)? = null
open fun setTrailers(trailers: List<ExtractorLink>?) {}
protected lateinit var viewModel: ResultViewModel2 //by activityViewModels()
protected lateinit var syncModel: SyncViewModel
//protected open val resultLayout = R.layout.fragment_result_swipe
override var layout = R.layout.fragment_result_swipe
/* override var layout = R.layout.fragment_result_swipe
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
viewModel =
ViewModelProvider(this)[ResultViewModel2::class.java]
syncModel =
ViewModelProvider(this)[SyncViewModel::class.java]
return super.onCreateView(inflater, container, savedInstanceState)
//return inflater.inflate(resultLayout, container, false)
@ -286,20 +203,22 @@ open class ResultFragment : FullScreenPlayer() {
private fun updateUI() {
syncModel.updateUserData()
viewModel.reloadEpisodes()
}
}*/
data class StoredData(
val url: String?,
val url: String,
val apiName: String,
val showFillers: Boolean,
val dubStatus: DubStatus,
val start: AutoResume?,
val playerAction: Int
val playerAction: Int,
val restart : Boolean,
)
fun getStoredData(context: Context): StoredData? {
fun Fragment.getStoredData(): StoredData? {
val context = this.context ?: this.activity ?: return null
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
val url = arguments?.getString(URL_BUNDLE)
val url = arguments?.getString(URL_BUNDLE) ?: return null
val apiName = arguments?.getString(API_NAME_BUNDLE) ?: return null
val showFillers =
settingsManager.getBoolean(context.getString(R.string.show_fillers_key), false)
@ -310,6 +229,11 @@ open class ResultFragment : FullScreenPlayer() {
val playerAction = getPlayerAction(context)
val restart = arguments?.getBoolean(RESTART_BUNDLE) ?: false
if (restart) {
arguments?.putBoolean(RESTART_BUNDLE, false)
}
val start = startAction?.let { action ->
val startValue = arguments?.getInt(START_VALUE_BUNDLE)
val resumeEpisode = arguments?.getInt(EPISODE_BUNDLE)
@ -324,10 +248,10 @@ open class ResultFragment : FullScreenPlayer() {
season = resumeSeason
)
}
return StoredData(url, apiName, showFillers, dubStatus, start, playerAction)
return StoredData(url, apiName, showFillers, dubStatus, start, playerAction, restart)
}
private fun reloadViewModel(forceReload: Boolean) {
/*private fun reloadViewModel(forceReload: Boolean) {
if (!viewModel.hasLoaded() || forceReload) {
val storedData = getStoredData(activity ?: context ?: return) ?: return
@ -346,26 +270,6 @@ open class ResultFragment : FullScreenPlayer() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
result_cast_items?.layoutManager = object : LinearListLayout(view.context) {
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 {
this.orientation = RecyclerView.HORIZONTAL
}
result_cast_items?.adapter = ActorAdaptor()
updateUIListener = ::updateUI
@ -401,97 +305,7 @@ open class ResultFragment : FullScreenPlayer() {
result_season_button?.isFocusableInTouchMode = isTv
result_episode_select?.isFocusableInTouchMode = isTv
result_dub_select?.isFocusableInTouchMode = isTv
observeNullable(viewModel.resumeWatching) { resume ->
if (resume == null) {
result_resume_parent?.isVisible = false
return@observeNullable
}
result_resume_parent?.isVisible = true
resume.progress?.let { progress ->
result_resume_series_title?.apply {
isVisible = !resume.isMovie
text =
if (resume.isMovie) null else activity?.getNameFull(
resume.result.name,
resume.result.episode,
resume.result.season
)
}
result_resume_series_progress_text?.setText(progress.progressLeft)
result_resume_series_progress?.apply {
isVisible = true
this.max = progress.maxProgress
this.progress = progress.progress
}
result_resume_progress_holder?.isVisible = true
} ?: run {
result_resume_progress_holder?.isVisible = false
result_resume_series_progress?.isVisible = false
result_resume_series_title?.isVisible = false
result_resume_series_progress_text?.isVisible = false
}
result_resume_series_button?.isVisible = !resume.isMovie
result_resume_series_button_play?.isVisible = !resume.isMovie
val click = View.OnClickListener {
viewModel.handleAction(
activity,
EpisodeClickEvent(
storedData?.playerAction ?: ACTION_PLAY_EPISODE_IN_PLAYER,
resume.result
)
)
}
result_resume_series_button?.setOnClickListener(click)
result_resume_series_button_play?.setOnClickListener(click)
}
context?.let { ctx ->
//result_bookmark_button?.isVisible = ctx.isTvSettings()
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
Kitsu.isEnabled =
settingsManager.getBoolean(ctx.getString(R.string.show_kitsu_posters_key), true)
if (storedData?.url != null) {
result_reload_connectionerror.setOnClickListener {
viewModel.load(
activity,
storedData.url,
storedData.apiName,
storedData.showFillers,
storedData.dubStatus,
storedData.start
)
}
result_reload_connection_open_in_browser?.setOnClickListener {
val i = Intent(ACTION_VIEW)
i.data = Uri.parse(storedData.url)
try {
startActivity(i)
} catch (e: Exception) {
logError(e)
}
}
// bloats the navigation on tv
if (!isTrueTvSettings()) {
result_meta_site?.setOnClickListener {
it.context?.openBrowser(storedData.url)
}
result_meta_site?.isFocusable = true
} else {
result_meta_site?.isFocusable = false
}
if (restart || !viewModel.hasLoaded()) {
//viewModel.clear()
viewModel.load(
@ -504,6 +318,5 @@ open class ResultFragment : FullScreenPlayer() {
)
}
}
}
}
}*/
}

View file

@ -5,7 +5,6 @@ import android.app.Dialog
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Rect
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.Editable
@ -23,6 +22,8 @@ import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import com.discord.panels.OverlappingPanelsLayout
import com.discord.panels.PanelsChildGestureRegionObserver
import com.google.android.gms.cast.framework.CastButtonFactory
@ -34,6 +35,7 @@ import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.databinding.FragmentResultBinding
@ -50,11 +52,16 @@ import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup
import com.lagradost.cloudstream3.ui.player.CSPlayerEvent
import com.lagradost.cloudstream3.ui.player.FullScreenPlayer
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
import com.lagradost.cloudstream3.ui.result.ResultFragment.getStoredData
import com.lagradost.cloudstream3.ui.result.ResultFragment.updateUIEvent
import com.lagradost.cloudstream3.ui.search.SearchAdapter
import com.lagradost.cloudstream3.ui.search.SearchHelper
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
@ -63,6 +70,7 @@ import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
import com.lagradost.cloudstream3.utils.UIHelper.populateChips
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
@ -70,17 +78,29 @@ import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
open class ResultFragmentPhone : ResultFragment(),
open class ResultFragmentPhone : FullScreenPlayer(),
PanelsChildGestureRegionObserver.GestureRegionsListener {
protected lateinit var viewModel: ResultViewModel2
protected lateinit var syncModel: SyncViewModel
protected var binding: FragmentResultSwipeBinding? = null
protected var resultBinding: FragmentResultBinding? = null
protected var recommendationBinding: ResultRecommendationsBinding? = null
protected var syncBinding: ResultSyncBinding? = null
override var layout = R.layout.fragment_result_swipe
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
viewModel =
ViewModelProvider(this)[ResultViewModel2::class.java]
syncModel =
ViewModelProvider(this)[SyncViewModel::class.java]
updateUIEvent += ::updateUI
val root = super.onCreateView(inflater, container, savedInstanceState) ?: return null
FragmentResultSwipeBinding.bind(root).let { bind ->
resultBinding =
@ -180,7 +200,7 @@ open class ResultFragmentPhone : ResultFragment(),
}
override fun setTrailers(trailers: List<ExtractorLink>?) {
private fun setTrailers(trailers: List<ExtractorLink>?) {
context?.updateHasTrailers()
if (!LoadResponse.isTrailersEnabled) return
currentTrailers = trailers?.sortedBy { -it.quality } ?: emptyList()
@ -196,6 +216,7 @@ open class ResultFragmentPhone : ResultFragment(),
}
obs.removeGestureRegionsUpdateListener(this)
}
updateUIEvent -= ::updateUI
binding = null
resultBinding = null
syncBinding = null
@ -224,42 +245,122 @@ open class ResultFragmentPhone : ResultFragment(),
return
}
val valid = url.startsWith("http")
binding?.resultOpenInBrowser?.apply {
isVisible = url.startsWith("http")
isVisible = valid
setOnClickListener {
val i = Intent(Intent.ACTION_VIEW)
i.data = Uri.parse(url)
try {
startActivity(i)
} catch (e: Exception) {
logError(e)
context?.openBrowser(url)
}
}
resultBinding?.resultReloadConnectionOpenInBrowser?.setOnClickListener {
view?.context?.openBrowser(url)
}
resultBinding?.resultMetaSite?.setOnClickListener {
view?.context?.openBrowser(url)
}
}
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() {
afterPluginsLoadedEvent += ::reloadViewModel
activity?.let {
it.window?.navigationBarColor =
it.colorFromAttribute(R.attr.primaryBlackBackground)
}
super.onResume()
}
override fun onStop() {
afterPluginsLoadedEvent -= ::reloadViewModel
super.onStop()
}
private fun updateUI(id : Int?) {
syncModel.updateUserData()
viewModel.reloadEpisodes()
}
@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val apiName = arguments?.getString(API_NAME_BUNDLE) ?: return
super.onViewCreated(view, savedInstanceState)
// ===== setup =====
UIHelper.fixPaddingStatusbar(binding?.resultTopBar)
val storedData = (activity ?: context)?.let {
getStoredData(it)
}
val storedData = getStoredData() ?: return
activity?.window?.decorView?.clearFocus()
activity?.loadCache()
context?.updateHasTrailers()
hideKeyboard()
if (storedData.restart || !viewModel.hasLoaded())
viewModel.load(
activity,
storedData.url,
storedData.apiName,
storedData.showFillers,
storedData.dubStatus,
storedData.start
)
setUrl(storedData?.url)
syncModel.addFromUrl(storedData?.url)
val api = APIHolder.getApiFromNameNull(apiName)
setUrl(storedData.url)
syncModel.addFromUrl(storedData.url)
val api = APIHolder.getApiFromNameNull(storedData.apiName)
PanelsChildGestureRegionObserver.Provider.get().addGestureRegionsUpdateListener(this)
// ===== ===== =====
resultBinding?.apply {
resultReloadConnectionerror.setOnClickListener {
viewModel.load(
activity,
storedData.url,
storedData.apiName,
storedData.showFillers,
storedData.dubStatus,
storedData.start
)
}
resultCastItems.layoutManager = object : LinearListLayout(view.context) {
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 {
this.orientation = RecyclerView.HORIZONTAL
}
resultCastItems.adapter = ActorAdaptor()
resultEpisodes.adapter =
EpisodeAdapter(
api?.hasDownloadSupport == true,
{ episodeClick ->
viewModel.handleAction(activity, episodeClick)
viewModel.handleAction(episodeClick)
},
{ downloadClickEvent ->
DownloadButtonSetup.handleDownloadClick(downloadClickEvent)
@ -291,6 +392,8 @@ open class ResultFragmentPhone : ResultFragment(),
resultBack.setOnClickListener {
activity?.popCurrentPage()
}
resultMiniSync.adapter = ImageAdapter(
nextFocusDown = R.id.result_sync_set_score,
clickCallback = { action ->
@ -368,7 +471,6 @@ open class ResultFragmentPhone : ResultFragment(),
}
}
PanelsChildGestureRegionObserver.Provider.get().addGestureRegionsUpdateListener(this)
/*
result_bookmark_button?.setOnClickListener {
@ -381,6 +483,50 @@ open class ResultFragmentPhone : ResultFragment(),
}
}*/
observeNullable(viewModel.resumeWatching) { resume ->
resultBinding?.apply {
if (resume == null) {
resultResumeParent.isVisible = false
return@observeNullable
}
resultResumeParent.isVisible = true
resume.progress?.let { progress ->
resultResumeSeriesTitle.apply {
isVisible = !resume.isMovie
text =
if (resume.isMovie) null else context?.getNameFull(
resume.result.name,
resume.result.episode,
resume.result.season
)
}
resultResumeSeriesProgressText.setText(progress.progressLeft)
resultResumeSeriesProgress.apply {
isVisible = true
this.max = progress.maxProgress
this.progress = progress.progress
}
resultResumeProgressHolder.isVisible = true
} ?: run {
resultResumeProgressHolder.isVisible = false
resultResumeSeriesProgress.isVisible = false
resultResumeSeriesTitle.isVisible = false
resultResumeSeriesProgressText.isVisible = false
}
resultResumeSeriesButton.isVisible = !resume.isMovie
resultResumeSeriesButton.setOnClickListener {
viewModel.handleAction(
EpisodeClickEvent(
storedData.playerAction, //?: ACTION_PLAY_EPISODE_IN_PLAYER,
resume.result
)
)
}
}
}
observeNullable(viewModel.subscribeStatus) { isSubscribed ->
binding?.resultSubscribe?.isVisible = isSubscribed != null
if (isSubscribed == null) return@observeNullable
@ -419,13 +565,11 @@ open class ResultFragmentPhone : ResultFragment(),
resultPlayMovie.setText(text)
resultPlayMovie.setOnClickListener {
viewModel.handleAction(
activity,
EpisodeClickEvent(ACTION_CLICK_DEFAULT, ep)
)
}
resultPlayMovie.setOnLongClickListener {
viewModel.handleAction(
activity,
EpisodeClickEvent(ACTION_SHOW_OPTIONS, ep)
)
return@setOnLongClickListener true
@ -446,7 +590,6 @@ open class ResultFragmentPhone : ResultFragment(),
when (click.action) {
DOWNLOAD_ACTION_DOWNLOAD -> {
viewModel.handleAction(
activity,
EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, ep)
)
}
@ -529,7 +672,7 @@ open class ResultFragmentPhone : ResultFragment(),
}
(data as? Resource.Failure)?.let { data ->
resultErrorText.text = (storedData?.url?.plus("\n") ?: "") + data.errorString
resultErrorText.text = storedData.url.plus("\n") + data.errorString
}
binding?.resultBookmarkFab?.isVisible = data is Resource.Success

View file

@ -9,11 +9,14 @@ import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.databinding.FragmentResultTvBinding
@ -24,27 +27,32 @@ import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup
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
import com.lagradost.cloudstream3.ui.search.SearchAdapter
import com.lagradost.cloudstream3.ui.search.SearchHelper
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
import com.lagradost.cloudstream3.utils.UIHelper.setImage
import kotlinx.coroutines.delay
class ResultFragmentTv : ResultFragment() {
override var layout = R.layout.fragment_result_tv
class ResultFragmentTv : Fragment() {
protected lateinit var viewModel: ResultViewModel2
private var binding: FragmentResultTvBinding? = null
override fun onDestroyView() {
binding = null
updateUIEvent -= ::updateUI
super.onDestroyView()
}
@ -52,11 +60,18 @@ class ResultFragmentTv : ResultFragment() {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = super.onCreateView(inflater, container, savedInstanceState) ?: return null
binding = FragmentResultTvBinding.bind(root)
): View {
viewModel =
ViewModelProvider(this)[ResultViewModel2::class.java]
updateUIEvent += ::updateUI
return root
val localBinding = FragmentResultTvBinding.inflate(inflater, container, false)
binding = localBinding
return localBinding.root
}
private fun updateUI(id : Int?) {
viewModel.reloadEpisodes()
}
private var currentRecommendations: List<SearchResponse> = emptyList()
@ -102,25 +117,6 @@ class ResultFragmentTv : ResultFragment() {
return focus == binding?.resultRoot
}
override fun setTrailers(trailers: List<ExtractorLink>?) {
context?.updateHasTrailers()
if (!LoadResponse.isTrailersEnabled) return
binding?.resultPlayTrailer?.apply {
isGone = trailers.isNullOrEmpty()
setOnClickListener {
if (trailers.isNullOrEmpty()) return@setOnClickListener
activity.navigate(
R.id.global_to_navigation_player, GeneratorPlayer.newInstance(
ExtractorLinkGenerator(
trailers,
emptyList()
)
)
)
}
}
}
private fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
currentRecommendations = rec ?: emptyList()
val isInvalid = rec.isNullOrEmpty()
@ -145,15 +141,78 @@ class ResultFragmentTv : ResultFragment() {
var loadingDialog: Dialog? = null
var popupDialog: Dialog? = null
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()
}
@SuppressLint("SetTextI18n")
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
)
// ===== ===== =====
binding?.apply {
resultEpisodes.layoutManager =
LinearListLayout(resultEpisodes.context).apply {
setHorizontal()
}
resultReloadConnectionerror.setOnClickListener {
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)
//}
resultSeasonSelection.setAdapter()
resultRangeSelection.setAdapter()
resultDubSelection.setAdapter()
@ -180,12 +239,97 @@ class ResultFragmentTv : ResultFragment() {
EpisodeAdapter(
false,
{ episodeClick ->
viewModel.handleAction(activity, episodeClick)
viewModel.handleAction(episodeClick)
},
{ downloadClickEvent ->
DownloadButtonSetup.handleDownloadClick(downloadClickEvent)
}
)
resultCastItems.layoutManager = object : LinearListLayout(view.context) {
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 {
this.orientation = RecyclerView.HORIZONTAL
}
resultCastItems.adapter = ActorAdaptor()
}
observeNullable(viewModel.resumeWatching) { resume ->
binding?.apply {
if (resume == null) {
resultResumeParent.isVisible = false
return@observeNullable
}
resultResumeParent.isVisible = true
resume.progress?.let { progress ->
resultResumeSeriesTitle.apply {
isVisible = !resume.isMovie
text =
if (resume.isMovie) null else context?.getNameFull(
resume.result.name,
resume.result.episode,
resume.result.season
)
}
resultResumeSeriesProgressText.setText(progress.progressLeft)
resultResumeSeriesProgress.apply {
isVisible = true
this.max = progress.maxProgress
this.progress = progress.progress
}
resultResumeProgressHolder.isVisible = true
} ?: run {
resultResumeProgressHolder.isVisible = false
resultResumeSeriesProgress.isVisible = false
resultResumeSeriesTitle.isVisible = false
resultResumeSeriesProgressText.isVisible = false
}
resultResumeSeriesButton.isVisible = !resume.isMovie
resultResumeSeriesButton.setOnClickListener {
viewModel.handleAction(
EpisodeClickEvent(
storedData.playerAction, //?: ACTION_PLAY_EPISODE_IN_PLAYER,
resume.result
)
)
}
}
}
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()
)
)
)
}
}
}
observe(viewModel.watchStatus) { watchType ->
@ -211,13 +355,11 @@ class ResultFragmentTv : ResultFragment() {
resultPlayMovie.setText(text)
resultPlayMovie.setOnClickListener {
viewModel.handleAction(
activity,
EpisodeClickEvent(ACTION_CLICK_DEFAULT, ep)
)
}
resultPlayMovie.setOnLongClickListener {
viewModel.handleAction(
activity,
EpisodeClickEvent(ACTION_SHOW_OPTIONS, ep)
)
return@setOnLongClickListener true
@ -397,8 +539,7 @@ class ResultFragmentTv : ResultFragment() {
is Resource.Failure -> {
resultErrorText.text =
(this@ResultFragmentTv.context?.let { getStoredData(it) }?.url?.plus("\n")
?: "") + data.errorString
storedData.url.plus("\n") + data.errorString
}
}
@ -407,7 +548,7 @@ class ResultFragmentTv : ResultFragment() {
resultLoading.isVisible = data is Resource.Loading
resultLoadingError.isVisible = data is Resource.Failure
resultReloadConnectionOpenInBrowser.isVisible = data is Resource.Failure
//resultReloadConnectionOpenInBrowser.isVisible = data is Resource.Failure
}
}
}

View file

@ -19,6 +19,7 @@ import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.CommonActivity.getCastSession
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
@ -1144,9 +1145,9 @@ class ResultViewModel2 : ViewModel() {
}
fun handleAction(activity: Activity?, click: EpisodeClickEvent) =
fun handleAction(click: EpisodeClickEvent) =
viewModelScope.launchSafe {
handleEpisodeClickEvent(activity, click)
handleEpisodeClickEvent(click)
}
data class ExternalApp(
@ -1176,7 +1177,7 @@ class ResultViewModel2 : ViewModel() {
_episodeSynopsis.postValue(null)
}
private suspend fun handleEpisodeClickEvent(activity: Activity?, click: EpisodeClickEvent) {
private suspend fun handleEpisodeClickEvent(click: EpisodeClickEvent) {
when (click.action) {
ACTION_SHOW_OPTIONS -> {
val options = mutableListOf<Pair<UiText, Int>>()
@ -1234,7 +1235,6 @@ class ResultViewModel2 : ViewModel() {
options
) { result ->
handleEpisodeClickEvent(
activity,
click.copy(action = result ?: return@postPopup)
)
}
@ -1244,13 +1244,11 @@ class ResultViewModel2 : ViewModel() {
activity?.let { ctx ->
if (ctx.isConnectedToChromecast()) {
handleEpisodeClickEvent(
activity,
click.copy(action = ACTION_CHROME_CAST_EPISODE)
)
} else {
val action = getPlayerAction(ctx)
handleEpisodeClickEvent(
activity,
click.copy(action = action)
)
}
@ -2210,7 +2208,6 @@ class ResultViewModel2 : ViewModel() {
for (ep in currentRange) {
if (ep.getWatchProgress() > 0.9) continue
handleAction(
activity,
EpisodeClickEvent(
getPlayerAction(activity),
ep
@ -2231,7 +2228,6 @@ class ResultViewModel2 : ViewModel() {
}
?: return@launchSafe
handleAction(
activity,
EpisodeClickEvent(
getPlayerAction(activity),
episode

View file

@ -33,6 +33,7 @@ import androidx.core.widget.ContentLoadingProgressBar
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -54,6 +55,7 @@ import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringResumeWatching
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
import com.lagradost.cloudstream3.ui.WebviewFragment
import com.lagradost.cloudstream3.ui.result.ResultFragment
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
@ -597,6 +599,14 @@ object AppUtils {
startAction: Int = 0,
startValue: Int = 0
) {
try {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
Kitsu.isEnabled =
settingsManager.getBoolean(this.getString(R.string.show_kitsu_posters_key), true)
}catch (t : Throwable) {
logError(t)
}
this.runOnUiThread {
// viewModelStore.clear()
this.navigate(

View file

@ -88,6 +88,7 @@
android:text="@string/reload_error"
app:icon="@drawable/ic_baseline_autorenew_24" />
<!--
<com.google.android.material.button.MaterialButton
android:id="@+id/result_reload_connection_open_in_browser"
style="@style/BlackButton"
@ -99,6 +100,7 @@
android:minWidth="200dp"
android:text="@string/result_open_in_browser"
app:icon="@drawable/ic_baseline_public_24" />
-->
<TextView
android:id="@+id/result_error_text"
@ -439,7 +441,7 @@
android:minWidth="250dp"
android:nextFocusRight="@id/result_play_trailer"
android:nextFocusUp="@id/result_cast_items"
android:nextFocusDown="@id/result_resume_series_button_play"
android:nextFocusDown="@id/result_resume_series_button"
android:text="@string/play_movie_button"
android:visibility="visible"
app:icon="@drawable/ic_baseline_play_arrow_24">
@ -457,7 +459,7 @@
android:minWidth="250dp"
android:nextFocusRight="@id/download_button"
android:nextFocusUp="@id/result_cast_items"
android:nextFocusDown="@id/result_resume_series_button_play"
android:nextFocusDown="@id/result_resume_series_button"
android:text="@string/play_trailer_button"
android:visibility="gone"
app:icon="@drawable/ic_baseline_play_arrow_24">
@ -486,7 +488,7 @@
android:minWidth="250dp"
android:nextFocusLeft="@id/download_button"
android:nextFocusRight="@id/result_bookmark_button"
android:nextFocusDown="@id/result_resume_series_button_play"
android:nextFocusDown="@id/result_resume_series_button"
android:text="@string/type_none"
android:visibility="visible" />
</LinearLayout>
@ -521,7 +523,7 @@
android:orientation="horizontal">
<ImageView
android:id="@+id/result_resume_series_button_play"
android:id="@+id/result_resume_series_button"
android:layout_width="30dp"
android:layout_height="30dp"
@ -600,7 +602,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:nextFocusUp="@id/result_resume_series_button_play"
android:nextFocusUp="@id/result_resume_series_button"
android:nextFocusDown="@id/result_range_selection"
android:orientation="horizontal"
android:paddingBottom="10dp"