diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f451b05a..c2ba2907 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -60,7 +60,7 @@ android { targetSdk = 33 /* Android 14 is Fu*ked ^ https://developer.android.com/about/versions/14/behavior-changes-14#safer-dynamic-code-loading*/ versionCode = 63 - versionName = "4.3.1" + versionName = "4.3.2" resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}") resValue("string", "commit_hash", "git rev-parse --short HEAD".execute() ?: "") @@ -163,10 +163,10 @@ dependencies { // Android Core & Lifecycle implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.appcompat:appcompat:1.6.1") - implementation("androidx.navigation:navigation-ui-ktx:2.7.6") + implementation("androidx.navigation:navigation-ui-ktx:2.7.7") implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0") - implementation("androidx.navigation:navigation-fragment-ktx:2.7.6") + implementation("androidx.navigation:navigation-fragment-ktx:2.7.7") // Design & UI implementation("jp.wasabeef:glide-transformations:4.3.0") diff --git a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt index c93f0f9b..1680d698 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt @@ -11,7 +11,9 @@ import androidx.fragment.app.FragmentActivity import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall import com.lagradost.cloudstream3.plugins.PluginManager -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.AppUtils.openBrowser import com.lagradost.cloudstream3.utils.Coroutines.runOnMainThread import com.lagradost.cloudstream3.utils.DataStore.getKey @@ -31,7 +33,6 @@ import org.acra.sender.ReportSenderFactory import java.io.File import java.io.FileNotFoundException import java.io.PrintStream -import java.lang.Exception import java.lang.ref.WeakReference import kotlin.concurrent.thread import kotlin.system.exitProcess @@ -211,7 +212,7 @@ class AcraApplication : Application() { fun openBrowser(url: String, activity: FragmentActivity?) { openBrowser( url, - isTvSettings(), + isLayout(TV or EMULATOR), activity?.supportFragmentManager?.fragments?.lastOrNull() ) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt index e6c90c33..4dc78dc7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt @@ -34,7 +34,7 @@ import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.player.PlayerEventType import com.lagradost.cloudstream3.ui.result.ResultFragment import com.lagradost.cloudstream3.ui.result.UiText -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateTv +import com.lagradost.cloudstream3.ui.settings.Globals.updateTv import com.lagradost.cloudstream3.utils.AppUtils.isRtl import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.Event diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 7a25b738..273e267b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -18,6 +18,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.simklAp import com.lagradost.cloudstream3.syncproviders.SyncIdName import com.lagradost.cloudstream3.syncproviders.providers.SimklApi import com.lagradost.cloudstream3.ui.player.SubtitleData +import com.lagradost.cloudstream3.ui.result.ResultViewModel2 import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.Coroutines.mainWork @@ -119,7 +120,8 @@ object APIHolder { } fun LoadResponse.getId(): Int { - return getLoadResponseIdFromUrl(url, apiName) + // this fixes an issue with outdated api as getLoadResponseIdFromUrl might be fucked + return (if (this is ResultViewModel2.LoadResponseFromSearch) this.id else null) ?: getLoadResponseIdFromUrl(url, apiName) } /** diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 48798ce6..5a7e72ef 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -86,6 +86,7 @@ import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager.loadAllOnlinePlugins import com.lagradost.cloudstream3.plugins.PluginManager.loadSinglePlugin import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver +import com.lagradost.cloudstream3.services.SubscriptionWorkManager import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.OAuth2Apis import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.accountManagers import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appString @@ -112,11 +113,11 @@ import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.ui.search.SearchFragment import com.lagradost.cloudstream3.ui.search.SearchResultBuilder -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTruePhone -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateTv +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.PHONE +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout +import com.lagradost.cloudstream3.ui.settings.Globals.updateTv import com.lagradost.cloudstream3.ui.settings.SettingsGeneral import com.lagradost.cloudstream3.ui.setup.HAS_DONE_SETUP_KEY import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions @@ -290,7 +291,8 @@ var app = Requests(responseParser = object : ResponseParser { defaultHeaders = mapOf("user-agent" to USER_AGENT) } -class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAuthenticator.BiometricAuthCallback { +class MainActivity : AppCompatActivity(), ColorPickerDialogListener, + BiometricAuthenticator.BiometricAuthCallback { companion object { const val TAG = "MAINACT" const val ANIMATED_OUTLINE: Boolean = false @@ -336,10 +338,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu // kinda shitty solution, but cant com main->home otherwise for popups val bookmarksUpdatedEvent = Event() + /** * Used by DataStoreHelper to fully reload home when switching accounts */ val reloadHomeEvent = Event() + /** * Used by DataStoreHelper to fully reload library when switching accounts */ @@ -467,7 +471,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu } var lastPopup: SearchResponse? = null - fun loadPopup(result: SearchResponse, load : Boolean = true) { + fun loadPopup(result: SearchResponse, load: Boolean = true) { lastPopup = result val syncName = syncViewModel.syncName(result.apiName) @@ -488,8 +492,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu .contains(DubStatus.Dubbed) ) DubStatus.Dubbed else DubStatus.Subbed, null ) - }else { - viewModel.loadSmall(this,result) + } else { + viewModel.loadSmall(this, result) } } @@ -554,7 +558,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu binding?.navHostFragment?.apply { val params = layoutParams as ConstraintLayout.LayoutParams val push = - if (!dontPush && isTvSettings()) resources.getDimensionPixelSize(R.dimen.navbar_width) else 0 + if (!dontPush && isLayout(TV or EMULATOR)) resources.getDimensionPixelSize(R.dimen.navbar_width) else 0 if (!this.isLtr()) { params.setMargins( @@ -581,7 +585,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu } Configuration.ORIENTATION_PORTRAIT -> { - isTvSettings() + isLayout(TV or EMULATOR) } else -> { @@ -787,9 +791,10 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu } lateinit var viewModel: ResultViewModel2 - lateinit var syncViewModel : SyncViewModel + lateinit var syncViewModel: SyncViewModel + /** kinda dirty, however it signals that we should use the watch status as sync or not*/ - var isLocalList : Boolean = false + var isLocalList: Boolean = false override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? { viewModel = ViewModelProvider(this)[ResultViewModel2::class.java] @@ -1105,8 +1110,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu } } - private fun centerView(view : View?) { - if(view == null) return + private fun centerView(view: View?) { + if (view == null) return try { Log.v(TAG, "centerView: $view") val r = Rect(0, 0, 0, 0) @@ -1172,11 +1177,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu // just in case, MAIN SHOULD *NEVER* BOOT LOOP CRASH binding = try { - if (isTvSettings()) { + if (isLayout(TV or EMULATOR)) { val newLocalBinding = ActivityMainTvBinding.inflate(layoutInflater, null, false) setContentView(newLocalBinding.root) - if (isTrueTvSettings() && ANIMATED_OUTLINE) { + if (isLayout(TV) && ANIMATED_OUTLINE) { TvFocus.focusOutline = WeakReference(newLocalBinding.focusOutline) newLocalBinding.root.viewTreeObserver.addOnScrollChangedListener { TvFocus.updateFocusView(TvFocus.lastFocus.get(), same = true) @@ -1188,7 +1193,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu newLocalBinding.focusOutline.isVisible = false } - if(isTrueTvSettings()) { + if (isLayout(TV)) { // Put here any button you don't want focusing it to center the view val exceptionButtons = listOf( R.id.home_preview_play_btt, @@ -1205,7 +1210,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu 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) @@ -1223,18 +1228,21 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu null } - changeStatusBarState(isEmulatorSettings()) + changeStatusBarState(isLayout(EMULATOR)) /** Biometric stuff for users without accounts **/ val authEnabled = settingsManager.getBoolean(getString(R.string.biometric_key), false) - val noAccounts = settingsManager.getBoolean(getString(R.string.skip_startup_account_select_key), false) || accounts.count() <= 1 + val noAccounts = settingsManager.getBoolean( + getString(R.string.skip_startup_account_select_key), + false + ) || accounts.count() <= 1 - if (isTruePhone() && authEnabled && noAccounts) { + if (isLayout(PHONE) && authEnabled && noAccounts) { if (deviceHasPasswordPinLock(this)) { startBiometricAuthentication(this, R.string.biometric_authentication_title, false) - BiometricAuthenticator.promptInfo?.let { - BiometricAuthenticator.biometricPrompt?.authenticate(it) + BiometricAuthenticator.promptInfo?.let { promt -> + BiometricAuthenticator.biometricPrompt?.authenticate(promt) } // hide background while authenticating, Sorry moms & dads 🙏 @@ -1326,7 +1334,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu } - fun setUserData(status : Resource?) { + fun setUserData(status: Resource?) { if (isLocalList) return bottomPreviewBinding?.apply { when (status) { @@ -1351,7 +1359,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu } } - fun setWatchStatus(state : WatchType?) { + fun setWatchStatus(state: WatchType?) { if (!isLocalList || state == null) return bottomPreviewBinding?.resultviewPreviewBookmark?.apply { @@ -1360,13 +1368,42 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu } } - observe(viewModel.watchStatus) { state -> - setWatchStatus(state) - } - observe(syncViewModel.userData) { status -> - setUserData(status) + fun setSubscribeStatus(state: Boolean?) { + bottomPreviewBinding?.resultviewPreviewSubscribe?.apply { + if (state != null) { + val drawable = if (state) { + R.drawable.ic_baseline_notifications_active_24 + } else { + R.drawable.baseline_notifications_none_24 + } + setImageResource(drawable) + } + isVisible = state != null + + setOnClickListener { + viewModel.toggleSubscriptionStatus(context) { newStatus: Boolean? -> + if (newStatus == null) return@toggleSubscriptionStatus + + val message = if (newStatus) { + // Kinda icky to have this here, but it works. + SubscriptionWorkManager.enqueuePeriodicWork(context) + R.string.subscription_new + } else { + R.string.subscription_deleted + } + + val name = (viewModel.page.value as? Resource.Success)?.value?.title + ?: txt(R.string.no_data).asStringNull(context) ?: "" + showToast(txt(message, name), Toast.LENGTH_SHORT) + } + } + } } + observe(viewModel.watchStatus,::setWatchStatus) + observe(syncViewModel.userData, ::setUserData) + observeNullable(viewModel.subscribeStatus, ::setSubscribeStatus) + observeNullable(viewModel.page) { resource -> if (resource == null) { hidePreviewPopupDialog() @@ -1408,6 +1445,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu setUserData(syncViewModel.userData.value) setWatchStatus(viewModel.watchStatus.value) + setSubscribeStatus(viewModel.subscribeStatus.value) resultviewPreviewBookmark.setOnClickListener { //viewModel.updateWatchStatus(WatchType.PLANTOWATCH) @@ -1426,7 +1464,9 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu ) } } else { - val value = (syncViewModel.userData.value as? Resource.Success)?.value?.status ?: SyncWatchType.NONE + val value = + (syncViewModel.userData.value as? Resource.Success)?.value?.status + ?: SyncWatchType.NONE this@MainActivity.showBottomDialog( SyncWatchType.values().map { getString(it.stringRes) }.toList(), @@ -1453,7 +1493,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu resultviewPreviewFavorite.setImageResource(drawable) } - resultviewPreviewFavorite.setOnClickListener{ + resultviewPreviewFavorite.setOnClickListener { viewModel.toggleFavoriteStatus(this@MainActivity) { newStatus: Boolean? -> if (newStatus == null) return@toggleFavoriteStatus @@ -1469,7 +1509,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu } } - if (!isTvSettings()) // dont want this clickable on tv layout + if (isLayout(PHONE)) // dont want this clickable on tv layout resultviewPreviewDescription.setOnClickListener { view -> view.context?.let { ctx -> val builder: AlertDialog.Builder = @@ -1544,7 +1584,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu } } - if (isTvSettings()) { + if (isLayout(TV or EMULATOR)) { if (navDestination.matchDestination(R.id.navigation_home)) { attachBackPressedCallback() } else detachBackPressedCallback() @@ -1580,7 +1620,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu itemRippleColor = rippleColor itemActiveIndicatorColor = rippleColor setupWithNavController(navController) - if (isTvSettings()) { + if (isLayout(TV or EMULATOR)) { background?.alpha = 200 } else { background?.alpha = 255 diff --git a/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt b/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt index adf5abfa..e2bcd6e1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt @@ -1,5 +1,6 @@ package com.lagradost.cloudstream3.services +import android.annotation.SuppressLint import android.app.NotificationManager import android.app.PendingIntent import android.content.Context @@ -12,7 +13,7 @@ import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.R -import com.lagradost.cloudstream3.mvvm.safeApiCall +import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.AppUtils.createNotificationChannel @@ -97,128 +98,138 @@ class SubscriptionWorkManager(val context: Context, workerParams: WorkerParamete ) } + @SuppressLint("UnspecifiedImmutableFlag") override suspend fun doWork(): Result { + try { // println("Update subscriptions!") - context.createNotificationChannel( - SUBSCRIPTION_CHANNEL_ID, - SUBSCRIPTION_CHANNEL_NAME, - SUBSCRIPTION_CHANNEL_DESCRIPTION - ) - - setForeground( - ForegroundInfo( - SUBSCRIPTION_NOTIFICATION_ID, - progressNotificationBuilder.build() + context.createNotificationChannel( + SUBSCRIPTION_CHANNEL_ID, + SUBSCRIPTION_CHANNEL_NAME, + SUBSCRIPTION_CHANNEL_DESCRIPTION ) - ) - val subscriptions = getAllSubscriptions() + setForeground( + ForegroundInfo( + SUBSCRIPTION_NOTIFICATION_ID, + progressNotificationBuilder.build() + ) + ) - if (subscriptions.isEmpty()) { - WorkManager.getInstance(context).cancelWorkById(this.id) + val subscriptions = getAllSubscriptions() + + if (subscriptions.isEmpty()) { + WorkManager.getInstance(context).cancelWorkById(this.id) + return Result.success() + } + + val max = subscriptions.size + var progress = 0 + + updateProgress(max, progress, true) + + // We need all plugins loaded. + PluginManager.loadAllOnlinePlugins(context) + PluginManager.loadAllLocalPlugins(context, false) + + subscriptions.apmap { savedData -> + try { + val id = savedData.id ?: return@apmap null + val api = getApiFromNameNull(savedData.apiName) ?: return@apmap null + + // Reasonable timeout to prevent having this worker run forever. + val response = withTimeoutOrNull(60_000) { + api.load(savedData.url) as? EpisodeResponse + } ?: return@apmap null + + val dubPreference = + getDub(id) ?: if ( + context.getApiDubstatusSettings().contains(DubStatus.Dubbed) + ) { + DubStatus.Dubbed + } else { + DubStatus.Subbed + } + + val latestEpisodes = response.getLatestEpisodes() + val latestPreferredEpisode = latestEpisodes[dubPreference] + + val (shouldUpdate, latestEpisode) = if (latestPreferredEpisode != null) { + val latestSeenEpisode = + savedData.lastSeenEpisodeCount[dubPreference] ?: Int.MIN_VALUE + val shouldUpdate = latestPreferredEpisode > latestSeenEpisode + shouldUpdate to latestPreferredEpisode + } else { + val latestEpisode = latestEpisodes[DubStatus.None] ?: Int.MIN_VALUE + val latestSeenEpisode = + savedData.lastSeenEpisodeCount[DubStatus.None] ?: Int.MIN_VALUE + val shouldUpdate = latestEpisode > latestSeenEpisode + shouldUpdate to latestEpisode + } + + DataStoreHelper.updateSubscribedData( + id, + savedData, + response + ) + + if (shouldUpdate) { + val updateHeader = savedData.name + val updateDescription = txt( + R.string.subscription_episode_released, + latestEpisode, + savedData.name + ).asString(context) + + val intent = Intent(context, MainActivity::class.java).apply { + data = savedData.url.toUri() + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + + val pendingIntent = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PendingIntent.getActivity( + context, + 0, + intent, + PendingIntent.FLAG_IMMUTABLE + ) + } else { + PendingIntent.getActivity(context, 0, intent, 0) + } + + val poster = ioWork { + savedData.posterUrl?.let { url -> + context.getImageBitmapFromUrl( + url, + savedData.posterHeaders + ) + } + } + + val updateNotification = + updateNotificationBuilder.setContentTitle(updateHeader) + .setContentText(updateDescription) + .setContentIntent(pendingIntent) + .setLargeIcon(poster) + .build() + + notificationManager.notify(id, updateNotification) + } + + // You can probably get some issues here since this is async but it does not matter much. + updateProgress(max, ++progress, false) + } catch (t: Throwable) { + logError(t) + } + } + + return Result.success() + } catch (t: Throwable) { + logError(t) + // ye, while this is not correct, but because gods know why android just crashes + // and this causes major battery usage as it retries it inf times. This is better, just + // in case android decides to be android and fuck us return Result.success() } - - val max = subscriptions.size - var progress = 0 - - updateProgress(max, progress, true) - - // We need all plugins loaded. - PluginManager.loadAllOnlinePlugins(context) - PluginManager.loadAllLocalPlugins(context, false) - - subscriptions.apmap { savedData -> - try { - val id = savedData.id ?: return@apmap null - val api = getApiFromNameNull(savedData.apiName) ?: return@apmap null - - // Reasonable timeout to prevent having this worker run forever. - val response = withTimeoutOrNull(60_000) { - api.load(savedData.url) as? EpisodeResponse - } ?: return@apmap null - - val dubPreference = - getDub(id) ?: if ( - context.getApiDubstatusSettings().contains(DubStatus.Dubbed) - ) { - DubStatus.Dubbed - } else { - DubStatus.Subbed - } - - val latestEpisodes = response.getLatestEpisodes() - val latestPreferredEpisode = latestEpisodes[dubPreference] - - val (shouldUpdate, latestEpisode) = if (latestPreferredEpisode != null) { - val latestSeenEpisode = - savedData.lastSeenEpisodeCount[dubPreference] ?: Int.MIN_VALUE - val shouldUpdate = latestPreferredEpisode > latestSeenEpisode - shouldUpdate to latestPreferredEpisode - } else { - val latestEpisode = latestEpisodes[DubStatus.None] ?: Int.MIN_VALUE - val latestSeenEpisode = - savedData.lastSeenEpisodeCount[DubStatus.None] ?: Int.MIN_VALUE - val shouldUpdate = latestEpisode > latestSeenEpisode - shouldUpdate to latestEpisode - } - - DataStoreHelper.updateSubscribedData( - id, - savedData, - response - ) - - if (shouldUpdate) { - val updateHeader = savedData.name - val updateDescription = txt( - R.string.subscription_episode_released, - latestEpisode, - savedData.name - ).asString(context) - - val intent = Intent(context, MainActivity::class.java).apply { - data = savedData.url.toUri() - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - } - - val pendingIntent = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - PendingIntent.getActivity( - context, - 0, - intent, - PendingIntent.FLAG_IMMUTABLE - ) - } else { - PendingIntent.getActivity(context, 0, intent, 0) - } - - val poster = ioWork { - savedData.posterUrl?.let { url -> - context.getImageBitmapFromUrl( - url, - savedData.posterHeaders - ) - } - } - - val updateNotification = - updateNotificationBuilder.setContentTitle(updateHeader) - .setContentText(updateDescription) - .setContentIntent(pendingIntent) - .setLargeIcon(poster) - .build() - - notificationManager.notify(id, updateNotification) - } - - // You can probably get some issues here since this is async but it does not matter much. - updateProgress(max, ++progress, false) - } catch (_: Throwable) { - } - } - - return Result.success() } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt index 99723e90..7552fe9d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt @@ -8,7 +8,8 @@ import com.lagradost.cloudstream3.syncproviders.SyncIdName import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.library.ListSorting import com.lagradost.cloudstream3.ui.result.txt -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.Coroutines.ioWork import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllFavorites import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions @@ -71,9 +72,9 @@ class LocalList : SyncAPI { }?.distinctBy { it.first } ?: return null val list = ioWork { - val isTrueTv = isTrueTvSettings() + val isTrueTv = isLayout(TV) - val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate { + val baseMap = WatchType.entries.filter { it != WatchType.NONE }.associate { // None is not something to display it.stringRes to emptyList() } + mapOf( diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt index 60260edf..de0b5c05 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt @@ -12,7 +12,9 @@ import com.lagradost.cloudstream3.databinding.AccountListItemBinding import com.lagradost.cloudstream3.databinding.AccountListItemEditBinding import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountEditDialog import com.lagradost.cloudstream3.ui.result.setImage -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.UIHelper.setImage @@ -38,7 +40,7 @@ class AccountAdapter( is AccountListItemBinding -> binding.apply { if (account == null) return@apply - val isTv = isTvSettings() || !root.isInTouchMode + val isTv = isLayout(TV or EMULATOR) || !root.isInTouchMode val isLastUsedAccount = account.keyIndex == DataStoreHelper.selectedKeyIndex @@ -80,7 +82,7 @@ class AccountAdapter( is AccountListItemEditBinding -> binding.apply { if (account == null) return@apply - val isTv = isTvSettings() || !root.isInTouchMode + val isTv = isLayout(TV or EMULATOR) || !root.isInTouchMode val isLastUsedAccount = account.keyIndex == DataStoreHelper.selectedKeyIndex 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 1dae5a0f..41aef176 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 @@ -18,8 +18,10 @@ import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.AutofitRecyclerView import com.lagradost.cloudstream3.ui.account.AccountAdapter.Companion.VIEW_TYPE_EDIT_ACCOUNT import com.lagradost.cloudstream3.ui.account.AccountAdapter.Companion.VIEW_TYPE_SELECT_ACCOUNT -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTruePhone -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.PHONE +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.BiometricAuthenticator import com.lagradost.cloudstream3.utils.BiometricAuthenticator.deviceHasPasswordPinLock import com.lagradost.cloudstream3.utils.BiometricAuthenticator.startBiometricAuthentication @@ -54,7 +56,7 @@ class AccountSelectActivity : AppCompatActivity(), BiometricAuthenticator.Biomet fun askBiometricAuth() { - if (isTruePhone() && authEnabled) { + if (isLayout(PHONE) && authEnabled) { if (deviceHasPasswordPinLock(this)) { startBiometricAuthentication( this, @@ -62,8 +64,8 @@ class AccountSelectActivity : AppCompatActivity(), BiometricAuthenticator.Biomet false ) - BiometricAuthenticator.promptInfo?.let { - BiometricAuthenticator.biometricPrompt?.authenticate(it) + BiometricAuthenticator.promptInfo?.let { promt -> + BiometricAuthenticator.biometricPrompt?.authenticate(promt) } } } @@ -127,7 +129,7 @@ class AccountSelectActivity : AppCompatActivity(), BiometricAuthenticator.Biomet recyclerView.adapter = adapter - if (isTvSettings()) { + if (isLayout(TV or EMULATOR)) { binding.editAccountButton.setBackgroundResource( R.drawable.player_button_tv_attr_no_bg ) @@ -168,7 +170,7 @@ class AccountSelectActivity : AppCompatActivity(), BiometricAuthenticator.Biomet viewModel.toggleIsEditing() } - if (isTvSettings()) { + if (isLayout(TV or EMULATOR)) { recyclerView.spanCount = if (liveAccounts.count() + 1 <= 6) { liveAccounts.count() + 1 } else 6 diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt index 27c2e1a3..e08eb772 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt @@ -5,6 +5,7 @@ import android.content.ClipboardManager import android.content.Context import android.os.Build import android.os.Bundle +import android.text.format.Formatter.formatShortFileSize import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -13,17 +14,25 @@ import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.view.isGone import androidx.core.view.isVisible +import androidx.core.widget.doOnTextChanged import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.databinding.FragmentDownloadsBinding +import com.lagradost.cloudstream3.databinding.StreamInputBinding import com.lagradost.cloudstream3.isMovieType +import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick +import com.lagradost.cloudstream3.ui.player.BasicLink import com.lagradost.cloudstream3.ui.player.GeneratorPlayer import com.lagradost.cloudstream3.ui.player.LinkGenerator +import com.lagradost.cloudstream3.ui.result.FOCUS_SELF +import com.lagradost.cloudstream3.ui.result.setLinearListLayout +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE @@ -34,15 +43,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadManager -import android.text.format.Formatter.formatShortFileSize -import androidx.core.widget.doOnTextChanged -import com.lagradost.cloudstream3.databinding.FragmentDownloadsBinding -import com.lagradost.cloudstream3.databinding.StreamInputBinding -import com.lagradost.cloudstream3.mvvm.normalSafeApiCall -import com.lagradost.cloudstream3.ui.player.BasicLink -import com.lagradost.cloudstream3.ui.result.FOCUS_SELF -import com.lagradost.cloudstream3.ui.result.setLinearListLayout -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import java.net.URI @@ -200,7 +200,7 @@ class DownloadFragment : Fragment() { } // Should be visible in emulator layout - binding?.downloadStreamButton?.isGone = isTrueTvSettings() + binding?.downloadStreamButton?.isGone = isLayout(TV) binding?.downloadStreamButton?.setOnClickListener { val dialog = Dialog(it.context ?: return@setOnClickListener, R.style.AlertDialogCustom) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/button/PieFetchButton.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/button/PieFetchButton.kt index d20fcf93..a729f33a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/button/PieFetchButton.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/button/PieFetchButton.kt @@ -2,16 +2,19 @@ package com.lagradost.cloudstream3.ui.download.button import android.content.Context import android.graphics.drawable.Drawable +import android.os.Looper import android.util.AttributeSet import android.util.Log import android.view.View import android.view.animation.AnimationUtils import android.widget.ImageView import android.widget.TextView +import androidx.annotation.MainThread import androidx.core.content.ContextCompat import androidx.core.view.isGone import androidx.core.view.isVisible import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DELETE_FILE import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_LONG_CLICK @@ -241,40 +244,54 @@ open class PieFetchButton(context: Context, attributeSet: AttributeSet) : } }*/ + @MainThread + private fun setStatusInternal(status : DownloadStatusTell?) { + val isPreActive = isZeroBytes && status == DownloadStatusTell.IsDownloading + if (animateWaiting && (status == DownloadStatusTell.IsPending || isPreActive)) { + val animation = AnimationUtils.loadAnimation(context, waitingAnimation) + progressBarBackground.startAnimation(animation) + } else { + progressBarBackground.clearAnimation() + } + + val progressDrawable = + if (status == DownloadStatusTell.IsDownloading && !isPreActive) activeOutline else nonActiveOutline + + progressBarBackground.background = + ContextCompat.getDrawable(context, progressDrawable) + + val drawable = getDrawableFromStatus(status) + statusView.setImageDrawable(drawable) + val isDrawable = drawable != null + + statusView.isVisible = isDrawable + val hide = hideWhenIcon && isDrawable + if (hide) { + progressBar.clearAnimation() + progressBarBackground.clearAnimation() + } + progressBarBackground.isGone = hide + progressBar.isGone = hide + } + /** Also sets currentStatus */ override fun setStatus(status: DownloadStatusTell?) { currentStatus = status - //progressBar.isVisible = - // status != null && status != DownloadStatusTell.Complete && status != DownloadStatusTell.Error - //progressBarBackground.isVisible = status != null && status != DownloadStatusTell.Complete - progressBarBackground.post { - val isPreActive = isZeroBytes && status == DownloadStatusTell.IsDownloading - if (animateWaiting && (status == DownloadStatusTell.IsPending || isPreActive)) { - val animation = AnimationUtils.loadAnimation(context, waitingAnimation) - progressBarBackground.startAnimation(animation) - } else { - progressBarBackground.clearAnimation() + // runs on the main thread, but also instant if it already is + if (Looper.myLooper() == Looper.getMainLooper()) { + try { + setStatusInternal(status) + } catch (t : Throwable) { + logError(t) // just in case setStatusInternal throws because thread + progressBarBackground.post { + setStatusInternal(status) + } } - - val progressDrawable = - if (status == DownloadStatusTell.IsDownloading && !isPreActive) activeOutline else nonActiveOutline - - progressBarBackground.background = - ContextCompat.getDrawable(context, progressDrawable) - - val drawable = getDrawableFromStatus(status) - statusView.setImageDrawable(drawable) - val isDrawable = drawable != null - - statusView.isVisible = isDrawable - val hide = hideWhenIcon && isDrawable - if (hide) { - progressBar.clearAnimation() - progressBarBackground.clearAnimation() + } else { + progressBarBackground.post { + setStatusInternal(status) } - progressBarBackground.isGone = hide - progressBar.isGone = hide } } 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 cd843517..7a68330f 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 @@ -42,8 +42,10 @@ import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountSelectLine import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.ui.search.* import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.PHONE +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.AppUtils.isRecyclerScrollable import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.AppUtils.ownHide @@ -311,7 +313,7 @@ class HomeFragment : Fragment() { button?.isVisible = isValid button?.isChecked = isValid && selectedTypes.any { types.contains(it) } button?.isFocusable = true - if (isTrueTvSettings()) { + if (isLayout(TV)) { button?.isFocusableInTouchMode = true } @@ -435,7 +437,7 @@ class HomeFragment : Fragment() { bottomSheetDialog?.ownShow() val layout = - if (isTvSettings()) R.layout.fragment_home_tv else R.layout.fragment_home + if (isLayout(TV or EMULATOR)) R.layout.fragment_home_tv else R.layout.fragment_home val root = inflater.inflate(layout, container, false) binding = try { FragmentHomeBinding.bind(root) @@ -449,6 +451,11 @@ class HomeFragment : Fragment() { } override fun onDestroyView() { + homeMasterAdapter?.onSaveInstanceState( + instanceState, + binding?.homeMasterRecycler + ) + bottomSheetDialog?.ownHide() binding = null super.onDestroyView() @@ -485,6 +492,10 @@ class HomeFragment : Fragment() { private var bottomSheetDialog: BottomSheetDialog? = null + // https://github.com/vivchar/RendererRecyclerViewAdapter/blob/185251ee9d94fb6eb3e063b00d646b745186c365/example/src/main/java/com/github/vivchar/example/pages/github/GithubFragment.kt#L32 + // cry about it, but this is android we are talking about, we cant do the most simple shit without making a global variable + private var instanceState: Bundle = Bundle() + private var homeMasterAdapter: HomeParentItemAdapterPreview? = null @SuppressLint("SetTextI18n") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -505,15 +516,16 @@ class HomeFragment : Fragment() { activity.loadSearchResult(listHomepageItems.random()) } } - - homeMasterRecycler.adapter = - HomeParentItemAdapterPreview( - mutableListOf(), - homeViewModel - ) + homeMasterAdapter = HomeParentItemAdapterPreview( + mutableListOf(), + homeViewModel, + ).apply { + onRestoreInstanceState(instanceState) + } + homeMasterRecycler.adapter = homeMasterAdapter //fixPaddingStatusbar(homeLoadingStatusbar) - homeApiFab.isVisible = !isTvSettings() + homeApiFab.isVisible = isLayout(PHONE) homeMasterRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { @@ -521,7 +533,7 @@ class HomeFragment : Fragment() { homeApiFab.shrink() // hide homeRandom.shrink() } else if (dy < -5) { - if (!isTvSettings()) { + if (isLayout(PHONE)) { homeApiFab.extend() // show homeRandom.extend() } @@ -540,7 +552,7 @@ class HomeFragment : Fragment() { settingsManager.getBoolean( getString(R.string.random_button_key), false - ) && !isTvSettings() + ) && isLayout(PHONE) binding?.homeRandom?.visibility = View.GONE } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt index 443278a9..50111428 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt @@ -1,5 +1,7 @@ package com.lagradost.cloudstream3.ui.home +import android.os.Bundle +import android.os.Parcelable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -7,16 +9,20 @@ import android.widget.TextView import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListUpdateCallback import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.lagradost.cloudstream3.HomePageList import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.databinding.HomepageParentBinding +import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.result.FOCUS_SELF import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.search.SearchClickCallback import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.PHONE +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.AppUtils.isRecyclerScrollable class LoadClickCallback( @@ -32,18 +38,90 @@ open class ParentItemAdapter( private val clickCallback: (SearchClickCallback) -> Unit, private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit, private val expandCallback: ((String) -> Unit)? = null, -) : RecyclerView.Adapter() { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { +) : RecyclerView.Adapter() { + // Ok, this is fucked, but there is a reason for this as we want to resume 1. when scrolling up and down + // and 2. when doing into a thing and coming back. 1 is always active, but 2 requires doing it in the fragment + // as OnCreateView is called and this adapter is recreated losing the internal state to the GC + // + // 1. This works by having the adapter having a internal state "scrollStates" that keeps track of the states + // when a view recycles, it looks up this internal state + // 2. To solve the the coming back shit we have to save "scrollStates" to a Bundle inside the + // fragment via onSaveInstanceState, because this cant be easy for some reason as the adapter does + // not have a state but the layout-manager for no reason, then it is resumed via onRestoreInstanceState + // + // Even when looking at a real example they do this :skull: + // https://github.com/vivchar/RendererRecyclerViewAdapter/blob/185251ee9d94fb6eb3e063b00d646b745186c365/example/src/main/java/com/github/vivchar/example/pages/github/GithubFragment.kt#L32 + private val scrollStates = mutableMapOf() + companion object { + private const val SCROLL_KEY: String = "ParentItemAdapter::scrollStates.keys" + private const val SCROLL_VALUE: String = "ParentItemAdapter::scrollStates.values" + } + + open fun onRestoreInstanceState(savedInstanceState: Bundle?) { + try { + val keys = savedInstanceState?.getIntArray(SCROLL_KEY) ?: intArrayOf() + val values = savedInstanceState?.getParcelableArray(SCROLL_VALUE) ?: arrayOf() + for ((k, v) in keys.zip(values)) { + this.scrollStates[k] = v + } + } catch (t: Throwable) { + logError(t) + } + } + + open fun onSaveInstanceState(outState: Bundle, recyclerView: RecyclerView? = null) { + if (recyclerView != null) { + for (position in items.indices) { + val holder = recyclerView.findViewHolderForAdapterPosition(position) ?: continue + saveHolder(holder) + } + } + + outState.putIntArray(SCROLL_KEY, scrollStates.keys.toIntArray()) + outState.putParcelableArray(SCROLL_VALUE, scrollStates.values.toTypedArray()) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + when (holder) { + is ParentViewHolder -> { + holder.bind(items[position]) + scrollStates[holder.absoluteAdapterPosition]?.let { + holder.binding.homeChildRecyclerview.layoutManager?.onRestoreInstanceState(it) + } + } + } + } + + private fun saveHolder(holder : ViewHolder) { + when (holder) { + is ParentViewHolder -> { + scrollStates[holder.absoluteAdapterPosition] = + holder.binding.homeChildRecyclerview.layoutManager?.onSaveInstanceState() + } + } + } + + override fun onViewRecycled(holder: ViewHolder) { + saveHolder(holder) + super.onViewRecycled(holder) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val layoutResId = when { - isTrueTvSettings() -> R.layout.homepage_parent_tv - parent.context.isEmulatorSettings() -> R.layout.homepage_parent_emulator + isLayout(TV) -> R.layout.homepage_parent_tv + isLayout(EMULATOR) -> R.layout.homepage_parent_emulator else -> R.layout.homepage_parent } - val root = LayoutInflater.from(parent.context).inflate(layoutResId, parent, false) - - val binding = HomepageParentBinding.bind(root) + val inflater = LayoutInflater.from(parent.context) + val binding = try { + HomepageParentBinding.bind(inflater.inflate(layoutResId, parent, false)) + } catch (t : Throwable) { + logError(t) + // just in case someone forgot we don't want to crash + HomepageParentBinding.inflate(inflater) + } return ParentViewHolder( binding, @@ -53,14 +131,6 @@ open class ParentItemAdapter( ) } - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - when (holder) { - is ParentViewHolder -> { - holder.bind(items[position]) - } - } - } - override fun getItemCount(): Int { return items.size } @@ -116,7 +186,6 @@ open class ParentItemAdapter( } override fun onChanged(_position: Int, count: Int, payload: Any?) { - val position = _position + delta // I know kinda messy, what this does is using the update or bind instead of onCreateViewHolder -> bind @@ -155,15 +224,15 @@ open class ParentItemAdapter( //diffResult.dispatchUpdatesTo(this) } - class ParentViewHolder - constructor( + + class ParentViewHolder( val binding: HomepageParentBinding, // val viewModel: HomeViewModel, private val clickCallback: (SearchClickCallback) -> Unit, private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit, private val expandCallback: ((String) -> Unit)? = null, ) : - RecyclerView.ViewHolder(binding.root) { + ViewHolder(binding.root) { val title: TextView = binding.homeChildMoreInfo private val recyclerView: RecyclerView = binding.homeChildRecyclerview private val startFocus = R.id.nav_rail_view @@ -237,7 +306,7 @@ open class ParentItemAdapter( }) //(recyclerView.adapter as HomeChildItemAdapter).notifyDataSetChanged() - if (!isTrueTvSettings()) { + if (isLayout(PHONE)) { title.setOnClickListener { moreInfoClickCallback.invoke(expand) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt index 0e397f81..7ad15e4e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt @@ -36,8 +36,9 @@ import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA import com.lagradost.cloudstream3.ui.search.SearchClickCallback -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes @@ -48,7 +49,8 @@ import com.lagradost.cloudstream3.utils.UIHelper.populateChips class HomeParentItemAdapterPreview( items: MutableList, private val viewModel: HomeViewModel, -) : ParentItemAdapter(items, clickCallback = { +) : ParentItemAdapter(items, + clickCallback = { viewModel.click(it) }, moreInfoClickCallback = { viewModel.popup(it) @@ -78,13 +80,13 @@ class HomeParentItemAdapterPreview( return when (viewType) { VIEW_TYPE_HEADER -> { val inflater = LayoutInflater.from(parent.context) - val binding = if (isTvSettings()) FragmentHomeHeadTvBinding.inflate( + val binding = if (isLayout(TV or EMULATOR)) FragmentHomeHeadTvBinding.inflate( inflater, parent, false ) else FragmentHomeHeadBinding.inflate(inflater, parent, false) - if (binding is FragmentHomeHeadTvBinding && parent.context.isEmulatorSettings()) { + if (binding is FragmentHomeHeadTvBinding && isLayout(EMULATOR)) { binding.homeBookmarkParentItemMoreInfo.isVisible = true val marginInDp = 50 @@ -598,7 +600,7 @@ class HomeParentItemAdapterPreview( if ( binding is FragmentHomeHeadBinding || binding is FragmentHomeHeadTvBinding && - binding.root.context.isEmulatorSettings() + isLayout(EMULATOR) ) { val title = (binding as? FragmentHomeHeadBinding)?.homeWatchParentItemTitle ?: (binding as? FragmentHomeHeadTvBinding)?.homeWatchParentItemTitle @@ -628,7 +630,7 @@ class HomeParentItemAdapterPreview( if ( binding is FragmentHomeHeadBinding || binding is FragmentHomeHeadTvBinding && - binding.root.context.isEmulatorSettings() + isLayout(EMULATOR) ) { val title = (binding as? FragmentHomeHeadBinding)?.homeBookmarkParentItemTitle ?: (binding as? FragmentHomeHeadTvBinding)?.homeBookmarkParentItemTitle diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt index 666fbc24..f0542b77 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt @@ -10,7 +10,9 @@ import androidx.viewbinding.ViewBinding import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.databinding.HomeScrollViewBinding import com.lagradost.cloudstream3.databinding.HomeScrollViewTvBinding -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.UIHelper.setImage class HomeScrollAdapter : RecyclerView.Adapter() { @@ -40,7 +42,7 @@ class HomeScrollAdapter : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val inflater = LayoutInflater.from(parent.context) - val binding = if (isTvSettings()) { + val binding = if (isLayout(TV or EMULATOR)) { HomeScrollViewTvBinding.inflate(inflater, parent, false) } else { HomeScrollViewBinding.inflate(inflater, parent, false) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt index f471fefd..b0d4bdf8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt @@ -34,7 +34,8 @@ import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_FOCUSED import com.lagradost.cloudstream3.ui.search.SearchClickCallback import com.lagradost.cloudstream3.ui.search.SearchHelper -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.AppUtils.addProgramsToContinueWatching import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.Coroutines.ioSafe @@ -132,7 +133,7 @@ class HomeViewModel : ViewModel() { private fun loadResumeWatching() = viewModelScope.launchSafe { val resumeWatchingResult = getResumeWatching() - if (isTrueTvSettings() && resumeWatchingResult != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (isLayout(TV) && resumeWatchingResult != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ioSafe { // this WILL crash on non tvs, so keep this inside a try catch activity?.addProgramsToContinueWatching(resumeWatchingResult) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt index fa91d990..664946b1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt @@ -49,6 +49,11 @@ import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA +import com.lagradost.cloudstream3.ui.settings.Globals +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.PHONE +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.ui.settings.SettingsFragment import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult @@ -101,7 +106,7 @@ class LibraryFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { val layout = - if (SettingsFragment.isTvSettings()) R.layout.fragment_library_tv else R.layout.fragment_library + if (isLayout(TV or EMULATOR)) R.layout.fragment_library_tv else R.layout.fragment_library val root = inflater.inflate(layout, container, false) binding = try { FragmentLibraryBinding.bind(root) @@ -220,7 +225,7 @@ class LibraryFragment : Fragment() { settingsManager.getBoolean( getString(R.string.random_button_key), false - ) && !SettingsFragment.isTvSettings() + ) && isLayout(PHONE) binding?.libraryRandom?.visibility = View.GONE } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt index 6731eae2..c41ec681 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt @@ -1,7 +1,6 @@ package com.lagradost.cloudstream3.ui.library import android.os.Build -import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup import androidx.core.view.doOnAttach @@ -12,7 +11,9 @@ import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.databinding.LibraryViewpagerPageBinding import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.ui.search.SearchClickCallback -import com.lagradost.cloudstream3.ui.settings.SettingsFragment +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount class ViewpagerAdapter( @@ -73,7 +74,7 @@ class ViewpagerAdapter( val diff = scrollY - oldScrollY //Expand the top Appbar based on scroll direction up/down, simulate phone behavior - if (SettingsFragment.isTvSettings()) { + if (isLayout(TV or EMULATOR)) { binding.root.rootView.findViewById(R.id.search_bar) .apply { if (diff <= 0) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt index e8d74752..6735a350 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt @@ -46,6 +46,7 @@ import com.lagradost.cloudstream3.ui.player.GeneratorPlayer.Companion.subsProvid import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.result.txt +import com.lagradost.cloudstream3.ui.settings.Globals import com.lagradost.cloudstream3.ui.settings.SettingsFragment import com.lagradost.cloudstream3.utils.AppUtils.isUsingMobileData import com.lagradost.cloudstream3.utils.DataStoreHelper @@ -1514,7 +1515,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { } } // cs3 is peak media center - setRemainingTimeCounter(durationMode || SettingsFragment.isTrueTvSettings()) + setRemainingTimeCounter(durationMode || Globals.isLayout(Globals.TV)) playerBinding?.exoPosition?.doOnTextChanged { _, _, _, _ -> updateRemainingTime() } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index 01069f66..7ff56886 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -39,7 +39,10 @@ import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSub import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog import com.lagradost.cloudstream3.ui.result.* -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.ui.subtitles.SUBTITLE_AUTO_SELECT_KEY import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getAutoSelectLanguageISO639_1 import com.lagradost.cloudstream3.utils.* @@ -1275,8 +1278,7 @@ class GeneratorPlayer : FullScreenPlayer() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // this is used instead of layout-television to follow the settings and some TV devices are not classified as TV for some reason - isTv = isTvSettings() - layout = if (isTv) R.layout.fragment_player_tv else R.layout.fragment_player + layout = if (isLayout(TV or EMULATOR)) R.layout.fragment_player_tv else R.layout.fragment_player viewModel = ViewModelProvider(this)[PlayerGeneratorViewModel::class.java] sync = ViewModelProvider(this)[SyncViewModel::class.java] diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PreviewGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PreviewGenerator.kt index 6414374b..fb600ef1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PreviewGenerator.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PreviewGenerator.kt @@ -9,6 +9,9 @@ import android.util.Log import androidx.annotation.WorkerThread import androidx.core.graphics.scale import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.ui.settings.Globals +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.ui.settings.SettingsFragment import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.ExtractorLink @@ -63,7 +66,7 @@ interface IPreviewGenerator { companion object { fun new(): IPreviewGenerator { /** because TV has low ram + not show we disable this for now */ - return if (SettingsFragment.isTrueTvSettings()) { + return if (isLayout(TV)) { empty() } else { PreviewGenerator() diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt index 5b300c06..26cf9918 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt @@ -34,7 +34,8 @@ import com.lagradost.cloudstream3.ui.search.SearchAdapter import com.lagradost.cloudstream3.ui.search.SearchClickCallback import com.lagradost.cloudstream3.ui.search.SearchHelper import com.lagradost.cloudstream3.ui.search.SearchViewModel -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.AppUtils.ownShow import com.lagradost.cloudstream3.utils.UIHelper import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar @@ -277,7 +278,7 @@ class QuickSearchFragment : Fragment() { activity?.popCurrentPage() } - if (isTrueTvSettings()) { + if (isLayout(TV)) { binding?.quickSearch?.requestFocus() } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt index b12475bf..fad349c8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt @@ -15,8 +15,10 @@ import com.lagradost.cloudstream3.databinding.ResultEpisodeLargeBinding import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_LONG_CLICK import com.lagradost.cloudstream3.ui.download.DownloadClickEvent -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.PHONE +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.toPx @@ -172,15 +174,13 @@ class EpisodeAdapter( @SuppressLint("SetTextI18n") fun bind(card: ResultEpisode) { localCard = card - val setWidth = - if (isTvSettings()) TV_EP_SIZE_LARGE.toPx else ViewGroup.LayoutParams.MATCH_PARENT + if (isLayout(TV or EMULATOR)) TV_EP_SIZE_LARGE.toPx else ViewGroup.LayoutParams.MATCH_PARENT binding.episodeLinHolder.layoutParams.width = setWidth binding.episodeHolderLarge.layoutParams.width = setWidth binding.episodeHolder.layoutParams.width = setWidth - val isTrueTv = isTrueTvSettings() binding.apply { downloadButton.isVisible = hasDownloadSupport @@ -249,7 +249,7 @@ class EpisodeAdapter( var isExpanded = false setOnClickListener { - if (isTrueTv) { + if (isLayout(TV)) { clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_DESCRIPTION, card)) } else { isExpanded = !isExpanded @@ -260,7 +260,7 @@ class EpisodeAdapter( } } - if (!isTrueTv) { + if (isLayout(EMULATOR or PHONE)) { episodePoster.setOnClickListener { clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card)) } @@ -275,7 +275,7 @@ class EpisodeAdapter( clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card)) } - if (isTrueTv) { + if (isLayout(TV)) { itemView.isFocusable = true itemView.isFocusableInTouchMode = true //itemView.touchscreenBlocksFocus = false @@ -300,11 +300,9 @@ class EpisodeAdapter( ) : RecyclerView.ViewHolder(binding.root) { @SuppressLint("SetTextI18n") fun bind(card: ResultEpisode) { - val isTrueTv = isTrueTvSettings() - binding.episodeHolder.layoutParams.apply { width = - if (isTvSettings()) TV_EP_SIZE_SMALL.toPx else ViewGroup.LayoutParams.MATCH_PARENT + if (isLayout(TV or EMULATOR)) TV_EP_SIZE_SMALL.toPx else ViewGroup.LayoutParams.MATCH_PARENT } binding.apply { @@ -361,7 +359,7 @@ class EpisodeAdapter( clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card)) } - if (isTrueTv) { + if (isLayout(TV)) { itemView.isFocusable = true itemView.isFocusableInTouchMode = true //itemView.touchscreenBlocksFocus = false diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ImageAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ImageAdapter.kt index ca2934ef..7b7bae43 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ImageAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ImageAdapter.kt @@ -5,7 +5,8 @@ import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.databinding.ResultMiniImageBinding -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout /* class ImageAdapter(context: Context, val resource: Int) : ArrayAdapter(context, resource) { @@ -83,7 +84,7 @@ class ImageAdapter( this.nextFocusUpId = nextFocusUp } if (clickCallback != null) { - if (isTrueTvSettings()) { + if (isLayout(TV)) { isClickable = true isLongClickable = true isFocusable = true 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 2d921626..3263ee93 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 @@ -39,8 +39,10 @@ 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.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.isRtl import com.lagradost.cloudstream3.utils.AppUtils.loadCache @@ -598,7 +600,7 @@ class ResultFragmentTv : Fragment() { } observeNullable(viewModel.subscribeStatus) { isSubscribed -> - binding?.resultSubscribe?.isVisible = isSubscribed != null && requireContext().isEmulatorSettings() + binding?.resultSubscribe?.isVisible = isSubscribed != null && isLayout(EMULATOR) binding?.resultSubscribeButton?.apply { if (isSubscribed == null) return@observeNullable @@ -752,7 +754,7 @@ class ResultFragmentTv : Fragment() { setRecommendations(recommendations, null) } - if (isTrueTvSettings()) { + if (isLayout(TV)) { observe(viewModel.episodeSynopsis) { description -> view.context?.let { ctx -> val builder: AlertDialog.Builder = @@ -878,7 +880,7 @@ class ResultFragmentTv : Fragment() { resultDescription.apply { setTextHtml(d.plotText) setOnClickListener { - if (context.isEmulatorSettings()) { + if (isLayout(EMULATOR)) { isExpanded = !isExpanded maxLines = if (isExpanded) { Integer.MAX_VALUE 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 52e598b2..c90e01d0 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 @@ -928,15 +928,20 @@ class ResultViewModel2 : ViewModel() { ) { val isSubscribed = _subscribeStatus.value ?: return val response = currentResponse ?: return - if (response !is EpisodeResponse) return - val currentId = currentId ?: return + // This might be a bit confusing, but even if the loadresponse is not a EpisodeResponse + // _subscribeStatus might be true. + if (isSubscribed) { removeSubscribedData(currentId) statusChangedCallback?.invoke(false) - _subscribeStatus.postValue(false) + _subscribeStatus.postValue(if (response is EpisodeResponse) false else null) + MainActivity.reloadLibraryEvent(true) } else { + if (response !is EpisodeResponse) { + return + } checkAndWarnDuplicates( context, LibraryListType.SUBSCRIPTIONS, @@ -981,8 +986,8 @@ class ResultViewModel2 : ViewModel() { ) _subscribeStatus.postValue(true) - statusChangedCallback?.invoke(true) + MainActivity.reloadLibraryEvent(true) } } } @@ -2046,12 +2051,15 @@ class ResultViewModel2 : ViewModel() { } private fun postSubscription(loadResponse: LoadResponse) { + val id = loadResponse.getId() + val data = getSubscribedData(id) if (loadResponse.isEpisodeBased()) { - val id = loadResponse.getId() - val data = getSubscribedData(id) updateSubscribedData(id, data, loadResponse as? EpisodeResponse) - val isSubscribed = data != null - _subscribeStatus.postValue(isSubscribed) + _subscribeStatus.postValue(data != null) + } + // lets say that we have subscribed, then we must be able to unsubscribe no matter what + else if (data != null) { + _subscribeStatus.postValue(true) } } @@ -2585,6 +2593,7 @@ class ResultViewModel2 : ViewModel() { override var posterHeaders: Map? = null, override var backgroundPosterUrl: String? = null, override var contentRating: String? = null, + val id : Int?, ) : LoadResponse fun loadSmall(activity: Activity?, searchResponse : SearchResponse) = ioSafe { @@ -2594,7 +2603,7 @@ class ResultViewModel2 : ViewModel() { val api = APIHolder.getApiFromNameNull(searchResponse.apiName) ?: APIHolder.getApiFromUrlNull(searchResponse.url) ?: APIRepository.noneApi val repo = APIRepository(api) val response = LoadResponseFromSearch(name = searchResponse.name, url = searchResponse.url, apiName = api.name, type = searchResponse.type ?: TvType.Others, - posterUrl = searchResponse.posterUrl).apply { + posterUrl = searchResponse.posterUrl, id = searchResponse.id).apply { if (searchResponse is SyncAPI.LibraryItem) { this.plot = searchResponse.plot this.rating = searchResponse.personalRating?.times(100) ?: searchResponse.rating @@ -2606,12 +2615,14 @@ class ResultViewModel2 : ViewModel() { this.tags = searchResponse.tags } } - val mainId = searchResponse.id ?: response.getId() + val mainId = response.getId() postSuccessful( loadResponse = response, mainId = mainId, - apiRepository = repo, updateEpisodes = false, updateFillers = false) + apiRepository = repo, + updateEpisodes = false, + updateFillers = false) } fun load( diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/SelectAdaptor.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SelectAdaptor.kt index 6fe45730..5a23bfc1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/SelectAdaptor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SelectAdaptor.kt @@ -6,7 +6,8 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import com.google.android.material.button.MaterialButton import com.lagradost.cloudstream3.databinding.ResultSelectionBinding -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout typealias SelectData = Pair @@ -72,8 +73,7 @@ class SelectAdaptor(val callback: (Any) -> Unit) : RecyclerView.Adapter Unit ) { - val isTrueTv = isTrueTvSettings() - if (isTrueTv) { + if (isLayout(TV)) { item.isFocusable = true item.isFocusableInTouchMode = true } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt index 243d9f4e..4b4700ce 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt @@ -54,8 +54,9 @@ import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.updateChips import com.lagradost.cloudstream3.ui.home.ParentItemAdapter import com.lagradost.cloudstream3.ui.result.FOCUS_SELF import com.lagradost.cloudstream3.ui.result.setLinearListLayout -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.AppUtils.ownHide import com.lagradost.cloudstream3.utils.AppUtils.ownShow import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus @@ -107,13 +108,16 @@ class SearchFragment : Fragment() { ) bottomSheetDialog?.ownShow() - val layout = if (isTvSettings()) R.layout.fragment_search_tv else R.layout.fragment_search - val root = inflater.inflate(layout, container, false) - // TODO TRYCATCH - binding = FragmentSearchBinding.bind(root) + binding = try { + val layout = if (isLayout(TV or EMULATOR)) R.layout.fragment_search_tv else R.layout.fragment_search + val root = inflater.inflate(layout, container, false) + FragmentSearchBinding.bind(root) + } catch (t : Throwable) { + FragmentSearchBinding.inflate(inflater) + } - return root + return binding?.root } private fun fixGrid() { @@ -369,7 +373,7 @@ class SearchFragment : Fragment() { selectedSearchTypes = DataStoreHelper.searchPreferenceTags.toMutableList() - if (isTrueTvSettings()) { + if (isLayout(TV)) { binding?.searchFilter?.isFocusable = true binding?.searchFilter?.isFocusableInTouchMode = true } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt index 3e33e01a..5b943105 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt @@ -1,6 +1,5 @@ package com.lagradost.cloudstream3.ui.search -import android.app.Activity import android.widget.Toast import com.lagradost.cloudstream3.CommonActivity.activity import com.lagradost.cloudstream3.CommonActivity.showToast @@ -10,7 +9,8 @@ import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_PLAY_FILE import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick import com.lagradost.cloudstream3.ui.download.DownloadClickEvent import com.lagradost.cloudstream3.ui.result.START_ACTION_LOAD_EP -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.PHONE +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper @@ -56,7 +56,7 @@ object SearchHelper { } } SEARCH_ACTION_SHOW_METADATA -> { - if(!isTvSettings()) { // we only want this on phone as UI is not done yet on tv + if(isLayout(PHONE)) { // we only want this on phone as UI is not done yet on tv (activity as? MainActivity?)?.apply { loadPopup(callback.card) } ?: kotlin.run { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt index e1b72b30..d18c0197 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt @@ -17,7 +17,8 @@ import com.lagradost.cloudstream3.SearchQuality import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.isMovieType import com.lagradost.cloudstream3.syncproviders.SyncAPI -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.AppUtils.getNameFull import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual @@ -164,7 +165,7 @@ object SearchResultBuilder { bg.isFocusable = false bg.isFocusableInTouchMode = false - if(!isTrueTvSettings()) { + if(!isLayout(TV)) { bg.setOnClickListener { click(it) } @@ -207,7 +208,7 @@ object SearchResultBuilder { */ - if (isTrueTvSettings()) { + if (isLayout(TV)) { // bg.isFocusable = true // bg.isFocusableInTouchMode = true // bg.touchscreenBlocksFocus = false diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/Globals.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/Globals.kt new file mode 100644 index 00000000..aa513d87 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/Globals.kt @@ -0,0 +1,56 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.app.UiModeManager +import android.content.Context +import android.content.res.Configuration +import android.os.Build +import androidx.preference.PreferenceManager +import com.lagradost.cloudstream3.R + +object Globals { + var beneneCount = 0 + + const val PHONE : Int = 0b001 + const val TV : Int = 0b010 + const val EMULATOR : Int = 0b100 + private const val INVALID = -1 + private var layoutId = INVALID + + private fun Context.getLayoutInt(): Int { + val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) + return settingsManager.getInt(this.getString(R.string.app_layout_key), -1) + } + + private fun Context.isAutoTv(): Boolean { + val uiModeManager = getSystemService(Context.UI_MODE_SERVICE) as UiModeManager? + // AFT = Fire TV + val model = Build.MODEL.lowercase() + return uiModeManager?.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION || Build.MODEL.contains( + "AFT" + ) || model.contains("firestick") || model.contains("fire tv") || model.contains("chromecast") + } + + private fun Context.layoutIntCorrected(): Int { + return when(getLayoutInt()) { + -1 -> if (isAutoTv()) TV else PHONE + 0 -> PHONE + 1 -> TV + 2 -> EMULATOR + else -> PHONE + } + } + + fun Context.updateTv() { + layoutId = layoutIntCorrected() + } + + /** Returns true if the layout is any of the flags, + * so isLayout(TV or EMULATOR) is a valid statement for checking if the layout is in the emulator + * or tv. Auto will become the "TV" or the "PHONE" layout. + * + * Valid flags are: PHONE, TV, EMULATOR + * */ + fun isLayout(flags: Int) : Boolean { + return (layoutId and flags) != 0 + } +} 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 14f11c1e..298431ee 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 @@ -29,8 +29,10 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.simklAp import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI import com.lagradost.cloudstream3.syncproviders.OAuth2API +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar @@ -76,7 +78,7 @@ class SettingsAccount : PreferenceFragmentCompat() { showAccountSwitch(activity, api) } - if (isTvSettings()) { + if (isLayout(TV or EMULATOR)) { binding.accountSwitchAccount.requestFocus() } } @@ -140,7 +142,7 @@ class SettingsAccount : PreferenceFragmentCompat() { binding.loginUsernameInput to api.requiresUsername ) - if (isTvSettings()) { + if (isLayout(TV or EMULATOR)) { visibilityMap.forEach { (input, isVisible) -> input.isVisible = isVisible diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt index 4ab7c9b7..caff5df6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt @@ -1,9 +1,5 @@ package com.lagradost.cloudstream3.ui.settings -import android.app.UiModeManager -import android.content.Context -import android.content.res.Configuration -import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -16,18 +12,19 @@ import androidx.core.view.updateLayoutParams import androidx.fragment.app.Fragment import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat -import androidx.preference.PreferenceManager import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.MaterialToolbar -import com.lagradost.cloudstream3.AcraApplication.Companion.context import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.databinding.MainSettingsBinding import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.accountManagers import com.lagradost.cloudstream3.ui.home.HomeFragment import com.lagradost.cloudstream3.ui.result.txt +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout +import com.lagradost.cloudstream3.utils.UIHelper import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper -import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.toPx @@ -35,10 +32,6 @@ import java.io.File class SettingsFragment : Fragment() { companion object { - var beneneCount = 0 - - private var isTv: Boolean = false - private var isTrueTv: Boolean = false fun PreferenceFragmentCompat?.getPref(id: Int): Preference? { if (this == null) return null @@ -55,12 +48,12 @@ class SettingsFragment : Fragment() { * On TV you cannot properly scroll to the bottom of settings, this fixes that. * */ fun PreferenceFragmentCompat.setPaddingBottom() { - if (isTvSettings()) { + if (isLayout(TV or EMULATOR)) { listView?.setPadding(0, 0, 0, 100.toPx) } } fun PreferenceFragmentCompat.setToolBarScrollFlags() { - if (isTvSettings()) { + if (isLayout(TV or EMULATOR)) { val settingsAppbar = view?.findViewById(R.id.settings_toolbar) settingsAppbar?.updateLayoutParams { @@ -69,7 +62,7 @@ class SettingsFragment : Fragment() { } } fun Fragment?.setToolBarScrollFlags() { - if (isTvSettings()) { + if (isLayout(TV or EMULATOR)) { val settingsAppbar = this?.view?.findViewById(R.id.settings_toolbar) settingsAppbar?.updateLayoutParams { @@ -88,7 +81,7 @@ class SettingsFragment : Fragment() { activity?.onBackPressedDispatcher?.onBackPressed() } } - fixPaddingStatusbar(settingsToolbar) + UIHelper.fixPaddingStatusbar(settingsToolbar) } fun Fragment?.setUpToolbar(@StringRes title: Int) { @@ -103,7 +96,7 @@ class SettingsFragment : Fragment() { activity?.onBackPressedDispatcher?.onBackPressed() } } - fixPaddingStatusbar(settingsToolbar) + UIHelper.fixPaddingStatusbar(settingsToolbar) } fun getFolderSize(dir: File): Long { @@ -119,60 +112,7 @@ class SettingsFragment : Fragment() { return size } - - private fun Context.getLayoutInt(): Int { - val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) - return settingsManager.getInt(this.getString(R.string.app_layout_key), -1) - } - - private fun Context.isTvSettings(): Boolean { - var value = getLayoutInt() - if (value == -1) { - value = if (isAutoTv()) 1 else 0 - } - return value == 1 || value == 2 - } - - private fun Context.isTrueTvSettings(): Boolean { - var value = getLayoutInt() - if (value == -1) { - value = if (isAutoTv()) 1 else 0 - } - return value == 1 - } - - fun Context.updateTv() { - isTrueTv = isTrueTvSettings() - isTv = isTvSettings() - } - - fun isTrueTvSettings(): Boolean { - return isTrueTv - } - - fun isTvSettings(): Boolean { - return isTv - } - - fun Context.isEmulatorSettings(): Boolean { - return getLayoutInt() == 2 - } - - // phone exclusive - fun isTruePhone(): Boolean { - return !isTrueTvSettings() && !isTvSettings() && context?.isEmulatorSettings() != true - } - - private fun Context.isAutoTv(): Boolean { - val uiModeManager = getSystemService(Context.UI_MODE_SERVICE) as UiModeManager? - // AFT = Fire TV - val model = Build.MODEL.lowercase() - return uiModeManager?.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION || Build.MODEL.contains( - "AFT" - ) || model.contains("firestick") || model.contains("fire tv") || model.contains("chromecast") - } } - override fun onDestroyView() { binding = null super.onDestroyView() @@ -197,8 +137,6 @@ class SettingsFragment : Fragment() { // used to debug leaks showToast(activity,"${VideoDownloadManager.downloadStatusEvent.size} : ${VideoDownloadManager.downloadProgressEvent.size}") - val isTrueTv = isTrueTvSettings() - for (syncApi in accountManagers) { val login = syncApi.loginInfo() val pic = login?.profilePicture ?: continue @@ -226,7 +164,7 @@ class SettingsFragment : Fragment() { setOnClickListener { navigate(navigationId) } - if (isTrueTv) { + if (isLayout(TV)) { isFocusable = true isFocusableInTouchMode = true } @@ -234,7 +172,7 @@ class SettingsFragment : Fragment() { } // Default focus on TV - if (isTrueTv) { + if (isLayout(TV)) { settingsGeneral.requestFocus() } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt index abd7fb9a..6cf00375 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt @@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.network.initClient import com.lagradost.cloudstream3.ui.EasterEggMonke +import com.lagradost.cloudstream3.ui.settings.Globals.beneneCount import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags @@ -378,30 +379,30 @@ class SettingsGeneral : PreferenceFragmentCompat() { } try { - SettingsFragment.beneneCount = + beneneCount = settingsManager.getInt(getString(R.string.benene_count), 0) getPref(R.string.benene_count)?.let { pref -> pref.summary = - if (SettingsFragment.beneneCount <= 0) getString(R.string.benene_count_text_none) else getString( + if (beneneCount <= 0) getString(R.string.benene_count_text_none) else getString( R.string.benene_count_text ).format( - SettingsFragment.beneneCount + beneneCount ) pref.setOnPreferenceClickListener { try { - SettingsFragment.beneneCount++ - if (SettingsFragment.beneneCount%20 == 0) { + beneneCount++ + if (beneneCount%20 == 0) { val intent = Intent(context, EasterEggMonke::class.java) startActivity(intent) } settingsManager.edit().putInt( getString(R.string.benene_count), - SettingsFragment.beneneCount + beneneCount ) .apply() it.summary = - getString(R.string.benene_count_text).format(SettingsFragment.beneneCount) + getString(R.string.benene_count_text).format(beneneCount) } catch (e: Exception) { logError(e) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt index 63053236..cc14e761 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt @@ -9,11 +9,11 @@ import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.SearchQuality import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.search.SearchResultBuilder +import com.lagradost.cloudstream3.ui.settings.Globals.updateTv import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateTv import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt index f0b8a0bd..ebd3260f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt @@ -29,7 +29,8 @@ import com.lagradost.cloudstream3.plugins.RepositoryManager import com.lagradost.cloudstream3.ui.result.FOCUS_SELF import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.result.setText -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.utils.AppUtils.downloadAllPluginsDialog @@ -97,7 +98,7 @@ class ExtensionsFragment : Fragment() { nextLeft = R.id.nav_rail_view ) - if (!isTrueTvSettings()) + if (!isLayout(TV)) binding?.addRepoButton?.let { button -> button.post { setPadding( @@ -286,7 +287,7 @@ class ExtensionsFragment : Fragment() { } } - val isTv = isTrueTvSettings() + val isTv = isLayout(TV) binding?.apply { addRepoButton.isGone = isTv addRepoButtonImageviewHolder.isVisible = isTv diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt index c3fb4fc2..04da30c7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt @@ -17,7 +17,8 @@ import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.VotingApi.getVotes import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.result.txt -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.main @@ -44,7 +45,7 @@ class PluginAdapter( private val plugins: MutableList = mutableListOf() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - val layout = if(isTrueTvSettings()) R.layout.repository_item_tv else R.layout.repository_item + val layout = if(isLayout(TV)) R.layout.repository_item_tv else R.layout.repository_item val inflated = LayoutInflater.from(parent.context).inflate(layout, parent, false) return PluginViewHolder( diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt index c5256ffa..acfbc584 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt @@ -17,7 +17,9 @@ import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips import com.lagradost.cloudstream3.ui.result.FOCUS_SELF import com.lagradost.cloudstream3.ui.result.setLinearListLayout -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.ui.settings.appLanguages @@ -155,7 +157,7 @@ class PluginsFragment : Fragment() { pluginViewModel.handlePluginAction(activity, url, it, isLocal) } - if (isTvSettings()) { + if (isLayout(TV or EMULATOR)) { // Scrolling down does not reveal the whole RecyclerView on TV, add to bypass that. binding?.pluginRecyclerView?.setPadding(0, 0, 0, 200.toPx) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/RepoAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/RepoAdapter.kt index ee78886e..faf6d38b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/RepoAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/RepoAdapter.kt @@ -10,7 +10,8 @@ import com.lagradost.cloudstream3.databinding.RepositoryItemBinding import com.lagradost.cloudstream3.databinding.RepositoryItemTvBinding import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES import com.lagradost.cloudstream3.ui.result.txt -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper class RepoAdapter( @@ -23,7 +24,7 @@ class RepoAdapter( private val repositories: MutableList = mutableListOf() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - val layout = if (isTrueTvSettings()) RepositoryItemTvBinding.inflate( + val layout = if (isLayout(TV)) RepositoryItemTvBinding.inflate( LayoutInflater.from(parent.context), parent, false diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/testing/TestFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/testing/TestFragment.kt index 3fbd1131..7878afaa 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/testing/TestFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/testing/TestFragment.kt @@ -11,7 +11,8 @@ import com.lagradost.cloudstream3.databinding.FragmentTestingBinding import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observeNullable -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar @@ -62,7 +63,7 @@ class TestFragment : Fragment() { } } - if (isTrueTvSettings()) { + if (isLayout(TV)) { providerTest.playPauseButton?.isFocusableInTouchMode = true providerTest.playPauseButton?.requestFocus() } @@ -75,7 +76,7 @@ class TestFragment : Fragment() { fun focusRecyclerView() { // Hack to make it possible to focus the recyclerview. - if (isTrueTvSettings()) { + if (isLayout(TV)) { providerTestRecyclerView.requestFocus() providerTestAppbar.setExpanded(false, true) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/ChromecastSubtitlesFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/ChromecastSubtitlesFragment.kt index 71fac2ed..bb9558b8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/ChromecastSubtitlesFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/ChromecastSubtitlesFragment.kt @@ -13,8 +13,8 @@ import android.view.ViewGroup import android.widget.TextView import android.widget.Toast import androidx.fragment.app.Fragment -import com.fasterxml.jackson.annotation.JsonProperty import androidx.media3.common.text.Cue +import com.fasterxml.jackson.annotation.JsonProperty import com.google.android.gms.cast.TextTrackStyle import com.google.android.gms.cast.TextTrackStyle.* import com.jaredrummler.android.colorpicker.ColorPickerDialog @@ -24,7 +24,9 @@ import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.databinding.ChromecastSubtitleSettingsBinding -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.Event import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog @@ -173,7 +175,7 @@ class ChromecastSubtitlesFragment : Fragment() { state = getCurrentSavedStyle() context?.updateState() - val isTvSettings = isTvSettings() + val isTvSettings = isLayout(TV or EMULATOR) fun View.setFocusableInTv() { this.isFocusableInTouchMode = isTvSettings diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt index 83521873..1466afed 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt @@ -28,7 +28,9 @@ import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.databinding.SubtitleSettingsBinding -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.Event import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog @@ -252,7 +254,7 @@ class SubtitlesFragment : Fragment() { state = getCurrentSavedStyle() context?.updateState() - val isTvTrueSettings = isTrueTvSettings() + val isTvTrueSettings = isLayout(TV) fun View.setFocusableInTv() { this.isFocusableInTouchMode = isTvTrueSettings diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt index 1be966b6..ff27b192 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt @@ -61,8 +61,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStri import com.lagradost.cloudstream3.syncproviders.providers.Kitsu import com.lagradost.cloudstream3.ui.WebviewFragment import com.lagradost.cloudstream3.ui.result.ResultFragment -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals import com.lagradost.cloudstream3.ui.settings.extensions.PluginsViewModel.Companion.downloadAll import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData import com.lagradost.cloudstream3.utils.Coroutines.ioSafe @@ -78,7 +77,6 @@ import okhttp3.Cache import java.io.* import java.net.URL import java.net.URLDecoder -import kotlin.system.measureTimeMillis object AppUtils { fun RecyclerView.setMaxViewPoolSize(maxViewTypeId: Int, maxPoolSize: Int) { @@ -583,7 +581,7 @@ object AppUtils { //private val viewModel: ResultViewModel by activityViewModels() private fun getResultsId(): Int { - return if (isTvSettings()) { + return if (Globals.isLayout(Globals.TV or Globals.EMULATOR)) { R.id.global_to_navigation_results_tv } else { R.id.global_to_navigation_results_phone @@ -707,7 +705,7 @@ object AppUtils { * Sets the focus to the negative button when in TV and Emulator layout. **/ fun AlertDialog.setDefaultFocus(buttonFocus: Int = DialogInterface.BUTTON_NEGATIVE) { - if (!isTvSettings()) return + if (!Globals.isLayout(Globals.TV or Globals.EMULATOR)) return this.getButton(buttonFocus).run { isFocusableInTouchMode = true requestFocus() diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt index f34e7238..70edf80c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt @@ -21,7 +21,9 @@ import com.lagradost.cloudstream3.databinding.BottomInputDialogBinding import com.lagradost.cloudstream3.databinding.BottomSelectionDialogBinding import com.lagradost.cloudstream3.databinding.BottomTextDialogBinding import com.lagradost.cloudstream3.databinding.OptionsPopupTvBinding -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes import com.lagradost.cloudstream3.utils.UIHelper.setImage @@ -54,7 +56,7 @@ object SingleSelectionHelper { ) { if (this == null) return - if (isTvSettings()) { + if (isLayout(TV or EMULATOR)) { val binding = OptionsPopupTvBinding.inflate(layoutInflater) val dialog = AlertDialog.Builder(this, R.style.AlertDialogCustom) .setView(binding.root) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt index a3b5c1e8..eedb626a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt @@ -69,8 +69,9 @@ import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.result.UiImage import com.lagradost.cloudstream3.ui.result.UiText import com.lagradost.cloudstream3.ui.result.txt -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.Globals +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import jp.wasabeef.glide.transformations.BlurTransformation import kotlin.math.roundToInt @@ -468,7 +469,7 @@ object UIHelper { } fun Context.getStatusBarHeight(): Int { - if (isTvSettings()) { + if (isLayout(Globals.TV or EMULATOR)) { return 0 } @@ -570,7 +571,7 @@ object UIHelper { (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) //} - changeStatusBarState(isEmulatorSettings()) + changeStatusBarState(isLayout(EMULATOR)) } fun Context.shouldShowPIPMode(isInPlayer: Boolean): Boolean { diff --git a/app/src/main/res/layout/bottom_resultview_preview.xml b/app/src/main/res/layout/bottom_resultview_preview.xml index 4a64114e..3372fe7b 100644 --- a/app/src/main/res/layout/bottom_resultview_preview.xml +++ b/app/src/main/res/layout/bottom_resultview_preview.xml @@ -41,22 +41,34 @@ android:layout_marginStart="10dp" android:orientation="vertical"> - + tools:text="The Perfect Run" /> - + - + + + android:layout_height="wrap_content" + android:orientation="horizontal" + android:padding="7dp"> + tools:visibility="visible" /> + tools:visibility="visible" />