From 510d11f705b5c112773a2d6f31d7a1c64d20a713 Mon Sep 17 00:00:00 2001 From: KingLucius Date: Sat, 9 Mar 2024 16:24:38 +0200 Subject: [PATCH 1/4] New TV UI (#950) --- .../lagradost/cloudstream3/MainActivity.kt | 20 +- .../cloudstream3/ui/home/HomeFragment.kt | 1 + .../ui/result/ResultFragmentTv.kt | 289 ++++--- .../ui/result/ResultViewModel2.kt | 7 +- .../res/drawable/ic_baseline_film_roll_24.xml | 10 + .../res/drawable/ic_baseline_resume_arrow.xml | 11 + .../drawable/ic_baseline_resume_arrow2.xml | 12 + .../res/drawable/outline_bookmark_add_24.xml | 5 + .../res/drawable/player_button_tv_attr.xml | 4 +- .../drawable/player_button_tv_attr_no_bg.xml | 2 +- app/src/main/res/layout/cast_item.xml | 1 - .../main/res/layout/fragment_home_head_tv.xml | 2 + app/src/main/res/layout/fragment_result.xml | 10 +- .../main/res/layout/fragment_result_tv.xml | 786 ++++++++---------- .../res/layout/player_custom_layout_tv.xml | 8 +- app/src/main/res/values/strings.xml | 5 +- app/src/main/res/values/styles.xml | 29 + 17 files changed, 633 insertions(+), 569 deletions(-) create mode 100644 app/src/main/res/drawable/ic_baseline_film_roll_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_resume_arrow.xml create mode 100644 app/src/main/res/drawable/ic_baseline_resume_arrow2.xml create mode 100644 app/src/main/res/drawable/outline_bookmark_add_24.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 6308117b..fa6cae18 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -1189,8 +1189,26 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu 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 -> + if (exceptionButtons.contains(newFocus?.id)) return@addOnGlobalFocusChangeListener centerView(newFocus) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index d54ea488..cd843517 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -529,6 +529,7 @@ class HomeFragment : Fragment() { super.onScrolled(recyclerView, dx, dy) } }) + } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt index 427e9cb3..85e948c2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt @@ -33,13 +33,13 @@ import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup import com.lagradost.cloudstream3.ui.player.ExtractorLinkGenerator import com.lagradost.cloudstream3.ui.player.GeneratorPlayer +import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment import com.lagradost.cloudstream3.ui.result.ResultFragment.getStoredData import com.lagradost.cloudstream3.ui.result.ResultFragment.updateUIEvent import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_FOCUSED import com.lagradost.cloudstream3.ui.search.SearchAdapter import com.lagradost.cloudstream3.ui.search.SearchHelper 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.isRtl 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). */ private fun focusPlayButton() { - binding?.resultPlayMovie?.requestFocus() - binding?.resultPlaySeries?.requestFocus() - binding?.resultResumeSeries?.requestFocus() + binding?.resultPlayMovieButton?.requestFocus() + binding?.resultPlaySeriesButton?.requestFocus() + binding?.resultResumeSeriesButton?.requestFocus() } private fun setRecommendations(rec: List?, validApiName: String?) { @@ -246,37 +246,15 @@ class ResultFragmentTv : Fragment() { storedData.start ) // ===== ===== ===== + var comingSoon = false binding?.apply { //episodesShadow.rotationX = 180.0f//if(episodesShadow.isRtl()) 180.0f else 0.0f - - val leftListener: View.OnFocusChangeListener = - View.OnFocusChangeListener { _, hasFocus -> - if (!hasFocus) return@OnFocusChangeListener - toggleEpisodes(false) - } - val rightListener: View.OnFocusChangeListener = - View.OnFocusChangeListener { _, hasFocus -> - if (!hasFocus) return@OnFocusChangeListener - toggleEpisodes(true) - } - - resultPlayMovie.onFocusChangeListener = leftListener - resultPlaySeries.onFocusChangeListener = leftListener - resultResumeSeries.onFocusChangeListener = leftListener - resultPlayTrailer.onFocusChangeListener = leftListener - resultEpisodesShow.onFocusChangeListener = rightListener - resultDescription.onFocusChangeListener = leftListener - resultBookmarkButton.onFocusChangeListener = leftListener - 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 + // parallax on background + resultFinishLoading.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { view, _, scrollY, _, oldScrollY -> + backgroundPosterHolder.translationY = -scrollY.toFloat() * 0.8f + }) redirectToPlay.setOnFocusChangeListener { _, hasFocus -> if (!hasFocus) return@setOnFocusChangeListener @@ -284,13 +262,14 @@ class ResultFragmentTv : Fragment() { binding?.apply { val views = listOf( - resultPlayMovie, - resultPlaySeries, - resultResumeSeries, - resultPlayTrailer, + resultPlayMovieButton, + resultPlaySeriesButton, + resultResumeSeriesButton, + resultPlayTrailerButton, resultBookmarkButton, resultFavoriteButton, - resultSubscribeButton + resultSubscribeButton, + resultSearchButton ) for (requestView in views) { 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 -> if (!hasFocus) return@setOnFocusChangeListener toggleEpisodes(true) @@ -313,7 +287,7 @@ class ResultFragmentTv : Fragment() { resultSeasonSelection, resultRangeSelection, resultEpisodes, - resultPlayTrailer, + resultPlayTrailerButton, ) for (requestView in views) { 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( isHorizontal = false, nextUp = FOCUS_SELF, @@ -430,9 +443,9 @@ class ResultFragmentTv : Fragment() { val aboveCast = listOf( binding?.resultEpisodesShow, - binding?.resultBookmarkButton, - binding?.resultFavoriteButton, - binding?.resultSubscribeButton, + binding?.resultBookmark, + binding?.resultFavorite, + binding?.resultSubscribe, ).firstOrNull { it?.isVisible == true } @@ -443,8 +456,15 @@ class ResultFragmentTv : Fragment() { observeNullable(viewModel.resumeWatching) { resume -> binding?.apply { + + // > resultResumeSeries is visible when not null + if (resume == null) { + resultResumeSeries.isVisible = false + return@observeNullable + } + // show progress no matter if series or movie - resume?.progress?.let { progress -> + resume.progress?.let { progress -> resultResumeSeriesProgressText.setText(progress.progressLeft) resultResumeSeriesProgress.apply { isVisible = true @@ -456,37 +476,24 @@ class ResultFragmentTv : Fragment() { resultResumeProgressHolder.isVisible = false } - // if movie then hide both as movie button is - // 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 - } - + resultPlayMovie.isVisible = false resultPlaySeries.isVisible = false resultResumeSeries.isVisible = true focusPlayButton() + // Stops last button right focus if it is a movie + if (resume.isMovie) + resultSearchButton.nextFocusRightId = R.id.result_search_Button - resultResumeSeries.text = - if (resume.isMovie) context?.getString(R.string.play_movie_button) else context?.getNameFull( - null, // resume.result.name, we don't want episode title - resume.result.episode, - resume.result.season - ) + resultResumeSeriesText.text = + when { + resume.isMovie -> context?.getString(R.string.resume) + resume.result.season != null -> + "${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( EpisodeClickEvent( storedData.playerAction, //?: ACTION_PLAY_EPISODE_IN_PLAYER, @@ -495,7 +502,7 @@ class ResultFragmentTv : Fragment() { ) } - resultResumeSeries.setOnLongClickListener { + resultResumeSeriesButton.setOnLongClickListener { viewModel.handleAction( EpisodeClickEvent(ACTION_SHOW_OPTIONS, resume.result) ) @@ -509,9 +516,9 @@ class ResultFragmentTv : Fragment() { context?.updateHasTrailers() if (!LoadResponse.isTrailersEnabled) return@observe val trailers = trailersLinks.flatMap { it.mirros } - binding?.resultPlayTrailer?.apply { - isGone = trailers.isEmpty() - setOnClickListener { + binding?.apply { + resultPlayTrailer.isGone = trailers.isEmpty() + resultPlayTrailerButton.setOnClickListener { if (trailers.isEmpty()) return@setOnClickListener activity.navigate( R.id.global_to_navigation_player, GeneratorPlayer.newInstance( @@ -526,24 +533,38 @@ class ResultFragmentTv : Fragment() { } observe(viewModel.watchStatus) { watchType -> - binding?.resultBookmarkButton?.apply { - setText(watchType.stringRes) - setOnClickListener { view -> - activity?.showBottomDialog( - WatchType.values().map { view.context.getString(it.stringRes) }.toList(), - watchType.ordinal, - view.context.getString(R.string.action_add_to_bookmarks), - showApply = false, - {}) { - viewModel.updateWatchStatus(WatchType.values()[it], context) + binding?.apply { + 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 -> + activity?.showBottomDialog( + WatchType.entries.map { view.context.getString(it.stringRes) }.toList(), + watchType.ordinal, + view.context.getString(R.string.action_add_to_bookmarks), + showApply = false, + {}) { + viewModel.updateWatchStatus(WatchType.entries[it], context) + } } } } } observeNullable(viewModel.favoriteStatus) { isFavorite -> + + binding?.resultFavorite?.isVisible = isFavorite != null + binding?.resultFavoriteButton?.apply { - isVisible = isFavorite != null + if (isFavorite == null) return@observeNullable val drawable = if (isFavorite) { @@ -552,14 +573,8 @@ class ResultFragmentTv : Fragment() { 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) - setText(text) + setOnClickListener { viewModel.toggleFavoriteStatus(context) { newStatus: Boolean? -> 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 -> + binding?.resultSubscribe?.isVisible = isSubscribed != null && requireContext().isEmulatorSettings() binding?.resultSubscribeButton?.apply { - isVisible = isSubscribed != null && context.isEmulatorSettings() + if (isSubscribed == null) return@observeNullable val drawable = if (isSubscribed) { @@ -589,14 +614,8 @@ class ResultFragmentTv : Fragment() { R.drawable.baseline_notifications_none_24 } - val text = if (isSubscribed) { - R.string.action_unsubscribe - } else { - R.string.action_subscribe - } - setIconResource(drawable) - setText(text) + setOnClickListener { viewModel.toggleSubscriptionStatus(context) { newStatus: Boolean? -> if (newStatus == null) return@toggleSubscriptionStatus @@ -614,32 +633,47 @@ class ResultFragmentTv : Fragment() { 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 -> + if (data == null) return@observeNullable + binding?.apply { - resultPlayMovie.isVisible = data is Resource.Success - resultPlaySeries.isVisible = data == null - seriesHolder.isVisible = data == null - resultEpisodesShow.isVisible = data == null + resultPlayMovie.isVisible = (data is Resource.Success) && !comingSoon + resultPlaySeries.isVisible = false + resultEpisodesShow.isVisible = false (data as? Resource.Success)?.value?.let { (text, ep) -> - resultPlayMovie.setText(text) - resultPlayMovie.setOnClickListener { + //resultPlayMovieText.setText(text) + resultPlayMovieButton.setOnClickListener { viewModel.handleAction( EpisodeClickEvent(ACTION_CLICK_DEFAULT, ep) ) } - resultPlayMovie.setOnLongClickListener { + resultPlayMovieButton.setOnLongClickListener { viewModel.handleAction( EpisodeClickEvent(ACTION_SHOW_OPTIONS, ep) ) return@setOnLongClickListener true } - focusPlayButton() + //focusPlayButton() + resultPlayMovieButton.requestFocus() + + // Stops last button right focus + resultSearchButton.nextFocusRightId = R.id.result_search_Button } } + //focusPlayButton() } observeNullable(viewModel.selectPopup) { popup -> @@ -736,19 +770,26 @@ class ResultFragmentTv : Fragment() { // Used to request focus the first time the episodes are loaded. var hasLoadedEpisodesOnce = false observeNullable(viewModel.episodes) { episodes -> + if (episodes == null) return@observeNullable + 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 if (episodes is Resource.Success) { val first = episodes.value.firstOrNull() if (first != null) { - resultPlaySeries.text = context?.getNameFull( - null, // resume.result.name, we don't want episode title - first.episode, - first.season - ) - - resultPlaySeries.setOnClickListener { + resultPlaySeriesText.text = //"${getString(R.string.season_short)}${first.season}:${getString(R.string.episode_short)}${first.episode}" + when { + first.season != null -> + "${getString(R.string.season_short)}${first.season}:${getString(R.string.episode_short)}${first.episode}" + else -> "${getString(R.string.episode)} ${first.episode}" + } + resultPlaySeriesButton.setOnClickListener { viewModel.handleAction( EpisodeClickEvent( ACTION_CLICK_DEFAULT, @@ -756,7 +797,7 @@ class ResultFragmentTv : Fragment() { ) ) } - resultPlaySeries.setOnLongClickListener { + resultPlaySeriesButton.setOnLongClickListener { viewModel.handleAction( EpisodeClickEvent(ACTION_SHOW_OPTIONS, first) ) @@ -765,6 +806,7 @@ class ResultFragmentTv : Fragment() { if (!hasLoadedEpisodesOnce) { hasLoadedEpisodesOnce = true focusPlayButton() + resultPlaySeries.requestFocus() } } @@ -826,6 +868,7 @@ class ResultFragmentTv : Fragment() { resultMetaYear.setText(d.yearText) resultMetaDuration.setText(d.durationText) resultMetaRating.setText(d.ratingText) + resultMetaStatus.setText(d.onGoingText) resultMetaContentRating.setText(d.contentRatingText) resultCastText.setText(d.actorsText) resultNextAiring.setText(d.nextAiringEpisode) @@ -859,8 +902,12 @@ class ResultFragmentTv : Fragment() { radius = 0, 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 + UIHelper.populateChips(resultTag, d.tags) resultCastItems.isGone = d.actors.isNullOrEmpty() (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 resultMetaContentRating.width = 0 } + + resultSearchButton.setOnClickListener { + QuickSearchFragment.pushSearch(activity, d.title) + } } is Resource.Loading -> { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt index c24efe56..a05b4059 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt @@ -20,6 +20,7 @@ import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.APIHolder.unixTime import com.lagradost.cloudstream3.APIHolder.unixTimeMS +import com.lagradost.cloudstream3.AcraApplication.Companion.context import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.CommonActivity.activity 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.mvvm.* 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.providers.Kitsu import com.lagradost.cloudstream3.syncproviders.providers.SimklApi @@ -261,8 +263,7 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData { metaText = if (repo.providerType == ProviderType.MetaProvider) txt(R.string.provider_info_meta) else null, durationText = if (dur == null || dur <= 0) null else txt( - R.string.duration_format, - dur + secondsToReadable(dur * 60, "0 mins") ), onGoingText = if (this is EpisodeResponse) { txt( @@ -2464,7 +2465,7 @@ class ResultViewModel2 : ViewModel() { ResumeProgress( progress = (viewPos.position / 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")) ) } diff --git a/app/src/main/res/drawable/ic_baseline_film_roll_24.xml b/app/src/main/res/drawable/ic_baseline_film_roll_24.xml new file mode 100644 index 00000000..941d936f --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_film_roll_24.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_resume_arrow.xml b/app/src/main/res/drawable/ic_baseline_resume_arrow.xml new file mode 100644 index 00000000..0326fbd4 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_resume_arrow.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_resume_arrow2.xml b/app/src/main/res/drawable/ic_baseline_resume_arrow2.xml new file mode 100644 index 00000000..fc533a0e --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_resume_arrow2.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/outline_bookmark_add_24.xml b/app/src/main/res/drawable/outline_bookmark_add_24.xml new file mode 100644 index 00000000..a4e18af3 --- /dev/null +++ b/app/src/main/res/drawable/outline_bookmark_add_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/player_button_tv_attr.xml b/app/src/main/res/drawable/player_button_tv_attr.xml index 4c90a64e..ed83887d 100644 --- a/app/src/main/res/drawable/player_button_tv_attr.xml +++ b/app/src/main/res/drawable/player_button_tv_attr.xml @@ -3,13 +3,13 @@ - + - + \ No newline at end of file diff --git a/app/src/main/res/drawable/player_button_tv_attr_no_bg.xml b/app/src/main/res/drawable/player_button_tv_attr_no_bg.xml index b9b927da..0dd8c256 100644 --- a/app/src/main/res/drawable/player_button_tv_attr_no_bg.xml +++ b/app/src/main/res/drawable/player_button_tv_attr_no_bg.xml @@ -3,7 +3,7 @@ - + \ No newline at end of file diff --git a/app/src/main/res/layout/cast_item.xml b/app/src/main/res/layout/cast_item.xml index f164384b..99a9750b 100644 --- a/app/src/main/res/layout/cast_item.xml +++ b/app/src/main/res/layout/cast_item.xml @@ -17,7 +17,6 @@ android:layout_width="100dp" android:layout_height="wrap_content" android:orientation="vertical" - android:focusable="true" android:padding="5dp"> diff --git a/app/src/main/res/layout/fragment_result.xml b/app/src/main/res/layout/fragment_result.xml index 70461518..045b9cc2 100644 --- a/app/src/main/res/layout/fragment_result.xml +++ b/app/src/main/res/layout/fragment_result.xml @@ -412,7 +412,7 @@ android:foreground="@drawable/outline_drawable" android:maxLength="1000" android:nextFocusUp="@id/result_back" - android:nextFocusDown="@id/result_bookmark_button" + android:nextFocusDown="@id/result_bookmark_Button" android:paddingTop="5dp" android:textColor="?attr/textColor" android:textSize="15sp" @@ -474,7 +474,7 @@ android:fadingEdge="horizontal" android:focusable="false" android:focusableInTouchMode="false" - android:nextFocusUp="@id/result_bookmark_button" + android:nextFocusUp="@id/result_bookmark_Button" android:nextFocusDown="@id/result_play_movie" android:orientation="horizontal" android:paddingTop="5dp" @@ -580,7 +580,7 @@ android:layout_marginStart="0dp" android:layout_marginEnd="0dp" android:layout_marginBottom="10dp" - android:nextFocusUp="@id/result_bookmark_button" + android:nextFocusUp="@id/result_bookmark_Button" android:nextFocusDown="@id/result_download_movie" android:text="@string/play_movie_button" android:visibility="visible" @@ -658,7 +658,7 @@ android:layout_marginStart="0dp" android:layout_marginEnd="0dp" android:layout_marginBottom="10dp" - android:nextFocusUp="@id/result_bookmark_button" + android:nextFocusUp="@id/result_bookmark_Button" android:nextFocusDown="@id/result_download_movie" android:text="@string/resume" android:visibility="visible" @@ -674,7 +674,7 @@ android:layout_marginStart="0dp" android:layout_marginEnd="0dp" android:layout_marginBottom="10dp" - android:nextFocusUp="@id/result_bookmark_button" + android:nextFocusUp="@id/result_bookmark_Button" android:nextFocusDown="@id/result_download_movie" android:text="@string/next_episode" android:visibility="gone" diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml index a7ba4334..ba8b728e 100644 --- a/app/src/main/res/layout/fragment_result_tv.xml +++ b/app/src/main/res/layout/fragment_result_tv.xml @@ -78,6 +78,30 @@ https://developer.android.com/design/ui/tv/samples/jet-fit + + + + + + + + + - - - - - - - - - - + android:layout_marginTop="225dp"> @@ -221,157 +220,289 @@ https://developer.android.com/design/ui/tv/samples/jet-fit android:textStyle="normal" tools:text="5d 3h 30m" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_marginTop="10dp" + android:baselineAligned="false"> + app:layout_constraintTop_toTopOf="parent" + tools:ignore="UselessParent"> - - - - - - - - - - - - - @@ -513,10 +594,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit android:descendantFocusability="afterDescendants" android:fadingEdge="horizontal" - android:focusable="false" - android:focusableInTouchMode="false" - android:nextFocusUp="@id/result_episodes_show" - android:nextFocusDown="@id/result_recommendations_filter_selection" + android:nextFocusUp="@id/result_description" + android:nextFocusDown="@id/result_recommendations_list" android:orientation="horizontal" android:paddingTop="5dp" android:requiresFadingEdge="horizontal" @@ -525,8 +604,23 @@ https://developer.android.com/design/ui/tv/samples/jet-fit tools:listitem="@layout/cast_item" tools:visibility="visible" /> + + @@ -576,7 +670,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit android:layout_height="match_parent" android:layout_gravity="end" android:visibility="gone" - tools:visibility="visible"> + tools:visibility="invisible"> + - + + \ No newline at end of file diff --git a/app/src/main/res/layout/player_custom_layout_tv.xml b/app/src/main/res/layout/player_custom_layout_tv.xml index 89355a72..d8406b35 100644 --- a/app/src/main/res/layout/player_custom_layout_tv.xml +++ b/app/src/main/res/layout/player_custom_layout_tv.xml @@ -533,18 +533,14 @@ android:id="@id/exo_position" android:layout_width="wrap_content" android:layout_height="30dp" - android:layout_gravity="center" - android:layout_marginStart="20dp" - android:gravity="end|center_vertical" + android:gravity="center" android:includeFontPadding="false" android:minWidth="50dp" - android:paddingLeft="4dp" - android:paddingRight="4dp" android:textColor="@android:color/white" android:textSize="14sp" android:textStyle="normal" 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" /> +30 This will permanently delete %s\nAre you sure? %dm\nremaining + %s\nremaining Ongoing Completed Status @@ -745,7 +746,8 @@ Display a toggle button for screen orientation Enable automatic switching of screen orientation based on video orientation Auto rotate - + Favorite + Unfavorite Unlock CloudStream Lock with Biometrics @@ -758,4 +760,5 @@ 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, Clear the app data wholly and restore the backup. Any inconvenience if arrived is deeply regretted. + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index b30d7397..0a693769 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -814,6 +814,35 @@ 0dp + + + +