mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
result -> result specific
This commit is contained in:
parent
d5c42f7d5a
commit
03d50a943a
14 changed files with 704 additions and 680 deletions
|
@ -223,7 +223,7 @@ dependencies {
|
||||||
//implementation("com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT")
|
//implementation("com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT")
|
||||||
|
|
||||||
// newpipe yt taken from https://github.com/TeamNewPipe/NewPipe/blob/dev/app/build.gradle#L204
|
// newpipe yt taken from https://github.com/TeamNewPipe/NewPipe/blob/dev/app/build.gradle#L204
|
||||||
implementation("com.github.TeamNewPipe:NewPipeExtractor:master-SNAPSHOT")
|
implementation("com.github.TeamNewPipe:NewPipeExtractor:8495ad619e")
|
||||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.6")
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.6")
|
||||||
|
|
||||||
// Library/extensions searching with Levenshtein distance
|
// Library/extensions searching with Levenshtein distance
|
||||||
|
|
|
@ -361,14 +361,11 @@ class HomeFragment : Fragment() {
|
||||||
?.toMutableList()
|
?.toMutableList()
|
||||||
?: mutableListOf(TvType.Movie, TvType.TvSeries)
|
?: mutableListOf(TvType.Movie, TvType.TvSeries)
|
||||||
|
|
||||||
val cancelBtt = dialog.findViewById<MaterialButton>(R.id.cancel_btt)
|
binding.cancelBtt.setOnClickListener {
|
||||||
val applyBtt = dialog.findViewById<MaterialButton>(R.id.apply_btt)
|
|
||||||
|
|
||||||
cancelBtt?.setOnClickListener {
|
|
||||||
dialog.dismissSafe()
|
dialog.dismissSafe()
|
||||||
}
|
}
|
||||||
|
|
||||||
applyBtt?.setOnClickListener {
|
binding.applyBtt.setOnClickListener {
|
||||||
if (currentApiName != selectedApiName) {
|
if (currentApiName != selectedApiName) {
|
||||||
currentApiName?.let(callback)
|
currentApiName?.let(callback)
|
||||||
}
|
}
|
||||||
|
@ -493,15 +490,54 @@ class HomeFragment : Fragment() {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
fixGrid()
|
fixGrid()
|
||||||
|
|
||||||
binding?.homeChangeApiLoading?.setOnClickListener(apiChangeClickListener)
|
binding?.apply {
|
||||||
binding?.homeChangeApiLoading?.setOnClickListener(apiChangeClickListener)
|
homeChangeApiLoading.setOnClickListener(apiChangeClickListener)
|
||||||
binding?.homeApiFab?.setOnClickListener(apiChangeClickListener)
|
//homeChangeApiLoading.setOnClickListener(apiChangeClickListener)
|
||||||
binding?.homeRandom?.setOnClickListener {
|
homeApiFab.setOnClickListener(apiChangeClickListener)
|
||||||
|
homeRandom.setOnClickListener {
|
||||||
if (listHomepageItems.isNotEmpty()) {
|
if (listHomepageItems.isNotEmpty()) {
|
||||||
activity.loadSearchResult(listHomepageItems.random())
|
activity.loadSearchResult(listHomepageItems.random())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
homeMasterRecycler.adapter =
|
||||||
|
HomeParentItemAdapterPreview(
|
||||||
|
mutableListOf(),
|
||||||
|
homeViewModel
|
||||||
|
)
|
||||||
|
fixPaddingStatusbar(homeLoadingStatusbar)
|
||||||
|
|
||||||
|
if (isTvSettings()) {
|
||||||
|
homeApiFab.isVisible = false
|
||||||
|
if (isTrueTvSettings()) {
|
||||||
|
homeChangeApiLoading.isVisible = true
|
||||||
|
homeChangeApiLoading.isFocusable = true
|
||||||
|
homeChangeApiLoading.isFocusableInTouchMode = true
|
||||||
|
}
|
||||||
|
// home_bookmark_select?.isFocusable = true
|
||||||
|
// home_bookmark_select?.isFocusableInTouchMode = true
|
||||||
|
} else {
|
||||||
|
homeApiFab.isVisible = true
|
||||||
|
homeChangeApiLoading.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
homeMasterRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
if (dy > 0) { //check for scroll down
|
||||||
|
homeApiFab.shrink() // hide
|
||||||
|
homeRandom.shrink()
|
||||||
|
} else if (dy < -5) {
|
||||||
|
if (!isTvSettings()) {
|
||||||
|
homeApiFab.extend() // show
|
||||||
|
homeRandom.extend()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//Load value for toggling Random button. Hide at startup
|
//Load value for toggling Random button. Hide at startup
|
||||||
context?.let {
|
context?.let {
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(it)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(it)
|
||||||
|
@ -551,13 +587,9 @@ class HomeFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
is Resource.Failure -> {
|
is Resource.Failure -> {
|
||||||
|
|
||||||
homeLoadingShimmer.stopShimmer()
|
homeLoadingShimmer.stopShimmer()
|
||||||
|
|
||||||
resultErrorText.text = data.errorString
|
resultErrorText.text = data.errorString
|
||||||
|
|
||||||
homeReloadConnectionerror.setOnClickListener(apiChangeClickListener)
|
homeReloadConnectionerror.setOnClickListener(apiChangeClickListener)
|
||||||
|
|
||||||
homeReloadConnectionOpenInBrowser.setOnClickListener { view ->
|
homeReloadConnectionOpenInBrowser.setOnClickListener { view ->
|
||||||
val validAPIs = apis//.filter { api -> api.hasMainPage }
|
val validAPIs = apis//.filter { api -> api.hasMainPage }
|
||||||
|
|
||||||
|
@ -598,7 +630,6 @@ class HomeFragment : Fragment() {
|
||||||
|
|
||||||
//context?.fixPaddingStatusbarView(home_statusbar)
|
//context?.fixPaddingStatusbarView(home_statusbar)
|
||||||
//context?.fixPaddingStatusbar(home_padding)
|
//context?.fixPaddingStatusbar(home_padding)
|
||||||
fixPaddingStatusbar(binding?.homeLoadingStatusbar)
|
|
||||||
|
|
||||||
observeNullable(homeViewModel.popup) { item ->
|
observeNullable(homeViewModel.popup) { item ->
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
|
@ -620,52 +651,13 @@ class HomeFragment : Fragment() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
binding?.homeMasterRecycler?.adapter =
|
|
||||||
HomeParentItemAdapterPreview(
|
|
||||||
mutableListOf(),
|
|
||||||
homeViewModel
|
|
||||||
)
|
|
||||||
|
|
||||||
homeViewModel.reloadStored()
|
homeViewModel.reloadStored()
|
||||||
//loadHomePage(false)
|
//loadHomePage(false)
|
||||||
binding?.homeMasterRecycler?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
|
||||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
|
||||||
|
|
||||||
binding?.apply {
|
|
||||||
if (dy > 0) { //check for scroll down
|
|
||||||
homeApiFab.shrink() // hide
|
|
||||||
homeRandom.shrink()
|
|
||||||
} else if (dy < -5) {
|
|
||||||
if (!isTvSettings()) {
|
|
||||||
homeApiFab.extend() // show
|
|
||||||
homeRandom.extend()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
super.onScrolled(recyclerView, dx, dy)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// nice profile pic on homepage
|
// nice profile pic on homepage
|
||||||
//home_profile_picture_holder?.isVisible = false
|
//home_profile_picture_holder?.isVisible = false
|
||||||
// just in case
|
// just in case
|
||||||
binding?.apply {
|
|
||||||
if (isTvSettings()) {
|
|
||||||
homeApiFab.isVisible = false
|
|
||||||
if (isTrueTvSettings()) {
|
|
||||||
homeChangeApiLoading.isVisible = true
|
|
||||||
homeChangeApiLoading.isFocusable = true
|
|
||||||
homeChangeApiLoading.isFocusableInTouchMode = true
|
|
||||||
}
|
|
||||||
// home_bookmark_select?.isFocusable = true
|
|
||||||
// home_bookmark_select?.isFocusableInTouchMode = true
|
|
||||||
} else {
|
|
||||||
homeApiFab.isVisible = true
|
|
||||||
homeChangeApiLoading.isVisible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//TODO READD THIS
|
//TODO READD THIS
|
||||||
/*for (syncApi in OAuth2Apis) {
|
/*for (syncApi in OAuth2Apis) {
|
||||||
val login = syncApi.loginInfo()
|
val login = syncApi.loginInfo()
|
||||||
|
|
|
@ -401,7 +401,7 @@ class HomeParentItemAdapterPreview(
|
||||||
observe(viewModel.bookmarks) {
|
observe(viewModel.bookmarks) {
|
||||||
updateBookmarks(it)
|
updateBookmarks(it)
|
||||||
}
|
}
|
||||||
observe(viewModel.availableWatchStatusTypes) { (visible, checked) ->
|
observe(viewModel.availableWatchStatusTypes) { (checked, visible) ->
|
||||||
for ((chip, watch) in toggleList) {
|
for ((chip, watch) in toggleList) {
|
||||||
chip.apply {
|
chip.apply {
|
||||||
isVisible = visible.contains(watch)
|
isVisible = visible.contains(watch)
|
||||||
|
|
|
@ -74,15 +74,15 @@ abstract class AbstractPlayerFragment(
|
||||||
var isBuffering = true
|
var isBuffering = true
|
||||||
protected open var hasPipModeSupport = true
|
protected open var hasPipModeSupport = true
|
||||||
|
|
||||||
lateinit var playerPausePlayHolderHolder : FrameLayout
|
var playerPausePlayHolderHolder : FrameLayout? = null
|
||||||
lateinit var playerPausePlay : ImageView
|
var playerPausePlay : ImageView? = null
|
||||||
lateinit var playerBuffering : ProgressBar
|
var playerBuffering : ProgressBar? = null
|
||||||
var playerView : PlayerView? = null
|
var playerView : PlayerView? = null
|
||||||
var piphide : FrameLayout? = null
|
var piphide : FrameLayout? = null
|
||||||
var subtitleHolder : FrameLayout? = null
|
var subtitleHolder : FrameLayout? = null
|
||||||
|
|
||||||
@LayoutRes
|
@LayoutRes
|
||||||
protected var layout: Int = R.layout.fragment_player
|
protected open var layout: Int = R.layout.fragment_player
|
||||||
|
|
||||||
open fun nextEpisode() {
|
open fun nextEpisode() {
|
||||||
throw NotImplementedError()
|
throw NotImplementedError()
|
||||||
|
@ -141,15 +141,15 @@ abstract class AbstractPlayerFragment(
|
||||||
|
|
||||||
isBuffering = CSPlayerLoading.IsBuffering == isPlaying
|
isBuffering = CSPlayerLoading.IsBuffering == isPlaying
|
||||||
if (isBuffering) {
|
if (isBuffering) {
|
||||||
playerPausePlayHolderHolder.isVisible = false
|
playerPausePlayHolderHolder?.isVisible = false
|
||||||
playerBuffering.isVisible = true
|
playerBuffering?.isVisible = true
|
||||||
} else {
|
} else {
|
||||||
playerPausePlayHolderHolder.isVisible = true
|
playerPausePlayHolderHolder?.isVisible = true
|
||||||
playerBuffering.isVisible = false
|
playerBuffering?.isVisible = false
|
||||||
|
|
||||||
if (wasPlaying != isPlaying) {
|
if (wasPlaying != isPlaying) {
|
||||||
playerPausePlay.setImageResource(if (isPlayingRightNow) R.drawable.play_to_pause else R.drawable.pause_to_play)
|
playerPausePlay?.setImageResource(if (isPlayingRightNow) R.drawable.play_to_pause else R.drawable.pause_to_play)
|
||||||
val drawable = playerPausePlay.drawable
|
val drawable = playerPausePlay?.drawable
|
||||||
|
|
||||||
var startedAnimation = false
|
var startedAnimation = false
|
||||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
|
||||||
|
@ -171,10 +171,10 @@ abstract class AbstractPlayerFragment(
|
||||||
|
|
||||||
// somehow the phone is wacked
|
// somehow the phone is wacked
|
||||||
if (!startedAnimation) {
|
if (!startedAnimation) {
|
||||||
playerPausePlay.setImageResource(if (isPlayingRightNow) R.drawable.netflix_pause else R.drawable.netflix_play)
|
playerPausePlay?.setImageResource(if (isPlayingRightNow) R.drawable.netflix_pause else R.drawable.netflix_play)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
playerPausePlay.setImageResource(if (isPlayingRightNow) R.drawable.netflix_pause else R.drawable.netflix_play)
|
playerPausePlay?.setImageResource(if (isPlayingRightNow) R.drawable.netflix_pause else R.drawable.netflix_play)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
@ -22,18 +21,16 @@ import com.google.android.material.chip.ChipDrawable
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||||
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
|
||||||
import com.lagradost.cloudstream3.DubStatus
|
import com.lagradost.cloudstream3.DubStatus
|
||||||
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.SearchResponse
|
import com.lagradost.cloudstream3.SearchResponse
|
||||||
import com.lagradost.cloudstream3.TvType
|
import com.lagradost.cloudstream3.TvType
|
||||||
import com.lagradost.cloudstream3.mvvm.*
|
import com.lagradost.cloudstream3.mvvm.*
|
||||||
import com.lagradost.cloudstream3.services.SubscriptionWorkManager
|
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
|
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
|
||||||
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
|
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
|
||||||
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
|
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
|
||||||
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
|
import com.lagradost.cloudstream3.ui.player.FullScreenPlayer
|
||||||
import com.lagradost.cloudstream3.ui.result.EpisodeAdapter.Companion.getPlayerAction
|
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.isTrueTvSettings
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||||
|
@ -47,7 +44,6 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.getVideoWatchState
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
||||||
import kotlinx.android.synthetic.main.fragment_result.download_button
|
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_items
|
||||||
|
@ -89,10 +85,8 @@ 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_tag_holder
|
||||||
import kotlinx.android.synthetic.main.fragment_result.result_title
|
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.result_vpn
|
||||||
import kotlinx.android.synthetic.main.fragment_result_swipe.*
|
import kotlinx.android.synthetic.main.fragment_result_tv.result_resume_series_button_play
|
||||||
import kotlinx.android.synthetic.main.fragment_result_tv.*
|
import kotlinx.android.synthetic.main.fragment_result_tv.temporary_no_focus
|
||||||
import kotlinx.android.synthetic.main.result_sync.*
|
|
||||||
import kotlinx.android.synthetic.main.trailer_custom_layout.*
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
@ -194,7 +188,7 @@ fun ResultEpisode.getWatchProgress(): Float {
|
||||||
return (getDisplayPosition() / 1000).toFloat() / (duration / 1000).toFloat()
|
return (getDisplayPosition() / 1000).toFloat() / (duration / 1000).toFloat()
|
||||||
}
|
}
|
||||||
|
|
||||||
open class ResultFragment : ResultTrailerPlayer() {
|
open class ResultFragment : FullScreenPlayer() {
|
||||||
companion object {
|
companion object {
|
||||||
const val URL_BUNDLE = "url"
|
const val URL_BUNDLE = "url"
|
||||||
const val API_NAME_BUNDLE = "apiName"
|
const val API_NAME_BUNDLE = "apiName"
|
||||||
|
@ -251,7 +245,9 @@ open class ResultFragment : ResultTrailerPlayer() {
|
||||||
|
|
||||||
protected lateinit var viewModel: ResultViewModel2 //by activityViewModels()
|
protected lateinit var viewModel: ResultViewModel2 //by activityViewModels()
|
||||||
protected lateinit var syncModel: SyncViewModel
|
protected lateinit var syncModel: SyncViewModel
|
||||||
protected open val resultLayout = R.layout.fragment_result_swipe
|
//protected open val resultLayout = R.layout.fragment_result_swipe
|
||||||
|
|
||||||
|
override var layout = R.layout.fragment_result_swipe
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -263,7 +259,8 @@ open class ResultFragment : ResultTrailerPlayer() {
|
||||||
syncModel =
|
syncModel =
|
||||||
ViewModelProvider(this)[SyncViewModel::class.java]
|
ViewModelProvider(this)[SyncViewModel::class.java]
|
||||||
|
|
||||||
return inflater.inflate(resultLayout, container, false)
|
return super.onCreateView(inflater, container, savedInstanceState)
|
||||||
|
//return inflater.inflate(resultLayout, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
@ -285,167 +282,12 @@ open class ResultFragment : ResultTrailerPlayer() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 0 = LOADING, 1 = ERROR LOADING, 2 = LOADED
|
|
||||||
private fun updateVisStatus(state: Int) {
|
|
||||||
when (state) {
|
|
||||||
0 -> {
|
|
||||||
result_bookmark_fab?.isGone = true
|
|
||||||
result_loading?.isVisible = true
|
|
||||||
result_finish_loading?.isVisible = false
|
|
||||||
result_loading_error?.isVisible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
1 -> {
|
|
||||||
result_bookmark_fab?.isGone = true
|
|
||||||
result_loading?.isVisible = false
|
|
||||||
result_finish_loading?.isVisible = false
|
|
||||||
result_loading_error?.isVisible = true
|
|
||||||
result_reload_connection_open_in_browser?.isVisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
2 -> {
|
|
||||||
result_bookmark_fab?.isGone = isTrueTvSettings()
|
|
||||||
result_bookmark_fab?.extend()
|
|
||||||
//if (result_bookmark_button?.context?.isTrueTvSettings() == true) {
|
|
||||||
// when {
|
|
||||||
// result_play_movie?.isVisible == true -> {
|
|
||||||
// result_play_movie?.requestFocus()
|
|
||||||
// }
|
|
||||||
// result_resume_series_button?.isVisible == true -> {
|
|
||||||
// result_resume_series_button?.requestFocus()
|
|
||||||
// }
|
|
||||||
// else -> {
|
|
||||||
// result_bookmark_button?.requestFocus()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
result_loading?.isVisible = false
|
|
||||||
result_finish_loading?.isVisible = true
|
|
||||||
result_loading_error?.isVisible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateUI() {
|
private fun updateUI() {
|
||||||
syncModel.updateUserData()
|
syncModel.updateUserData()
|
||||||
viewModel.reloadEpisodes()
|
viewModel.reloadEpisodes()
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun updateMovie(data: Resource<Pair<UiText, ResultEpisode>>?) {
|
|
||||||
when (data) {
|
|
||||||
is Resource.Success -> {
|
|
||||||
data.value.let { (text, ep) ->
|
|
||||||
result_play_movie.setText(text)
|
|
||||||
result_play_movie?.setOnClickListener {
|
|
||||||
viewModel.handleAction(
|
|
||||||
activity,
|
|
||||||
EpisodeClickEvent(ACTION_CLICK_DEFAULT, ep)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
result_play_movie?.setOnLongClickListener {
|
|
||||||
viewModel.handleAction(
|
|
||||||
activity,
|
|
||||||
EpisodeClickEvent(ACTION_SHOW_OPTIONS, ep)
|
|
||||||
)
|
|
||||||
return@setOnLongClickListener true
|
|
||||||
}
|
|
||||||
|
|
||||||
val show =
|
|
||||||
viewModel.currentRepo?.api?.hasDownloadSupport == true && !isTvSettings()
|
|
||||||
if (show) {
|
|
||||||
download_button?.setDefaultClickListener(
|
|
||||||
VideoDownloadHelper.DownloadEpisodeCached(
|
|
||||||
ep.name,
|
|
||||||
ep.poster,
|
|
||||||
0,
|
|
||||||
null,
|
|
||||||
ep.id,
|
|
||||||
ep.id,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
System.currentTimeMillis(),
|
|
||||||
)
|
|
||||||
) { click ->
|
|
||||||
when (click.action) {
|
|
||||||
DOWNLOAD_ACTION_DOWNLOAD -> {
|
|
||||||
viewModel.handleAction(
|
|
||||||
activity,
|
|
||||||
EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, ep)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> handleDownloadClick(click)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
download_button?.isVisible = show
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
download_button?.isVisible = false
|
|
||||||
result_play_movie?.isVisible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun updateEpisodes(episodes: Resource<List<ResultEpisode>>?) {
|
|
||||||
when (episodes) {
|
|
||||||
is Resource.Loading -> {
|
|
||||||
result_episode_loading?.isVisible = true
|
|
||||||
result_episodes?.isVisible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
is Resource.Success -> {
|
|
||||||
result_episodes?.isVisible = true
|
|
||||||
result_episode_loading?.isVisible = false
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Do not use this.isTv, that is the player
|
|
||||||
val isTv = isTvSettings()
|
|
||||||
val hasEpisodes =
|
|
||||||
!(result_episodes?.adapter as? EpisodeAdapter?)?.cardList.isNullOrEmpty()
|
|
||||||
|
|
||||||
if (isTv && hasEpisodes) {
|
|
||||||
// Make it impossible to focus anywhere else!
|
|
||||||
temporary_no_focus?.isFocusable = true
|
|
||||||
temporary_no_focus?.requestFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
(result_episodes?.adapter as? EpisodeAdapter)?.updateList(episodes.value)
|
|
||||||
|
|
||||||
if (isTv && hasEpisodes) main {
|
|
||||||
delay(500)
|
|
||||||
temporary_no_focus?.isFocusable = false
|
|
||||||
// This might make some people sad as it changes the focus when leaving an episode :(
|
|
||||||
result_episodes?.requestFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
result_episode_loading?.isVisible = false
|
|
||||||
result_episodes?.isVisible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class StoredData(
|
data class StoredData(
|
||||||
val url: String?,
|
val url: String?,
|
||||||
val apiName: String,
|
val apiName: String,
|
||||||
|
@ -455,7 +297,7 @@ open class ResultFragment : ResultTrailerPlayer() {
|
||||||
val playerAction: Int
|
val playerAction: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getStoredData(context: Context): StoredData? {
|
fun getStoredData(context: Context): StoredData? {
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
val url = arguments?.getString(URL_BUNDLE)
|
val url = arguments?.getString(URL_BUNDLE)
|
||||||
val apiName = arguments?.getString(API_NAME_BUNDLE) ?: return null
|
val apiName = arguments?.getString(API_NAME_BUNDLE) ?: return null
|
||||||
|
@ -537,7 +379,6 @@ open class ResultFragment : ResultTrailerPlayer() {
|
||||||
context?.updateHasTrailers()
|
context?.updateHasTrailers()
|
||||||
activity?.loadCache()
|
activity?.loadCache()
|
||||||
|
|
||||||
fixPaddingStatusbar(result_top_bar)
|
|
||||||
//activity?.fixPaddingStatusbar(result_barstatus)
|
//activity?.fixPaddingStatusbar(result_barstatus)
|
||||||
|
|
||||||
/* val backParameter = result_back.layoutParams as FrameLayout.LayoutParams
|
/* val backParameter = result_back.layoutParams as FrameLayout.LayoutParams
|
||||||
|
@ -554,35 +395,6 @@ open class ResultFragment : ResultTrailerPlayer() {
|
||||||
val storedData = (activity ?: context)?.let {
|
val storedData = (activity ?: context)?.let {
|
||||||
getStoredData(it)
|
getStoredData(it)
|
||||||
}
|
}
|
||||||
syncModel.addFromUrl(storedData?.url)
|
|
||||||
|
|
||||||
val api = getApiFromNameNull(storedData?.apiName)
|
|
||||||
|
|
||||||
result_episodes?.adapter =
|
|
||||||
EpisodeAdapter(
|
|
||||||
api?.hasDownloadSupport == true && !isTvSettings(),
|
|
||||||
{ episodeClick ->
|
|
||||||
viewModel.handleAction(activity, episodeClick)
|
|
||||||
},
|
|
||||||
{ downloadClickEvent ->
|
|
||||||
handleDownloadClick(downloadClickEvent)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// This is to band-aid FireTV navigation
|
// This is to band-aid FireTV navigation
|
||||||
val isTv = isTvSettings()
|
val isTv = isTvSettings()
|
||||||
|
@ -638,215 +450,6 @@ open class ResultFragment : ResultTrailerPlayer() {
|
||||||
result_resume_series_button_play?.setOnClickListener(click)
|
result_resume_series_button_play?.setOnClickListener(click)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
observeNullable(viewModel.episodes) { episodes ->
|
|
||||||
updateEpisodes(episodes)
|
|
||||||
}
|
|
||||||
|
|
||||||
result_cast_items?.setOnFocusChangeListener { _, hasFocus ->
|
|
||||||
// Always escape focus
|
|
||||||
if (hasFocus) result_bookmark_button?.requestFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
observe(viewModel.trailers) { trailers ->
|
|
||||||
setTrailers(trailers.flatMap { it.mirros }) // I dont care about subtitles yet!
|
|
||||||
}
|
|
||||||
|
|
||||||
observe(viewModel.recommendations) { recommendations ->
|
|
||||||
setRecommendations(recommendations, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
observeNullable(viewModel.movie) { data ->
|
|
||||||
updateMovie(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
observe(viewModel.page) { data ->
|
|
||||||
if (data == null) return@observe
|
|
||||||
when (data) {
|
|
||||||
is Resource.Success -> {
|
|
||||||
val d = data.value
|
|
||||||
|
|
||||||
updateVisStatus(2)
|
|
||||||
|
|
||||||
result_vpn.setText(d.vpnText)
|
|
||||||
result_info.setText(d.metaText)
|
|
||||||
result_no_episodes.setText(d.noEpisodesFoundText)
|
|
||||||
result_title.setText(d.titleText)
|
|
||||||
result_meta_site.setText(d.apiName)
|
|
||||||
result_meta_type.setText(d.typeText)
|
|
||||||
result_meta_year.setText(d.yearText)
|
|
||||||
result_meta_duration.setText(d.durationText)
|
|
||||||
result_meta_rating.setText(d.ratingText)
|
|
||||||
result_cast_text.setText(d.actorsText)
|
|
||||||
result_next_airing.setText(d.nextAiringEpisode)
|
|
||||||
result_next_airing_time.setText(d.nextAiringDate)
|
|
||||||
result_poster.setImage(d.posterImage)
|
|
||||||
result_poster_background.setImage(d.posterBackgroundImage)
|
|
||||||
//result_trailer_thumbnail.setImage(d.posterBackgroundImage, fadeIn = false)
|
|
||||||
|
|
||||||
if (d.posterImage != null && !isTrueTvSettings())
|
|
||||||
result_poster_holder?.setOnClickListener {
|
|
||||||
try {
|
|
||||||
context?.let { ctx ->
|
|
||||||
runBlocking {
|
|
||||||
val sourceBuilder = AlertDialog.Builder(ctx)
|
|
||||||
sourceBuilder.setView(R.layout.result_poster)
|
|
||||||
|
|
||||||
val sourceDialog = sourceBuilder.create()
|
|
||||||
sourceDialog.show()
|
|
||||||
|
|
||||||
sourceDialog.findViewById<ImageView?>(R.id.imgPoster)
|
|
||||||
?.apply {
|
|
||||||
setImage(d.posterImage)
|
|
||||||
setOnClickListener {
|
|
||||||
sourceDialog.dismissSafe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
result_cast_items?.isVisible = d.actors != null
|
|
||||||
(result_cast_items?.adapter as? ActorAdaptor)?.apply {
|
|
||||||
updateList(d.actors ?: emptyList())
|
|
||||||
}
|
|
||||||
|
|
||||||
observeNullable(viewModel.subscribeStatus) { isSubscribed ->
|
|
||||||
result_subscribe?.isVisible = isSubscribed != null
|
|
||||||
if (isSubscribed == null) return@observeNullable
|
|
||||||
|
|
||||||
val drawable = if (isSubscribed) {
|
|
||||||
R.drawable.ic_baseline_notifications_active_24
|
|
||||||
} else {
|
|
||||||
R.drawable.baseline_notifications_none_24
|
|
||||||
}
|
|
||||||
|
|
||||||
result_subscribe?.setImageResource(drawable)
|
|
||||||
}
|
|
||||||
|
|
||||||
result_subscribe?.setOnClickListener {
|
|
||||||
val isSubscribed =
|
|
||||||
viewModel.toggleSubscriptionStatus() ?: return@setOnClickListener
|
|
||||||
|
|
||||||
val message = if (isSubscribed) {
|
|
||||||
// Kinda icky to have this here, but it works.
|
|
||||||
SubscriptionWorkManager.enqueuePeriodicWork(context)
|
|
||||||
R.string.subscription_new
|
|
||||||
} else {
|
|
||||||
R.string.subscription_deleted
|
|
||||||
}
|
|
||||||
|
|
||||||
val name = (viewModel.page.value as? Resource.Success)?.value?.title
|
|
||||||
?: txt(R.string.no_data).asStringNull(context) ?: ""
|
|
||||||
showToast(txt(message, name), Toast.LENGTH_SHORT)
|
|
||||||
}
|
|
||||||
|
|
||||||
result_open_in_browser?.isVisible = d.url.startsWith("http")
|
|
||||||
result_open_in_browser?.setOnClickListener {
|
|
||||||
val i = Intent(ACTION_VIEW)
|
|
||||||
i.data = Uri.parse(d.url)
|
|
||||||
try {
|
|
||||||
startActivity(i)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result_search?.setOnClickListener {
|
|
||||||
QuickSearchFragment.pushSearch(activity, d.title)
|
|
||||||
}
|
|
||||||
|
|
||||||
result_share?.setOnClickListener {
|
|
||||||
try {
|
|
||||||
val i = Intent(ACTION_SEND)
|
|
||||||
i.type = "text/plain"
|
|
||||||
i.putExtra(EXTRA_SUBJECT, d.title)
|
|
||||||
i.putExtra(EXTRA_TEXT, d.url)
|
|
||||||
startActivity(createChooser(i, d.title))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (syncModel.addSyncs(d.syncData)) {
|
|
||||||
syncModel.updateMetaAndUser()
|
|
||||||
syncModel.updateSynced()
|
|
||||||
} else {
|
|
||||||
syncModel.addFromUrl(d.url)
|
|
||||||
}
|
|
||||||
|
|
||||||
result_description.setTextHtml(d.plotText)
|
|
||||||
if (this !is ResultFragmentTv) // dont want this clickable on tv layout
|
|
||||||
result_description?.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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
d.comingSoon.let { soon ->
|
|
||||||
result_coming_soon?.isVisible = soon
|
|
||||||
result_data_holder?.isGone = soon
|
|
||||||
}
|
|
||||||
|
|
||||||
val tags = d.tags
|
|
||||||
result_tag_holder?.isVisible = tags.isNotEmpty()
|
|
||||||
result_tag?.apply {
|
|
||||||
removeAllViews()
|
|
||||||
tags.forEach { tag ->
|
|
||||||
val chip = Chip(context)
|
|
||||||
val chipDrawable = ChipDrawable.createFromAttributes(
|
|
||||||
context,
|
|
||||||
null,
|
|
||||||
0,
|
|
||||||
R.style.ChipFilled
|
|
||||||
)
|
|
||||||
chip.setChipDrawable(chipDrawable)
|
|
||||||
chip.text = tag
|
|
||||||
chip.isChecked = false
|
|
||||||
chip.isCheckable = false
|
|
||||||
chip.isFocusable = false
|
|
||||||
chip.isClickable = false
|
|
||||||
chip.setTextColor(context.colorFromAttribute(R.attr.textColor))
|
|
||||||
addView(chip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if (tags.isNotEmpty()) {
|
|
||||||
//result_tag_holder?.visibility = VISIBLE
|
|
||||||
//val isOnTv = isTrueTvSettings()
|
|
||||||
|
|
||||||
|
|
||||||
/*for ((index, tag) in tags.withIndex()) {
|
|
||||||
val viewBtt = layoutInflater.inflate(R.layout.result_tag, null)
|
|
||||||
val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card)
|
|
||||||
btt.text = tag
|
|
||||||
btt.isFocusable = !isOnTv
|
|
||||||
btt.isClickable = !isOnTv
|
|
||||||
result_tag?.addView(viewBtt, index)
|
|
||||||
}*/
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
is Resource.Failure -> {
|
|
||||||
result_error_text.text = storedData?.url?.plus("\n") + data.errorString
|
|
||||||
updateVisStatus(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
is Resource.Loading -> {
|
|
||||||
updateVisStatus(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context?.let { ctx ->
|
context?.let { ctx ->
|
||||||
|
|
||||||
//result_bookmark_button?.isVisible = ctx.isTvSettings()
|
//result_bookmark_button?.isVisible = ctx.isTvSettings()
|
||||||
|
@ -879,17 +482,6 @@ open class ResultFragment : ResultTrailerPlayer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result_open_in_browser?.isVisible = storedData.url.startsWith("http")
|
|
||||||
result_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
|
// bloats the navigation on tv
|
||||||
if (!isTrueTvSettings()) {
|
if (!isTrueTvSettings()) {
|
||||||
result_meta_site?.setOnClickListener {
|
result_meta_site?.setOnClickListener {
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
package com.lagradost.cloudstream3.ui.result
|
package com.lagradost.cloudstream3.ui.result
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
import android.content.Intent
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
|
@ -15,6 +18,7 @@ import android.view.animation.DecelerateInterpolator
|
||||||
import android.widget.AbsListView
|
import android.widget.AbsListView
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.widget.NestedScrollView
|
import androidx.core.widget.NestedScrollView
|
||||||
|
@ -41,12 +45,19 @@ import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
import com.lagradost.cloudstream3.mvvm.observeNullable
|
import com.lagradost.cloudstream3.mvvm.observeNullable
|
||||||
|
import com.lagradost.cloudstream3.services.SubscriptionWorkManager
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
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.CSPlayerEvent
|
||||||
|
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.html
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
|
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
|
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
|
||||||
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
||||||
|
@ -55,14 +66,25 @@ import com.lagradost.cloudstream3.utils.UIHelper
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.populateChips
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
|
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||||
|
import kotlinx.android.synthetic.main.download_button.result_download_movie
|
||||||
|
import kotlinx.android.synthetic.main.fragment_result.download_button
|
||||||
|
import kotlinx.android.synthetic.main.fragment_result.result_episode_loading
|
||||||
|
import kotlinx.android.synthetic.main.fragment_result.result_episodes
|
||||||
|
import kotlinx.android.synthetic.main.fragment_result.result_play_movie
|
||||||
|
import kotlinx.android.synthetic.main.fragment_result_tv.temporary_no_focus
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
|
|
||||||
class ResultFragmentPhone : ResultFragment() {
|
open class ResultFragmentPhone : ResultFragment(),
|
||||||
private var binding: FragmentResultSwipeBinding? = null
|
PanelsChildGestureRegionObserver.GestureRegionsListener {
|
||||||
private var resultBinding: FragmentResultBinding? = null
|
protected var binding: FragmentResultSwipeBinding? = null
|
||||||
private var recommendationBinding: ResultRecommendationsBinding? = null
|
protected var resultBinding: FragmentResultBinding? = null
|
||||||
private var syncBinding: ResultSyncBinding? = null
|
protected var recommendationBinding: ResultRecommendationsBinding? = null
|
||||||
|
protected var syncBinding: ResultSyncBinding? = null
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
|
@ -205,68 +227,57 @@ class ResultFragmentPhone : ResultFragment() {
|
||||||
|
|
||||||
var selectSeason: String? = null
|
var selectSeason: String? = null
|
||||||
|
|
||||||
|
private fun setUrl(url : String?) {
|
||||||
|
if(url == null) {
|
||||||
|
binding?.resultOpenInBrowser?.isVisible = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
binding?.resultOpenInBrowser?.apply {
|
||||||
|
isVisible = url.startsWith("http")
|
||||||
|
setOnClickListener {
|
||||||
|
val i = Intent(Intent.ACTION_VIEW)
|
||||||
|
i.data = Uri.parse(url)
|
||||||
|
try {
|
||||||
|
startActivity(i)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
val apiName = arguments?.getString(API_NAME_BUNDLE) ?: return
|
val apiName = arguments?.getString(API_NAME_BUNDLE) ?: return
|
||||||
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
UIHelper.fixPaddingStatusbar(binding?.resultTopBar)
|
||||||
|
val storedData = (activity ?: context)?.let {
|
||||||
playerBinding?.playerOpenSource?.setOnClickListener {
|
getStoredData(it)
|
||||||
currentTrailers.getOrNull(currentTrailerIndex)?.let {
|
|
||||||
context?.openBrowser(it.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding?.resultOverlappingPanels?.setStartPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
|
|
||||||
binding?.resultOverlappingPanels?.setEndPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
|
|
||||||
|
|
||||||
recommendationBinding?.resultRecommendationsList?.apply {
|
|
||||||
spanCount = 3
|
|
||||||
adapter =
|
|
||||||
SearchAdapter(
|
|
||||||
ArrayList(),
|
|
||||||
this,
|
|
||||||
) { callback ->
|
|
||||||
SearchHelper.handleSearchClickCallback(callback)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelsChildGestureRegionObserver.Provider.get().addGestureRegionsUpdateListener(this)
|
setUrl(storedData?.url)
|
||||||
|
syncModel.addFromUrl(storedData?.url)
|
||||||
|
|
||||||
resultBinding?.resultCastItems?.let {
|
val api = APIHolder.getApiFromNameNull(apiName)
|
||||||
|
|
||||||
|
resultBinding?.apply {
|
||||||
|
resultEpisodes.adapter =
|
||||||
|
EpisodeAdapter(
|
||||||
|
api?.hasDownloadSupport == true,
|
||||||
|
{ episodeClick ->
|
||||||
|
viewModel.handleAction(activity, episodeClick)
|
||||||
|
},
|
||||||
|
{ downloadClickEvent ->
|
||||||
|
DownloadButtonSetup.handleDownloadClick(downloadClickEvent)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
resultCastItems.let {
|
||||||
PanelsChildGestureRegionObserver.Provider.get().register(it)
|
PanelsChildGestureRegionObserver.Provider.get().register(it)
|
||||||
}
|
}
|
||||||
|
resultScroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||||
|
|
||||||
binding?.resultBack?.setOnClickListener {
|
|
||||||
activity?.popCurrentPage()
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
result_bookmark_button?.setOnClickListener {
|
|
||||||
it.popupMenuNoIcons(
|
|
||||||
items = WatchType.values()
|
|
||||||
.map { watchType -> Pair(watchType.internalId, watchType.stringRes) },
|
|
||||||
//.map { watchType -> Triple(watchType.internalId, watchType.iconRes, watchType.stringRes) },
|
|
||||||
) {
|
|
||||||
viewModel.updateWatchStatus(WatchType.fromInternalId(this.itemId))
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
binding?.resultMiniSync?.adapter = ImageAdapter(
|
|
||||||
nextFocusDown = R.id.result_sync_set_score,
|
|
||||||
clickCallback = { action ->
|
|
||||||
if (action == IMAGE_CLICK || action == IMAGE_LONG_CLICK) {
|
|
||||||
if (binding?.resultOverlappingPanels?.getSelectedPanel()?.ordinal == 1) {
|
|
||||||
binding?.resultOverlappingPanels?.openStartPanel()
|
|
||||||
} else {
|
|
||||||
binding?.resultOverlappingPanels?.closePanels()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
resultBinding?.resultScroll?.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
|
||||||
val dy = scrollY - oldScrollY
|
val dy = scrollY - oldScrollY
|
||||||
if (dy > 0) { //check for scroll down
|
if (dy > 0) { //check for scroll down
|
||||||
binding?.resultBookmarkFab?.shrink()
|
binding?.resultBookmarkFab?.shrink()
|
||||||
|
@ -280,11 +291,43 @@ class ResultFragmentPhone : ResultFragment() {
|
||||||
player.handleEvent(CSPlayerEvent.Pause)
|
player.handleEvent(CSPlayerEvent.Pause)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//result_poster_blur_holder?.translationY = -scrollY.toFloat()
|
|
||||||
})
|
})
|
||||||
val api = APIHolder.getApiFromNameNull(apiName)
|
}
|
||||||
|
|
||||||
binding?.mediaRouteButton?.apply {
|
binding?.apply {
|
||||||
|
resultOverlappingPanels.setStartPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
|
||||||
|
resultOverlappingPanels.setEndPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
|
||||||
|
resultBack.setOnClickListener {
|
||||||
|
activity?.popCurrentPage()
|
||||||
|
}
|
||||||
|
resultMiniSync.adapter = ImageAdapter(
|
||||||
|
nextFocusDown = R.id.result_sync_set_score,
|
||||||
|
clickCallback = { action ->
|
||||||
|
if (action == IMAGE_CLICK || action == IMAGE_LONG_CLICK) {
|
||||||
|
if (binding?.resultOverlappingPanels?.getSelectedPanel()?.ordinal == 1) {
|
||||||
|
binding?.resultOverlappingPanels?.openStartPanel()
|
||||||
|
} else {
|
||||||
|
binding?.resultOverlappingPanels?.closePanels()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
resultSubscribe.setOnClickListener {
|
||||||
|
val isSubscribed =
|
||||||
|
viewModel.toggleSubscriptionStatus() ?: return@setOnClickListener
|
||||||
|
|
||||||
|
val message = if (isSubscribed) {
|
||||||
|
// Kinda icky to have this here, but it works.
|
||||||
|
SubscriptionWorkManager.enqueuePeriodicWork(context)
|
||||||
|
R.string.subscription_new
|
||||||
|
} else {
|
||||||
|
R.string.subscription_deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
val name = (viewModel.page.value as? Resource.Success)?.value?.title
|
||||||
|
?: txt(R.string.no_data).asStringNull(context) ?: ""
|
||||||
|
CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
|
||||||
|
}
|
||||||
|
mediaRouteButton.apply {
|
||||||
val chromecastSupport = api?.hasChromecastSupport == true
|
val chromecastSupport = api?.hasChromecastSupport == true
|
||||||
alpha = if (chromecastSupport) 1f else 0.3f
|
alpha = if (chromecastSupport) 1f else 0.3f
|
||||||
if (!chromecastSupport) {
|
if (!chromecastSupport) {
|
||||||
|
@ -311,6 +354,203 @@ class ResultFragmentPhone : ResultFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playerBinding?.apply {
|
||||||
|
playerOpenSource.setOnClickListener {
|
||||||
|
currentTrailers.getOrNull(currentTrailerIndex)?.let {
|
||||||
|
context?.openBrowser(it.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recommendationBinding?.apply {
|
||||||
|
resultRecommendationsList.apply {
|
||||||
|
spanCount = 3
|
||||||
|
adapter =
|
||||||
|
SearchAdapter(
|
||||||
|
ArrayList(),
|
||||||
|
this,
|
||||||
|
) { callback ->
|
||||||
|
SearchHelper.handleSearchClickCallback(callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PanelsChildGestureRegionObserver.Provider.get().addGestureRegionsUpdateListener(this)
|
||||||
|
|
||||||
|
/*
|
||||||
|
result_bookmark_button?.setOnClickListener {
|
||||||
|
it.popupMenuNoIcons(
|
||||||
|
items = WatchType.values()
|
||||||
|
.map { watchType -> Pair(watchType.internalId, watchType.stringRes) },
|
||||||
|
//.map { watchType -> Triple(watchType.internalId, watchType.iconRes, watchType.stringRes) },
|
||||||
|
) {
|
||||||
|
viewModel.updateWatchStatus(WatchType.fromInternalId(this.itemId))
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
observeNullable(viewModel.subscribeStatus) { isSubscribed ->
|
||||||
|
binding?.resultSubscribe?.isVisible = isSubscribed != null
|
||||||
|
if (isSubscribed == null) return@observeNullable
|
||||||
|
|
||||||
|
val drawable = if (isSubscribed) {
|
||||||
|
R.drawable.ic_baseline_notifications_active_24
|
||||||
|
} else {
|
||||||
|
R.drawable.baseline_notifications_none_24
|
||||||
|
}
|
||||||
|
|
||||||
|
binding?.resultSubscribe?.setImageResource(drawable)
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(viewModel.trailers) { trailers ->
|
||||||
|
setTrailers(trailers.flatMap { it.mirros }) // I dont care about subtitles yet!
|
||||||
|
}
|
||||||
|
|
||||||
|
observeNullable(viewModel.episodes) { episodes ->
|
||||||
|
resultBinding?.apply {
|
||||||
|
// no failure?
|
||||||
|
resultEpisodeLoading.isVisible = episodes is Resource.Loading
|
||||||
|
resultEpisodes.isVisible = episodes is Resource.Success
|
||||||
|
if(episodes is Resource.Success) {
|
||||||
|
(resultEpisodes.adapter as? EpisodeAdapter)?.updateList(episodes.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
observeNullable(viewModel.movie) { data ->
|
||||||
|
resultBinding?.apply {
|
||||||
|
resultPlayMovie.isVisible = data is Resource.Success
|
||||||
|
downloadButton.isVisible =
|
||||||
|
data is Resource.Success && viewModel.currentRepo?.api?.hasDownloadSupport == true
|
||||||
|
|
||||||
|
(data as? Resource.Success)?.value?.let { (text, ep) ->
|
||||||
|
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
|
||||||
|
}
|
||||||
|
downloadButton.setDefaultClickListener(
|
||||||
|
VideoDownloadHelper.DownloadEpisodeCached(
|
||||||
|
ep.name,
|
||||||
|
ep.poster,
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
ep.id,
|
||||||
|
ep.id,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
System.currentTimeMillis(),
|
||||||
|
)
|
||||||
|
) { click ->
|
||||||
|
when (click.action) {
|
||||||
|
DOWNLOAD_ACTION_DOWNLOAD -> {
|
||||||
|
viewModel.handleAction(
|
||||||
|
activity,
|
||||||
|
EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, ep)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> DownloadButtonSetup.handleDownloadClick(click)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(viewModel.page) { data ->
|
||||||
|
if (data == null) return@observe
|
||||||
|
resultBinding?.apply {
|
||||||
|
(data as? Resource.Success)?.value?.let { d ->
|
||||||
|
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)
|
||||||
|
resultPosterBackground.setImage(d.posterBackgroundImage)
|
||||||
|
resultDescription.setTextHtml(d.plotText)
|
||||||
|
resultDescription.setOnClickListener { view ->
|
||||||
|
// todo bottom 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
populateChips(resultTag, d.tags)
|
||||||
|
|
||||||
|
resultComingSoon.isVisible = d.comingSoon
|
||||||
|
resultDataHolder.isGone = d.comingSoon
|
||||||
|
|
||||||
|
resultCastItems.isGone = d.actors.isNullOrEmpty()
|
||||||
|
(resultCastItems.adapter as? ActorAdaptor)?.updateList(d.actors ?: emptyList())
|
||||||
|
|
||||||
|
if (syncModel.addSyncs(d.syncData)) {
|
||||||
|
syncModel.updateMetaAndUser()
|
||||||
|
syncModel.updateSynced()
|
||||||
|
} else {
|
||||||
|
syncModel.addFromUrl(d.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding?.apply {
|
||||||
|
resultSearch.setOnClickListener {
|
||||||
|
QuickSearchFragment.pushSearch(activity, d.title)
|
||||||
|
}
|
||||||
|
|
||||||
|
resultShare.setOnClickListener {
|
||||||
|
try {
|
||||||
|
val i = Intent(Intent.ACTION_SEND)
|
||||||
|
i.type = "text/plain"
|
||||||
|
i.putExtra(Intent.EXTRA_SUBJECT, d.title)
|
||||||
|
i.putExtra(Intent.EXTRA_TEXT, d.url)
|
||||||
|
startActivity(Intent.createChooser(i, d.title))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setUrl(d.url)
|
||||||
|
resultBookmarkFab.apply {
|
||||||
|
isVisible = true
|
||||||
|
extend()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(data as? Resource.Failure)?.let { data ->
|
||||||
|
resultErrorText.text = (storedData?.url?.plus("\n") ?: "") + data.errorString
|
||||||
|
}
|
||||||
|
|
||||||
|
binding?.resultBookmarkFab?.isVisible = data is Resource.Success
|
||||||
|
resultFinishLoading.isVisible = data is Resource.Success
|
||||||
|
|
||||||
|
resultLoading.isVisible = data is Resource.Loading
|
||||||
|
|
||||||
|
resultLoadingError.isVisible = data is Resource.Failure
|
||||||
|
resultErrorText.isVisible = data is Resource.Failure
|
||||||
|
resultReloadConnectionOpenInBrowser.isVisible = data is Resource.Failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
observeNullable(viewModel.episodesCountText) { count ->
|
observeNullable(viewModel.episodesCountText) { count ->
|
||||||
resultBinding?.resultEpisodesText.setText(count)
|
resultBinding?.resultEpisodesText.setText(count)
|
||||||
|
@ -350,6 +590,7 @@ class ResultFragmentPhone : ResultFragment() {
|
||||||
(binding?.resultMiniSync?.adapter as? ImageAdapter)?.updateList(newList.mapNotNull { it.icon })
|
(binding?.resultMiniSync?.adapter as? ImageAdapter)?.updateList(newList.mapNotNull { it.icon })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var currentSyncProgress = 0
|
var currentSyncProgress = 0
|
||||||
fun setSyncMaxEpisodes(totalEpisodes: Int?) {
|
fun setSyncMaxEpisodes(totalEpisodes: Int?) {
|
||||||
syncBinding?.resultSyncEpisodes?.max = (totalEpisodes ?: 0) * 1000
|
syncBinding?.resultSyncEpisodes?.max = (totalEpisodes ?: 0) * 1000
|
||||||
|
@ -439,7 +680,22 @@ class ResultFragmentPhone : ResultFragment() {
|
||||||
}
|
}
|
||||||
binding?.resultOverlappingPanels?.setStartPanelLockState(if (closed) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED)
|
binding?.resultOverlappingPanels?.setStartPanelLockState(if (closed) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED)
|
||||||
}
|
}
|
||||||
|
observe(viewModel.recommendations) { recommendations ->
|
||||||
|
setRecommendations(recommendations, null)
|
||||||
|
}
|
||||||
|
observe(viewModel.episodeSynopsis) { description ->
|
||||||
|
// TODO bottom dialog
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
context?.let { ctx ->
|
context?.let { ctx ->
|
||||||
val arrayAdapter = ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
|
val arrayAdapter = ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
|
||||||
/*
|
/*
|
||||||
|
@ -653,7 +909,7 @@ class ResultFragmentPhone : ResultFragment() {
|
||||||
binding?.resultOverlappingPanels?.setChildGestureRegions(gestureRegions)
|
binding?.resultOverlappingPanels?.setChildGestureRegions(gestureRegions)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
private fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
||||||
val isInvalid = rec.isNullOrEmpty()
|
val isInvalid = rec.isNullOrEmpty()
|
||||||
val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName
|
val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package com.lagradost.cloudstream3.ui.result
|
package com.lagradost.cloudstream3.ui.result
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
@ -20,19 +22,32 @@ import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
import com.lagradost.cloudstream3.mvvm.observeNullable
|
import com.lagradost.cloudstream3.mvvm.observeNullable
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
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.ExtractorLinkGenerator
|
import com.lagradost.cloudstream3.ui.player.ExtractorLinkGenerator
|
||||||
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
|
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.html
|
||||||
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
|
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||||
|
import kotlinx.android.synthetic.main.fragment_result.download_button
|
||||||
|
import kotlinx.android.synthetic.main.fragment_result.result_episodes
|
||||||
|
import kotlinx.android.synthetic.main.fragment_result.result_play_movie
|
||||||
|
import kotlinx.android.synthetic.main.fragment_result_tv.temporary_no_focus
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
class ResultFragmentTv : ResultFragment() {
|
class ResultFragmentTv : ResultFragment() {
|
||||||
override val resultLayout = R.layout.fragment_result_tv
|
override var layout = R.layout.fragment_result_tv
|
||||||
|
|
||||||
private var binding: FragmentResultTvBinding? = null
|
private var binding: FragmentResultTvBinding? = null
|
||||||
|
|
||||||
|
@ -95,20 +110,6 @@ class ResultFragmentTv : ResultFragment() {
|
||||||
return focus == binding?.resultRoot
|
return focus == binding?.resultRoot
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateEpisodes(episodes: Resource<List<ResultEpisode>>?) {
|
|
||||||
super.updateEpisodes(episodes)
|
|
||||||
if (episodes is Resource.Success && hasNoFocus()) {
|
|
||||||
binding?.resultEpisodes?.requestFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateMovie(data: Resource<Pair<UiText, ResultEpisode>>?) {
|
|
||||||
super.updateMovie(data)
|
|
||||||
if (data is Resource.Success && hasNoFocus()) {
|
|
||||||
binding?.resultPlayMovie?.requestFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setTrailers(trailers: List<ExtractorLink>?) {
|
override fun setTrailers(trailers: List<ExtractorLink>?) {
|
||||||
context?.updateHasTrailers()
|
context?.updateHasTrailers()
|
||||||
if (!LoadResponse.isTrailersEnabled) return
|
if (!LoadResponse.isTrailersEnabled) return
|
||||||
|
@ -128,7 +129,7 @@ class ResultFragmentTv : ResultFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
private fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
||||||
currentRecommendations = rec ?: emptyList()
|
currentRecommendations = rec ?: emptyList()
|
||||||
val isInvalid = rec.isNullOrEmpty()
|
val isInvalid = rec.isNullOrEmpty()
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
|
@ -151,6 +152,8 @@ class ResultFragmentTv : ResultFragment() {
|
||||||
|
|
||||||
var loadingDialog: Dialog? = null
|
var loadingDialog: Dialog? = null
|
||||||
var popupDialog: Dialog? = null
|
var popupDialog: Dialog? = null
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
|
@ -163,6 +166,34 @@ class ResultFragmentTv : ResultFragment() {
|
||||||
resultRangeSelection.setAdapter()
|
resultRangeSelection.setAdapter()
|
||||||
resultDubSelection.setAdapter()
|
resultDubSelection.setAdapter()
|
||||||
resultRecommendationsFilterSelection.setAdapter()
|
resultRecommendationsFilterSelection.setAdapter()
|
||||||
|
|
||||||
|
resultCastItems.setOnFocusChangeListener { _, hasFocus ->
|
||||||
|
// Always escape focus
|
||||||
|
if (hasFocus) binding?.resultBookmarkButton?.requestFocus()
|
||||||
|
}
|
||||||
|
resultBack.setOnClickListener {
|
||||||
|
activity?.popCurrentPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
resultRecommendationsList.spanCount = 8
|
||||||
|
resultRecommendationsList.adapter =
|
||||||
|
SearchAdapter(
|
||||||
|
ArrayList(),
|
||||||
|
resultRecommendationsList,
|
||||||
|
) { callback ->
|
||||||
|
SearchHelper.handleSearchClickCallback(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
resultEpisodes.adapter =
|
||||||
|
EpisodeAdapter(
|
||||||
|
false,
|
||||||
|
{ episodeClick ->
|
||||||
|
viewModel.handleAction(activity, episodeClick)
|
||||||
|
},
|
||||||
|
{ downloadClickEvent ->
|
||||||
|
DownloadButtonSetup.handleDownloadClick(downloadClickEvent)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
observe(viewModel.watchStatus) { watchType ->
|
observe(viewModel.watchStatus) { watchType ->
|
||||||
|
@ -181,6 +212,30 @@ class ResultFragmentTv : ResultFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
observeNullable(viewModel.movie) { data ->
|
||||||
|
binding?.apply {
|
||||||
|
resultPlayMovie.isVisible = data is Resource.Success
|
||||||
|
(data as? Resource.Success)?.value?.let { (text, ep) ->
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if (hasNoFocus()) {
|
||||||
|
resultPlayMovie.requestFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
observeNullable(viewModel.selectPopup) { popup ->
|
observeNullable(viewModel.selectPopup) { popup ->
|
||||||
if (popup == null) {
|
if (popup == null) {
|
||||||
|
@ -257,21 +312,111 @@ class ResultFragmentTv : ResultFragment() {
|
||||||
observe(viewModel.seasonSelections) {
|
observe(viewModel.seasonSelections) {
|
||||||
binding?.resultSeasonSelection.update(it)
|
binding?.resultSeasonSelection.update(it)
|
||||||
}
|
}
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
observeNullable(viewModel.episodes) { episodes ->
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
resultBack.setOnClickListener {
|
resultEpisodes.isVisible = episodes is Resource.Success
|
||||||
activity?.popCurrentPage()
|
resultEpisodeLoading.isVisible = episodes is Resource.Loading
|
||||||
|
if (episodes is Resource.Success) {
|
||||||
|
/*
|
||||||
|
* 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()
|
||||||
|
|
||||||
|
if (hasEpisodes) {
|
||||||
|
// Make it impossible to focus anywhere else!
|
||||||
|
temporaryNoFocus.isFocusable = true
|
||||||
|
temporaryNoFocus.requestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
resultRecommendationsList.spanCount = 8
|
(resultEpisodes.adapter as? EpisodeAdapter)?.updateList(episodes.value)
|
||||||
resultRecommendationsList.adapter =
|
|
||||||
SearchAdapter(
|
if (hasEpisodes) main {
|
||||||
ArrayList(),
|
delay(500)
|
||||||
resultRecommendationsList,
|
temporaryNoFocus.isFocusable = false
|
||||||
) { callback ->
|
// This might make some people sad as it changes the focus when leaving an episode :(
|
||||||
SearchHelper.handleSearchClickCallback(callback)
|
temporaryNoFocus.requestFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNoFocus())
|
||||||
|
binding?.resultEpisodes?.requestFocus()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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 =
|
||||||
|
(this@ResultFragmentTv.context?.let { getStoredData(it) }?.url?.plus("\n")
|
||||||
|
?: "") + data.errorString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resultFinishLoading.isVisible = data is Resource.Success
|
||||||
|
|
||||||
|
resultLoading.isVisible = data is Resource.Loading
|
||||||
|
|
||||||
|
resultLoadingError.isVisible = data is Resource.Failure
|
||||||
|
resultReloadConnectionOpenInBrowser.isVisible = data is Resource.Failure
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,21 +10,13 @@ import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.discord.panels.PanelsChildGestureRegionObserver
|
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.ui.player.CSPlayerEvent
|
import com.lagradost.cloudstream3.ui.player.CSPlayerEvent
|
||||||
import com.lagradost.cloudstream3.ui.player.FullScreenPlayer
|
|
||||||
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
||||||
import com.lagradost.cloudstream3.utils.IOnBackPressed
|
import com.lagradost.cloudstream3.utils.IOnBackPressed
|
||||||
import kotlinx.android.synthetic.main.fragment_result.*
|
|
||||||
import kotlinx.android.synthetic.main.fragment_result.result_smallscreen_holder
|
|
||||||
import kotlinx.android.synthetic.main.fragment_result_swipe.*
|
|
||||||
import kotlinx.android.synthetic.main.fragment_result_tv.*
|
|
||||||
import kotlinx.android.synthetic.main.fragment_trailer.*
|
|
||||||
|
|
||||||
|
|
||||||
open class ResultTrailerPlayer : FullScreenPlayer(),
|
open class ResultTrailerPlayer : ResultFragmentPhone(), IOnBackPressed {
|
||||||
PanelsChildGestureRegionObserver.GestureRegionsListener, IOnBackPressed {
|
|
||||||
|
|
||||||
override var lockRotation = false
|
override var lockRotation = false
|
||||||
override var isFullScreenPlayer = false
|
override var isFullScreenPlayer = false
|
||||||
|
@ -60,13 +52,13 @@ open class ResultTrailerPlayer : FullScreenPlayer(),
|
||||||
screenHeight
|
screenHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
result_trailer_loading?.isVisible = false
|
//result_trailer_loading?.isVisible = false
|
||||||
result_smallscreen_holder?.isVisible = !isFullScreenPlayer
|
resultBinding?.resultSmallscreenHolder?.isVisible = !isFullScreenPlayer
|
||||||
result_fullscreen_holder?.isVisible = isFullScreenPlayer
|
binding?.resultFullscreenHolder?.isVisible = isFullScreenPlayer
|
||||||
|
|
||||||
val to = sw * h / w
|
val to = sw * h / w
|
||||||
|
|
||||||
player_background?.apply {
|
resultBinding?.fragmentTrailer?.playerBackground?.apply {
|
||||||
isVisible = true
|
isVisible = true
|
||||||
layoutParams =
|
layoutParams =
|
||||||
FrameLayout.LayoutParams(
|
FrameLayout.LayoutParams(
|
||||||
|
@ -79,12 +71,13 @@ open class ResultTrailerPlayer : FullScreenPlayer(),
|
||||||
layoutParams =
|
layoutParams =
|
||||||
FrameLayout.LayoutParams(
|
FrameLayout.LayoutParams(
|
||||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||||
result_top_holder?.measuredHeight ?: FrameLayout.LayoutParams.MATCH_PARENT
|
resultBinding?.resultTopHolder?.measuredHeight
|
||||||
|
?: FrameLayout.LayoutParams.MATCH_PARENT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playerBinding?.playerIntroPlay?.isGone == true) {
|
if (playerBinding?.playerIntroPlay?.isGone == true) {
|
||||||
result_top_holder?.apply {
|
resultBinding?.resultTopHolder?.apply {
|
||||||
|
|
||||||
val anim = ValueAnimator.ofInt(
|
val anim = ValueAnimator.ofInt(
|
||||||
measuredHeight,
|
measuredHeight,
|
||||||
|
@ -135,20 +128,26 @@ open class ResultTrailerPlayer : FullScreenPlayer(),
|
||||||
playerBinding?.playerFullscreen?.setImageResource(if (fullscreen) R.drawable.baseline_fullscreen_exit_24 else R.drawable.baseline_fullscreen_24)
|
playerBinding?.playerFullscreen?.setImageResource(if (fullscreen) R.drawable.baseline_fullscreen_exit_24 else R.drawable.baseline_fullscreen_24)
|
||||||
if (fullscreen) {
|
if (fullscreen) {
|
||||||
enterFullscreen()
|
enterFullscreen()
|
||||||
result_top_bar?.isVisible = false
|
binding?.apply {
|
||||||
result_fullscreen_holder?.isVisible = true
|
resultTopBar.isVisible = false
|
||||||
result_main_holder?.isVisible = false
|
resultFullscreenHolder.isVisible = true
|
||||||
player_background?.let { view ->
|
resultMainHolder.isVisible = false
|
||||||
(view.parent as ViewGroup?)?.removeView(view)
|
|
||||||
result_fullscreen_holder?.addView(view)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
result_top_bar?.isVisible = true
|
resultBinding?.fragmentTrailer?.playerBackground?.let { view ->
|
||||||
result_fullscreen_holder?.isVisible = false
|
|
||||||
result_main_holder?.isVisible = true
|
|
||||||
player_background?.let { view ->
|
|
||||||
(view.parent as ViewGroup?)?.removeView(view)
|
(view.parent as ViewGroup?)?.removeView(view)
|
||||||
result_smallscreen_holder?.addView(view)
|
binding?.resultFullscreenHolder?.addView(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
binding?.apply {
|
||||||
|
resultTopBar.isVisible = true
|
||||||
|
resultFullscreenHolder.isVisible = false
|
||||||
|
resultMainHolder.isVisible = true
|
||||||
|
resultBinding?.fragmentTrailer?.playerBackground?.let { view ->
|
||||||
|
(view.parent as ViewGroup?)?.removeView(view)
|
||||||
|
resultBinding?.resultSmallscreenHolder?.addView(view)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exitFullscreen()
|
exitFullscreen()
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
object SyncUtil {
|
object SyncUtil {
|
||||||
private val regexs = listOf(
|
private val regexs = listOf(
|
||||||
Regex("""(9anime)\.(?:to|center|id)/watch/(?:.*?)\.([^/?]*)"""),
|
Regex("""(9anime)\.(?:to|center|id)/watch/.*?\.([^/?]*)"""),
|
||||||
Regex("""(gogoanime|gogoanimes)\..*?/category/([^/?]*)"""),
|
Regex("""(gogoanime|gogoanimes)\..*?/category/([^/?]*)"""),
|
||||||
Regex("""(twist\.moe)/a/([^/?]*)"""),
|
Regex("""(twist\.moe)/a/([^/?]*)"""),
|
||||||
)
|
)
|
||||||
|
@ -44,6 +44,13 @@ object SyncUtil {
|
||||||
matchList[site]?.let { realSite ->
|
matchList[site]?.let { realSite ->
|
||||||
getIdsFromSlug(slug, realSite)?.let {
|
getIdsFromSlug(slug, realSite)?.let {
|
||||||
return it
|
return it
|
||||||
|
} ?: kotlin.run {
|
||||||
|
if (slug.endsWith("-dub")) {
|
||||||
|
println("testing non -dub slug $slug")
|
||||||
|
getIdsFromSlug(slug.removeSuffix("-dub"), realSite)?.let {
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,12 +46,16 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||||
import com.bumptech.glide.request.RequestListener
|
import com.bumptech.glide.request.RequestListener
|
||||||
import com.bumptech.glide.request.RequestOptions.bitmapTransform
|
import com.bumptech.glide.request.RequestOptions.bitmapTransform
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
|
import com.google.android.material.chip.Chip
|
||||||
|
import com.google.android.material.chip.ChipDrawable
|
||||||
|
import com.google.android.material.chip.ChipGroup
|
||||||
import com.lagradost.cloudstream3.CommonActivity.activity
|
import com.lagradost.cloudstream3.CommonActivity.activity
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.ui.result.UiImage
|
import com.lagradost.cloudstream3.ui.result.UiImage
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||||
import jp.wasabeef.glide.transformations.BlurTransformation
|
import jp.wasabeef.glide.transformations.BlurTransformation
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@ -73,6 +77,30 @@ object UIHelper {
|
||||||
|| Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
|
|| Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun populateChips(view: ChipGroup?, tags : List<String>) {
|
||||||
|
if(view == null) return
|
||||||
|
view.removeAllViews()
|
||||||
|
val context = view.context ?: return
|
||||||
|
|
||||||
|
tags.forEach { tag ->
|
||||||
|
val chip = Chip(context)
|
||||||
|
val chipDrawable = ChipDrawable.createFromAttributes(
|
||||||
|
context,
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
R.style.ChipFilled
|
||||||
|
)
|
||||||
|
chip.setChipDrawable(chipDrawable)
|
||||||
|
chip.text = tag
|
||||||
|
chip.isChecked = false
|
||||||
|
chip.isCheckable = false
|
||||||
|
chip.isFocusable = false
|
||||||
|
chip.isClickable = false
|
||||||
|
chip.setTextColor(context.colorFromAttribute(R.attr.textColor))
|
||||||
|
view.addView(chip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Activity.requestRW() {
|
fun Activity.requestRW() {
|
||||||
ActivityCompat.requestPermissions(
|
ActivityCompat.requestPermissions(
|
||||||
this,
|
this,
|
||||||
|
|
|
@ -465,6 +465,7 @@
|
||||||
</com.google.android.material.button.MaterialButton>
|
</com.google.android.material.button.MaterialButton>
|
||||||
|
|
||||||
<com.lagradost.cloudstream3.ui.download.button.DownloadButton
|
<com.lagradost.cloudstream3.ui.download.button.DownloadButton
|
||||||
|
android:visibility="gone"
|
||||||
android:id="@+id/download_button"
|
android:id="@+id/download_button"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -216,32 +216,6 @@
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<!-- This nested layout is necessary because of buffering and clicking-->
|
<!-- This nested layout is necessary because of buffering and clicking-->
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/player_pause_play_holder_holder"
|
|
||||||
android:layout_width="100dp"
|
|
||||||
android:layout_height="100dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/player_pause_play"
|
|
||||||
android:layout_width="70dp"
|
|
||||||
android:layout_height="70dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:background="@drawable/video_tap_button"
|
|
||||||
android:nextFocusLeft="@id/exo_rew"
|
|
||||||
|
|
||||||
android:nextFocusRight="@id/exo_ffwd"
|
|
||||||
|
|
||||||
android:nextFocusUp="@id/player_go_back"
|
|
||||||
android:nextFocusDown="@id/player_lock"
|
|
||||||
|
|
||||||
android:src="@drawable/netflix_pause"
|
|
||||||
app:tint="@color/white"
|
|
||||||
tools:ignore="ContentDescription" />
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/player_center_menu"
|
android:id="@+id/player_center_menu"
|
||||||
|
@ -298,7 +272,32 @@
|
||||||
app:tint="@color/white"
|
app:tint="@color/white"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/player_pause_play_holder_holder"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/player_pause_play"
|
||||||
|
android:layout_width="70dp"
|
||||||
|
android:layout_height="70dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="@drawable/video_tap_button"
|
||||||
|
android:nextFocusLeft="@id/exo_rew"
|
||||||
|
|
||||||
|
android:nextFocusRight="@id/exo_ffwd"
|
||||||
|
|
||||||
|
android:nextFocusUp="@id/player_go_back"
|
||||||
|
android:nextFocusDown="@id/player_lock"
|
||||||
|
|
||||||
|
android:src="@drawable/netflix_pause"
|
||||||
|
app:tint="@color/white"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
</FrameLayout>
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/player_ffwd_holder"
|
android:id="@+id/player_ffwd_holder"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
|
@ -76,10 +76,10 @@
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:visibility="gone"
|
|
||||||
android:id="@+id/player_top_holder"
|
android:id="@+id/player_top_holder"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -113,7 +113,8 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
android:layout_marginTop="2dp">
|
android:layout_marginTop="2dp"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/player_episode_filler"
|
android:id="@+id/player_episode_filler"
|
||||||
|
@ -136,9 +137,13 @@
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/player_go_back_holder"
|
android:id="@+id/player_go_back_holder"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
android:layout_margin="5dp"
|
android:layout_margin="5dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:focusableInTouchMode="false"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
@ -206,33 +211,7 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<!-- This nested layout is necessary because of buffering and clicking-->
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/player_pause_play_holder_holder"
|
|
||||||
android:layout_width="100dp"
|
|
||||||
android:layout_height="100dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/player_pause_play"
|
|
||||||
android:layout_width="70dp"
|
|
||||||
android:layout_height="70dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:background="@drawable/video_tap_button"
|
|
||||||
android:nextFocusLeft="@id/exo_rew"
|
|
||||||
|
|
||||||
android:nextFocusRight="@id/exo_ffwd"
|
|
||||||
|
|
||||||
android:nextFocusUp="@id/player_go_back"
|
|
||||||
android:nextFocusDown="@id/player_lock"
|
|
||||||
|
|
||||||
android:src="@drawable/netflix_pause"
|
|
||||||
app:tint="@color/white"
|
|
||||||
tools:ignore="ContentDescription" />
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/player_center_menu"
|
android:id="@+id/player_center_menu"
|
||||||
|
@ -288,7 +267,33 @@
|
||||||
app:tint="@color/white"
|
app:tint="@color/white"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
<!-- This nested layout is necessary because of buffering and clicking-->
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/player_pause_play_holder_holder"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/player_pause_play"
|
||||||
|
android:layout_width="70dp"
|
||||||
|
android:layout_height="70dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="@drawable/video_tap_button"
|
||||||
|
android:nextFocusLeft="@id/exo_rew"
|
||||||
|
|
||||||
|
android:nextFocusRight="@id/exo_ffwd"
|
||||||
|
|
||||||
|
android:nextFocusUp="@id/player_go_back"
|
||||||
|
android:nextFocusDown="@id/player_lock"
|
||||||
|
|
||||||
|
android:src="@drawable/netflix_pause"
|
||||||
|
app:tint="@color/white"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
</FrameLayout>
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/player_ffwd_holder"
|
android:id="@+id/player_ffwd_holder"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
|
@ -436,7 +436,7 @@
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/navigation_results_phone"
|
android:id="@+id/navigation_results_phone"
|
||||||
android:name="com.lagradost.cloudstream3.ui.result.ResultFragmentPhone"
|
android:name="com.lagradost.cloudstream3.ui.result.ResultTrailerPlayer"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:enterAnim="@anim/enter_anim"
|
app:enterAnim="@anim/enter_anim"
|
||||||
app:exitAnim="@anim/exit_anim"
|
app:exitAnim="@anim/exit_anim"
|
||||||
|
|
Loading…
Reference in a new issue