diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index fff737f6..b284eeb3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -19,7 +19,6 @@ import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.widget.Toast -import android.widget.Toast.LENGTH_LONG import androidx.activity.OnBackPressedCallback import androidx.activity.result.ActivityResultLauncher import androidx.annotation.IdRes @@ -1188,8 +1187,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) } } @@ -1220,8 +1237,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu // hide background while authenticating, Sorry moms & dads 🙏 binding?.navHostFragment?.isInvisible = true - } else { - showToast(R.string.phone_not_secured, LENGTH_LONG) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountSelectActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountSelectActivity.kt index ccaa38f0..1dae5a0f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountSelectActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountSelectActivity.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.content.Intent import android.os.Bundle import android.util.Log -import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModelProvider import androidx.preference.PreferenceManager @@ -57,14 +56,16 @@ class AccountSelectActivity : AppCompatActivity(), BiometricAuthenticator.Biomet if (isTruePhone() && authEnabled) { if (deviceHasPasswordPinLock(this)) { - startBiometricAuthentication(this, R.string.biometric_authentication_title, false) + startBiometricAuthentication( + this, + R.string.biometric_authentication_title, + false + ) BiometricAuthenticator.promptInfo?.let { BiometricAuthenticator.biometricPrompt?.authenticate(it) } } - } else { - showToast(R.string.phone_not_secured, Toast.LENGTH_LONG) } } 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/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt index d04757da..14f11c1e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt @@ -11,6 +11,7 @@ import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.fragment.app.FragmentActivity import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceManager import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent @@ -261,15 +262,20 @@ class SettingsAccount : PreferenceFragmentCompat() { setPreferencesFromResource(R.xml.settings_account, rootKey) getPref(R.string.biometric_key)?.setOnPreferenceClickListener { + val authEnabled = PreferenceManager.getDefaultSharedPreferences( + context ?: return@setOnPreferenceClickListener false + ) + .getBoolean(getString(R.string.biometric_key), false) + if (authEnabled) { BackupUtils.backup(activity) val title = activity?.getString(R.string.biometric_setting) val warning = activity?.getString(R.string.biometric_warning) - activity?.showBottomDialogText( - title as String, - warning.html() - ) { onDialogDismissedEvent } - + activity?.showBottomDialogText( + title as String, + warning.html() + ) { onDialogDismissedEvent } + } true } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt index db001ef5..d3c97cd1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt @@ -65,13 +65,16 @@ object BackupUtils { PLUGINS_KEY_LOCAL, OPEN_SUBTITLES_USER_KEY, - "nginx_user", // Nginx user key - "biometric_key" // can lock down users if backup is shared on a incompatible device + + DOWNLOAD_EPISODE_CACHE, + + "biometric_key", // can lock down users if backup is shared on a incompatible device + "nginx_user" // Nginx user key ) - /** false if blacklisted key */ + /** false if key should not be contained in backup */ private fun String.isTransferable(): Boolean { - return !nonTransferableKeys.contains(this) + return !nonTransferableKeys.any { this.contains(it) } } private var restoreFileSelector: ActivityResultLauncher>? = null @@ -257,4 +260,4 @@ object BackupUtils { setKeyRaw(it.key, it.value, isEditingAppSettings) } } -} \ No newline at end of file +} 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" /> enable_skip_op_from_database rotate_video_key auto_rotate_video_key + biometric_key %d %s | %s %s • %s @@ -306,6 +307,7 @@ +30 This will permanently delete %s\nAre you sure? %dm\nremaining + %s\nremaining Ongoing Completed Status @@ -756,17 +758,17 @@ 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 - biometric_key Password/PIN Authentication Biometric authentication is not supported on this device - Please disable fingerprint authentication if device has no lock. Unlock the app with Fingerprint, Face ID, PIN, Pattern and Password. This window will close after few failed attempts. You\'ll have to restart the App. 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 + + + +