New TV UI (#950)

This commit is contained in:
KingLucius 2024-03-09 16:24:38 +02:00 committed by GitHub
parent bd69054f5d
commit 510d11f705
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 633 additions and 569 deletions

View file

@ -1189,8 +1189,26 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu
newLocalBinding.focusOutline.isVisible = false newLocalBinding.focusOutline.isVisible = false
} }
if (isTrueTvSettings()) { if(isTrueTvSettings()) {
// Put here any button you don't want focusing it to center the view
val exceptionButtons = listOf(
R.id.home_preview_play_btt,
R.id.home_preview_info_btt,
R.id.home_preview_hidden_next_focus,
R.id.home_preview_hidden_prev_focus,
R.id.result_play_movie_button,
R.id.result_play_series_button,
R.id.result_resume_series_button,
R.id.result_play_trailer_button,
R.id.result_bookmark_Button,
R.id.result_favorite_Button,
R.id.result_subscribe_Button,
R.id.result_search_Button,
R.id.result_episodes_show_button,
)
newLocalBinding.root.viewTreeObserver.addOnGlobalFocusChangeListener { _, newFocus -> newLocalBinding.root.viewTreeObserver.addOnGlobalFocusChangeListener { _, newFocus ->
if (exceptionButtons.contains(newFocus?.id)) return@addOnGlobalFocusChangeListener
centerView(newFocus) centerView(newFocus)
} }
} }

View file

@ -529,6 +529,7 @@ class HomeFragment : Fragment() {
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
} }
}) })
} }

View file

