forked from recloudstream/cloudstream
android tv resultview testing
This commit is contained in:
parent
cd9bdb8ba7
commit
1bc4c7e56d
11 changed files with 1287 additions and 395 deletions
|
@ -156,7 +156,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
|
||||
// Fucks up anime info layout since that has its own layout
|
||||
cast_mini_controller_holder?.isVisible =
|
||||
!listOf(R.id.navigation_results, R.id.navigation_player).contains(destination.id)
|
||||
!listOf(
|
||||
R.id.navigation_results_phone,
|
||||
R.id.navigation_results_tv,
|
||||
R.id.navigation_player
|
||||
).contains(destination.id)
|
||||
|
||||
val isNavVisible = listOf(
|
||||
R.id.navigation_home,
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.content.Intent.*
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Rect
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
|
@ -26,17 +23,18 @@ import androidx.core.widget.doOnTextChanged
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.discord.panels.OverlappingPanelsLayout
|
||||
import com.discord.panels.PanelsChildGestureRegionObserver
|
||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||
import com.google.android.gms.cast.framework.CastContext
|
||||
import com.google.android.gms.cast.framework.CastState
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
||||
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||
import com.lagradost.cloudstream3.DubStatus
|
||||
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.WatchType
|
||||
|
@ -45,8 +43,6 @@ import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownload
|
|||
import com.lagradost.cloudstream3.ui.download.EasyDownloadButton
|
||||
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.SearchHelper
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
|
@ -59,21 +55,56 @@ import com.lagradost.cloudstream3.utils.Coroutines.ioWork
|
|||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
||||
import kotlinx.android.synthetic.main.fragment_result.*
|
||||
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_download_movie
|
||||
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_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_movie_download_icon
|
||||
import kotlinx.android.synthetic.main.fragment_result.result_movie_download_text
|
||||
import kotlinx.android.synthetic.main.fragment_result.result_movie_download_text_precentage
|
||||
import kotlinx.android.synthetic.main.fragment_result.result_movie_progress_downloaded
|
||||
import kotlinx.android.synthetic.main.fragment_result.result_movie_progress_downloaded_holder
|
||||
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_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_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_scroll
|
||||
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_swipe.*
|
||||
import kotlinx.android.synthetic.main.fragment_result_tv.*
|
||||
import kotlinx.android.synthetic.main.fragment_trailer.*
|
||||
import kotlinx.android.synthetic.main.result_recommendations.*
|
||||
import kotlinx.android.synthetic.main.result_sync.*
|
||||
import kotlinx.android.synthetic.main.trailer_custom_layout.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
const val START_ACTION_RESUME_LATEST = 1
|
||||
|
@ -159,7 +190,7 @@ fun ResultEpisode.getWatchProgress(): Float {
|
|||
return (getDisplayPosition() / 1000).toFloat() / (duration / 1000).toFloat()
|
||||
}
|
||||
|
||||
class ResultFragment : ResultTrailerPlayer() {
|
||||
open class ResultFragment : ResultTrailerPlayer() {
|
||||
companion object {
|
||||
const val URL_BUNDLE = "url"
|
||||
const val API_NAME_BUNDLE = "apiName"
|
||||
|
@ -168,6 +199,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
const val START_ACTION_BUNDLE = "startAction"
|
||||
const val START_VALUE_BUNDLE = "startValue"
|
||||
const val RESTART_BUNDLE = "restart"
|
||||
|
||||
fun newInstance(
|
||||
card: SearchResponse, startAction: Int = 0, startValue: Int? = null
|
||||
): Bundle {
|
||||
|
@ -211,8 +243,11 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
private var updateUIListener: (() -> Unit)? = null
|
||||
}
|
||||
|
||||
private lateinit var viewModel: ResultViewModel2 //by activityViewModels()
|
||||
private lateinit var syncModel: SyncViewModel
|
||||
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 fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
|
@ -224,7 +259,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
syncModel =
|
||||
ViewModelProvider(this)[SyncViewModel::class.java]
|
||||
|
||||
return inflater.inflate(R.layout.fragment_result_swipe, container, false)
|
||||
return inflater.inflate(resultLayout, container, false)
|
||||
}
|
||||
|
||||
private var downloadButton: EasyDownloadButton? = null
|
||||
|
@ -232,12 +267,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
updateUIListener = null
|
||||
(result_episodes?.adapter as EpisodeAdapter?)?.killAdapter()
|
||||
downloadButton?.dispose()
|
||||
//somehow this still leaks and I dont know why????
|
||||
// todo look at https://github.com/discord/OverlappingPanels/blob/70b4a7cf43c6771873b1e091029d332896d41a1a/sample_app/src/main/java/com/discord/sampleapp/MainActivity.kt
|
||||
PanelsChildGestureRegionObserver.Provider.get().removeGestureRegionsUpdateListener(this)
|
||||
result_cast_items?.let {
|
||||
PanelsChildGestureRegionObserver.Provider.get().unregister(it)
|
||||
}
|
||||
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
|
@ -289,121 +319,8 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
}
|
||||
}
|
||||
|
||||
var currentTrailers: List<ExtractorLink> = emptyList()
|
||||
var currentTrailerIndex = 0
|
||||
open fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
||||
|
||||
override fun nextMirror() {
|
||||
currentTrailerIndex++
|
||||
loadTrailer()
|
||||
}
|
||||
|
||||
override fun hasNextMirror(): Boolean {
|
||||
return currentTrailerIndex + 1 < currentTrailers.size
|
||||
}
|
||||
|
||||
override fun playerError(exception: Exception) {
|
||||
if (player.getIsPlaying()) { // because we dont want random toasts in player
|
||||
super.playerError(exception)
|
||||
} else {
|
||||
nextMirror()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadTrailer(index: Int? = null) {
|
||||
val isSuccess =
|
||||
currentTrailers.getOrNull(index ?: currentTrailerIndex)?.let { trailer ->
|
||||
context?.let { ctx ->
|
||||
player.onPause()
|
||||
player.loadPlayer(
|
||||
ctx,
|
||||
false,
|
||||
trailer,
|
||||
null,
|
||||
startPosition = 0L,
|
||||
subtitles = emptySet(),
|
||||
subtitle = null,
|
||||
autoPlay = false
|
||||
)
|
||||
true
|
||||
} ?: run {
|
||||
false
|
||||
}
|
||||
} ?: run {
|
||||
false
|
||||
}
|
||||
result_trailer_loading?.isVisible = isSuccess
|
||||
result_smallscreen_holder?.isVisible = !isSuccess && !isFullScreenPlayer
|
||||
|
||||
// We don't want the trailer to be focusable if it's not visible
|
||||
result_smallscreen_holder?.descendantFocusability = if (isSuccess) {
|
||||
ViewGroup.FOCUS_AFTER_DESCENDANTS
|
||||
} else {
|
||||
ViewGroup.FOCUS_BLOCK_DESCENDANTS
|
||||
}
|
||||
result_fullscreen_holder?.isVisible = !isSuccess && isFullScreenPlayer
|
||||
}
|
||||
|
||||
private fun setTrailers(trailers: List<ExtractorLink>?) {
|
||||
context?.updateHasTrailers()
|
||||
if (!LoadResponse.isTrailersEnabled) return
|
||||
currentTrailers = trailers?.sortedBy { -it.quality } ?: emptyList()
|
||||
loadTrailer()
|
||||
}
|
||||
|
||||
private fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
||||
val isInvalid = rec.isNullOrEmpty()
|
||||
result_recommendations?.isGone = isInvalid
|
||||
result_recommendations_btt?.isGone = isInvalid
|
||||
result_recommendations_btt?.setOnClickListener {
|
||||
val nextFocusDown = if (result_overlapping_panels?.getSelectedPanel()?.ordinal == 1) {
|
||||
result_overlapping_panels?.openEndPanel()
|
||||
R.id.result_recommendations
|
||||
} else {
|
||||
result_overlapping_panels?.closePanels()
|
||||
R.id.result_description
|
||||
}
|
||||
|
||||
result_recommendations_btt?.nextFocusDownId = nextFocusDown
|
||||
result_search?.nextFocusDownId = nextFocusDown
|
||||
result_open_in_browser?.nextFocusDownId = nextFocusDown
|
||||
result_share?.nextFocusDownId = nextFocusDown
|
||||
}
|
||||
result_overlapping_panels?.setEndPanelLockState(if (isInvalid) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED)
|
||||
|
||||
val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName
|
||||
rec?.map { it.apiName }?.distinct()?.let { apiNames ->
|
||||
// very dirty selection
|
||||
result_recommendations_filter_button?.isVisible = apiNames.size > 1
|
||||
result_recommendations_filter_button?.text = matchAgainst
|
||||
result_recommendations_filter_button?.setOnClickListener { _ ->
|
||||
activity?.showBottomDialog(
|
||||
apiNames,
|
||||
apiNames.indexOf(matchAgainst),
|
||||
getString(R.string.home_change_provider_img_des), false, {}
|
||||
) {
|
||||
setRecommendations(rec, apiNames[it])
|
||||
}
|
||||
}
|
||||
} ?: run {
|
||||
result_recommendations_filter_button?.isVisible = false
|
||||
}
|
||||
|
||||
result_recommendations?.post {
|
||||
rec?.let { list ->
|
||||
(result_recommendations?.adapter as SearchAdapter?)?.updateList(list.filter { it.apiName == matchAgainst })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fixGrid() {
|
||||
activity?.getSpanCount()?.let { _ ->
|
||||
//result_recommendations?.spanCount = count // this is due to discord not changing size with rotation
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
fixGrid()
|
||||
}
|
||||
|
||||
private fun updateUI() {
|
||||
|
@ -411,27 +328,11 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
viewModel.reloadEpisodes()
|
||||
}
|
||||
|
||||
var loadingDialog: Dialog? = null
|
||||
var popupDialog: Dialog? = null
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
result_cast_items?.let {
|
||||
PanelsChildGestureRegionObserver.Provider.get().register(it)
|
||||
}
|
||||
result_cast_items?.adapter = ActorAdaptor()
|
||||
fixGrid()
|
||||
result_recommendations?.spanCount = 3
|
||||
result_overlapping_panels?.setStartPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
|
||||
result_overlapping_panels?.setEndPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
|
||||
|
||||
player_open_source?.setOnClickListener {
|
||||
currentTrailers.getOrNull(currentTrailerIndex)?.let {
|
||||
context?.openBrowser(it.url)
|
||||
}
|
||||
}
|
||||
|
||||
updateUIListener = ::updateUI
|
||||
|
||||
|
@ -515,10 +416,6 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
//result_poster_blur_holder?.translationY = -scrollY.toFloat()
|
||||
})
|
||||
|
||||
result_back.setOnClickListener {
|
||||
activity?.popCurrentPage()
|
||||
}
|
||||
|
||||
result_episodes.adapter =
|
||||
EpisodeAdapter(
|
||||
ArrayList(),
|
||||
|
@ -531,15 +428,6 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
}
|
||||
)
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
observe(viewModel.watchStatus) { watchType ->
|
||||
result_bookmark_button?.text = getString(watchType.stringRes)
|
||||
|
@ -567,23 +455,11 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets next focus to allow navigation up and down between 2 views
|
||||
* if either of them is null nothing happens.
|
||||
**/
|
||||
fun setFocusUpAndDown(upper: View?, down: View?) {
|
||||
if (upper == null || down == null) return
|
||||
upper.nextFocusDownId = down.id
|
||||
down.nextFocusUpId = upper.id
|
||||
}
|
||||
|
||||
// This is to band-aid FireTV navigation
|
||||
result_season_button?.isFocusableInTouchMode = context?.isTvSettings() == true
|
||||
result_episode_select?.isFocusableInTouchMode = context?.isTvSettings() == true
|
||||
result_dub_select?.isFocusableInTouchMode = context?.isTvSettings() == true
|
||||
|
||||
|
||||
|
||||
context?.let { ctx ->
|
||||
val arrayAdapter = ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
|
||||
/*
|
||||
|
@ -633,19 +509,6 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
}
|
||||
}
|
||||
|
||||
result_mini_sync?.adapter = ImageAdapter(
|
||||
R.layout.result_mini_image,
|
||||
nextFocusDown = R.id.result_sync_set_score,
|
||||
clickCallback = { action ->
|
||||
if (action == IMAGE_CLICK || action == IMAGE_LONG_CLICK) {
|
||||
if (result_overlapping_panels?.getSelectedPanel()?.ordinal == 1) {
|
||||
result_overlapping_panels?.openStartPanel()
|
||||
} else {
|
||||
result_overlapping_panels?.closePanels()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
observe(syncModel.synced) { list ->
|
||||
result_sync_names?.text =
|
||||
list.filter { it.isSynced && it.hasAccount }.joinToString { it.name }
|
||||
|
@ -769,8 +632,9 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
}
|
||||
|
||||
result_resume_series_button?.isVisible = !value.isMovie
|
||||
result_resume_series_button_play?.isVisible = !value.isMovie
|
||||
|
||||
result_resume_series_button?.setOnClickListener {
|
||||
val click = View.OnClickListener {
|
||||
viewModel.handleAction(
|
||||
activity,
|
||||
EpisodeClickEvent(
|
||||
|
@ -778,6 +642,9 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
result_resume_series_button?.setOnClickListener(click)
|
||||
result_resume_series_button_play?.setOnClickListener(click)
|
||||
}
|
||||
is Some.None -> {
|
||||
result_resume_parent?.isVisible = false
|
||||
|
@ -803,156 +670,6 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
}
|
||||
}
|
||||
|
||||
observe(viewModel.selectedSeason) { text ->
|
||||
result_season_button.setText(text)
|
||||
|
||||
// If the season button is visible the result season button will be next focus down
|
||||
if (result_season_button?.isVisible == true)
|
||||
if (result_resume_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_season_button)
|
||||
else
|
||||
setFocusUpAndDown(result_bookmark_button, result_season_button)
|
||||
}
|
||||
|
||||
observe(viewModel.selectedDubStatus) { status ->
|
||||
result_dub_select?.setText(status)
|
||||
|
||||
if (result_dub_select?.isVisible == true)
|
||||
if (result_season_button?.isVisible != true && result_episode_select?.isVisible != true) {
|
||||
if (result_resume_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_dub_select)
|
||||
else
|
||||
setFocusUpAndDown(result_bookmark_button, result_dub_select)
|
||||
}
|
||||
}
|
||||
|
||||
observe(viewModel.selectPopup) { popup ->
|
||||
when (popup) {
|
||||
is Some.Success -> {
|
||||
popupDialog?.dismissSafe(activity)
|
||||
|
||||
popupDialog = activity?.let { act ->
|
||||
val pop = popup.value
|
||||
val options = pop.getOptions(act)
|
||||
val title = pop.getTitle(act)
|
||||
|
||||
act.showBottomDialogInstant(
|
||||
options, title, {
|
||||
popupDialog = null
|
||||
pop.callback(null)
|
||||
}, {
|
||||
popupDialog = null
|
||||
pop.callback(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
is Some.None -> {
|
||||
popupDialog?.dismissSafe(activity)
|
||||
popupDialog = null
|
||||
}
|
||||
}
|
||||
|
||||
//showBottomDialogInstant
|
||||
}
|
||||
|
||||
observe(viewModel.loadedLinks) { load ->
|
||||
when (load) {
|
||||
is Some.Success -> {
|
||||
if (loadingDialog?.isShowing != true) {
|
||||
loadingDialog?.dismissSafe(activity)
|
||||
loadingDialog = null
|
||||
}
|
||||
loadingDialog = loadingDialog ?: context?.let { ctx ->
|
||||
val builder =
|
||||
BottomSheetDialog(ctx)
|
||||
builder.setContentView(R.layout.bottom_loading)
|
||||
builder.setOnDismissListener {
|
||||
loadingDialog = null
|
||||
viewModel.cancelLinks()
|
||||
}
|
||||
//builder.setOnCancelListener {
|
||||
// it?.dismiss()
|
||||
//}
|
||||
builder.setCanceledOnTouchOutside(true)
|
||||
|
||||
builder.show()
|
||||
|
||||
builder
|
||||
}
|
||||
}
|
||||
is Some.None -> {
|
||||
loadingDialog?.dismissSafe(activity)
|
||||
loadingDialog = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
observe(viewModel.selectedRange) { range ->
|
||||
result_episode_select.setText(range)
|
||||
|
||||
// If Season button is invisible then the bookmark button next focus is episode select
|
||||
if (result_episode_select?.isVisible == true)
|
||||
if (result_season_button?.isVisible != true) {
|
||||
if (result_resume_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_episode_select)
|
||||
else
|
||||
setFocusUpAndDown(result_bookmark_button, result_episode_select)
|
||||
}
|
||||
}
|
||||
|
||||
// val preferDub = context?.getApiDubstatusSettings()?.all { it == DubStatus.Dubbed } == true
|
||||
|
||||
observe(viewModel.dubSubSelections) { range ->
|
||||
result_dub_select.setOnClickListener { view ->
|
||||
view?.context?.let { ctx ->
|
||||
view.popupMenuNoIconsAndNoStringRes(range
|
||||
.mapNotNull { (text, status) ->
|
||||
Pair(
|
||||
status.ordinal,
|
||||
text?.asStringNull(ctx) ?: return@mapNotNull null
|
||||
)
|
||||
}) {
|
||||
viewModel.changeDubStatus(DubStatus.values()[itemId])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
observe(viewModel.rangeSelections) { range ->
|
||||
result_episode_select?.setOnClickListener { view ->
|
||||
view?.context?.let { ctx ->
|
||||
val names = range
|
||||
.mapNotNull { (text, r) ->
|
||||
r to (text?.asStringNull(ctx) ?: return@mapNotNull null)
|
||||
}
|
||||
|
||||
view.popupMenuNoIconsAndNoStringRes(names.mapIndexed { index, (_, name) ->
|
||||
index to name
|
||||
}) {
|
||||
viewModel.changeRange(names[itemId].first)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
observe(viewModel.seasonSelections) { seasonList ->
|
||||
result_season_button?.setOnClickListener { view ->
|
||||
view?.context?.let { ctx ->
|
||||
val names = seasonList
|
||||
.mapNotNull { (text, r) ->
|
||||
r to (text?.asStringNull(ctx) ?: return@mapNotNull null)
|
||||
}
|
||||
|
||||
view.popupMenuNoIconsAndNoStringRes(names.mapIndexed { index, (_, name) ->
|
||||
index to name
|
||||
}) {
|
||||
viewModel.changeSeason(names[itemId].first)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result_cast_items?.setOnFocusChangeListener { _, hasFocus ->
|
||||
// Always escape focus
|
||||
if (hasFocus) result_bookmark_button?.requestFocus()
|
||||
|
@ -1178,14 +895,6 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
}
|
||||
}
|
||||
|
||||
result_recommendations?.adapter =
|
||||
SearchAdapter(
|
||||
ArrayList(),
|
||||
result_recommendations,
|
||||
) { callback ->
|
||||
SearchHelper.handleSearchClickCallback(activity, callback)
|
||||
}
|
||||
|
||||
context?.let { ctx ->
|
||||
val dubStatus = if(ctx.getApiDubstatusSettings().contains(DubStatus.Dubbed)) DubStatus.Dubbed else DubStatus.Subbed
|
||||
|
||||
|
@ -1239,16 +948,5 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
PanelsChildGestureRegionObserver.Provider.get().addGestureRegionsUpdateListener(this)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
PanelsChildGestureRegionObserver.Provider.get().addGestureRegionsUpdateListener(this)
|
||||
}
|
||||
|
||||
override fun onGestureRegionsUpdate(gestureRegions: List<Rect>) {
|
||||
result_overlapping_panels?.setChildGestureRegions(gestureRegions)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,380 @@
|
|||
package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import android.app.Dialog
|
||||
import android.graphics.Rect
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import com.discord.panels.OverlappingPanelsLayout
|
||||
import com.discord.panels.PanelsChildGestureRegionObserver
|
||||
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.R
|
||||
import com.lagradost.cloudstream3.SearchResponse
|
||||
import com.lagradost.cloudstream3.mvvm.Some
|
||||
import com.lagradost.cloudstream3.mvvm.observe
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
||||
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
|
||||
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.dismissSafe
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
||||
import kotlinx.android.synthetic.main.fragment_result.*
|
||||
import kotlinx.android.synthetic.main.fragment_result_swipe.*
|
||||
import kotlinx.android.synthetic.main.result_recommendations.*
|
||||
import kotlinx.android.synthetic.main.trailer_custom_layout.*
|
||||
|
||||
class ResultFragmentPhone : ResultFragment() {
|
||||
var currentTrailers: List<ExtractorLink> = emptyList()
|
||||
var currentTrailerIndex = 0
|
||||
|
||||
override fun nextMirror() {
|
||||
currentTrailerIndex++
|
||||
loadTrailer()
|
||||
}
|
||||
|
||||
override fun hasNextMirror(): Boolean {
|
||||
return currentTrailerIndex + 1 < currentTrailers.size
|
||||
}
|
||||
|
||||
override fun playerError(exception: Exception) {
|
||||
if (player.getIsPlaying()) { // because we dont want random toasts in player
|
||||
super.playerError(exception)
|
||||
} else {
|
||||
nextMirror()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadTrailer(index: Int? = null) {
|
||||
val isSuccess =
|
||||
currentTrailers.getOrNull(index ?: currentTrailerIndex)?.let { trailer ->
|
||||
context?.let { ctx ->
|
||||
player.onPause()
|
||||
player.loadPlayer(
|
||||
ctx,
|
||||
false,
|
||||
trailer,
|
||||
null,
|
||||
startPosition = 0L,
|
||||
subtitles = emptySet(),
|
||||
subtitle = null,
|
||||
autoPlay = false
|
||||
)
|
||||
true
|
||||
} ?: run {
|
||||
false
|
||||
}
|
||||
} ?: run {
|
||||
false
|
||||
}
|
||||
result_trailer_loading?.isVisible = isSuccess
|
||||
result_smallscreen_holder?.isVisible = !isSuccess && !isFullScreenPlayer
|
||||
|
||||
// We don't want the trailer to be focusable if it's not visible
|
||||
result_smallscreen_holder?.descendantFocusability = if (isSuccess) {
|
||||
ViewGroup.FOCUS_AFTER_DESCENDANTS
|
||||
} else {
|
||||
ViewGroup.FOCUS_BLOCK_DESCENDANTS
|
||||
}
|
||||
result_fullscreen_holder?.isVisible = !isSuccess && isFullScreenPlayer
|
||||
}
|
||||
|
||||
override fun setTrailers(trailers: List<ExtractorLink>?) {
|
||||
context?.updateHasTrailers()
|
||||
if (!LoadResponse.isTrailersEnabled) return
|
||||
currentTrailers = trailers?.sortedBy { -it.quality } ?: emptyList()
|
||||
loadTrailer()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
//somehow this still leaks and I dont know why????
|
||||
// todo look at https://github.com/discord/OverlappingPanels/blob/70b4a7cf43c6771873b1e091029d332896d41a1a/sample_app/src/main/java/com/discord/sampleapp/MainActivity.kt
|
||||
PanelsChildGestureRegionObserver.Provider.get().let { obs ->
|
||||
result_cast_items?.let {
|
||||
obs.unregister(it)
|
||||
}
|
||||
obs.removeGestureRegionsUpdateListener(this)
|
||||
}
|
||||
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
var loadingDialog: Dialog? = null
|
||||
var popupDialog: Dialog? = null
|
||||
|
||||
/**
|
||||
* Sets next focus to allow navigation up and down between 2 views
|
||||
* if either of them is null nothing happens.
|
||||
**/
|
||||
private fun setFocusUpAndDown(upper: View?, down: View?) {
|
||||
if (upper == null || down == null) return
|
||||
upper.nextFocusDownId = down.id
|
||||
down.nextFocusUpId = upper.id
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
player_open_source?.setOnClickListener {
|
||||
currentTrailers.getOrNull(currentTrailerIndex)?.let {
|
||||
context?.openBrowser(it.url)
|
||||
}
|
||||
}
|
||||
result_recommendations?.spanCount = 3
|
||||
result_overlapping_panels?.setStartPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
|
||||
result_overlapping_panels?.setEndPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
|
||||
|
||||
result_recommendations?.adapter =
|
||||
SearchAdapter(
|
||||
ArrayList(),
|
||||
result_recommendations,
|
||||
) { callback ->
|
||||
SearchHelper.handleSearchClickCallback(activity, callback)
|
||||
}
|
||||
PanelsChildGestureRegionObserver.Provider.get().addGestureRegionsUpdateListener(this)
|
||||
|
||||
result_cast_items?.let {
|
||||
PanelsChildGestureRegionObserver.Provider.get().register(it)
|
||||
}
|
||||
|
||||
|
||||
result_back?.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))
|
||||
}
|
||||
}
|
||||
|
||||
result_mini_sync?.adapter = ImageAdapter(
|
||||
R.layout.result_mini_image,
|
||||
nextFocusDown = R.id.result_sync_set_score,
|
||||
clickCallback = { action ->
|
||||
if (action == IMAGE_CLICK || action == IMAGE_LONG_CLICK) {
|
||||
if (result_overlapping_panels?.getSelectedPanel()?.ordinal == 1) {
|
||||
result_overlapping_panels?.openStartPanel()
|
||||
} else {
|
||||
result_overlapping_panels?.closePanels()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
observe(viewModel.selectPopup) { popup ->
|
||||
when (popup) {
|
||||
is Some.Success -> {
|
||||
popupDialog?.dismissSafe(activity)
|
||||
|
||||
popupDialog = activity?.let { act ->
|
||||
val pop = popup.value
|
||||
val options = pop.getOptions(act)
|
||||
val title = pop.getTitle(act)
|
||||
|
||||
act.showBottomDialogInstant(
|
||||
options, title, {
|
||||
popupDialog = null
|
||||
pop.callback(null)
|
||||
}, {
|
||||
popupDialog = null
|
||||
pop.callback(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
is Some.None -> {
|
||||
popupDialog?.dismissSafe(activity)
|
||||
popupDialog = null
|
||||
}
|
||||
}
|
||||
|
||||
//showBottomDialogInstant
|
||||
}
|
||||
|
||||
observe(viewModel.loadedLinks) { load ->
|
||||
when (load) {
|
||||
is Some.Success -> {
|
||||
if (loadingDialog?.isShowing != true) {
|
||||
loadingDialog?.dismissSafe(activity)
|
||||
loadingDialog = null
|
||||
}
|
||||
loadingDialog = loadingDialog ?: context?.let { ctx ->
|
||||
val builder =
|
||||
BottomSheetDialog(ctx)
|
||||
builder.setContentView(R.layout.bottom_loading)
|
||||
builder.setOnDismissListener {
|
||||
loadingDialog = null
|
||||
viewModel.cancelLinks()
|
||||
}
|
||||
//builder.setOnCancelListener {
|
||||
// it?.dismiss()
|
||||
//}
|
||||
builder.setCanceledOnTouchOutside(true)
|
||||
|
||||
builder.show()
|
||||
|
||||
builder
|
||||
}
|
||||
}
|
||||
is Some.None -> {
|
||||
loadingDialog?.dismissSafe(activity)
|
||||
loadingDialog = null
|
||||
}
|
||||
}
|
||||
}
|
||||
observe(viewModel.selectedSeason) { text ->
|
||||
result_season_button.setText(text)
|
||||
|
||||
// If the season button is visible the result season button will be next focus down
|
||||
if (result_season_button?.isVisible == true)
|
||||
if (result_resume_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_season_button)
|
||||
else
|
||||
setFocusUpAndDown(result_bookmark_button, result_season_button)
|
||||
}
|
||||
|
||||
observe(viewModel.selectedDubStatus) { status ->
|
||||
result_dub_select?.setText(status)
|
||||
|
||||
if (result_dub_select?.isVisible == true)
|
||||
if (result_season_button?.isVisible != true && result_episode_select?.isVisible != true) {
|
||||
if (result_resume_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_dub_select)
|
||||
else
|
||||
setFocusUpAndDown(result_bookmark_button, result_dub_select)
|
||||
}
|
||||
}
|
||||
observe(viewModel.selectedRange) { range ->
|
||||
result_episode_select.setText(range)
|
||||
|
||||
// If Season button is invisible then the bookmark button next focus is episode select
|
||||
if (result_episode_select?.isVisible == true)
|
||||
if (result_season_button?.isVisible != true) {
|
||||
if (result_resume_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_episode_select)
|
||||
else
|
||||
setFocusUpAndDown(result_bookmark_button, result_episode_select)
|
||||
}
|
||||
}
|
||||
|
||||
// val preferDub = context?.getApiDubstatusSettings()?.all { it == DubStatus.Dubbed } == true
|
||||
|
||||
observe(viewModel.dubSubSelections) { range ->
|
||||
result_dub_select.setOnClickListener { view ->
|
||||
view?.context?.let { ctx ->
|
||||
view.popupMenuNoIconsAndNoStringRes(range
|
||||
.mapNotNull { (text, status) ->
|
||||
Pair(
|
||||
status.ordinal,
|
||||
text?.asStringNull(ctx) ?: return@mapNotNull null
|
||||
)
|
||||
}) {
|
||||
viewModel.changeDubStatus(DubStatus.values()[itemId])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
observe(viewModel.rangeSelections) { range ->
|
||||
result_episode_select?.setOnClickListener { view ->
|
||||
view?.context?.let { ctx ->
|
||||
val names = range
|
||||
.mapNotNull { (text, r) ->
|
||||
r to (text?.asStringNull(ctx) ?: return@mapNotNull null)
|
||||
}
|
||||
|
||||
view.popupMenuNoIconsAndNoStringRes(names.mapIndexed { index, (_, name) ->
|
||||
index to name
|
||||
}) {
|
||||
viewModel.changeRange(names[itemId].first)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
observe(viewModel.seasonSelections) { seasonList ->
|
||||
result_season_button?.setOnClickListener { view ->
|
||||
view?.context?.let { ctx ->
|
||||
val names = seasonList
|
||||
.mapNotNull { (text, r) ->
|
||||
r to (text?.asStringNull(ctx) ?: return@mapNotNull null)
|
||||
}
|
||||
|
||||
view.popupMenuNoIconsAndNoStringRes(names.mapIndexed { index, (_, name) ->
|
||||
index to name
|
||||
}) {
|
||||
viewModel.changeSeason(names[itemId].first)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
PanelsChildGestureRegionObserver.Provider.get().addGestureRegionsUpdateListener(this)
|
||||
}
|
||||
|
||||
override fun onGestureRegionsUpdate(gestureRegions: List<Rect>) {
|
||||
result_overlapping_panels?.setChildGestureRegions(gestureRegions)
|
||||
}
|
||||
|
||||
override fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
||||
val isInvalid = rec.isNullOrEmpty()
|
||||
result_recommendations?.isGone = isInvalid
|
||||
result_recommendations_btt?.isGone = isInvalid
|
||||
result_recommendations_btt?.setOnClickListener {
|
||||
val nextFocusDown = if (result_overlapping_panels?.getSelectedPanel()?.ordinal == 1) {
|
||||
result_overlapping_panels?.openEndPanel()
|
||||
R.id.result_recommendations
|
||||
} else {
|
||||
result_overlapping_panels?.closePanels()
|
||||
R.id.result_description
|
||||
}
|
||||
|
||||
result_recommendations_btt?.nextFocusDownId = nextFocusDown
|
||||
result_search?.nextFocusDownId = nextFocusDown
|
||||
result_open_in_browser?.nextFocusDownId = nextFocusDown
|
||||
result_share?.nextFocusDownId = nextFocusDown
|
||||
}
|
||||
result_overlapping_panels?.setEndPanelLockState(if (isInvalid) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED)
|
||||
|
||||
val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName
|
||||
rec?.map { it.apiName }?.distinct()?.let { apiNames ->
|
||||
// very dirty selection
|
||||
result_recommendations_filter_button?.isVisible = apiNames.size > 1
|
||||
result_recommendations_filter_button?.text = matchAgainst
|
||||
result_recommendations_filter_button?.setOnClickListener { _ ->
|
||||
activity?.showBottomDialog(
|
||||
apiNames,
|
||||
apiNames.indexOf(matchAgainst),
|
||||
getString(R.string.home_change_provider_img_des), false, {}
|
||||
) {
|
||||
setRecommendations(rec, apiNames[it])
|
||||
}
|
||||
}
|
||||
} ?: run {
|
||||
result_recommendations_filter_button?.isVisible = false
|
||||
}
|
||||
|
||||
result_recommendations?.post {
|
||||
rec?.let { list ->
|
||||
(result_recommendations?.adapter as SearchAdapter?)?.updateList(list.filter { it.apiName == matchAgainst })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.SearchResponse
|
||||
|
||||
class ResultFragmentTv : ResultFragment() {
|
||||
override val resultLayout = R.layout.fragment_result_tv
|
||||
override fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
||||
|
||||
}
|
||||
}
|
|
@ -21,11 +21,13 @@ sealed class UiText {
|
|||
data class DynamicString(val value: String) : UiText() {
|
||||
override fun toString(): String = value
|
||||
}
|
||||
|
||||
class StringResource(
|
||||
@StringRes val resId: Int,
|
||||
val args: List<Any>
|
||||
) : UiText() {
|
||||
override fun toString(): String = "resId = $resId\nargs = ${args.toList().map { "(${it::class} = $it)" }}"
|
||||
override fun toString(): String =
|
||||
"resId = $resId\nargs = ${args.toList().map { "(${it::class} = $it)" }}"
|
||||
}
|
||||
|
||||
fun asStringNull(context: Context?): String? {
|
||||
|
@ -137,7 +139,14 @@ fun TextView?.setText(text: UiText?) {
|
|||
if (text == null) {
|
||||
this.isVisible = false
|
||||
} else {
|
||||
val str = text.asStringNull(context)
|
||||
val str = text.asStringNull(context)?.let {
|
||||
if (this.maxLines == 1) {
|
||||
it.replace("\n", " ")
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
this.isGone = str.isNullOrBlank()
|
||||
this.text = str
|
||||
}
|
||||
|
@ -155,9 +164,9 @@ fun TextView?.setTextHtml(text: UiText?) {
|
|||
}
|
||||
|
||||
fun TextView?.setTextHtml(text: Some<UiText>?) {
|
||||
setTextHtml(if(text is Some.Success) text.value else null)
|
||||
setTextHtml(if (text is Some.Success) text.value else null)
|
||||
}
|
||||
|
||||
fun TextView?.setText(text: Some<UiText>?) {
|
||||
setText(if(text is Some.Success) text.value else null)
|
||||
setText(if (text is Some.Success) text.value else null)
|
||||
}
|
|
@ -44,6 +44,8 @@ import com.lagradost.cloudstream3.isMovieType
|
|||
import com.lagradost.cloudstream3.mapper
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.ui.result.ResultFragment
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||
import com.lagradost.cloudstream3.utils.FillerEpisodeCheck.toClassDir
|
||||
import com.lagradost.cloudstream3.utils.JsUnpacker.Companion.load
|
||||
|
@ -313,6 +315,15 @@ object AppUtils {
|
|||
|
||||
//private val viewModel: ResultViewModel by activityViewModels()
|
||||
|
||||
private fun getResultsId(context: Context) : Int {
|
||||
return R.id.global_to_navigation_results_phone
|
||||
//return if(context.isTvSettings()) {
|
||||
// R.id.global_to_navigation_results_tv
|
||||
//} else {
|
||||
// R.id.global_to_navigation_results_phone
|
||||
//}
|
||||
}
|
||||
|
||||
fun AppCompatActivity.loadResult(
|
||||
url: String,
|
||||
apiName: String,
|
||||
|
@ -322,7 +333,7 @@ object AppUtils {
|
|||
this.runOnUiThread {
|
||||
// viewModelStore.clear()
|
||||
this.navigate(
|
||||
R.id.global_to_navigation_results,
|
||||
getResultsId(this.applicationContext ?: return@runOnUiThread),
|
||||
ResultFragment.newInstance(url, apiName, startAction, startValue)
|
||||
)
|
||||
}
|
||||
|
@ -336,7 +347,7 @@ object AppUtils {
|
|||
this?.runOnUiThread {
|
||||
// viewModelStore.clear()
|
||||
this.navigate(
|
||||
R.id.global_to_navigation_results,
|
||||
getResultsId(this),
|
||||
ResultFragment.newInstance(card, startAction, startValue)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -465,6 +465,7 @@
|
|||
android:textColor="?attr/grayTextColor"
|
||||
android:textSize="15sp"
|
||||
tools:text="@string/provider_info_meta" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_no_episodes"
|
||||
android:layout_width="match_parent"
|
||||
|
|
699
app/src/main/res/layout/fragment_result_tv.xml
Normal file
699
app/src/main/res/layout/fragment_result_tv.xml
Normal file
|
@ -0,0 +1,699 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/result_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
style="@style/DarkFragment"
|
||||
android:background="?attr/primaryBlackBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
android:id="@+id/result_loading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical"
|
||||
app:shimmer_auto_start="true"
|
||||
app:shimmer_base_alpha="0.2"
|
||||
app:shimmer_duration="@integer/loading_time"
|
||||
app:shimmer_highlight_alpha="0.3"
|
||||
tools:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/result_padding"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="@dimen/loading_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<include layout="@layout/loading_poster" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/loading_margin"
|
||||
android:layout_marginEnd="@dimen/loading_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/loading_line" />
|
||||
|
||||
<include layout="@layout/loading_line" />
|
||||
|
||||
<include layout="@layout/loading_line" />
|
||||
|
||||
<include layout="@layout/loading_line" />
|
||||
|
||||
<include layout="@layout/loading_line_short" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<include layout="@layout/loading_episode" />
|
||||
|
||||
<include layout="@layout/loading_episode" />
|
||||
|
||||
<include layout="@layout/loading_episode" />
|
||||
</LinearLayout>
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/result_loading_error"
|
||||
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="gone">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/result_reload_connectionerror"
|
||||
style="@style/WhiteButton"
|
||||
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="5dp"
|
||||
android:minWidth="200dp"
|
||||
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"
|
||||
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="5dp"
|
||||
android:minWidth="200dp"
|
||||
android:text="@string/result_open_in_browser"
|
||||
app:icon="@drawable/ic_baseline_public_24" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_error_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="5dp"
|
||||
android:gravity="center"
|
||||
android:textColor="?attr/textColor" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/result_finish_loading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/result_scroll"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/primaryGrayBackground">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/primaryBlackBackground"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
tools:visibility="gone"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/result_trailer_loading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical"
|
||||
app:shimmer_auto_start="true"
|
||||
app:shimmer_base_alpha="0.2"
|
||||
app:shimmer_duration="@integer/loading_time"
|
||||
app:shimmer_highlight_alpha="0.3">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/result_padding"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:background="@color/grayShimmer"
|
||||
app:cardCornerRadius="@dimen/loading_radius"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="150dp"
|
||||
android:foreground="@drawable/outline_drawable" />
|
||||
</LinearLayout>
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:id="@+id/result_smallscreen_holder"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include layout="@layout/fragment_trailer" />
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/result_padding"
|
||||
android:paddingEnd="@dimen/result_padding">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:maxLines="2"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="The Perfect Run The Perfect Run" />
|
||||
|
||||
<com.lagradost.cloudstream3.widget.FlowLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:itemSpacing="10dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/result_meta_site"
|
||||
style="@style/SmallBlackButton"
|
||||
android:layout_gravity="center_vertical"
|
||||
tools:text="Gogoanime" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_meta_type"
|
||||
style="@style/ResultInfoText"
|
||||
tools:text="Movie" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_meta_year"
|
||||
style="@style/ResultInfoText"
|
||||
tools:text="2022" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_meta_rating"
|
||||
style="@style/ResultInfoText"
|
||||
tools:text="Rated: 8.5/10.0" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_meta_status"
|
||||
style="@style/ResultInfoText"
|
||||
tools:text="Ongoing" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_meta_duration"
|
||||
style="@style/ResultInfoText"
|
||||
tools:text="121min" />
|
||||
</com.lagradost.cloudstream3.widget.FlowLayout>
|
||||
|
||||
<!--
|
||||
This has half margin and half padding to make TV focus on description look better.
|
||||
The focus outline now settles between the poster and text.
|
||||
-->
|
||||
<TextView
|
||||
android:padding="5dp"
|
||||
android:maxLength="1000"
|
||||
android:ellipsize="end"
|
||||
android:id="@+id/result_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="@drawable/outline_drawable"
|
||||
android:nextFocusUp="@id/result_back"
|
||||
android:nextFocusDown="@id/result_bookmark_button"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="15sp"
|
||||
tools:text="Ryan Quicksave Romano is an eccentric adventurer with a strange power: he can create a save-point in time and redo his life whenever he dies. Arriving in New Rome, the glitzy capital of sin of a rebuilding Europe, he finds the city torn between mega-corporations, sponsored heroes, superpowered criminals, and true monsters. It's a time of chaos, where potions can grant the power to rule the world and dangers lurk everywhere. " />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_cast_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textColor="?attr/grayTextColor"
|
||||
android:textSize="15sp"
|
||||
tools:text="Cast: Joe Ligma" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
tools:visibility="gone"
|
||||
android:nextFocusUp="@id/result_bookmark_button"
|
||||
android:nextFocusDown="@id/result_play_movie"
|
||||
|
||||
android:id="@+id/result_cast_items"
|
||||
android:layout_width="match_parent"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:layout_height="wrap_content"
|
||||
android:fadingEdge="horizontal"
|
||||
android:focusableInTouchMode="false"
|
||||
android:focusable="false"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="5dp"
|
||||
android:requiresFadingEdge="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="2"
|
||||
tools:listitem="@layout/cast_item" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_vpn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?attr/grayTextColor"
|
||||
android:textSize="15sp"
|
||||
tools:text="@string/vpn_torrent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:textColor="?attr/grayTextColor"
|
||||
android:textSize="15sp"
|
||||
tools:text="@string/provider_info_meta" />
|
||||
<TextView
|
||||
android:id="@+id/result_no_episodes"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:textColor="?attr/grayTextColor"
|
||||
android:textSize="15sp"
|
||||
tools:text="@string/no_episodes_found" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_tag_holder"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/result_tags"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="normal"
|
||||
android:visibility="gone" />
|
||||
|
||||
|
||||
<com.lagradost.cloudstream3.widget.FlowLayout
|
||||
android:id="@+id/result_tag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_coming_soon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:paddingTop="50dp"
|
||||
android:text="@string/coming_soon"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/result_data_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/result_add_sync"
|
||||
style="@style/WhiteButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="0dp"
|
||||
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/add_sync"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_baseline_add_24" />
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/result_movie_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:orientation="horizontal"
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/result_play_movie"
|
||||
style="@style/WhiteButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:nextFocusUp="@id/result_bookmark_button"
|
||||
android:nextFocusDown="@id/result_download_movie"
|
||||
android:text="@string/play_movie_button"
|
||||
android:visibility="visible"
|
||||
app:icon="@drawable/ic_baseline_play_arrow_24">
|
||||
|
||||
<requestFocus />
|
||||
|
||||
</com.google.android.material.button.MaterialButton>
|
||||
|
||||
|
||||
<FrameLayout
|
||||
android:layout_marginStart="5dp"
|
||||
android:id="@+id/result_movie_progress_downloaded_holder"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/result_download_movie"
|
||||
style="@style/BlackButton"
|
||||
|
||||
android:layout_width="match_parent"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
|
||||
android:nextFocusUp="@id/result_play_movie"
|
||||
android:nextFocusDown="@id/result_season_button"
|
||||
android:visibility="visible" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.core.widget.ContentLoadingProgressBar
|
||||
android:id="@+id/result_movie_progress_downloaded"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_margin="5dp"
|
||||
android:background="@drawable/circle_shape"
|
||||
android:indeterminate="false"
|
||||
android:max="100"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:progress="30"
|
||||
android:progressDrawable="@drawable/circular_progress_bar_filled"
|
||||
android:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/result_movie_download_icon"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/download"
|
||||
android:src="@drawable/ic_baseline_play_arrow_24"
|
||||
android:visibility="visible"
|
||||
app:tint="?attr/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_movie_download_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:letterSpacing="0.09"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="Downloading" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_movie_download_text_precentage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:letterSpacing="0.09"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
tools:text="68%" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/result_resume_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/result_next_series_button"
|
||||
style="@style/WhiteButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:nextFocusUp="@id/result_bookmark_button"
|
||||
android:nextFocusDown="@id/result_download_movie"
|
||||
android:text="@string/next_episode"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/cast_ic_mini_controller_skip_next" />
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:layout_marginEnd="10dp"
|
||||
android:id="@+id/result_resume_series_button_play"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_gravity="center"
|
||||
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/download"
|
||||
android:src="@drawable/ic_baseline_play_arrow_24"
|
||||
android:visibility="visible"
|
||||
app:tint="?attr/white" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:id="@+id/result_resume_series_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="S1E1 Episode 1" />
|
||||
|
||||
<TextView
|
||||
android:maxLines="1"
|
||||
android:id="@+id/result_resume_series_progress_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="0"
|
||||
android:gravity="center"
|
||||
android:paddingStart="10dp"
|
||||
android:textColor="?attr/grayTextColor"
|
||||
tools:ignore="RtlSymmetry"
|
||||
tools:text="69m remaining" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/result_resume_progress_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="10dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<androidx.core.widget.ContentLoadingProgressBar
|
||||
android:id="@+id/result_resume_series_progress"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:indeterminate="false"
|
||||
android:max="100"
|
||||
android:progress="0"
|
||||
android:progressBackgroundTint="?attr/colorPrimary"
|
||||
android:visibility="visible"
|
||||
tools:progress="50"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/result_episodes_tab"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:paddingBottom="10dp"
|
||||
android:id="@+id/result_season_selection"
|
||||
tools:listitem="@layout/result_selection"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:paddingBottom="10dp"
|
||||
android:id="@+id/result_range_selection"
|
||||
tools:listitem="@layout/result_selection"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:paddingBottom="10dp"
|
||||
android:id="@+id/result_dub_selection"
|
||||
tools:listitem="@layout/result_selection"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/result_next_airing_holder"
|
||||
android:layout_gravity="start"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:gravity="center"
|
||||
|
||||
android:id="@+id/result_next_airing"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?attr/grayTextColor"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="normal"
|
||||
tools:text="Episode 1022 will be released in" />
|
||||
|
||||
<TextView
|
||||
android:paddingEnd="5dp"
|
||||
android:paddingStart="5dp"
|
||||
android:gravity="center"
|
||||
android:id="@+id/result_next_airing_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="normal"
|
||||
tools:text="5d 3h 30m" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
android:id="@+id/result_episode_loading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="15dp"
|
||||
android:orientation="vertical"
|
||||
app:shimmer_auto_start="true"
|
||||
app:shimmer_base_alpha="0.2"
|
||||
app:shimmer_duration="@integer/loading_time"
|
||||
app:shimmer_highlight_alpha="0.3"
|
||||
tools:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/loading_episode" />
|
||||
|
||||
<include layout="@layout/loading_episode" />
|
||||
|
||||
<include layout="@layout/loading_episode" />
|
||||
|
||||
<include layout="@layout/loading_episode" />
|
||||
</LinearLayout>
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
<!--<androidx.core.widget.ContentLoadingProgressBar
|
||||
android:id="@+id/result_episode_loading"
|
||||
|
||||
style="@style/Widget.AppCompat.ProgressBar"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp" />-->
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/result_episodes"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="0dp"
|
||||
android:clipToPadding="false"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:paddingBottom="100dp"
|
||||
tools:listitem="@layout/result_episode" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
8
app/src/main/res/layout/result_selection.xml
Normal file
8
app/src/main/res/layout/result_selection.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.button.MaterialButton android:id="@+id/result_season_button"
|
||||
style="@style/SelectableButton"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
tools:text="Season 1"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" />
|
|
@ -4,10 +4,35 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/mobile_navigation"
|
||||
app:startDestination="@+id/navigation_home">
|
||||
|
||||
<action
|
||||
android:id="@+id/global_to_navigation_results"
|
||||
app:destination="@id/navigation_results"
|
||||
android:id="@+id/global_to_navigation_results_tv"
|
||||
app:destination="@id/navigation_results_tv"
|
||||
app:enterAnim="@anim/enter_anim"
|
||||
app:exitAnim="@anim/exit_anim"
|
||||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim">
|
||||
<argument
|
||||
android:name="url"
|
||||
app:argType="string" />
|
||||
<argument
|
||||
android:name="apiName"
|
||||
app:argType="string" />
|
||||
<argument
|
||||
android:name="startAction"
|
||||
android:defaultValue="0"
|
||||
app:argType="integer" />
|
||||
<argument
|
||||
android:name="startValue"
|
||||
android:defaultValue="0"
|
||||
app:argType="integer" />
|
||||
<argument
|
||||
android:name="restart"
|
||||
android:defaultValue="false"
|
||||
app:argType="boolean" />
|
||||
</action>
|
||||
<action
|
||||
android:id="@+id/global_to_navigation_results_phone"
|
||||
app:destination="@id/navigation_results_phone"
|
||||
app:enterAnim="@anim/enter_anim"
|
||||
app:exitAnim="@anim/exit_anim"
|
||||
app:popEnterAnim="@anim/enter_anim"
|
||||
|
@ -181,13 +206,6 @@
|
|||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim"
|
||||
tools:layout="@layout/fragment_home">
|
||||
<action
|
||||
android:id="@+id/action_navigation_home_to_navigation_results"
|
||||
app:destination="@id/navigation_results"
|
||||
app:enterAnim="@anim/enter_anim"
|
||||
app:exitAnim="@anim/exit_anim"
|
||||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim" />
|
||||
<action
|
||||
android:id="@+id/action_navigation_home_to_navigation_quick_search"
|
||||
app:destination="@id/navigation_quick_search"
|
||||
|
@ -206,13 +224,6 @@
|
|||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim"
|
||||
tools:layout="@layout/fragment_search">
|
||||
<action
|
||||
android:id="@+id/action_navigation_search_to_navigation_results"
|
||||
app:destination="@id/navigation_results"
|
||||
app:enterAnim="@anim/enter_anim"
|
||||
app:exitAnim="@anim/exit_anim"
|
||||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -239,13 +250,6 @@
|
|||
android:name="folder"
|
||||
app:argType="string" />
|
||||
</action>
|
||||
<action
|
||||
android:id="@+id/action_navigation_downloads_to_navigation_results"
|
||||
app:destination="@id/navigation_results"
|
||||
app:enterAnim="@anim/enter_anim"
|
||||
app:exitAnim="@anim/exit_anim"
|
||||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim" />
|
||||
<action
|
||||
android:id="@+id/action_navigation_downloads_to_navigation_player"
|
||||
app:destination="@id/navigation_player"
|
||||
|
@ -360,6 +364,56 @@
|
|||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/navigation_results_phone"
|
||||
android:name="com.lagradost.cloudstream3.ui.result.ResultFragmentPhone"
|
||||
android:layout_height="match_parent"
|
||||
app:enterAnim="@anim/enter_anim"
|
||||
app:exitAnim="@anim/exit_anim"
|
||||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim"
|
||||
tools:layout="@layout/fragment_result_swipe">
|
||||
<action
|
||||
android:id="@+id/action_navigation_results_phone_to_navigation_quick_search"
|
||||
app:destination="@id/navigation_quick_search"
|
||||
app:enterAnim="@anim/enter_anim"
|
||||
app:exitAnim="@anim/exit_anim"
|
||||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim" />
|
||||
<action
|
||||
android:id="@+id/action_navigation_results_phone_to_navigation_player"
|
||||
app:destination="@id/navigation_player"
|
||||
app:enterAnim="@anim/enter_anim"
|
||||
app:exitAnim="@anim/exit_anim"
|
||||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/navigation_results_tv"
|
||||
android:name="com.lagradost.cloudstream3.ui.result.ResultFragmentTv"
|
||||
android:layout_height="match_parent"
|
||||
app:enterAnim="@anim/enter_anim"
|
||||
app:exitAnim="@anim/exit_anim"
|
||||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim"
|
||||
tools:layout="@layout/fragment_result_swipe">
|
||||
<action
|
||||
android:id="@+id/action_navigation_results_tv_to_navigation_quick_search"
|
||||
app:destination="@id/navigation_quick_search"
|
||||
app:enterAnim="@anim/enter_anim"
|
||||
app:exitAnim="@anim/exit_anim"
|
||||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim" />
|
||||
<action
|
||||
android:id="@+id/action_navigation_results_tv_to_navigation_player"
|
||||
app:destination="@id/navigation_player"
|
||||
app:enterAnim="@anim/enter_anim"
|
||||
app:exitAnim="@anim/exit_anim"
|
||||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim" />
|
||||
</fragment>
|
||||
|
||||
<!--<fragment
|
||||
android:id="@+id/navigation_results"
|
||||
android:name="com.lagradost.cloudstream3.ui.result.ResultFragment"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -382,7 +436,7 @@
|
|||
app:exitAnim="@anim/exit_anim"
|
||||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim" />
|
||||
</fragment>
|
||||
</fragment>-->
|
||||
|
||||
<fragment
|
||||
android:id="@+id/navigation_player"
|
||||
|
|
|
@ -338,6 +338,7 @@
|
|||
<style name="DarkFragment" parent="AppTheme">
|
||||
<item name="android:navigationBarColor">?attr/colorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="AlertDialogCustom" parent="Theme.AppCompat.Dialog.Alert">
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:textColor">?attr/textColor</item>
|
||||
|
@ -460,7 +461,7 @@
|
|||
<item name="drawableStartCompat">@drawable/ic_baseline_check_24_listview</item>
|
||||
</style>
|
||||
|
||||
<style name="NoCheckLabel" parent="@style/AppTextViewStyle">
|
||||
<style name="NoCheckLabel" parent="@style/AppTextViewStyle">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item>
|
||||
|
@ -476,7 +477,6 @@
|
|||
</style>
|
||||
|
||||
|
||||
|
||||
<style name="BlackButton" parent="NiceButton">
|
||||
<item name="strokeColor">?attr/textColor</item>
|
||||
<item name="backgroundTint">?attr/iconGrayBackground</item>
|
||||
|
@ -544,6 +544,23 @@
|
|||
<style name="MultiSelectButton" parent="BlackButton">
|
||||
<item name="android:layout_height">40dp</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
|
||||
<item name="strokeColor">?attr/textColor</item>
|
||||
<item name="backgroundTint">?attr/iconGrayBackground</item>
|
||||
<item name="iconTint">?attr/textColor</item>
|
||||
<item name="android:textColor">?attr/textColor</item>
|
||||
<item name="rippleColor">?attr/textColor</item>
|
||||
</style>
|
||||
|
||||
<style name="SelectableButton" parent="NiceButton">
|
||||
<item name="android:layout_height">40dp</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
|
||||
<item name="strokeColor">@color/selectable_black</item>
|
||||
<item name="backgroundTint">@color/selectable_white</item>
|
||||
<item name="iconTint">@color/selectable_black</item>
|
||||
<item name="android:textColor">@color/selectable_black</item>
|
||||
<item name="rippleColor">@color/selectable_black</item>
|
||||
</style>
|
||||
|
||||
<style name="VideoButton">
|
||||
|
|
Loading…
Reference in a new issue