@ -33,13 +33,13 @@ import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup 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.quicksearch.QuickSearchFragment
import com.lagradost.cloudstream3.ui.result.ResultFragment.getStoredData import com.lagradost.cloudstream3.ui.result.ResultFragment.getStoredData
import com.lagradost.cloudstream3.ui.result.ResultFragment.updateUIEvent import com.lagradost.cloudstream3.ui.result.ResultFragment.updateUIEvent
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_FOCUSED import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_FOCUSED
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.Companion.isEmulatorSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.AppUtils.isRtl import com.lagradost.cloudstream3.utils.AppUtils.isRtl
import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.loadCache
@ -129,9 +129,9 @@ class ResultFragmentTv : Fragment() {
* Note that this will steal any focus if the episode loading is too slow (unlikely). * Note that this will steal any focus if the episode loading is too slow (unlikely).
*/ */
private fun focusPlayButton() { private fun focusPlayButton() {
binding?.resultPlayMovie?.requestFocus() binding?.resultPlayMovieButton?.requestFocus()
binding?.resultPlaySeries?.requestFocus() binding?.resultPlaySeriesButton?.requestFocus()
binding?.resultResumeSeries?.requestFocus() binding?.resultResumeSeriesButton?.requestFocus()
} }
private fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) { private fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
@ -246,37 +246,15 @@ class ResultFragmentTv : Fragment() {
storedData.start storedData.start
) )
// ===== ===== ===== // ===== ===== =====
var comingSoon = false
binding?.apply { binding?.apply {
//episodesShadow.rotationX = 180.0f//if(episodesShadow.isRtl()) 180.0f else 0.0f //episodesShadow.rotationX = 180.0f//if(episodesShadow.isRtl()) 180.0f else 0.0f
val leftListener: View.OnFocusChangeListener = // parallax on background
View.OnFocusChangeListener { _, hasFocus -> resultFinishLoading.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { view, _, scrollY, _, oldScrollY ->
if (!hasFocus) return@OnFocusChangeListener backgroundPosterHolder.translationY = -scrollY.toFloat() * 0.8f
toggleEpisodes(false) })
}
val rightListener: View.OnFocusChangeListener =
View.OnFocusChangeListener { _, hasFocus ->
if (!hasFocus) return@OnFocusChangeListener
toggleEpisodes(true)
}
resultPlayMovie.onFocusChangeListener = leftListener
resultPlaySeries.onFocusChangeListener = leftListener
resultResumeSeries.onFocusChangeListener = leftListener
resultPlayTrailer.onFocusChangeListener = leftListener
resultEpisodesShow.onFocusChangeListener = rightListener
resultDescription.onFocusChangeListener = leftListener
resultBookmarkButton.onFocusChangeListener = leftListener
resultFavoriteButton.onFocusChangeListener = leftListener
resultEpisodesShow.setOnClickListener {
// toggle, to make it more touch accessable just in case someone thinks that a
// tv layout is better but is using a touch device
toggleEpisodes(!episodeHolderTv.isVisible)
}
// resultEpisodes.onFocusChangeListener = leftListener
redirectToPlay.setOnFocusChangeListener { _, hasFocus -> redirectToPlay.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) return@setOnFocusChangeListener if (!hasFocus) return@setOnFocusChangeListener
@ -284,13 +262,14 @@ class ResultFragmentTv : Fragment() {
binding?.apply { binding?.apply {
val views = listOf( val views = listOf(
resultPlayMovie, resultPlayMovieButton,
resultPlaySeries, resultPlaySeriesButton,
resultResumeSeries, resultResumeSeriesButton,
resultPlayTrailer, resultPlayTrailerButton,
resultBookmarkButton, resultBookmarkButton,
resultFavoriteButton, resultFavoriteButton,
resultSubscribeButton resultSubscribeButton,
resultSearchButton
) )
for (requestView in views) { for (requestView in views) {
if (!requestView.isVisible) continue if (!requestView.isVisible) continue
@ -299,11 +278,6 @@ class ResultFragmentTv : Fragment() {
} }
} }
// parallax on background
resultFinishLoading.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
backgroundPosterHolder.translationY = -scrollY.toFloat() * 0.8f
})
redirectToEpisodes.setOnFocusChangeListener { _, hasFocus -> redirectToEpisodes.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) return@setOnFocusChangeListener if (!hasFocus) return@setOnFocusChangeListener
toggleEpisodes(true) toggleEpisodes(true)
@ -313,7 +287,7 @@ class ResultFragmentTv : Fragment() {
resultSeasonSelection, resultSeasonSelection,
resultRangeSelection, resultRangeSelection,
resultEpisodes, resultEpisodes,
resultPlayTrailer, resultPlayTrailerButton,
) )
for (requestView in views) { for (requestView in views) {
if (!requestView.isShown) continue if (!requestView.isShown) continue
@ -322,6 +296,45 @@ class ResultFragmentTv : Fragment() {
} }
} }
mapOf(
resultPlayMovieButton to resultPlayMovieText,
resultPlaySeriesButton to resultPlaySeriesText,
resultResumeSeriesButton to resultResumeSeriesText,
resultPlayTrailerButton to resultPlayTrailerText,
resultBookmarkButton to resultBookmarkText,
resultFavoriteButton to resultFavoriteText,
resultSubscribeButton to resultSubscribeText,
resultSearchButton to resultSearchText,
resultEpisodesShowButton to resultEpisodesShowText
).forEach { (button , text) ->
button.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
text.isSelected = false
return@setOnFocusChangeListener
}
text.isSelected = true
if (button.tag == context?.getString(R.string.tv_no_focus_tag)){
resultFinishLoading.scrollTo(0,0)
}
when (button.id) {
R.id.result_episodes_show_button -> {
toggleEpisodes(true)
}
else -> {
toggleEpisodes(false)
}
}
}
}
resultEpisodesShowButton.setOnClickListener {
// toggle, to make it more touch accessible just in case someone thinks that a
// tv layout is better but is using a touch device
toggleEpisodes(!episodeHolderTv.isVisible)
}
resultEpisodes.setLinearListLayout( resultEpisodes.setLinearListLayout(
isHorizontal = false, isHorizontal = false,
nextUp = FOCUS_SELF, nextUp = FOCUS_SELF,
@ -430,9 +443,9 @@ class ResultFragmentTv : Fragment() {
val aboveCast = listOf( val aboveCast = listOf(
binding?.resultEpisodesShow, binding?.resultEpisodesShow,
binding?.resultBookmarkButton, binding?.resultBookmark,
binding?.resultFavoriteButton, binding?.resultFavorite,
binding?.resultSubscribeButton, binding?.resultSubscribe,
).firstOrNull { ).firstOrNull {
it?.isVisible == true it?.isVisible == true
} }
@ -443,8 +456,15 @@ class ResultFragmentTv : Fragment() {
observeNullable(viewModel.resumeWatching) { resume -> observeNullable(viewModel.resumeWatching) { resume ->
binding?.apply { binding?.apply {
// > resultResumeSeries is visible when not null
if (resume == null) {
resultResumeSeries.isVisible = false
return@observeNullable
}
// show progress no matter if series or movie // show progress no matter if series or movie
resume?.progress?.let { progress -> resume.progress?.let { progress ->
resultResumeSeriesProgressText.setText(progress.progressLeft) resultResumeSeriesProgressText.setText(progress.progressLeft)
resultResumeSeriesProgress.apply { resultResumeSeriesProgress.apply {
isVisible = true isVisible = true
@ -456,37 +476,24 @@ class ResultFragmentTv : Fragment() {
resultResumeProgressHolder.isVisible = false resultResumeProgressHolder.isVisible = false
} }
// if movie then hide both as movie button is resultPlayMovie.isVisible = false
// always visible on movies, this is done in movie observe
if (resume?.isMovie == true) {
resultPlaySeries.isVisible = false
resultResumeSeries.isVisible = false
return@observeNullable
}
// if series then
// > resultPlaySeries is visible when null
// > resultResumeSeries is visible when not null
if (resume == null) {
resultPlaySeries.isVisible = true
resultResumeSeries.isVisible = false
return@observeNullable
}
resultPlaySeries.isVisible = false resultPlaySeries.isVisible = false
resultResumeSeries.isVisible = true resultResumeSeries.isVisible = true
focusPlayButton() focusPlayButton()
// Stops last button right focus if it is a movie
if (resume.isMovie)
resultSearchButton.nextFocusRightId = R.id.result_search_Button
resultResumeSeries.text = resultResumeSeriesText.text =
if (resume.isMovie) context?.getString(R.string.play_movie_button) else context?.getNameFull( when {
null, // resume.result.name, we don't want episode title resume.isMovie -> context?.getString(R.string.resume)
resume.result.episode, resume.result.season != null ->
resume.result.season "${getString(R.string.season_short)}${resume.result.season}:${getString(R.string.episode_short)}${resume.result.episode}"
) else -> "${getString(R.string.episode)}${resume.result.episode}"
}
resultResumeSeries.setOnClickListener { resultResumeSeriesButton.setOnClickListener {
viewModel.handleAction( viewModel.handleAction(
EpisodeClickEvent( EpisodeClickEvent(
storedData.playerAction, //?: ACTION_PLAY_EPISODE_IN_PLAYER, storedData.playerAction, //?: ACTION_PLAY_EPISODE_IN_PLAYER,
@ -495,7 +502,7 @@ class ResultFragmentTv : Fragment() {
) )
} }
resultResumeSeries.setOnLongClickListener { resultResumeSeriesButton.setOnLongClickListener {
viewModel.handleAction( viewModel.handleAction(
EpisodeClickEvent(ACTION_SHOW_OPTIONS, resume.result) EpisodeClickEvent(ACTION_SHOW_OPTIONS, resume.result)
) )
@ -509,9 +516,9 @@ class ResultFragmentTv : Fragment() {
context?.updateHasTrailers() context?.updateHasTrailers()
if (!LoadResponse.isTrailersEnabled) return@observe if (!LoadResponse.isTrailersEnabled) return@observe
val trailers = trailersLinks.flatMap { it.mirros } val trailers = trailersLinks.flatMap { it.mirros }
binding?.resultPlayTrailer?.apply { binding?.apply {
isGone = trailers.isEmpty() resultPlayTrailer.isGone = trailers.isEmpty()
setOnClickListener { resultPlayTrailerButton.setOnClickListener {
if (trailers.isEmpty()) return@setOnClickListener if (trailers.isEmpty()) return@setOnClickListener
activity.navigate( activity.navigate(
R.id.global_to_navigation_player, GeneratorPlayer.newInstance( R.id.global_to_navigation_player, GeneratorPlayer.newInstance(
@ -526,24 +533,38 @@ class ResultFragmentTv : Fragment() {
} }
observe(viewModel.watchStatus) { watchType -> observe(viewModel.watchStatus) { watchType ->
binding?.resultBookmarkButton?.apply { binding?.apply {
setText(watchType.stringRes) resultBookmarkText.setText(watchType.stringRes)
resultBookmarkButton.apply {
val drawable = if (watchType.stringRes == R.string.type_none) {
R.drawable.outline_bookmark_add_24
} else {
R.drawable.ic_baseline_bookmark_24
}
setIconResource(drawable)
setOnClickListener { view -> setOnClickListener { view ->
activity?.showBottomDialog( activity?.showBottomDialog(
WatchType.values().map { view.context.getString(it.stringRes) }.toList(), WatchType.entries.map { view.context.getString(it.stringRes) }.toList(),
watchType.ordinal, watchType.ordinal,
view.context.getString(R.string.action_add_to_bookmarks), view.context.getString(R.string.action_add_to_bookmarks),
showApply = false, showApply = false,
{}) { {}) {
viewModel.updateWatchStatus(WatchType.values()[it], context) viewModel.updateWatchStatus(WatchType.entries[it], context)
}
} }
} }
} }
} }
observeNullable(viewModel.favoriteStatus) { isFavorite -> observeNullable(viewModel.favoriteStatus) { isFavorite ->
binding?.resultFavorite?.isVisible = isFavorite != null
binding?.resultFavoriteButton?.apply { binding?.resultFavoriteButton?.apply {
isVisible = isFavorite != null
if (isFavorite == null) return@observeNullable if (isFavorite == null) return@observeNullable
val drawable = if (isFavorite) { val drawable = if (isFavorite) {
@ -552,14 +573,8 @@ class ResultFragmentTv : Fragment() {
R.drawable.ic_baseline_favorite_border_24 R.drawable.ic_baseline_favorite_border_24
} }
val text = if (isFavorite) {
R.string.action_remove_from_favorites
} else {
R.string.action_add_to_favorites
}
setIconResource(drawable) setIconResource(drawable)
setText(text)
setOnClickListener { setOnClickListener {
viewModel.toggleFavoriteStatus(context) { newStatus: Boolean? -> viewModel.toggleFavoriteStatus(context) { newStatus: Boolean? ->
if (newStatus == null) return@toggleFavoriteStatus if (newStatus == null) return@toggleFavoriteStatus
@ -576,11 +591,21 @@ class ResultFragmentTv : Fragment() {
} }
} }
} }
binding?.resultFavoriteText?.apply {
val text = if (isFavorite == true) {
R.string.unfavorite
} else {
R.string.favorite
}
setText(text)
}
} }
observeNullable(viewModel.subscribeStatus) { isSubscribed -> observeNullable(viewModel.subscribeStatus) { isSubscribed ->
binding?.resultSubscribe?.isVisible = isSubscribed != null && requireContext().isEmulatorSettings()
binding?.resultSubscribeButton?.apply { binding?.resultSubscribeButton?.apply {
isVisible = isSubscribed != null && context.isEmulatorSettings()
if (isSubscribed == null) return@observeNullable if (isSubscribed == null) return@observeNullable
val drawable = if (isSubscribed) { val drawable = if (isSubscribed) {
@ -589,14 +614,8 @@ class ResultFragmentTv : Fragment() {
R.drawable.baseline_notifications_none_24 R.drawable.baseline_notifications_none_24
} }
val text = if (isSubscribed) {
R.string.action_unsubscribe
} else {
R.string.action_subscribe
}
setIconResource(drawable) setIconResource(drawable)
setText(text)
setOnClickListener { setOnClickListener {
viewModel.toggleSubscriptionStatus(context) { newStatus: Boolean? -> viewModel.toggleSubscriptionStatus(context) { newStatus: Boolean? ->
if (newStatus == null) return@toggleSubscriptionStatus if (newStatus == null) return@toggleSubscriptionStatus
@ -614,32 +633,47 @@ class ResultFragmentTv : Fragment() {
CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT) CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
} }
} }
binding?.resultSubscribeText?.apply {
val text = if (isSubscribed) {
R.string.action_unsubscribe
} else {
R.string.action_subscribe
}
setText(text)
}
} }
} }
observeNullable(viewModel.movie) { data -> observeNullable(viewModel.movie) { data ->
if (data == null) return@observeNullable
binding?.apply { binding?.apply {
resultPlayMovie.isVisible = data is Resource.Success resultPlayMovie.isVisible = (data is Resource.Success) && !comingSoon
resultPlaySeries.isVisible = data == null resultPlaySeries.isVisible = false
seriesHolder.isVisible = data == null resultEpisodesShow.isVisible = false
resultEpisodesShow.isVisible = data == null
(data as? Resource.Success)?.value?.let { (text, ep) -> (data as? Resource.Success)?.value?.let { (text, ep) ->
resultPlayMovie.setText(text) //resultPlayMovieText.setText(text)
resultPlayMovie.setOnClickListener { resultPlayMovieButton.setOnClickListener {
viewModel.handleAction( viewModel.handleAction(
EpisodeClickEvent(ACTION_CLICK_DEFAULT, ep) EpisodeClickEvent(ACTION_CLICK_DEFAULT, ep)
) )
} }
resultPlayMovie.setOnLongClickListener { resultPlayMovieButton.setOnLongClickListener {
viewModel.handleAction( viewModel.handleAction(
EpisodeClickEvent(ACTION_SHOW_OPTIONS, ep) EpisodeClickEvent(ACTION_SHOW_OPTIONS, ep)
) )
return@setOnLongClickListener true return@setOnLongClickListener true
} }
focusPlayButton() //focusPlayButton()
resultPlayMovieButton.requestFocus()
// Stops last button right focus
resultSearchButton.nextFocusRightId = R.id.result_search_Button
} }
} }
//focusPlayButton()
} }
observeNullable(viewModel.selectPopup) { popup -> observeNullable(viewModel.selectPopup) { popup ->
@ -736,19 +770,26 @@ class ResultFragmentTv : Fragment() {
// Used to request focus the first time the episodes are loaded. // Used to request focus the first time the episodes are loaded.
var hasLoadedEpisodesOnce = false var hasLoadedEpisodesOnce = false
observeNullable(viewModel.episodes) { episodes -> observeNullable(viewModel.episodes) { episodes ->
if (episodes == null) return@observeNullable
binding?.apply { binding?.apply {
resultEpisodes.isVisible = episodes is Resource.Success
resultPlayMovie.isVisible = false
resultPlaySeries.isVisible = true && !comingSoon
resultEpisodes.isVisible = true && !comingSoon
resultEpisodesShow.isVisible = true && !comingSoon
// resultEpisodeLoading.isVisible = episodes is Resource.Loading // resultEpisodeLoading.isVisible = episodes is Resource.Loading
if (episodes is Resource.Success) { if (episodes is Resource.Success) {
val first = episodes.value.firstOrNull() val first = episodes.value.firstOrNull()
if (first != null) { if (first != null) {
resultPlaySeries.text = context?.getNameFull( resultPlaySeriesText.text = //"${getString(R.string.season_short)}${first.season}:${getString(R.string.episode_short)}${first.episode}"
null, // resume.result.name, we don't want episode title when {
first.episode, first.season != null ->
first.season "${getString(R.string.season_short)}${first.season}:${getString(R.string.episode_short)}${first.episode}"
) else -> "${getString(R.string.episode)} ${first.episode}"
}
resultPlaySeries.setOnClickListener { resultPlaySeriesButton.setOnClickListener {
viewModel.handleAction( viewModel.handleAction(
EpisodeClickEvent( EpisodeClickEvent(
ACTION_CLICK_DEFAULT, ACTION_CLICK_DEFAULT,
@ -756,7 +797,7 @@ class ResultFragmentTv : Fragment() {
) )
) )
} }
resultPlaySeries.setOnLongClickListener { resultPlaySeriesButton.setOnLongClickListener {
viewModel.handleAction( viewModel.handleAction(
EpisodeClickEvent(ACTION_SHOW_OPTIONS, first) EpisodeClickEvent(ACTION_SHOW_OPTIONS, first)
) )
@ -765,6 +806,7 @@ class ResultFragmentTv : Fragment() {
if (!hasLoadedEpisodesOnce) { if (!hasLoadedEpisodesOnce) {
hasLoadedEpisodesOnce = true hasLoadedEpisodesOnce = true
focusPlayButton() focusPlayButton()
resultPlaySeries.requestFocus()
} }
} }
@ -826,6 +868,7 @@ class ResultFragmentTv : Fragment() {
resultMetaYear.setText(d.yearText) resultMetaYear.setText(d.yearText)
resultMetaDuration.setText(d.durationText) resultMetaDuration.setText(d.durationText)
resultMetaRating.setText(d.ratingText) resultMetaRating.setText(d.ratingText)
resultMetaStatus.setText(d.onGoingText)
resultMetaContentRating.setText(d.contentRatingText) resultMetaContentRating.setText(d.contentRatingText)
resultCastText.setText(d.actorsText) resultCastText.setText(d.actorsText)
resultNextAiring.setText(d.nextAiringEpisode) resultNextAiring.setText(d.nextAiringEpisode)
@ -859,8 +902,12 @@ class ResultFragmentTv : Fragment() {
radius = 0, radius = 0,
errorImageDrawable = error errorImageDrawable = error
) )
resultComingSoon.isVisible = d.comingSoon comingSoon = d.comingSoon
resultTvComingSoon.isVisible = d.comingSoon
resultPlayMovie.isGone = d.comingSoon
resultPlaySeries.isGone = d.comingSoon
resultDataHolder.isGone = d.comingSoon resultDataHolder.isGone = d.comingSoon
UIHelper.populateChips(resultTag, d.tags) UIHelper.populateChips(resultTag, d.tags)
resultCastItems.isGone = d.actors.isNullOrEmpty() resultCastItems.isGone = d.actors.isNullOrEmpty()
(resultCastItems.adapter as? ActorAdaptor)?.updateList( (resultCastItems.adapter as? ActorAdaptor)?.updateList(
@ -871,6 +918,10 @@ class ResultFragmentTv : Fragment() {
// If there is no rating to display, we don't want an empty gap // If there is no rating to display, we don't want an empty gap
resultMetaContentRating.width = 0 resultMetaContentRating.width = 0
} }
resultSearchButton.setOnClickListener {
QuickSearchFragment.pushSearch(activity, d.title)
}
} }
is Resource.Loading -> { is Resource.Loading -> {

View file

@ -20,6 +20,7 @@ import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.APIHolder.unixTime import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.APIHolder.unixTimeMS import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.AcraApplication.Companion.context
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.activity import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.CommonActivity.getCastSession import com.lagradost.cloudstream3.CommonActivity.getCastSession
@ -31,6 +32,7 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.isMovie
import com.lagradost.cloudstream3.metaproviders.SyncRedirector import com.lagradost.cloudstream3.metaproviders.SyncRedirector
import com.lagradost.cloudstream3.mvvm.* import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.secondsToReadable
import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
import com.lagradost.cloudstream3.syncproviders.providers.SimklApi import com.lagradost.cloudstream3.syncproviders.providers.SimklApi
@ -261,8 +263,7 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData {
metaText = metaText =
if (repo.providerType == ProviderType.MetaProvider) txt(R.string.provider_info_meta) else null, if (repo.providerType == ProviderType.MetaProvider) txt(R.string.provider_info_meta) else null,
durationText = if (dur == null || dur <= 0) null else txt( durationText = if (dur == null || dur <= 0) null else txt(
R.string.duration_format, secondsToReadable(dur * 60, "0 mins")
dur
), ),
onGoingText = if (this is EpisodeResponse) { onGoingText = if (this is EpisodeResponse) {
txt( txt(
@ -2464,7 +2465,7 @@ class ResultViewModel2 : ViewModel() {
ResumeProgress( ResumeProgress(
progress = (viewPos.position / 1000).toInt(), progress = (viewPos.position / 1000).toInt(),
maxProgress = (viewPos.duration / 1000).toInt(), maxProgress = (viewPos.duration / 1000).toInt(),
txt(R.string.resume_time_left, (viewPos.duration - viewPos.position) / (60_000)) txt(R.string.resume_remaining, secondsToReadable(((viewPos.duration - viewPos.position) / 1_000).toInt(), "0 mins"))
) )
} }

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M240,760L240,800Q240,817 228.5,828.5Q217,840 200,840Q183,840 171.5,828.5Q160,817 160,800L160,160Q160,143 171.5,131.5Q183,120 200,120Q217,120 228.5,131.5Q240,143 240,160L240,200L320,200L320,160Q320,143 331.5,131.5Q343,120 360,120L600,120Q617,120 628.5,131.5Q640,143 640,160L640,200L720,200L720,160Q720,143 731.5,131.5Q743,120 760,120Q777,120 788.5,131.5Q800,143 800,160L800,800Q800,817 788.5,828.5Q777,840 760,840Q743,840 731.5,828.5Q720,817 720,800L720,760L640,760L640,800Q640,817 628.5,828.5Q617,840 600,840L360,840Q343,840 331.5,828.5Q320,817 320,800L320,760L240,760ZM240,680L320,680L320,600L240,600L240,680ZM240,520L320,520L320,440L240,440L240,520ZM240,360L320,360L320,280L240,280L240,360ZM640,680L720,680L720,600L640,600L640,680ZM640,520L720,520L720,440L640,440L640,520ZM640,360L720,360L720,280L640,280L640,360Z"/>
</vector>

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="m8.46,5l0,14l11,-7l-11,-7z"
android:strokeLineJoin="round"
android:strokeWidth="0.006"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="m8.46,5l0,14l11,-7l-11,-7z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M4.92,5.04h2.31v13.98h-2.31z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M17,11v6.97l-5,-2.14l-5,2.14V5h6V3H7C5.9,3 5,3.9 5,5v16l7,-3l7,3V11H17zM21,7h-2v2h-2V7h-2V5h2V3h2v2h2V7z"/>
</vector>

View file

@ -3,13 +3,13 @@
<item android:state_focused="true"> <item android:state_focused="true">
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<solid android:color="?attr/white" /> <solid android:color="?attr/white" />
<corners android:radius="3dp"/> <corners android:radius="@dimen/rounded_image_radius"/>
</shape> </shape>
</item> </item>
<item> <item>
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<solid android:color="@color/white_attr_20" /> <solid android:color="@color/white_attr_20" />
<corners android:radius="3dp"/> <corners android:radius="@dimen/rounded_image_radius"/>
</shape> </shape>
</item> </item>
</selector> </selector>

View file

@ -3,7 +3,7 @@
<item android:state_focused="true"> <item android:state_focused="true">
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<solid android:color="?attr/white" /> <solid android:color="?attr/white" />
<corners android:radius="3dp"/> <corners android:radius="@dimen/rounded_image_radius"/>
</shape> </shape>
</item> </item>
</selector> </selector>

View file

@ -17,7 +17,6 @@
android:layout_width="100dp" android:layout_width="100dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:focusable="true"
android:padding="5dp"> android:padding="5dp">
<!--app:cardCornerRadius="@dimen/roundedImageRadius"--> <!--app:cardCornerRadius="@dimen/roundedImageRadius"-->
<FrameLayout <FrameLayout

View file

@ -157,6 +157,7 @@
android:id="@+id/home_preview_hidden_prev_focus" android:id="@+id/home_preview_hidden_prev_focus"
android:layout_width="1dp" android:layout_width="1dp"
android:layout_height="1dp" android:layout_height="1dp"
android:tag="@string/tv_no_focus_tag"
android:focusable="false" /> android:focusable="false" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
@ -189,6 +190,7 @@
android:id="@+id/home_preview_hidden_next_focus" android:id="@+id/home_preview_hidden_next_focus"
android:layout_width="1dp" android:layout_width="1dp"
android:layout_height="1dp" android:layout_height="1dp"
android:tag="@string/tv_no_focus_tag"
android:focusable="false" /> android:focusable="false" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View file

@ -412,7 +412,7 @@
android:foreground="@drawable/outline_drawable" android:foreground="@drawable/outline_drawable"
android:maxLength="1000" android:maxLength="1000"
android:nextFocusUp="@id/result_back" android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_bookmark_button" android:nextFocusDown="@id/result_bookmark_Button"
android:paddingTop="5dp" android:paddingTop="5dp"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
android:textSize="15sp" android:textSize="15sp"
@ -474,7 +474,7 @@
android:fadingEdge="horizontal" android:fadingEdge="horizontal"
android:focusable="false" android:focusable="false"
android:focusableInTouchMode="false" android:focusableInTouchMode="false"
android:nextFocusUp="@id/result_bookmark_button" android:nextFocusUp="@id/result_bookmark_Button"
android:nextFocusDown="@id/result_play_movie" android:nextFocusDown="@id/result_play_movie"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingTop="5dp" android:paddingTop="5dp"
@ -580,7 +580,7 @@
android:layout_marginStart="0dp" android:layout_marginStart="0dp"
android:layout_marginEnd="0dp" android:layout_marginEnd="0dp"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:nextFocusUp="@id/result_bookmark_button" android:nextFocusUp="@id/result_bookmark_Button"
android:nextFocusDown="@id/result_download_movie" android:nextFocusDown="@id/result_download_movie"
android:text="@string/play_movie_button" android:text="@string/play_movie_button"
android:visibility="visible" android:visibility="visible"
@ -658,7 +658,7 @@
android:layout_marginStart="0dp" android:layout_marginStart="0dp"
android:layout_marginEnd="0dp" android:layout_marginEnd="0dp"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:nextFocusUp="@id/result_bookmark_button" android:nextFocusUp="@id/result_bookmark_Button"
android:nextFocusDown="@id/result_download_movie" android:nextFocusDown="@id/result_download_movie"
android:text="@string/resume" android:text="@string/resume"
android:visibility="visible" android:visibility="visible"
@ -674,7 +674,7 @@
android:layout_marginStart="0dp" android:layout_marginStart="0dp"
android:layout_marginEnd="0dp" android:layout_marginEnd="0dp"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:nextFocusUp="@id/result_bookmark_button" android:nextFocusUp="@id/result_bookmark_Button"
android:nextFocusDown="@id/result_download_movie" android:nextFocusDown="@id/result_download_movie"
android:text="@string/next_episode" android:text="@string/next_episode"
android:visibility="gone" android:visibility="gone"

View file

@ -78,6 +78,30 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
</LinearLayout> </LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout> </com.facebook.shimmer.ShimmerFrameLayout>
<FrameLayout
android:id="@+id/background_poster_holder"
android:layout_width="match_parent"
android:layout_height="275dp"
android:visibility="visible">
<com.lagradost.cloudstream3.utils.PercentageCropImageView
android:id="@+id/background_poster"
android:layout_width="match_parent"
android:layout_height="275dp"
android:layout_gravity="center"
android:alpha="0.8"
android:scaleType="matrix"
tools:src="@drawable/profile_bg_dark_blue" >
</com.lagradost.cloudstream3.utils.PercentageCropImageView>
<ImageView
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_gravity="bottom"
android:src="@drawable/background_shadow">
</ImageView>
</FrameLayout>
<LinearLayout <LinearLayout
android:id="@+id/result_loading_error" android:id="@+id/result_loading_error"
@ -124,31 +148,6 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:textColor="?attr/textColor" /> android:textColor="?attr/textColor" />
</LinearLayout> </LinearLayout>
<FrameLayout
android:id="@+id/background_poster_holder"
android:layout_width="match_parent"
android:layout_height="250dp"
android:visibility="visible">
<com.lagradost.cloudstream3.utils.PercentageCropImageView
android:id="@+id/background_poster"
android:layout_width="match_parent"
android:layout_height="275dp"
android:layout_gravity="center"
android:alpha="0.8"
android:scaleType="matrix"
tools:src="@drawable/profile_bg_dark_blue" >
</com.lagradost.cloudstream3.utils.PercentageCropImageView>
<ImageView
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_gravity="bottom"
android:src="@drawable/background_shadow">
</ImageView>
</FrameLayout>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/result_finish_loading" android:id="@+id/result_finish_loading"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -165,7 +164,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:layout_marginTop="175dp"> android:layout_marginTop="225dp">
<TextView <TextView
android:id="@+id/result_title" android:id="@+id/result_title"
@ -175,7 +174,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:gravity="center_vertical" android:gravity="center_vertical"
android:singleLine="true" android:singleLine="true"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
android:textSize="20sp" android:textSize="25sp"
android:textStyle="bold" android:textStyle="bold"
tools:text="The Perfect Run The Perfect Run" /> tools:text="The Perfect Run The Perfect Run" />
@ -221,157 +220,289 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:textStyle="normal" android:textStyle="normal"
tools:text="5d 3h 30m" /> tools:text="5d 3h 30m" />
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/result_resume_progress_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="horizontal"
android:visibility="gone"
tools:visibility="visible">
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/result_resume_series_progress"
app:trackCornerRadius="50dp"
android:layout_width="100dp"
android:layout_height="20dp"
android:layout_gravity="end|center_vertical"
android:layout_weight="1"
android:indeterminate="false"
android:max="100"
android:paddingEnd="10dp"
android:progress="0"
android:progressBackgroundTint="?attr/colorPrimary"
tools:progress="50"
tools:visibility="visible"
tools:ignore="RtlSymmetry" />
<TextView
android:id="@+id/result_resume_series_progress_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:textColor="?attr/grayTextColor"
tools:ignore="RtlSymmetry"
tools:text="69m remaining" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/result_play_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="5dp"
android:descendantFocusability="afterDescendants"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:id="@+id/result_play_movie"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/result_play_movie_button"
android:focusable="true"
style="@style/ResultSmallButtonTV"
app:iconPadding="0dp"
app:icon="@drawable/ic_baseline_play_arrow_24"
android:nextFocusUp="@id/result_play_movie_button"
android:nextFocusDown="@id/result_description"
android:tag="@string/tv_no_focus_tag">
</com.google.android.material.button.MaterialButton>
<TextView
android:id="@+id/result_play_movie_text"
style="@style/ResultMarqueeButtonText"
android:text="@string/movies_singular" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:id="@+id/result_play_series"
android:visibility="gone"
tools:visibility="visible"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/result_play_series_button"
android:focusable="true"
style="@style/ResultSmallButtonTV"
app:iconPadding="0dp"
app:icon="@drawable/ic_baseline_play_arrow_24"
android:nextFocusUp="@id/result_play_series_button"
android:nextFocusDown="@id/result_description"
android:tag="@string/tv_no_focus_tag">
</com.google.android.material.button.MaterialButton>
<TextView
android:id="@+id/result_play_series_text"
style="@style/ResultMarqueeButtonText"
android:text="@string/episode" />
</LinearLayout>
<LinearLayout
android:id="@+id/result_resume_series"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/result_resume_series_button"
android:focusable="true"
style="@style/ResultSmallButtonTV"
app:iconPadding="0dp"
app:icon="@drawable/ic_baseline_resume_arrow2"
android:nextFocusUp="@id/result_resume_series_button"
android:nextFocusDown="@id/result_description"
android:tag="@string/tv_no_focus_tag">
</com.google.android.material.button.MaterialButton>
<TextView
android:id="@+id/result_resume_series_text"
style="@style/ResultMarqueeButtonText"
android:text="@string/resume" />
</LinearLayout>
<LinearLayout
android:id="@+id/result_play_trailer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/result_play_trailer_button"
android:focusable="true"
style="@style/ResultSmallButtonTV"
app:iconPadding="0dp"
app:icon="@drawable/ic_baseline_film_roll_24"
android:nextFocusUp="@id/result_play_trailer_button"
android:nextFocusDown="@id/result_description"
android:tag="@string/tv_no_focus_tag">
</com.google.android.material.button.MaterialButton>
<TextView
android:id="@+id/result_play_trailer_text"
style="@style/ResultMarqueeButtonText"
android:text="@string/play_trailer_button" />
</LinearLayout>
<LinearLayout
android:id="@+id/result_bookmark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/result_bookmark_Button"
android:focusable="true"
style="@style/ResultSmallButtonTV"
app:iconPadding="0dp"
app:icon="@drawable/outline_bookmark_add_24"
android:nextFocusUp="@id/result_bookmark_Button"
android:nextFocusDown="@id/result_description"
android:tag="@string/tv_no_focus_tag">
</com.google.android.material.button.MaterialButton>
<TextView
android:id="@+id/result_bookmark_text"
style="@style/ResultMarqueeButtonText"
android:text="@string/type_none" />
</LinearLayout>
<LinearLayout
android:id="@+id/result_favorite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/result_favorite_Button"
android:focusable="true"
style="@style/ResultSmallButtonTV"
app:iconPadding="0dp"
app:icon="@drawable/ic_baseline_favorite_border_24"
android:nextFocusUp="@id/result_favorite_Button"
android:nextFocusDown="@id/result_description"
android:tag="@string/tv_no_focus_tag">
</com.google.android.material.button.MaterialButton>
<TextView
android:id="@+id/result_favorite_Text"
style="@style/ResultMarqueeButtonText"
android:text="@string/favorite" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:id="@+id/result_subscribe"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/result_subscribe_Button"
android:focusable="true"
style="@style/ResultSmallButtonTV"
app:iconPadding="0dp"
app:icon="@drawable/baseline_notifications_none_24"
android:nextFocusUp="@id/result_subscribe_Button"
android:nextFocusDown="@id/result_description"
android:tag="@string/tv_no_focus_tag">
</com.google.android.material.button.MaterialButton>
<TextView
android:id="@+id/result_subscribe_text"
style="@style/ResultMarqueeButtonText"
android:text="@string/action_subscribe" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:id="@+id/result_search"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/result_search_Button"
android:focusable="true"
style="@style/ResultSmallButtonTV"
app:iconPadding="0dp"
app:icon="@drawable/search_icon"
android:nextFocusUp="@id/result_search_Button"
android:nextFocusDown="@id/result_description"
android:tag="@string/tv_no_focus_tag">
</com.google.android.material.button.MaterialButton>
<TextView
android:id="@+id/result_search_text"
style="@style/ResultMarqueeButtonText"
android:text="@string/title_search" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:id="@+id/result_episodes_show"
android:visibility="gone"
tools:visibility="visible"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/result_episodes_show_button"
android:focusable="true"
style="@style/ResultSmallButtonTV"
app:iconPadding="0dp"
app:icon="@drawable/ic_baseline_sort_24"
android:nextFocusUp="@id/result_episodes_show_button"
android:nextFocusRight="@id/redirect_to_episodes"
android:nextFocusDown="@id/result_description"
android:tag="@string/tv_no_focus_tag">
</com.google.android.material.button.MaterialButton>
<TextView
android:id="@+id/result_episodes_show_text"
style="@style/ResultMarqueeButtonText"
android:text="@string/episodes" />
</LinearLayout>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:layout_marginTop="10dp"> android:layout_marginTop="10dp"
android:baselineAligned="false">
<LinearLayout
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_weight="0"
android:orientation="vertical">
<LinearLayout
android:id="@+id/result_movie_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:animateLayoutChanges="true"
android:orientation="vertical"
tools:visibility="visible">
<com.google.android.material.button.MaterialButton
android:id="@+id/result_play_movie"
style="@style/ResultButtonTV"
android:nextFocusRight="@id/result_description"
android:nextFocusUp="@id/result_play_movie"
android:nextFocusDown="@id/result_play_series"
android:text="@string/play_movie_button"
android:visibility="visible"
app:icon="@drawable/ic_baseline_play_arrow_24" />
<LinearLayout
android:id="@+id/series_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/result_play_series"
style="@style/ResultButtonTV"
android:nextFocusRight="@id/result_description"
android:nextFocusUp="@id/result_play_movie"
android:nextFocusDown="@id/result_resume_series"
android:text="@string/play_episode"
android:visibility="visible"
app:icon="@drawable/ic_baseline_play_arrow_24" />
<com.google.android.material.button.MaterialButton
android:id="@+id/result_resume_series"
style="@style/ResultButtonTV"
android:nextFocusRight="@id/result_description"
android:nextFocusUp="@id/result_play_series"
android:nextFocusDown="@id/result_play_trailer"
android:text="@string/resume"
android:visibility="visible"
app:icon="@drawable/ic_baseline_play_arrow_24" />
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/result_play_trailer"
style="@style/ResultButtonTV"
android:nextFocusRight="@id/result_description"
android:nextFocusUp="@id/result_resume_series"
android:nextFocusDown="@id/result_bookmark_button"
android:text="@string/play_trailer_button"
android:visibility="gone"
app:icon="@drawable/ic_baseline_play_arrow_24"
tools:visibility="visible" />
<!-- <com.lagradost.cloudstream3.ui.download.button.DownloadButton
android:visibility="gone"
android:id="@+id/download_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minWidth="250dp"
app:download_layout="@layout/download_button_layout" />
-->
<com.google.android.material.button.MaterialButton
android:id="@+id/result_bookmark_button"
style="@style/ResultButtonTV"
android:nextFocusRight="@id/result_description"
android:nextFocusUp="@id/result_play_trailer"
android:nextFocusDown="@id/result_favorite_button"
android:text="@string/type_none"
android:visibility="visible"
app:icon="@drawable/ic_baseline_bookmark_24" />
<com.google.android.material.button.MaterialButton
android:id="@+id/result_favorite_button"
style="@style/ResultButtonTV"
android:nextFocusRight="@id/result_description"
android:nextFocusUp="@id/result_bookmark_button"
android:nextFocusDown="@id/result_subscribe_button"
android:text="@string/action_add_to_favorites"
android:visibility="visible"
app:icon="@drawable/ic_baseline_favorite_border_24" />
<com.google.android.material.button.MaterialButton
android:id="@+id/result_subscribe_button"
style="@style/ResultButtonTV"
android:nextFocusRight="@id/result_description"
android:nextFocusUp="@id/result_favorite_button"
android:nextFocusDown="@id/result_episodes_show"
android:text="@string/action_subscribe"
android:visibility="visible"
app:icon="@drawable/ic_baseline_favorite_border_24" />
<com.google.android.material.button.MaterialButton
android:id="@+id/result_episodes_show"
style="@style/ResultButtonTV"
android:nextFocusRight="@id/redirect_to_episodes"
android:nextFocusUp="@id/result_subscribe_button"
android:nextFocusDown="@id/result_cast_items"
android:text="@string/episodes"
android:visibility="visible"
app:icon="@drawable/ic_baseline_sort_24"
tools:visibility="visible" />
<View
android:id="@+id/redirect_to_episodes"
android:layout_width="1dp"
android:layout_height="1dp"
android:focusable="true"
android:focusableInTouchMode="true" />
<View
android:id="@+id/redirect_to_play"
android:layout_width="1dp"
android:layout_height="1dp"
android:focusable="true"
android:focusableInTouchMode="true" />
</LinearLayout>
</LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/right_layout" android:id="@+id/right_layout"
@ -382,7 +513,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/linearLayout2" app:layout_constraintStart_toEndOf="@+id/linearLayout2"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent"
tools:ignore="UselessParent">
<com.lagradost.cloudstream3.widget.FlowLayout <com.lagradost.cloudstream3.widget.FlowLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -439,8 +571,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:foreground="@drawable/outline_drawable" android:foreground="@drawable/outline_drawable"
android:maxLines="7" android:maxLines="7"
android:focusable="true" android:focusable="true"
android:nextFocusUp="@id/result_back" android:nextFocusUp="@id/result_play_parent"
android:nextFocusDown="@id/result_bookmark_button" android:nextFocusDown="@id/result_cast_items"
android:padding="5dp" android:padding="5dp"
android:requiresFadingEdge="vertical" android:requiresFadingEdge="vertical"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
@ -450,59 +582,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
<com.google.android.material.chip.ChipGroup <com.google.android.material.chip.ChipGroup
android:id="@+id/result_tag" android:id="@+id/result_tag"
style="@style/ChipParent" style="@style/ChipParent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone">
<LinearLayout
android:id="@+id/result_resume_progress_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="horizontal"
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:paddingEnd="10dp"
android:progress="0"
android:progressBackgroundTint="?attr/colorPrimary"
android:visibility="gone"
tools:progress="50"
tools:visibility="visible" />
<TextView
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:maxLines="1"
android:paddingEnd="5dp"
android:textColor="?attr/grayTextColor"
android:visibility="gone"
tools:ignore="RtlSymmetry"
tools:text="69m remaining" />
</LinearLayout>
</FrameLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
@ -513,10 +594,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:descendantFocusability="afterDescendants" android:descendantFocusability="afterDescendants"
android:fadingEdge="horizontal" android:fadingEdge="horizontal"
android:focusable="false" android:nextFocusUp="@id/result_description"
android:focusableInTouchMode="false" android:nextFocusDown="@id/result_recommendations_list"
android:nextFocusUp="@id/result_episodes_show"
android:nextFocusDown="@id/result_recommendations_filter_selection"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingTop="5dp" android:paddingTop="5dp"
android:requiresFadingEdge="horizontal" android:requiresFadingEdge="horizontal"
@ -525,8 +604,23 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
tools:listitem="@layout/cast_item" tools:listitem="@layout/cast_item"
tools:visibility="visible" /> tools:visibility="visible" />
<TextView
android:id="@+id/result_tv_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"
tools:visibility="visible"
android:visibility="gone" />
<LinearLayout <LinearLayout
android:id="@+id/result_recommendations_holder" android:id="@+id/result_recommendations_holder"
android:descendantFocusability="afterDescendants"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
@ -540,7 +634,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:nextFocusUp="@id/result_cast_items" android:nextFocusUp="@id/result_cast_items"
android:nextFocusDown="@id/result_recommendations_list" android:nextFocusDown="@id/result_recommendations_list"
android:descendantFocusability="afterDescendants"
android:orientation="horizontal" android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="2" tools:itemCount="2"
@ -563,7 +657,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
android:descendantFocusability="afterDescendants" android:descendantFocusability="afterDescendants"
android:nextFocusUp="@id/result_recommendations_filter_selection" android:nextFocusUp="@id/result_cast_items"
android:orientation="vertical" android:orientation="vertical"
app:spanCount="8" app:spanCount="8"
tools:listitem="@layout/search_result_grid" /> tools:listitem="@layout/search_result_grid" />
@ -576,7 +670,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="end" android:layout_gravity="end"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"> tools:visibility="invisible">
<!-- <!--
@ -765,152 +859,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal">
<!--
<LinearLayout
android:id="@+id/result_movie_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:animateLayoutChanges="true"
android:orientation="vertical"
tools:visibility="visible">
<com.google.android.material.button.MaterialButton
android:id="@+id/result_play_movie"
style="@style/RegularButtonTV"
android:layout_width="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="5dp"
android:layout_marginBottom="10dp"
android:layout_weight="1"
android:minWidth="250dp"
android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_play_series"
android:text="@string/play_movie_button"
android:visibility="visible"
app:icon="@drawable/ic_baseline_play_arrow_24">
</com.google.android.material.button.MaterialButton>
<LinearLayout
android:id="@+id/series_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/result_play_series"
style="@style/RegularButtonTV"
android:layout_width="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="5dp"
android:layout_marginBottom="10dp"
android:layout_weight="1"
android:minWidth="250dp"
android:nextFocusUp="@id/result_play_movie"
android:nextFocusDown="@id/result_resume_series"
android:text="@string/play_episode"
android:visibility="visible"
app:icon="@drawable/ic_baseline_play_arrow_24"
tools:visibility="gone" />
<com.google.android.material.button.MaterialButton
android:id="@+id/result_resume_series"
style="@style/RegularButtonTV"
android:layout_width="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="5dp"
android:layout_marginBottom="10dp"
android:layout_weight="1"
android:minWidth="250dp"
android:nextFocusUp="@id/result_play_series"
android:nextFocusDown="@id/result_play_trailer"
android:text="@string/resume"
android:visibility="visible"
app:icon="@drawable/ic_baseline_play_arrow_24"
tools:visibility="gone" />
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/result_play_trailer"
style="@style/RegularButtonTV"
android:layout_width="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="5dp"
android:layout_marginBottom="10dp"
android:layout_weight="1"
android:minWidth="250dp"
android:nextFocusUp="@id/result_resume_series"
android:nextFocusDown="@id/result_bookmark_button"
android:text="@string/play_trailer_button"
android:visibility="gone"
app:icon="@drawable/ic_baseline_play_arrow_24"
tools:visibility="visible">
</com.google.android.material.button.MaterialButton>
<com.google.android.material.button.MaterialButton
android:id="@+id/result_bookmark_button"
style="@style/RegularButtonTV"
android:layout_width="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="5dp"
android:layout_marginBottom="10dp"
android:layout_weight="1"
android:minWidth="250dp"
android:nextFocusUp="@id/result_play_trailer"
android:nextFocusDown="@id/result_resume_series_button"
android:text="@string/type_none"
android:visibility="visible" />
<LinearLayout
android:id="@+id/result_resume_progress_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="horizontal"
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:paddingEnd="10dp"
android:progress="0"
android:progressBackgroundTint="?attr/colorPrimary"
android:visibility="visible"
tools:progress="50"
tools:visibility="visible" />
<TextView
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:maxLines="1"
android:paddingEnd="5dp"
android:textColor="?attr/grayTextColor"
tools:ignore="RtlSymmetry"
tools:text="69m remaining" />
</LinearLayout>
</LinearLayout>
-->
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/result_poster_holder" android:id="@+id/result_poster_holder"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -1067,70 +1016,47 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:visibility="gone" android:visibility="gone"
app:icon="@drawable/ic_baseline_add_24" /> app:icon="@drawable/ic_baseline_add_24" />
</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="gone">
<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 <LinearLayout
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_weight="0"
android:orientation="vertical">
<LinearLayout
android:id="@+id/result_movie_parent"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:layout_gravity="start"
android:animateLayoutChanges="true"
android:orientation="vertical"
tools:visibility="visible">
<ImageView <LinearLayout
android:id="@+id/result_resume_series_button" android:id="@+id/series_holder"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/download"
android:nextFocusUp="@id/result_play_movie"
android:nextFocusDown="@id/result_season_selection"
android:src="@drawable/ic_baseline_play_arrow_24"
android:visibility="visible"
app:tint="?attr/white" />
<TextView
android:id="@+id/result_resume_series_title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:orientation="vertical">
android:gravity="center"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:textColor="?attr/textColor"
android:textSize="17sp"
android:textStyle="bold"
tools:text="S1E1 Episode 1" />
<View
android:id="@+id/redirect_to_episodes"
android:layout_width="1dp"
android:layout_height="1dp"
android:focusable="true"
android:focusableInTouchMode="true" />
<View
android:id="@+id/redirect_to_play"
android:layout_width="1dp"
android:layout_height="1dp"
android:focusable="true"
android:focusableInTouchMode="true" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout>-->
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</FrameLayout> </FrameLayout>

View file

@ -533,18 +533,14 @@
android:id="@id/exo_position" android:id="@id/exo_position"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="30dp" android:layout_height="30dp"
android:layout_gravity="center" android:gravity="center"
android:layout_marginStart="20dp"
android:gravity="end|center_vertical"
android:includeFontPadding="false" android:includeFontPadding="false"
android:minWidth="50dp" android:minWidth="50dp"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="14sp" android:textSize="14sp"
android:textStyle="normal" android:textStyle="normal"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@id/player_pause_play" app:layout_constraintStart_toEndOf="@id/player_pause_play"
tools:text="15:30" /> tools:text="15:30" />
<FrameLayout <FrameLayout

View file

@ -306,6 +306,7 @@
<string name="go_forward_30">+30</string> <string name="go_forward_30">+30</string>
<string name="delete_message" formatted="true">This will permanently delete %s\nAre you sure?</string> <string name="delete_message" formatted="true">This will permanently delete %s\nAre you sure?</string>
<string name="resume_time_left" formatted="true">%dm\nremaining</string> <string name="resume_time_left" formatted="true">%dm\nremaining</string>
<string name="resume_remaining" formatted="true">%s\nremaining</string>
<string name="status_ongoing">Ongoing</string> <string name="status_ongoing">Ongoing</string>
<string name="status_completed">Completed</string> <string name="status_completed">Completed</string>
<string name="status">Status</string> <string name="status">Status</string>
@ -745,7 +746,8 @@
<string name="rotate_video_desc">Display a toggle button for screen orientation</string> <string name="rotate_video_desc">Display a toggle button for screen orientation</string>
<string name="auto_rotate_video_desc">Enable automatic switching of screen orientation based on video orientation</string> <string name="auto_rotate_video_desc">Enable automatic switching of screen orientation based on video orientation</string>
<string name="auto_rotate_video">Auto rotate</string> <string name="auto_rotate_video">Auto rotate</string>
<string name="favorite">Favorite</string>
<string name="unfavorite">Unfavorite</string>
<!-- For Biometrics --> <!-- For Biometrics -->
<string name="biometric_authentication_title">Unlock CloudStream</string> <string name="biometric_authentication_title">Unlock CloudStream</string>
<string name="biometric_setting">Lock with Biometrics</string> <string name="biometric_setting">Lock with Biometrics</string>
@ -758,4 +760,5 @@
<string name="biometric_warning">Your CloudStream data has been backed up now, although probability of this rare case is very low but all <string name="biometric_warning">Your CloudStream data has been backed up now, although probability of this rare case is very low but all
devices behave differently, in case you get locked down from accessing the app in worst case scenario, devices behave differently, in case you get locked down from accessing the app in worst case scenario,
Clear the app data wholly and restore the backup. Any inconvenience if arrived is deeply regretted.</string> Clear the app data wholly and restore the backup. Any inconvenience if arrived is deeply regretted.</string>
</resources> </resources>

View file

@ -814,6 +814,35 @@
<item name="android:insetTop">0dp</item> <item name="android:insetTop">0dp</item>
</style> </style>
<style name="ResultSmallButtonTV">
<item name="android:tag">@string/tv_no_focus_tag</item>
<item name="android:stateListAnimator">@null</item>
<item name="strokeColor">@color/transparent</item>
<item name="backgroundTint">@null</item>
<item name="android:background">@drawable/player_button_tv_attr</item>
<item name="rippleColor">@color/white</item>
<item name="android:shadowColor">@color/transparent</item>
<item name="iconTint">@color/player_on_button_tv_attr</item>
<item name="iconGravity">textStart</item>
<item name="android:layout_width">60dp</item>
<item name="android:layout_height">40dp</item>
<item name="android:gravity">center</item>
<item name="android:layout_gravity">center</item>
<item name="android:layout_marginStart">4dp</item>
<item name="android:layout_marginEnd">4dp</item>
<item name="android:layout_marginBottom">4dp</item>
</style>
<style name="ResultMarqueeButtonText">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:gravity">top|center_horizontal</item>
<item name="android:singleLine">true</item>
<item name="android:scrollHorizontally">true</item>
<item name="android:marqueeRepeatLimit">marquee_forever</item>
<item name="android:ellipsize">marquee</item>
</style>
<style name="VideoButtonTV"> <style name="VideoButtonTV">
<item name="android:tag">@string/tv_no_focus_tag</item> <item name="android:tag">@string/tv_no_focus_tag</item>
<item name="android:stateListAnimator">@null</item> <item name="android:stateListAnimator">@null</item>