mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Merge remote-tracking branch 'origin/master' into agp
This commit is contained in:
commit
ef6d270ac2
79 changed files with 1845 additions and 1204 deletions
|
@ -62,7 +62,7 @@ android {
|
||||||
targetSdk = 33 /* Android 14 is Fu*ked
|
targetSdk = 33 /* Android 14 is Fu*ked
|
||||||
^ https://developer.android.com/about/versions/14/behavior-changes-14#safer-dynamic-code-loading*/
|
^ https://developer.android.com/about/versions/14/behavior-changes-14#safer-dynamic-code-loading*/
|
||||||
versionCode = 63
|
versionCode = 63
|
||||||
versionName = "4.3.1"
|
versionName = "4.3.2"
|
||||||
|
|
||||||
resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}")
|
resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}")
|
||||||
resValue("string", "commit_hash", "git rev-parse --short HEAD".execute() ?: "")
|
resValue("string", "commit_hash", "git rev-parse --short HEAD".execute() ?: "")
|
||||||
|
@ -181,7 +181,7 @@ dependencies {
|
||||||
// Design & UI
|
// Design & UI
|
||||||
implementation("jp.wasabeef:glide-transformations:4.3.0")
|
implementation("jp.wasabeef:glide-transformations:4.3.0")
|
||||||
implementation("androidx.preference:preference-ktx:1.2.1")
|
implementation("androidx.preference:preference-ktx:1.2.1")
|
||||||
implementation("com.google.android.material:material:1.10.0")
|
implementation("com.google.android.material:material:1.11.0")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
|
|
||||||
|
@ -222,6 +222,7 @@ dependencies {
|
||||||
implementation("androidx.palette:palette-ktx:1.0.0") // Palette For Images -> Colors
|
implementation("androidx.palette:palette-ktx:1.0.0") // Palette For Images -> Colors
|
||||||
implementation("androidx.tvprovider:tvprovider:1.0.0")
|
implementation("androidx.tvprovider:tvprovider:1.0.0")
|
||||||
implementation("com.github.discord:OverlappingPanels:0.1.5") // Gestures
|
implementation("com.github.discord:OverlappingPanels:0.1.5") // Gestures
|
||||||
|
implementation ("androidx.biometric:biometric:1.2.0-alpha05") // Fingerprint Authentication
|
||||||
implementation("com.github.rubensousa:previewseekbar-media3:1.1.1.0") // SeekBar Preview
|
implementation("com.github.rubensousa:previewseekbar-media3:1.1.1.0") // SeekBar Preview
|
||||||
|
|
||||||
// Extensions & Other Libs
|
// Extensions & Other Libs
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> <!-- Used for Android TV watch next -->
|
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> <!-- Used for Android TV watch next -->
|
||||||
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" /> <!-- Used for updates without prompt -->
|
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" /> <!-- Used for updates without prompt -->
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- Used for update service -->
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- Used for update service -->
|
||||||
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||||
|
|
||||||
<!-- Required for getting arbitrary Aniyomi packages -->
|
<!-- Required for getting arbitrary Aniyomi packages -->
|
||||||
|
|
|
@ -11,7 +11,9 @@ import androidx.fragment.app.FragmentActivity
|
||||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
|
||||||
import com.lagradost.cloudstream3.plugins.PluginManager
|
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.AppUtils.openBrowser
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.runOnMainThread
|
import com.lagradost.cloudstream3.utils.Coroutines.runOnMainThread
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||||
|
@ -31,7 +33,6 @@ import org.acra.sender.ReportSenderFactory
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
import java.lang.Exception
|
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
@ -211,7 +212,7 @@ class AcraApplication : Application() {
|
||||||
fun openBrowser(url: String, activity: FragmentActivity?) {
|
fun openBrowser(url: String, activity: FragmentActivity?) {
|
||||||
openBrowser(
|
openBrowser(
|
||||||
url,
|
url,
|
||||||
isTvSettings(),
|
isLayout(TV or EMULATOR),
|
||||||
activity?.supportFragmentManager?.fragments?.lastOrNull()
|
activity?.supportFragmentManager?.fragments?.lastOrNull()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,9 @@ import android.util.DisplayMetrics
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.NO_ID
|
import android.view.View.NO_ID
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
@ -31,11 +29,12 @@ import com.google.android.material.chip.ChipGroup
|
||||||
import com.google.android.material.navigationrail.NavigationRailView
|
import com.google.android.material.navigationrail.NavigationRailView
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||||
|
import com.lagradost.cloudstream3.databinding.ToastBinding
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.ui.player.PlayerEventType
|
import com.lagradost.cloudstream3.ui.player.PlayerEventType
|
||||||
import com.lagradost.cloudstream3.ui.result.ResultFragment
|
import com.lagradost.cloudstream3.ui.result.ResultFragment
|
||||||
import com.lagradost.cloudstream3.ui.result.UiText
|
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.AppUtils.isRtl
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
import com.lagradost.cloudstream3.utils.Event
|
import com.lagradost.cloudstream3.utils.Event
|
||||||
|
@ -99,8 +98,7 @@ object CommonActivity {
|
||||||
var playerEventListener: ((PlayerEventType) -> Unit)? = null
|
var playerEventListener: ((PlayerEventType) -> Unit)? = null
|
||||||
var keyEventListener: ((Pair<KeyEvent?, Boolean>) -> Boolean)? = null
|
var keyEventListener: ((Pair<KeyEvent?, Boolean>) -> Boolean)? = null
|
||||||
|
|
||||||
|
private var currentToast: Toast? = null
|
||||||
var currentToast: Toast? = null
|
|
||||||
|
|
||||||
fun showToast(@StringRes message: Int, duration: Int? = null) {
|
fun showToast(@StringRes message: Int, duration: Int? = null) {
|
||||||
val act = activity ?: return
|
val act = activity ?: return
|
||||||
|
@ -156,25 +154,19 @@ object CommonActivity {
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logError(e)
|
logError(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val inflater =
|
val binding = ToastBinding.inflate(act.layoutInflater)
|
||||||
act.getSystemService(AppCompatActivity.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
binding.text.text = message.trim()
|
||||||
|
|
||||||
val layout: View = inflater.inflate(
|
|
||||||
R.layout.toast,
|
|
||||||
act.findViewById<View>(R.id.toast_layout_root) as ViewGroup?
|
|
||||||
)
|
|
||||||
|
|
||||||
val text = layout.findViewById(R.id.text) as TextView
|
|
||||||
text.text = message.trim()
|
|
||||||
|
|
||||||
|
// custom toasts are deprecated and won't appear when cs3 sets minSDK to api30 (A11)
|
||||||
val toast = Toast(act)
|
val toast = Toast(act)
|
||||||
toast.setGravity(Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM, 0, 5.toPx)
|
|
||||||
toast.duration = duration ?: Toast.LENGTH_SHORT
|
toast.duration = duration ?: Toast.LENGTH_SHORT
|
||||||
toast.view = layout
|
toast.setGravity(Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM, 0, 5.toPx)
|
||||||
//https://github.com/PureWriter/ToastCompat
|
toast.view = binding.root
|
||||||
toast.show()
|
|
||||||
currentToast = toast
|
currentToast = toast
|
||||||
|
toast.show()
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logError(e)
|
logError(e)
|
||||||
}
|
}
|
||||||
|
@ -319,6 +311,7 @@ object CommonActivity {
|
||||||
"Banana" -> R.style.OverlayPrimaryColorBanana
|
"Banana" -> R.style.OverlayPrimaryColorBanana
|
||||||
"Party" -> R.style.OverlayPrimaryColorParty
|
"Party" -> R.style.OverlayPrimaryColorParty
|
||||||
"Pink" -> R.style.OverlayPrimaryColorPink
|
"Pink" -> R.style.OverlayPrimaryColorPink
|
||||||
|
"Lavender" -> R.style.OverlayPrimaryColorLavender
|
||||||
"Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
"Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||||
R.style.OverlayPrimaryColorMonet else R.style.OverlayPrimaryColorNormal
|
R.style.OverlayPrimaryColorMonet else R.style.OverlayPrimaryColorNormal
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.simklAp
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncIdName
|
import com.lagradost.cloudstream3.syncproviders.SyncIdName
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.SimklApi
|
import com.lagradost.cloudstream3.syncproviders.providers.SimklApi
|
||||||
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
||||||
|
import com.lagradost.cloudstream3.ui.result.ResultViewModel2
|
||||||
import com.lagradost.cloudstream3.utils.*
|
import com.lagradost.cloudstream3.utils.*
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.mainWork
|
import com.lagradost.cloudstream3.utils.Coroutines.mainWork
|
||||||
|
@ -119,7 +120,8 @@ object APIHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun LoadResponse.getId(): Int {
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,6 +28,7 @@ import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
|
import androidx.core.view.isInvisible
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.marginStart
|
import androidx.core.view.marginStart
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
@ -85,6 +86,7 @@ import com.lagradost.cloudstream3.plugins.PluginManager
|
||||||
import com.lagradost.cloudstream3.plugins.PluginManager.loadAllOnlinePlugins
|
import com.lagradost.cloudstream3.plugins.PluginManager.loadAllOnlinePlugins
|
||||||
import com.lagradost.cloudstream3.plugins.PluginManager.loadSinglePlugin
|
import com.lagradost.cloudstream3.plugins.PluginManager.loadSinglePlugin
|
||||||
import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver
|
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.OAuth2Apis
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.accountManagers
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.accountManagers
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appString
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appString
|
||||||
|
@ -111,10 +113,11 @@ import com.lagradost.cloudstream3.ui.result.setText
|
||||||
import com.lagradost.cloudstream3.ui.result.txt
|
import com.lagradost.cloudstream3.ui.result.txt
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchFragment
|
import com.lagradost.cloudstream3.ui.search.SearchFragment
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
|
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
|
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
import com.lagradost.cloudstream3.ui.settings.Globals.TV
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateTv
|
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.settings.SettingsGeneral
|
||||||
import com.lagradost.cloudstream3.ui.setup.HAS_DONE_SETUP_KEY
|
import com.lagradost.cloudstream3.ui.setup.HAS_DONE_SETUP_KEY
|
||||||
import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions
|
import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions
|
||||||
|
@ -131,11 +134,15 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
||||||
import com.lagradost.cloudstream3.utils.BackupUtils.backup
|
import com.lagradost.cloudstream3.utils.BackupUtils.backup
|
||||||
import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup
|
import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup
|
||||||
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator
|
||||||
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.deviceHasPasswordPinLock
|
||||||
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.startBiometricAuthentication
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.accounts
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching
|
||||||
import com.lagradost.cloudstream3.utils.Event
|
import com.lagradost.cloudstream3.utils.Event
|
||||||
import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate
|
import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate
|
||||||
|
@ -166,7 +173,6 @@ import kotlin.math.absoluteValue
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
|
||||||
//https://github.com/videolan/vlc-android/blob/3706c4be2da6800b3d26344fc04fab03ffa4b860/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt#L1898
|
//https://github.com/videolan/vlc-android/blob/3706c4be2da6800b3d26344fc04fab03ffa4b860/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt#L1898
|
||||||
//https://wiki.videolan.org/Android_Player_Intents/
|
//https://wiki.videolan.org/Android_Player_Intents/
|
||||||
|
|
||||||
|
@ -285,7 +291,8 @@ var app = Requests(responseParser = object : ResponseParser {
|
||||||
defaultHeaders = mapOf("user-agent" to USER_AGENT)
|
defaultHeaders = mapOf("user-agent" to USER_AGENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
class MainActivity : AppCompatActivity(), ColorPickerDialogListener,
|
||||||
|
BiometricAuthenticator.BiometricAuthCallback {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "MAINACT"
|
const val TAG = "MAINACT"
|
||||||
const val ANIMATED_OUTLINE: Boolean = false
|
const val ANIMATED_OUTLINE: Boolean = false
|
||||||
|
@ -331,10 +338,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
|
|
||||||
// kinda shitty solution, but cant com main->home otherwise for popups
|
// kinda shitty solution, but cant com main->home otherwise for popups
|
||||||
val bookmarksUpdatedEvent = Event<Boolean>()
|
val bookmarksUpdatedEvent = Event<Boolean>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by DataStoreHelper to fully reload home when switching accounts
|
* Used by DataStoreHelper to fully reload home when switching accounts
|
||||||
*/
|
*/
|
||||||
val reloadHomeEvent = Event<Boolean>()
|
val reloadHomeEvent = Event<Boolean>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by DataStoreHelper to fully reload library when switching accounts
|
* Used by DataStoreHelper to fully reload library when switching accounts
|
||||||
*/
|
*/
|
||||||
|
@ -549,7 +558,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
binding?.navHostFragment?.apply {
|
binding?.navHostFragment?.apply {
|
||||||
val params = layoutParams as ConstraintLayout.LayoutParams
|
val params = layoutParams as ConstraintLayout.LayoutParams
|
||||||
val push =
|
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()) {
|
if (!this.isLtr()) {
|
||||||
params.setMargins(
|
params.setMargins(
|
||||||
|
@ -576,7 +585,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
Configuration.ORIENTATION_PORTRAIT -> {
|
Configuration.ORIENTATION_PORTRAIT -> {
|
||||||
isTvSettings()
|
isLayout(TV or EMULATOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -783,6 +792,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
|
|
||||||
lateinit var viewModel: ResultViewModel2
|
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*/
|
/** 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? {
|
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
|
||||||
|
@ -1167,11 +1177,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
|
|
||||||
// just in case, MAIN SHOULD *NEVER* BOOT LOOP CRASH
|
// just in case, MAIN SHOULD *NEVER* BOOT LOOP CRASH
|
||||||
binding = try {
|
binding = try {
|
||||||
if (isTvSettings()) {
|
if (isLayout(TV or EMULATOR)) {
|
||||||
val newLocalBinding = ActivityMainTvBinding.inflate(layoutInflater, null, false)
|
val newLocalBinding = ActivityMainTvBinding.inflate(layoutInflater, null, false)
|
||||||
setContentView(newLocalBinding.root)
|
setContentView(newLocalBinding.root)
|
||||||
|
|
||||||
if(isTrueTvSettings() && ANIMATED_OUTLINE) {
|
if (isLayout(TV) && ANIMATED_OUTLINE) {
|
||||||
TvFocus.focusOutline = WeakReference(newLocalBinding.focusOutline)
|
TvFocus.focusOutline = WeakReference(newLocalBinding.focusOutline)
|
||||||
newLocalBinding.root.viewTreeObserver.addOnScrollChangedListener {
|
newLocalBinding.root.viewTreeObserver.addOnScrollChangedListener {
|
||||||
TvFocus.updateFocusView(TvFocus.lastFocus.get(), same = true)
|
TvFocus.updateFocusView(TvFocus.lastFocus.get(), same = true)
|
||||||
|
@ -1183,14 +1193,30 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
newLocalBinding.focusOutline.isVisible = false
|
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,
|
||||||
|
R.id.home_preview_info_btt,
|
||||||
|
R.id.home_preview_hidden_next_focus,
|
||||||
|
R.id.home_preview_hidden_prev_focus,
|
||||||
|
R.id.result_play_movie_button,
|
||||||
|
R.id.result_play_series_button,
|
||||||
|
R.id.result_resume_series_button,
|
||||||
|
R.id.result_play_trailer_button,
|
||||||
|
R.id.result_bookmark_Button,
|
||||||
|
R.id.result_favorite_Button,
|
||||||
|
R.id.result_subscribe_Button,
|
||||||
|
R.id.result_search_Button,
|
||||||
|
R.id.result_episodes_show_button,
|
||||||
|
)
|
||||||
|
|
||||||
newLocalBinding.root.viewTreeObserver.addOnGlobalFocusChangeListener { _, newFocus ->
|
newLocalBinding.root.viewTreeObserver.addOnGlobalFocusChangeListener { _, newFocus ->
|
||||||
|
if (exceptionButtons.contains(newFocus?.id)) return@addOnGlobalFocusChangeListener
|
||||||
centerView(newFocus)
|
centerView(newFocus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ActivityMainBinding.bind(newLocalBinding.root) // this may crash
|
ActivityMainBinding.bind(newLocalBinding.root) // this may crash
|
||||||
} else {
|
} else {
|
||||||
val newLocalBinding = ActivityMainBinding.inflate(layoutInflater, null, false)
|
val newLocalBinding = ActivityMainBinding.inflate(layoutInflater, null, false)
|
||||||
|
@ -1202,7 +1228,27 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
null
|
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
|
||||||
|
|
||||||
|
if (isLayout(PHONE) && authEnabled && noAccounts) {
|
||||||
|
if (deviceHasPasswordPinLock(this)) {
|
||||||
|
startBiometricAuthentication(this, R.string.biometric_authentication_title, false)
|
||||||
|
|
||||||
|
BiometricAuthenticator.promptInfo?.let { promt ->
|
||||||
|
BiometricAuthenticator.biometricPrompt?.authenticate(promt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hide background while authenticating, Sorry moms & dads 🙏
|
||||||
|
binding?.navHostFragment?.isInvisible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Automatically enable jsdelivr if cant connect to raw.githubusercontent.com
|
// Automatically enable jsdelivr if cant connect to raw.githubusercontent.com
|
||||||
if (this.getKey<Boolean>(getString(R.string.jsdelivr_proxy_key)) == null && isNetworkAvailable()) {
|
if (this.getKey<Boolean>(getString(R.string.jsdelivr_proxy_key)) == null && isNetworkAvailable()) {
|
||||||
|
@ -1322,12 +1368,41 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
observe(viewModel.watchStatus) { state ->
|
fun setSubscribeStatus(state: Boolean?) {
|
||||||
setWatchStatus(state)
|
bottomPreviewBinding?.resultviewPreviewSubscribe?.apply {
|
||||||
|
if (state != null) {
|
||||||
|
val drawable = if (state) {
|
||||||
|
R.drawable.ic_baseline_notifications_active_24
|
||||||
|
} else {
|
||||||
|
R.drawable.baseline_notifications_none_24
|
||||||
}
|
}
|
||||||
observe(syncViewModel.userData) { status ->
|
setImageResource(drawable)
|
||||||
setUserData(status)
|
|
||||||
}
|
}
|
||||||
|
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 ->
|
observeNullable(viewModel.page) { resource ->
|
||||||
if (resource == null) {
|
if (resource == null) {
|
||||||
|
@ -1370,6 +1445,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
|
|
||||||
setUserData(syncViewModel.userData.value)
|
setUserData(syncViewModel.userData.value)
|
||||||
setWatchStatus(viewModel.watchStatus.value)
|
setWatchStatus(viewModel.watchStatus.value)
|
||||||
|
setSubscribeStatus(viewModel.subscribeStatus.value)
|
||||||
|
|
||||||
resultviewPreviewBookmark.setOnClickListener {
|
resultviewPreviewBookmark.setOnClickListener {
|
||||||
//viewModel.updateWatchStatus(WatchType.PLANTOWATCH)
|
//viewModel.updateWatchStatus(WatchType.PLANTOWATCH)
|
||||||
|
@ -1388,7 +1464,9 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} 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(
|
this@MainActivity.showBottomDialog(
|
||||||
SyncWatchType.values().map { getString(it.stringRes) }.toList(),
|
SyncWatchType.values().map { getString(it.stringRes) }.toList(),
|
||||||
|
@ -1431,7 +1509,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isTvSettings()) // dont want this clickable on tv layout
|
if (isLayout(PHONE)) // dont want this clickable on tv layout
|
||||||
resultviewPreviewDescription.setOnClickListener { view ->
|
resultviewPreviewDescription.setOnClickListener { view ->
|
||||||
view.context?.let { ctx ->
|
view.context?.let { ctx ->
|
||||||
val builder: AlertDialog.Builder =
|
val builder: AlertDialog.Builder =
|
||||||
|
@ -1506,7 +1584,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTvSettings()) {
|
if (isLayout(TV or EMULATOR)) {
|
||||||
if (navDestination.matchDestination(R.id.navigation_home)) {
|
if (navDestination.matchDestination(R.id.navigation_home)) {
|
||||||
attachBackPressedCallback()
|
attachBackPressedCallback()
|
||||||
} else detachBackPressedCallback()
|
} else detachBackPressedCallback()
|
||||||
|
@ -1542,7 +1620,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
itemRippleColor = rippleColor
|
itemRippleColor = rippleColor
|
||||||
itemActiveIndicatorColor = rippleColor
|
itemActiveIndicatorColor = rippleColor
|
||||||
setupWithNavController(navController)
|
setupWithNavController(navController)
|
||||||
if (isTvSettings()) {
|
if (isLayout(TV or EMULATOR)) {
|
||||||
background?.alpha = 200
|
background?.alpha = 200
|
||||||
} else {
|
} else {
|
||||||
background?.alpha = 255
|
background?.alpha = 255
|
||||||
|
@ -1743,6 +1821,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Biometric stuff **/
|
||||||
|
override fun onAuthenticationSuccess() {
|
||||||
|
// make background (nav host fragment) visible again
|
||||||
|
binding?.navHostFragment?.isInvisible = false
|
||||||
|
}
|
||||||
|
|
||||||
private var backPressedCallback: OnBackPressedCallback? = null
|
private var backPressedCallback: OnBackPressedCallback? = null
|
||||||
|
|
||||||
private fun attachBackPressedCallback() {
|
private fun attachBackPressedCallback() {
|
||||||
|
|
|
@ -49,11 +49,15 @@ inline fun debugWarning(assert: () -> Boolean, message: () -> String) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** NOTE: Only one observer at a time per value */
|
||||||
fun <T> LifecycleOwner.observe(liveData: LiveData<T>, action: (t: T) -> Unit) {
|
fun <T> LifecycleOwner.observe(liveData: LiveData<T>, action: (t: T) -> Unit) {
|
||||||
|
liveData.removeObservers(this)
|
||||||
liveData.observe(this) { it?.let { t -> action(t) } }
|
liveData.observe(this) { it?.let { t -> action(t) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** NOTE: Only one observer at a time per value */
|
||||||
fun <T> LifecycleOwner.observeNullable(liveData: LiveData<T>, action: (t: T) -> Unit) {
|
fun <T> LifecycleOwner.observeNullable(liveData: LiveData<T>, action: (t: T) -> Unit) {
|
||||||
|
liveData.removeObservers(this)
|
||||||
liveData.observe(this) { action(it) }
|
liveData.observe(this) { action(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.lagradost.cloudstream3.services
|
package com.lagradost.cloudstream3.services
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
@ -12,7 +13,7 @@ import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||||
import com.lagradost.cloudstream3.R
|
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.plugins.PluginManager
|
||||||
import com.lagradost.cloudstream3.ui.result.txt
|
import com.lagradost.cloudstream3.ui.result.txt
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.createNotificationChannel
|
import com.lagradost.cloudstream3.utils.AppUtils.createNotificationChannel
|
||||||
|
@ -97,7 +98,9 @@ class SubscriptionWorkManager(val context: Context, workerParams: WorkerParamete
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnspecifiedImmutableFlag")
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
|
try {
|
||||||
// println("Update subscriptions!")
|
// println("Update subscriptions!")
|
||||||
context.createNotificationChannel(
|
context.createNotificationChannel(
|
||||||
SUBSCRIPTION_CHANNEL_ID,
|
SUBSCRIPTION_CHANNEL_ID,
|
||||||
|
@ -215,10 +218,18 @@ class SubscriptionWorkManager(val context: Context, workerParams: WorkerParamete
|
||||||
|
|
||||||
// You can probably get some issues here since this is async but it does not matter much.
|
// You can probably get some issues here since this is async but it does not matter much.
|
||||||
updateProgress(max, ++progress, false)
|
updateProgress(max, ++progress, false)
|
||||||
} catch (_: Throwable) {
|
} 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()
|
return Result.success()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
|
@ -8,7 +8,8 @@ import com.lagradost.cloudstream3.syncproviders.SyncIdName
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
import com.lagradost.cloudstream3.ui.WatchType
|
||||||
import com.lagradost.cloudstream3.ui.library.ListSorting
|
import com.lagradost.cloudstream3.ui.library.ListSorting
|
||||||
import com.lagradost.cloudstream3.ui.result.txt
|
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.Coroutines.ioWork
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllFavorites
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllFavorites
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions
|
||||||
|
@ -71,9 +72,9 @@ class LocalList : SyncAPI {
|
||||||
}?.distinctBy { it.first } ?: return null
|
}?.distinctBy { it.first } ?: return null
|
||||||
|
|
||||||
val list = ioWork {
|
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
|
// None is not something to display
|
||||||
it.stringRes to emptyList<SyncAPI.LibraryItem>()
|
it.stringRes to emptyList<SyncAPI.LibraryItem>()
|
||||||
} + mapOf(
|
} + mapOf(
|
||||||
|
|
|
@ -440,9 +440,9 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
interceptor = interceptor
|
interceptor = interceptor
|
||||||
).isSuccessful
|
).isSuccessful
|
||||||
} else {
|
} else {
|
||||||
val statusResponse = status?.let { setStatus ->
|
val statusResponse = this.status?.let { setStatus ->
|
||||||
val newStatus =
|
val newStatus =
|
||||||
SimklListStatusType.values()
|
SimklListStatusType.entries
|
||||||
.firstOrNull { it.value == setStatus }?.originalName
|
.firstOrNull { it.value == setStatus }?.originalName
|
||||||
?: SimklListStatusType.Watching.originalName!!
|
?: SimklListStatusType.Watching.originalName!!
|
||||||
|
|
||||||
|
@ -479,9 +479,14 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
).isSuccessful
|
).isSuccessful
|
||||||
} ?: true
|
} ?: true
|
||||||
|
|
||||||
|
// You cannot rate if you are planning to watch it.
|
||||||
|
val shouldRate =
|
||||||
|
score != null && status != SimklListStatusType.Planning.value
|
||||||
|
val realScore = if (shouldRate) score else null
|
||||||
|
|
||||||
val historyResponse =
|
val historyResponse =
|
||||||
// Only post if there are episodes or score to upload
|
// Only post if there are episodes or score to upload
|
||||||
if (addEpisodes != null || score != null) {
|
if (addEpisodes != null || shouldRate) {
|
||||||
app.post(
|
app.post(
|
||||||
"${this.url}/sync/history",
|
"${this.url}/sync/history",
|
||||||
json = StatusRequest(
|
json = StatusRequest(
|
||||||
|
@ -492,8 +497,8 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
ids,
|
ids,
|
||||||
addEpisodes?.first,
|
addEpisodes?.first,
|
||||||
addEpisodes?.second,
|
addEpisodes?.second,
|
||||||
score,
|
realScore,
|
||||||
score?.let { time },
|
realScore?.let { time },
|
||||||
)
|
)
|
||||||
), movies = emptyList()
|
), movies = emptyList()
|
||||||
),
|
),
|
||||||
|
@ -827,7 +832,13 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
|
|
||||||
if (foundItem != null) {
|
if (foundItem != null) {
|
||||||
return SimklSyncStatus(
|
return SimklSyncStatus(
|
||||||
status = foundItem.status?.let { SyncWatchType.fromInternalId(SimklListStatusType.fromString(it)?.value) }
|
status = foundItem.status?.let {
|
||||||
|
SyncWatchType.fromInternalId(
|
||||||
|
SimklListStatusType.fromString(
|
||||||
|
it
|
||||||
|
)?.value
|
||||||
|
)
|
||||||
|
}
|
||||||
?: return null,
|
?: return null,
|
||||||
score = foundItem.user_rating,
|
score = foundItem.user_rating,
|
||||||
watchedEpisodes = foundItem.watched_episodes_count,
|
watchedEpisodes = foundItem.watched_episodes_count,
|
||||||
|
@ -859,8 +870,10 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
val builder = SimklScoreBuilder.Builder()
|
val builder = SimklScoreBuilder.Builder()
|
||||||
.apiUrl(this.mainUrl)
|
.apiUrl(this.mainUrl)
|
||||||
.score(status.score, simklStatus?.oldScore)
|
.score(status.score, simklStatus?.oldScore)
|
||||||
.status(status.status.internalId, (status as? SimklSyncStatus)?.oldStatus?.let { oldStatus ->
|
.status(
|
||||||
SimklListStatusType.values().firstOrNull {
|
status.status.internalId,
|
||||||
|
(status as? SimklSyncStatus)?.oldStatus?.let { oldStatus ->
|
||||||
|
SimklListStatusType.entries.firstOrNull {
|
||||||
it.originalName == oldStatus
|
it.originalName == oldStatus
|
||||||
}?.value
|
}?.value
|
||||||
})
|
})
|
||||||
|
@ -996,7 +1009,7 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
val list = getSyncListSmart() ?: return null
|
val list = getSyncListSmart() ?: return null
|
||||||
|
|
||||||
val baseMap =
|
val baseMap =
|
||||||
SimklListStatusType.values()
|
SimklListStatusType.entries
|
||||||
.filter { it.value >= 0 && it.value != SimklListStatusType.ReWatching.value }
|
.filter { it.value >= 0 && it.value != SimklListStatusType.ReWatching.value }
|
||||||
.associate {
|
.associate {
|
||||||
it.stringRes to emptyList<SyncAPI.LibraryItem>()
|
it.stringRes to emptyList<SyncAPI.LibraryItem>()
|
||||||
|
|
|
@ -12,7 +12,9 @@ import com.lagradost.cloudstream3.databinding.AccountListItemBinding
|
||||||
import com.lagradost.cloudstream3.databinding.AccountListItemEditBinding
|
import com.lagradost.cloudstream3.databinding.AccountListItemEditBinding
|
||||||
import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountEditDialog
|
import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountEditDialog
|
||||||
import com.lagradost.cloudstream3.ui.result.setImage
|
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.DataStoreHelper
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
|
|
||||||
|
@ -38,7 +40,7 @@ class AccountAdapter(
|
||||||
is AccountListItemBinding -> binding.apply {
|
is AccountListItemBinding -> binding.apply {
|
||||||
if (account == null) return@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
|
val isLastUsedAccount = account.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
|
|
||||||
|
@ -80,7 +82,7 @@ class AccountAdapter(
|
||||||
is AccountListItemEditBinding -> binding.apply {
|
is AccountListItemEditBinding -> binding.apply {
|
||||||
if (account == null) return@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
|
val isLastUsedAccount = account.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.ui.account
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
@ -17,13 +18,19 @@ import com.lagradost.cloudstream3.mvvm.observe
|
||||||
import com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
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_EDIT_ACCOUNT
|
||||||
import com.lagradost.cloudstream3.ui.account.AccountAdapter.Companion.VIEW_TYPE_SELECT_ACCOUNT
|
import com.lagradost.cloudstream3.ui.account.AccountAdapter.Companion.VIEW_TYPE_SELECT_ACCOUNT
|
||||||
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
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.accounts
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.accounts
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.selectedKeyIndex
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.selectedKeyIndex
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.setAccount
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setAccount
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||||
|
|
||||||
class AccountSelectActivity : AppCompatActivity() {
|
class AccountSelectActivity : AppCompatActivity(), BiometricAuthenticator.BiometricAuthCallback {
|
||||||
|
|
||||||
lateinit var viewModel: AccountViewModel
|
lateinit var viewModel: AccountViewModel
|
||||||
|
|
||||||
|
@ -41,13 +48,36 @@ class AccountSelectActivity : AppCompatActivity() {
|
||||||
)
|
)
|
||||||
|
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
val skipStartup = settingsManager.getBoolean(
|
val authEnabled = settingsManager.getBoolean(getString(R.string.biometric_key), false)
|
||||||
getString(R.string.skip_startup_account_select_key),
|
val skipStartup = settingsManager.getBoolean(getString(R.string.skip_startup_account_select_key), false
|
||||||
false
|
|
||||||
) || accounts.count() <= 1
|
) || accounts.count() <= 1
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this)[AccountViewModel::class.java]
|
viewModel = ViewModelProvider(this)[AccountViewModel::class.java]
|
||||||
|
|
||||||
|
fun askBiometricAuth() {
|
||||||
|
|
||||||
|
if (isLayout(PHONE) && authEnabled) {
|
||||||
|
if (deviceHasPasswordPinLock(this)) {
|
||||||
|
startBiometricAuthentication(
|
||||||
|
this,
|
||||||
|
R.string.biometric_authentication_title,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
BiometricAuthenticator.promptInfo?.let { promt ->
|
||||||
|
BiometricAuthenticator.biometricPrompt?.authenticate(promt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(viewModel.isAllowedLogin) { isAllowedLogin ->
|
||||||
|
if (isAllowedLogin) {
|
||||||
|
// We are allowed to continue to MainActivity
|
||||||
|
navigateToMainActivity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Don't show account selection if there is only
|
// Don't show account selection if there is only
|
||||||
// one account that exists
|
// one account that exists
|
||||||
if (!isEditingFromMainActivity && skipStartup) {
|
if (!isEditingFromMainActivity && skipStartup) {
|
||||||
|
@ -55,12 +85,6 @@ class AccountSelectActivity : AppCompatActivity() {
|
||||||
if (currentAccount?.lockPin != null) {
|
if (currentAccount?.lockPin != null) {
|
||||||
CommonActivity.init(this)
|
CommonActivity.init(this)
|
||||||
viewModel.handleAccountSelect(currentAccount, this, true)
|
viewModel.handleAccountSelect(currentAccount, this, true)
|
||||||
observe(viewModel.isAllowedLogin) { isAllowedLogin ->
|
|
||||||
if (isAllowedLogin) {
|
|
||||||
// We are allowed to continue to MainActivity
|
|
||||||
navigateToMainActivity()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (accounts.count() > 1) {
|
if (accounts.count() > 1) {
|
||||||
showToast(this, getString(
|
showToast(this, getString(
|
||||||
|
@ -88,12 +112,6 @@ class AccountSelectActivity : AppCompatActivity() {
|
||||||
// Handle the selected account
|
// Handle the selected account
|
||||||
accountSelectCallback = {
|
accountSelectCallback = {
|
||||||
viewModel.handleAccountSelect(it, this)
|
viewModel.handleAccountSelect(it, this)
|
||||||
observe(viewModel.isAllowedLogin) { isAllowedLogin ->
|
|
||||||
if (isAllowedLogin) {
|
|
||||||
// We are allowed to continue to MainActivity
|
|
||||||
navigateToMainActivity()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
accountCreateCallback = { viewModel.handleAccountUpdate(it, this) },
|
accountCreateCallback = { viewModel.handleAccountUpdate(it, this) },
|
||||||
accountEditCallback = {
|
accountEditCallback = {
|
||||||
|
@ -111,7 +129,7 @@ class AccountSelectActivity : AppCompatActivity() {
|
||||||
|
|
||||||
recyclerView.adapter = adapter
|
recyclerView.adapter = adapter
|
||||||
|
|
||||||
if (isTvSettings()) {
|
if (isLayout(TV or EMULATOR)) {
|
||||||
binding.editAccountButton.setBackgroundResource(
|
binding.editAccountButton.setBackgroundResource(
|
||||||
R.drawable.player_button_tv_attr_no_bg
|
R.drawable.player_button_tv_attr_no_bg
|
||||||
)
|
)
|
||||||
|
@ -152,12 +170,14 @@ class AccountSelectActivity : AppCompatActivity() {
|
||||||
viewModel.toggleIsEditing()
|
viewModel.toggleIsEditing()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTvSettings()) {
|
if (isLayout(TV or EMULATOR)) {
|
||||||
recyclerView.spanCount = if (liveAccounts.count() + 1 <= 6) {
|
recyclerView.spanCount = if (liveAccounts.count() + 1 <= 6) {
|
||||||
liveAccounts.count() + 1
|
liveAccounts.count() + 1
|
||||||
} else 6
|
} else 6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
askBiometricAuth()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateToMainActivity() {
|
private fun navigateToMainActivity() {
|
||||||
|
@ -165,4 +185,8 @@ class AccountSelectActivity : AppCompatActivity() {
|
||||||
startActivity(mainIntent)
|
startActivity(mainIntent)
|
||||||
finish() // Finish the account selection activity
|
finish() // Finish the account selection activity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onAuthenticationSuccess() {
|
||||||
|
Log.i(BiometricAuthenticator.TAG,"Authentication successful in AccountSelectActivity")
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -5,6 +5,7 @@ import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.format.Formatter.formatShortFileSize
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -13,17 +14,25 @@ import android.widget.Toast
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.widget.doOnTextChanged
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.R
|
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.isMovieType
|
||||||
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
|
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.GeneratorPlayer
|
||||||
import com.lagradost.cloudstream3.ui.player.LinkGenerator
|
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.AppUtils.loadResult
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE
|
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.UIHelper.navigate
|
||||||
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager
|
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
|
import java.net.URI
|
||||||
|
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ class DownloadFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should be visible in emulator layout
|
// Should be visible in emulator layout
|
||||||
binding?.downloadStreamButton?.isGone = isTrueTvSettings()
|
binding?.downloadStreamButton?.isGone = isLayout(TV)
|
||||||
binding?.downloadStreamButton?.setOnClickListener {
|
binding?.downloadStreamButton?.setOnClickListener {
|
||||||
val dialog =
|
val dialog =
|
||||||
Dialog(it.context ?: return@setOnClickListener, R.style.AlertDialogCustom)
|
Dialog(it.context ?: return@setOnClickListener, R.style.AlertDialogCustom)
|
||||||
|
|
|
@ -2,16 +2,19 @@ package com.lagradost.cloudstream3.ui.download.button
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.os.Looper
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.animation.AnimationUtils
|
import android.view.animation.AnimationUtils
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.MainThread
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.lagradost.cloudstream3.R
|
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_DELETE_FILE
|
||||||
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
|
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.DOWNLOAD_ACTION_LONG_CLICK
|
||||||
|
@ -241,14 +244,8 @@ open class PieFetchButton(context: Context, attributeSet: AttributeSet) :
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
/** Also sets currentStatus */
|
@MainThread
|
||||||
override fun setStatus(status: DownloadStatusTell?) {
|
private fun setStatusInternal(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
|
val isPreActive = isZeroBytes && status == DownloadStatusTell.IsDownloading
|
||||||
if (animateWaiting && (status == DownloadStatusTell.IsPending || isPreActive)) {
|
if (animateWaiting && (status == DownloadStatusTell.IsPending || isPreActive)) {
|
||||||
val animation = AnimationUtils.loadAnimation(context, waitingAnimation)
|
val animation = AnimationUtils.loadAnimation(context, waitingAnimation)
|
||||||
|
@ -276,6 +273,26 @@ open class PieFetchButton(context: Context, attributeSet: AttributeSet) :
|
||||||
progressBarBackground.isGone = hide
|
progressBarBackground.isGone = hide
|
||||||
progressBar.isGone = hide
|
progressBar.isGone = hide
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Also sets currentStatus */
|
||||||
|
override fun setStatus(status: DownloadStatusTell?) {
|
||||||
|
currentStatus = status
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
progressBarBackground.post {
|
||||||
|
setStatusInternal(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resetView() {
|
override fun resetView() {
|
||||||
|
|
|
@ -42,8 +42,10 @@ import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountSelectLine
|
||||||
import com.lagradost.cloudstream3.ui.result.txt
|
import com.lagradost.cloudstream3.ui.result.txt
|
||||||
import com.lagradost.cloudstream3.ui.search.*
|
import com.lagradost.cloudstream3.ui.search.*
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback
|
import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
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.isRecyclerScrollable
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.ownHide
|
import com.lagradost.cloudstream3.utils.AppUtils.ownHide
|
||||||
|
@ -311,7 +313,7 @@ class HomeFragment : Fragment() {
|
||||||
button?.isVisible = isValid
|
button?.isVisible = isValid
|
||||||
button?.isChecked = isValid && selectedTypes.any { types.contains(it) }
|
button?.isChecked = isValid && selectedTypes.any { types.contains(it) }
|
||||||
button?.isFocusable = true
|
button?.isFocusable = true
|
||||||
if (isTrueTvSettings()) {
|
if (isLayout(TV)) {
|
||||||
button?.isFocusableInTouchMode = true
|
button?.isFocusableInTouchMode = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,7 +437,7 @@ class HomeFragment : Fragment() {
|
||||||
|
|
||||||
bottomSheetDialog?.ownShow()
|
bottomSheetDialog?.ownShow()
|
||||||
val layout =
|
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)
|
val root = inflater.inflate(layout, container, false)
|
||||||
binding = try {
|
binding = try {
|
||||||
FragmentHomeBinding.bind(root)
|
FragmentHomeBinding.bind(root)
|
||||||
|
@ -449,6 +451,11 @@ class HomeFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
homeMasterAdapter?.onSaveInstanceState(
|
||||||
|
instanceState,
|
||||||
|
binding?.homeMasterRecycler
|
||||||
|
)
|
||||||
|
|
||||||
bottomSheetDialog?.ownHide()
|
bottomSheetDialog?.ownHide()
|
||||||
binding = null
|
binding = null
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
@ -485,6 +492,10 @@ class HomeFragment : Fragment() {
|
||||||
|
|
||||||
private var bottomSheetDialog: BottomSheetDialog? = null
|
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")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -505,15 +516,16 @@ class HomeFragment : Fragment() {
|
||||||
activity.loadSearchResult(listHomepageItems.random())
|
activity.loadSearchResult(listHomepageItems.random())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
homeMasterAdapter = HomeParentItemAdapterPreview(
|
||||||
homeMasterRecycler.adapter =
|
|
||||||
HomeParentItemAdapterPreview(
|
|
||||||
mutableListOf(),
|
mutableListOf(),
|
||||||
homeViewModel
|
homeViewModel,
|
||||||
)
|
).apply {
|
||||||
|
onRestoreInstanceState(instanceState)
|
||||||
|
}
|
||||||
|
homeMasterRecycler.adapter = homeMasterAdapter
|
||||||
//fixPaddingStatusbar(homeLoadingStatusbar)
|
//fixPaddingStatusbar(homeLoadingStatusbar)
|
||||||
|
|
||||||
homeApiFab.isVisible = !isTvSettings()
|
homeApiFab.isVisible = isLayout(PHONE)
|
||||||
|
|
||||||
homeMasterRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
homeMasterRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
@ -521,7 +533,7 @@ class HomeFragment : Fragment() {
|
||||||
homeApiFab.shrink() // hide
|
homeApiFab.shrink() // hide
|
||||||
homeRandom.shrink()
|
homeRandom.shrink()
|
||||||
} else if (dy < -5) {
|
} else if (dy < -5) {
|
||||||
if (!isTvSettings()) {
|
if (isLayout(PHONE)) {
|
||||||
homeApiFab.extend() // show
|
homeApiFab.extend() // show
|
||||||
homeRandom.extend()
|
homeRandom.extend()
|
||||||
}
|
}
|
||||||
|
@ -529,6 +541,7 @@ class HomeFragment : Fragment() {
|
||||||
super.onScrolled(recyclerView, dx, dy)
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -539,7 +552,7 @@ class HomeFragment : Fragment() {
|
||||||
settingsManager.getBoolean(
|
settingsManager.getBoolean(
|
||||||
getString(R.string.random_button_key),
|
getString(R.string.random_button_key),
|
||||||
false
|
false
|
||||||
) && !isTvSettings()
|
) && isLayout(PHONE)
|
||||||
binding?.homeRandom?.visibility = View.GONE
|
binding?.homeRandom?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.lagradost.cloudstream3.ui.home
|
package com.lagradost.cloudstream3.ui.home
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Parcelable
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -7,16 +9,20 @@ import android.widget.TextView
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.ListUpdateCallback
|
import androidx.recyclerview.widget.ListUpdateCallback
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||||
import com.lagradost.cloudstream3.HomePageList
|
import com.lagradost.cloudstream3.HomePageList
|
||||||
import com.lagradost.cloudstream3.LoadResponse
|
import com.lagradost.cloudstream3.LoadResponse
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.databinding.HomepageParentBinding
|
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.FOCUS_SELF
|
||||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse
|
import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
|
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
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.isRecyclerScrollable
|
||||||
|
|
||||||
class LoadClickCallback(
|
class LoadClickCallback(
|
||||||
|
@ -32,18 +38,90 @@ open class ParentItemAdapter(
|
||||||
private val clickCallback: (SearchClickCallback) -> Unit,
|
private val clickCallback: (SearchClickCallback) -> Unit,
|
||||||
private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit,
|
private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit,
|
||||||
private val expandCallback: ((String) -> Unit)? = null,
|
private val expandCallback: ((String) -> Unit)? = null,
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<ViewHolder>() {
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
// 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<Int, Parcelable?>()
|
||||||
|
|
||||||
|
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 {
|
val layoutResId = when {
|
||||||
isTrueTvSettings() -> R.layout.homepage_parent_tv
|
isLayout(TV) -> R.layout.homepage_parent_tv
|
||||||
parent.context.isEmulatorSettings() -> R.layout.homepage_parent_emulator
|
isLayout(EMULATOR) -> R.layout.homepage_parent_emulator
|
||||||
else -> R.layout.homepage_parent
|
else -> R.layout.homepage_parent
|
||||||
}
|
}
|
||||||
|
|
||||||
val root = LayoutInflater.from(parent.context).inflate(layoutResId, parent, false)
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
|
val binding = try {
|
||||||
val binding = HomepageParentBinding.bind(root)
|
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(
|
return ParentViewHolder(
|
||||||
binding,
|
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 {
|
override fun getItemCount(): Int {
|
||||||
return items.size
|
return items.size
|
||||||
}
|
}
|
||||||
|
@ -116,7 +186,6 @@ open class ParentItemAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onChanged(_position: Int, count: Int, payload: Any?) {
|
override fun onChanged(_position: Int, count: Int, payload: Any?) {
|
||||||
|
|
||||||
val position = _position + delta
|
val position = _position + delta
|
||||||
|
|
||||||
// I know kinda messy, what this does is using the update or bind instead of onCreateViewHolder -> bind
|
// 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)
|
//diffResult.dispatchUpdatesTo(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ParentViewHolder
|
|
||||||
constructor(
|
class ParentViewHolder(
|
||||||
val binding: HomepageParentBinding,
|
val binding: HomepageParentBinding,
|
||||||
// val viewModel: HomeViewModel,
|
// val viewModel: HomeViewModel,
|
||||||
private val clickCallback: (SearchClickCallback) -> Unit,
|
private val clickCallback: (SearchClickCallback) -> Unit,
|
||||||
private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit,
|
private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit,
|
||||||
private val expandCallback: ((String) -> Unit)? = null,
|
private val expandCallback: ((String) -> Unit)? = null,
|
||||||
) :
|
) :
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
ViewHolder(binding.root) {
|
||||||
val title: TextView = binding.homeChildMoreInfo
|
val title: TextView = binding.homeChildMoreInfo
|
||||||
private val recyclerView: RecyclerView = binding.homeChildRecyclerview
|
private val recyclerView: RecyclerView = binding.homeChildRecyclerview
|
||||||
private val startFocus = R.id.nav_rail_view
|
private val startFocus = R.id.nav_rail_view
|
||||||
|
@ -237,7 +306,7 @@ open class ParentItemAdapter(
|
||||||
})
|
})
|
||||||
|
|
||||||
//(recyclerView.adapter as HomeChildItemAdapter).notifyDataSetChanged()
|
//(recyclerView.adapter as HomeChildItemAdapter).notifyDataSetChanged()
|
||||||
if (!isTrueTvSettings()) {
|
if (isLayout(PHONE)) {
|
||||||
title.setOnClickListener {
|
title.setOnClickListener {
|
||||||
moreInfoClickCallback.invoke(expand)
|
moreInfoClickCallback.invoke(expand)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_LOAD
|
||||||
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
|
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
|
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
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.DataStoreHelper
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes
|
||||||
|
@ -48,7 +49,8 @@ import com.lagradost.cloudstream3.utils.UIHelper.populateChips
|
||||||
class HomeParentItemAdapterPreview(
|
class HomeParentItemAdapterPreview(
|
||||||
items: MutableList<HomeViewModel.ExpandableHomepageList>,
|
items: MutableList<HomeViewModel.ExpandableHomepageList>,
|
||||||
private val viewModel: HomeViewModel,
|
private val viewModel: HomeViewModel,
|
||||||
) : ParentItemAdapter(items, clickCallback = {
|
) : ParentItemAdapter(items,
|
||||||
|
clickCallback = {
|
||||||
viewModel.click(it)
|
viewModel.click(it)
|
||||||
}, moreInfoClickCallback = {
|
}, moreInfoClickCallback = {
|
||||||
viewModel.popup(it)
|
viewModel.popup(it)
|
||||||
|
@ -78,13 +80,13 @@ class HomeParentItemAdapterPreview(
|
||||||
return when (viewType) {
|
return when (viewType) {
|
||||||
VIEW_TYPE_HEADER -> {
|
VIEW_TYPE_HEADER -> {
|
||||||
val inflater = LayoutInflater.from(parent.context)
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
val binding = if (isTvSettings()) FragmentHomeHeadTvBinding.inflate(
|
val binding = if (isLayout(TV or EMULATOR)) FragmentHomeHeadTvBinding.inflate(
|
||||||
inflater,
|
inflater,
|
||||||
parent,
|
parent,
|
||||||
false
|
false
|
||||||
) else FragmentHomeHeadBinding.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
|
binding.homeBookmarkParentItemMoreInfo.isVisible = true
|
||||||
|
|
||||||
val marginInDp = 50
|
val marginInDp = 50
|
||||||
|
@ -598,7 +600,7 @@ class HomeParentItemAdapterPreview(
|
||||||
if (
|
if (
|
||||||
binding is FragmentHomeHeadBinding ||
|
binding is FragmentHomeHeadBinding ||
|
||||||
binding is FragmentHomeHeadTvBinding &&
|
binding is FragmentHomeHeadTvBinding &&
|
||||||
binding.root.context.isEmulatorSettings()
|
isLayout(EMULATOR)
|
||||||
) {
|
) {
|
||||||
val title = (binding as? FragmentHomeHeadBinding)?.homeWatchParentItemTitle
|
val title = (binding as? FragmentHomeHeadBinding)?.homeWatchParentItemTitle
|
||||||
?: (binding as? FragmentHomeHeadTvBinding)?.homeWatchParentItemTitle
|
?: (binding as? FragmentHomeHeadTvBinding)?.homeWatchParentItemTitle
|
||||||
|
@ -628,7 +630,7 @@ class HomeParentItemAdapterPreview(
|
||||||
if (
|
if (
|
||||||
binding is FragmentHomeHeadBinding ||
|
binding is FragmentHomeHeadBinding ||
|
||||||
binding is FragmentHomeHeadTvBinding &&
|
binding is FragmentHomeHeadTvBinding &&
|
||||||
binding.root.context.isEmulatorSettings()
|
isLayout(EMULATOR)
|
||||||
) {
|
) {
|
||||||
val title = (binding as? FragmentHomeHeadBinding)?.homeBookmarkParentItemTitle
|
val title = (binding as? FragmentHomeHeadBinding)?.homeBookmarkParentItemTitle
|
||||||
?: (binding as? FragmentHomeHeadTvBinding)?.homeBookmarkParentItemTitle
|
?: (binding as? FragmentHomeHeadTvBinding)?.homeBookmarkParentItemTitle
|
||||||
|
|
|
@ -10,7 +10,9 @@ import androidx.viewbinding.ViewBinding
|
||||||
import com.lagradost.cloudstream3.LoadResponse
|
import com.lagradost.cloudstream3.LoadResponse
|
||||||
import com.lagradost.cloudstream3.databinding.HomeScrollViewBinding
|
import com.lagradost.cloudstream3.databinding.HomeScrollViewBinding
|
||||||
import com.lagradost.cloudstream3.databinding.HomeScrollViewTvBinding
|
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
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
|
|
||||||
class HomeScrollAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
class HomeScrollAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
@ -40,7 +42,7 @@ class HomeScrollAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
val inflater = LayoutInflater.from(parent.context)
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
val binding = if (isTvSettings()) {
|
val binding = if (isLayout(TV or EMULATOR)) {
|
||||||
HomeScrollViewTvBinding.inflate(inflater, parent, false)
|
HomeScrollViewTvBinding.inflate(inflater, parent, false)
|
||||||
} else {
|
} else {
|
||||||
HomeScrollViewBinding.inflate(inflater, parent, false)
|
HomeScrollViewBinding.inflate(inflater, parent, false)
|
||||||
|
|
|
@ -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.SEARCH_ACTION_FOCUSED
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
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.addProgramsToContinueWatching
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
|
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
|
@ -132,7 +133,7 @@ class HomeViewModel : ViewModel() {
|
||||||
|
|
||||||
private fun loadResumeWatching() = viewModelScope.launchSafe {
|
private fun loadResumeWatching() = viewModelScope.launchSafe {
|
||||||
val resumeWatchingResult = getResumeWatching()
|
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 {
|
ioSafe {
|
||||||
// this WILL crash on non tvs, so keep this inside a try catch
|
// this WILL crash on non tvs, so keep this inside a try catch
|
||||||
activity?.addProgramsToContinueWatching(resumeWatchingResult)
|
activity?.addProgramsToContinueWatching(resumeWatchingResult)
|
||||||
|
|
|
@ -49,6 +49,11 @@ import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
|
||||||
import com.lagradost.cloudstream3.ui.result.txt
|
import com.lagradost.cloudstream3.ui.result.txt
|
||||||
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
|
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.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.ui.settings.SettingsFragment
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
|
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
||||||
|
@ -101,7 +106,7 @@ class LibraryFragment : Fragment() {
|
||||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
val layout =
|
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)
|
val root = inflater.inflate(layout, container, false)
|
||||||
binding = try {
|
binding = try {
|
||||||
FragmentLibraryBinding.bind(root)
|
FragmentLibraryBinding.bind(root)
|
||||||
|
@ -131,6 +136,18 @@ class LibraryFragment : Fragment() {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateRandom() {
|
||||||
|
val position = libraryViewModel.currentPage.value ?: 0
|
||||||
|
val pages = (libraryViewModel.pages.value as? Resource.Success)?.value ?: return
|
||||||
|
if (toggleRandomButton) {
|
||||||
|
listLibraryItems.clear()
|
||||||
|
listLibraryItems.addAll(pages[position].items)
|
||||||
|
binding?.libraryRandom?.isVisible = listLibraryItems.isNotEmpty()
|
||||||
|
} else {
|
||||||
|
binding?.libraryRandom?.isGone = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("ResourceType", "CutPasteId")
|
@SuppressLint("ResourceType", "CutPasteId")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
@ -208,7 +225,7 @@ class LibraryFragment : Fragment() {
|
||||||
settingsManager.getBoolean(
|
settingsManager.getBoolean(
|
||||||
getString(R.string.random_button_key),
|
getString(R.string.random_button_key),
|
||||||
false
|
false
|
||||||
) && !SettingsFragment.isTvSettings()
|
) && isLayout(PHONE)
|
||||||
binding?.libraryRandom?.visibility = View.GONE
|
binding?.libraryRandom?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,15 +412,7 @@ class LibraryFragment : Fragment() {
|
||||||
binding?.viewpager?.setCurrentItem(page, false)
|
binding?.viewpager?.setCurrentItem(page, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
observe(libraryViewModel.currentPage){
|
updateRandom()
|
||||||
if (toggleRandomButton) {
|
|
||||||
listLibraryItems.clear()
|
|
||||||
listLibraryItems.addAll(pages[it].items)
|
|
||||||
libraryRandom.isVisible = listLibraryItems.isNotEmpty()
|
|
||||||
} else {
|
|
||||||
libraryRandom.isGone = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only stop loading after 300ms to hide the fade effect the viewpager produces when updating
|
// Only stop loading after 300ms to hide the fade effect the viewpager produces when updating
|
||||||
// Without this there would be a flashing effect:
|
// Without this there would be a flashing effect:
|
||||||
|
@ -481,6 +490,7 @@ class LibraryFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
observe(libraryViewModel.currentPage) { position ->
|
observe(libraryViewModel.currentPage) { position ->
|
||||||
|
updateRandom()
|
||||||
val all = binding?.viewpager?.allViews?.toList()
|
val all = binding?.viewpager?.allViews?.toList()
|
||||||
?.filterIsInstance<AutofitRecyclerView>()
|
?.filterIsInstance<AutofitRecyclerView>()
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.lagradost.cloudstream3.ui.library
|
package com.lagradost.cloudstream3.ui.library
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.view.doOnAttach
|
import androidx.core.view.doOnAttach
|
||||||
|
@ -12,7 +11,9 @@ import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.databinding.LibraryViewpagerPageBinding
|
import com.lagradost.cloudstream3.databinding.LibraryViewpagerPageBinding
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
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
|
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
|
||||||
|
|
||||||
class ViewpagerAdapter(
|
class ViewpagerAdapter(
|
||||||
|
@ -73,7 +74,7 @@ class ViewpagerAdapter(
|
||||||
val diff = scrollY - oldScrollY
|
val diff = scrollY - oldScrollY
|
||||||
|
|
||||||
//Expand the top Appbar based on scroll direction up/down, simulate phone behavior
|
//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<AppBarLayout>(R.id.search_bar)
|
binding.root.rootView.findViewById<AppBarLayout>(R.id.search_bar)
|
||||||
.apply {
|
.apply {
|
||||||
if (diff <= 0)
|
if (diff <= 0)
|
||||||
|
|
|
@ -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.player.source_priority.QualityDataHelper
|
||||||
import com.lagradost.cloudstream3.ui.result.setText
|
import com.lagradost.cloudstream3.ui.result.setText
|
||||||
import com.lagradost.cloudstream3.ui.result.txt
|
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.ui.settings.SettingsFragment
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.isUsingMobileData
|
import com.lagradost.cloudstream3.utils.AppUtils.isUsingMobileData
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
|
@ -1514,7 +1515,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// cs3 is peak media center
|
// cs3 is peak media center
|
||||||
setRemainingTimeCounter(durationMode || SettingsFragment.isTrueTvSettings())
|
setRemainingTimeCounter(durationMode || Globals.isLayout(Globals.TV))
|
||||||
playerBinding?.exoPosition?.doOnTextChanged { _, _, _, _ ->
|
playerBinding?.exoPosition?.doOnTextChanged { _, _, _, _ ->
|
||||||
updateRemainingTime()
|
updateRemainingTime()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.QualityDataHelper
|
||||||
import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog
|
import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog
|
||||||
import com.lagradost.cloudstream3.ui.result.*
|
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.SUBTITLE_AUTO_SELECT_KEY
|
||||||
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getAutoSelectLanguageISO639_1
|
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getAutoSelectLanguageISO639_1
|
||||||
import com.lagradost.cloudstream3.utils.*
|
import com.lagradost.cloudstream3.utils.*
|
||||||
|
@ -1275,8 +1278,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
// this is used instead of layout-television to follow the settings and some TV devices are not classified as TV for some reason
|
// 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 (isLayout(TV or EMULATOR)) R.layout.fragment_player_tv else R.layout.fragment_player
|
||||||
layout = if (isTv) R.layout.fragment_player_tv else R.layout.fragment_player
|
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this)[PlayerGeneratorViewModel::class.java]
|
viewModel = ViewModelProvider(this)[PlayerGeneratorViewModel::class.java]
|
||||||
sync = ViewModelProvider(this)[SyncViewModel::class.java]
|
sync = ViewModelProvider(this)[SyncViewModel::class.java]
|
||||||
|
|
|
@ -9,6 +9,9 @@ import android.util.Log
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import androidx.core.graphics.scale
|
import androidx.core.graphics.scale
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
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.ui.settings.SettingsFragment
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
@ -63,7 +66,7 @@ interface IPreviewGenerator {
|
||||||
companion object {
|
companion object {
|
||||||
fun new(): IPreviewGenerator {
|
fun new(): IPreviewGenerator {
|
||||||
/** because TV has low ram + not show we disable this for now */
|
/** because TV has low ram + not show we disable this for now */
|
||||||
return if (SettingsFragment.isTrueTvSettings()) {
|
return if (isLayout(TV)) {
|
||||||
empty()
|
empty()
|
||||||
} else {
|
} else {
|
||||||
PreviewGenerator()
|
PreviewGenerator()
|
||||||
|
|
|
@ -34,7 +34,8 @@ import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchViewModel
|
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.AppUtils.ownShow
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper
|
import com.lagradost.cloudstream3.utils.UIHelper
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
||||||
|
@ -277,7 +278,7 @@ class QuickSearchFragment : Fragment() {
|
||||||
activity?.popCurrentPage()
|
activity?.popCurrentPage()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTrueTvSettings()) {
|
if (isLayout(TV)) {
|
||||||
binding?.quickSearch?.requestFocus()
|
binding?.quickSearch?.requestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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_DOWNLOAD
|
||||||
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_LONG_CLICK
|
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_LONG_CLICK
|
||||||
import com.lagradost.cloudstream3.ui.download.DownloadClickEvent
|
import com.lagradost.cloudstream3.ui.download.DownloadClickEvent
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
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.AppUtils.html
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
||||||
|
@ -172,15 +174,13 @@ class EpisodeAdapter(
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
fun bind(card: ResultEpisode) {
|
fun bind(card: ResultEpisode) {
|
||||||
localCard = card
|
localCard = card
|
||||||
|
|
||||||
val setWidth =
|
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.episodeLinHolder.layoutParams.width = setWidth
|
||||||
binding.episodeHolderLarge.layoutParams.width = setWidth
|
binding.episodeHolderLarge.layoutParams.width = setWidth
|
||||||
binding.episodeHolder.layoutParams.width = setWidth
|
binding.episodeHolder.layoutParams.width = setWidth
|
||||||
|
|
||||||
val isTrueTv = isTrueTvSettings()
|
|
||||||
|
|
||||||
binding.apply {
|
binding.apply {
|
||||||
downloadButton.isVisible = hasDownloadSupport
|
downloadButton.isVisible = hasDownloadSupport
|
||||||
|
@ -246,12 +246,21 @@ class EpisodeAdapter(
|
||||||
episodeDescript.apply {
|
episodeDescript.apply {
|
||||||
text = card.description.html()
|
text = card.description.html()
|
||||||
isGone = text.isNullOrBlank()
|
isGone = text.isNullOrBlank()
|
||||||
|
|
||||||
|
var isExpanded = false
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
|
if (isLayout(TV)) {
|
||||||
clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_DESCRIPTION, card))
|
clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_DESCRIPTION, card))
|
||||||
|
} else {
|
||||||
|
isExpanded = !isExpanded
|
||||||
|
maxLines = if (isExpanded) {
|
||||||
|
Integer.MAX_VALUE
|
||||||
|
} else 4
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isTrueTv) {
|
if (isLayout(EMULATOR or PHONE)) {
|
||||||
episodePoster.setOnClickListener {
|
episodePoster.setOnClickListener {
|
||||||
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
||||||
}
|
}
|
||||||
|
@ -266,7 +275,7 @@ class EpisodeAdapter(
|
||||||
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTrueTv) {
|
if (isLayout(TV)) {
|
||||||
itemView.isFocusable = true
|
itemView.isFocusable = true
|
||||||
itemView.isFocusableInTouchMode = true
|
itemView.isFocusableInTouchMode = true
|
||||||
//itemView.touchscreenBlocksFocus = false
|
//itemView.touchscreenBlocksFocus = false
|
||||||
|
@ -291,11 +300,9 @@ class EpisodeAdapter(
|
||||||
) : RecyclerView.ViewHolder(binding.root) {
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
fun bind(card: ResultEpisode) {
|
fun bind(card: ResultEpisode) {
|
||||||
val isTrueTv = isTrueTvSettings()
|
|
||||||
|
|
||||||
binding.episodeHolder.layoutParams.apply {
|
binding.episodeHolder.layoutParams.apply {
|
||||||
width =
|
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 {
|
binding.apply {
|
||||||
|
@ -352,7 +359,7 @@ class EpisodeAdapter(
|
||||||
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTrueTv) {
|
if (isLayout(TV)) {
|
||||||
itemView.isFocusable = true
|
itemView.isFocusable = true
|
||||||
itemView.isFocusableInTouchMode = true
|
itemView.isFocusableInTouchMode = true
|
||||||
//itemView.touchscreenBlocksFocus = false
|
//itemView.touchscreenBlocksFocus = false
|
||||||
|
|
|
@ -5,7 +5,8 @@ import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.lagradost.cloudstream3.databinding.ResultMiniImageBinding
|
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<Int>(context, resource) {
|
class ImageAdapter(context: Context, val resource: Int) : ArrayAdapter<Int>(context, resource) {
|
||||||
|
@ -83,7 +84,7 @@ class ImageAdapter(
|
||||||
this.nextFocusUpId = nextFocusUp
|
this.nextFocusUpId = nextFocusUp
|
||||||
}
|
}
|
||||||
if (clickCallback != null) {
|
if (clickCallback != null) {
|
||||||
if (isTrueTvSettings()) {
|
if (isLayout(TV)) {
|
||||||
isClickable = true
|
isClickable = true
|
||||||
isLongClickable = true
|
isLongClickable = true
|
||||||
isFocusable = true
|
isFocusable = true
|
||||||
|
|
|
@ -2,9 +2,6 @@ package com.lagradost.cloudstream3.ui.result
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.ClipData
|
|
||||||
import android.content.ClipboardManager
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
|
@ -34,7 +31,6 @@ import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.lagradost.cloudstream3.APIHolder
|
import com.lagradost.cloudstream3.APIHolder
|
||||||
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
||||||
import com.lagradost.cloudstream3.CommonActivity
|
import com.lagradost.cloudstream3.CommonActivity
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
|
||||||
import com.lagradost.cloudstream3.DubStatus
|
import com.lagradost.cloudstream3.DubStatus
|
||||||
import com.lagradost.cloudstream3.LoadResponse
|
import com.lagradost.cloudstream3.LoadResponse
|
||||||
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
||||||
|
@ -62,16 +58,15 @@ import com.lagradost.cloudstream3.ui.result.ResultFragment.updateUIEvent
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
|
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.html
|
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
|
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
|
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
|
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogText
|
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper
|
import com.lagradost.cloudstream3.utils.UIHelper
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
||||||
|
@ -688,14 +683,15 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||||
resultNextAiringTime.setText(d.nextAiringDate)
|
resultNextAiringTime.setText(d.nextAiringDate)
|
||||||
resultPoster.setImage(d.posterImage)
|
resultPoster.setImage(d.posterImage)
|
||||||
resultPosterBackground.setImage(d.posterBackgroundImage)
|
resultPosterBackground.setImage(d.posterBackgroundImage)
|
||||||
resultDescription.setTextHtml(d.plotText)
|
|
||||||
resultDescription.setOnClickListener {
|
var isExpanded = false
|
||||||
activity?.let { activity ->
|
resultDescription.apply {
|
||||||
activity.showBottomDialogText(
|
setTextHtml(d.plotText)
|
||||||
d.titleText.asString(activity),
|
setOnClickListener {
|
||||||
d.plotText.asString(activity).html(),
|
isExpanded = !isExpanded
|
||||||
{}
|
maxLines = if (isExpanded) {
|
||||||
)
|
Integer.MAX_VALUE
|
||||||
|
} else 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,14 +754,8 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||||
resultReloadConnectionOpenInBrowser.isVisible = data is Resource.Failure
|
resultReloadConnectionOpenInBrowser.isVisible = data is Resource.Failure
|
||||||
|
|
||||||
resultTitle.setOnLongClickListener {
|
resultTitle.setOnLongClickListener {
|
||||||
val titleToCopy = resultTitle.text
|
clipboardHelper(txt(R.string.title), resultTitle.text)
|
||||||
val clipboardManager =
|
true
|
||||||
activity?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager?
|
|
||||||
clipboardManager?.setPrimaryClip(ClipData.newPlainText("Title", titleToCopy))
|
|
||||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
|
|
||||||
showToast(R.string.copyTitle, Toast.LENGTH_SHORT)
|
|
||||||
}
|
|
||||||
return@setOnLongClickListener true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -901,14 +891,6 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||||
observe(viewModel.recommendations) { recommendations ->
|
observe(viewModel.recommendations) { recommendations ->
|
||||||
setRecommendations(recommendations, null)
|
setRecommendations(recommendations, null)
|
||||||
}
|
}
|
||||||
observe(viewModel.episodeSynopsis) { description ->
|
|
||||||
activity?.let { activity ->
|
|
||||||
activity.showBottomDialogText(
|
|
||||||
activity.getString(R.string.synopsis),
|
|
||||||
description.html()
|
|
||||||
) { viewModel.releaseEpisodeSynopsis() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
context?.let { ctx ->
|
context?.let { ctx ->
|
||||||
val arrayAdapter = ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
|
val arrayAdapter = ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -33,13 +33,16 @@ import com.lagradost.cloudstream3.ui.WatchType
|
||||||
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup
|
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup
|
||||||
import com.lagradost.cloudstream3.ui.player.ExtractorLinkGenerator
|
import com.lagradost.cloudstream3.ui.player.ExtractorLinkGenerator
|
||||||
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
|
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
|
||||||
|
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
|
||||||
import com.lagradost.cloudstream3.ui.result.ResultFragment.getStoredData
|
import com.lagradost.cloudstream3.ui.result.ResultFragment.getStoredData
|
||||||
import com.lagradost.cloudstream3.ui.result.ResultFragment.updateUIEvent
|
import com.lagradost.cloudstream3.ui.result.ResultFragment.updateUIEvent
|
||||||
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_FOCUSED
|
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_FOCUSED
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
|
import com.lagradost.cloudstream3.ui.settings.Globals
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
|
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.html
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.isRtl
|
import com.lagradost.cloudstream3.utils.AppUtils.isRtl
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
|
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
|
||||||
|
@ -129,9 +132,9 @@ class ResultFragmentTv : Fragment() {
|
||||||
* Note that this will steal any focus if the episode loading is too slow (unlikely).
|
* Note that this will steal any focus if the episode loading is too slow (unlikely).
|
||||||
*/
|
*/
|
||||||
private fun focusPlayButton() {
|
private fun focusPlayButton() {
|
||||||
binding?.resultPlayMovie?.requestFocus()
|
binding?.resultPlayMovieButton?.requestFocus()
|
||||||
binding?.resultPlaySeries?.requestFocus()
|
binding?.resultPlaySeriesButton?.requestFocus()
|
||||||
binding?.resultResumeSeries?.requestFocus()
|
binding?.resultResumeSeriesButton?.requestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
private fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
||||||
|
@ -246,37 +249,15 @@ class ResultFragmentTv : Fragment() {
|
||||||
storedData.start
|
storedData.start
|
||||||
)
|
)
|
||||||
// ===== ===== =====
|
// ===== ===== =====
|
||||||
|
var comingSoon = false
|
||||||
|
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
//episodesShadow.rotationX = 180.0f//if(episodesShadow.isRtl()) 180.0f else 0.0f
|
//episodesShadow.rotationX = 180.0f//if(episodesShadow.isRtl()) 180.0f else 0.0f
|
||||||
|
|
||||||
val leftListener: View.OnFocusChangeListener =
|
// parallax on background
|
||||||
View.OnFocusChangeListener { _, hasFocus ->
|
resultFinishLoading.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { view, _, scrollY, _, oldScrollY ->
|
||||||
if (!hasFocus) return@OnFocusChangeListener
|
backgroundPosterHolder.translationY = -scrollY.toFloat() * 0.8f
|
||||||
toggleEpisodes(false)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
val rightListener: View.OnFocusChangeListener =
|
|
||||||
View.OnFocusChangeListener { _, hasFocus ->
|
|
||||||
if (!hasFocus) return@OnFocusChangeListener
|
|
||||||
toggleEpisodes(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
resultPlayMovie.onFocusChangeListener = leftListener
|
|
||||||
resultPlaySeries.onFocusChangeListener = leftListener
|
|
||||||
resultResumeSeries.onFocusChangeListener = leftListener
|
|
||||||
resultPlayTrailer.onFocusChangeListener = leftListener
|
|
||||||
resultEpisodesShow.onFocusChangeListener = rightListener
|
|
||||||
resultDescription.onFocusChangeListener = leftListener
|
|
||||||
resultBookmarkButton.onFocusChangeListener = leftListener
|
|
||||||
resultFavoriteButton.onFocusChangeListener = leftListener
|
|
||||||
resultEpisodesShow.setOnClickListener {
|
|
||||||
// toggle, to make it more touch accessable just in case someone thinks that a
|
|
||||||
// tv layout is better but is using a touch device
|
|
||||||
toggleEpisodes(!episodeHolderTv.isVisible)
|
|
||||||
}
|
|
||||||
|
|
||||||
// resultEpisodes.onFocusChangeListener = leftListener
|
|
||||||
|
|
||||||
redirectToPlay.setOnFocusChangeListener { _, hasFocus ->
|
redirectToPlay.setOnFocusChangeListener { _, hasFocus ->
|
||||||
if (!hasFocus) return@setOnFocusChangeListener
|
if (!hasFocus) return@setOnFocusChangeListener
|
||||||
|
@ -284,13 +265,14 @@ class ResultFragmentTv : Fragment() {
|
||||||
|
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
val views = listOf(
|
val views = listOf(
|
||||||
resultPlayMovie,
|
resultPlayMovieButton,
|
||||||
resultPlaySeries,
|
resultPlaySeriesButton,
|
||||||
resultResumeSeries,
|
resultResumeSeriesButton,
|
||||||
resultPlayTrailer,
|
resultPlayTrailerButton,
|
||||||
resultBookmarkButton,
|
resultBookmarkButton,
|
||||||
resultFavoriteButton,
|
resultFavoriteButton,
|
||||||
resultSubscribeButton
|
resultSubscribeButton,
|
||||||
|
resultSearchButton
|
||||||
)
|
)
|
||||||
for (requestView in views) {
|
for (requestView in views) {
|
||||||
if (!requestView.isVisible) continue
|
if (!requestView.isVisible) continue
|
||||||
|
@ -299,11 +281,6 @@ class ResultFragmentTv : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parallax on background
|
|
||||||
resultFinishLoading.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
|
||||||
backgroundPosterHolder.translationY = -scrollY.toFloat() * 0.8f
|
|
||||||
})
|
|
||||||
|
|
||||||
redirectToEpisodes.setOnFocusChangeListener { _, hasFocus ->
|
redirectToEpisodes.setOnFocusChangeListener { _, hasFocus ->
|
||||||
if (!hasFocus) return@setOnFocusChangeListener
|
if (!hasFocus) return@setOnFocusChangeListener
|
||||||
toggleEpisodes(true)
|
toggleEpisodes(true)
|
||||||
|
@ -313,7 +290,7 @@ class ResultFragmentTv : Fragment() {
|
||||||
resultSeasonSelection,
|
resultSeasonSelection,
|
||||||
resultRangeSelection,
|
resultRangeSelection,
|
||||||
resultEpisodes,
|
resultEpisodes,
|
||||||
resultPlayTrailer,
|
resultPlayTrailerButton,
|
||||||
)
|
)
|
||||||
for (requestView in views) {
|
for (requestView in views) {
|
||||||
if (!requestView.isShown) continue
|
if (!requestView.isShown) continue
|
||||||
|
@ -322,6 +299,46 @@ class ResultFragmentTv : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mapOf(
|
||||||
|
resultPlayMovieButton to resultPlayMovieText,
|
||||||
|
resultPlaySeriesButton to resultPlaySeriesText,
|
||||||
|
resultResumeSeriesButton to resultResumeSeriesText,
|
||||||
|
resultPlayTrailerButton to resultPlayTrailerText,
|
||||||
|
resultBookmarkButton to resultBookmarkText,
|
||||||
|
resultFavoriteButton to resultFavoriteText,
|
||||||
|
resultSubscribeButton to resultSubscribeText,
|
||||||
|
resultSearchButton to resultSearchText,
|
||||||
|
resultEpisodesShowButton to resultEpisodesShowText
|
||||||
|
).forEach { (button , text) ->
|
||||||
|
|
||||||
|
button.setOnFocusChangeListener { view, hasFocus ->
|
||||||
|
if (!hasFocus) {
|
||||||
|
text.isSelected = false
|
||||||
|
if (view.id == R.id.result_episodes_show_button) toggleEpisodes(false)
|
||||||
|
return@setOnFocusChangeListener
|
||||||
|
}
|
||||||
|
|
||||||
|
text.isSelected = true
|
||||||
|
if (button.tag == context?.getString(R.string.tv_no_focus_tag)){
|
||||||
|
resultFinishLoading.scrollTo(0,0)
|
||||||
|
}
|
||||||
|
when (button.id) {
|
||||||
|
R.id.result_episodes_show_button -> {
|
||||||
|
toggleEpisodes(true)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
toggleEpisodes(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resultEpisodesShowButton.setOnClickListener {
|
||||||
|
// toggle, to make it more touch accessible just in case someone thinks that a
|
||||||
|
// tv layout is better but is using a touch device
|
||||||
|
toggleEpisodes(!episodeHolderTv.isVisible)
|
||||||
|
}
|
||||||
|
|
||||||
resultEpisodes.setLinearListLayout(
|
resultEpisodes.setLinearListLayout(
|
||||||
isHorizontal = false,
|
isHorizontal = false,
|
||||||
nextUp = FOCUS_SELF,
|
nextUp = FOCUS_SELF,
|
||||||
|
@ -363,10 +380,6 @@ class ResultFragmentTv : Fragment() {
|
||||||
|
|
||||||
resultMetaSite.isFocusable = false
|
resultMetaSite.isFocusable = false
|
||||||
|
|
||||||
//resultReloadConnectionOpenInBrowser.setOnClickListener {view ->
|
|
||||||
// view.context?.openBrowser(storedData?.url ?: return@setOnClickListener, fallbackWebview = true)
|
|
||||||
//}
|
|
||||||
|
|
||||||
resultSeasonSelection.setAdapter()
|
resultSeasonSelection.setAdapter()
|
||||||
resultRangeSelection.setAdapter()
|
resultRangeSelection.setAdapter()
|
||||||
resultDubSelection.setAdapter()
|
resultDubSelection.setAdapter()
|
||||||
|
@ -430,9 +443,9 @@ class ResultFragmentTv : Fragment() {
|
||||||
|
|
||||||
val aboveCast = listOf(
|
val aboveCast = listOf(
|
||||||
binding?.resultEpisodesShow,
|
binding?.resultEpisodesShow,
|
||||||
binding?.resultBookmarkButton,
|
binding?.resultBookmark,
|
||||||
binding?.resultFavoriteButton,
|
binding?.resultFavorite,
|
||||||
binding?.resultSubscribeButton,
|
binding?.resultSubscribe,
|
||||||
).firstOrNull {
|
).firstOrNull {
|
||||||
it?.isVisible == true
|
it?.isVisible == true
|
||||||
}
|
}
|
||||||
|
@ -443,8 +456,16 @@ class ResultFragmentTv : Fragment() {
|
||||||
|
|
||||||
observeNullable(viewModel.resumeWatching) { resume ->
|
observeNullable(viewModel.resumeWatching) { resume ->
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
|
|
||||||
|
if (resume == null) {
|
||||||
|
return@observeNullable
|
||||||
|
}
|
||||||
|
resultResumeSeries.isVisible = true
|
||||||
|
resultPlayMovie.isVisible = false
|
||||||
|
resultPlaySeries.isVisible = false
|
||||||
|
|
||||||
// show progress no matter if series or movie
|
// show progress no matter if series or movie
|
||||||
resume?.progress?.let { progress ->
|
resume.progress?.let { progress ->
|
||||||
resultResumeSeriesProgressText.setText(progress.progressLeft)
|
resultResumeSeriesProgressText.setText(progress.progressLeft)
|
||||||
resultResumeSeriesProgress.apply {
|
resultResumeSeriesProgress.apply {
|
||||||
isVisible = true
|
isVisible = true
|
||||||
|
@ -456,37 +477,20 @@ class ResultFragmentTv : Fragment() {
|
||||||
resultResumeProgressHolder.isVisible = false
|
resultResumeProgressHolder.isVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// if movie then hide both as movie button is
|
|
||||||
// always visible on movies, this is done in movie observe
|
|
||||||
|
|
||||||
if (resume?.isMovie == true) {
|
|
||||||
resultPlaySeries.isVisible = false
|
|
||||||
resultResumeSeries.isVisible = false
|
|
||||||
return@observeNullable
|
|
||||||
}
|
|
||||||
|
|
||||||
// if series then
|
|
||||||
// > resultPlaySeries is visible when null
|
|
||||||
// > resultResumeSeries is visible when not null
|
|
||||||
if (resume == null) {
|
|
||||||
resultPlaySeries.isVisible = true
|
|
||||||
resultResumeSeries.isVisible = false
|
|
||||||
return@observeNullable
|
|
||||||
}
|
|
||||||
|
|
||||||
resultPlaySeries.isVisible = false
|
|
||||||
resultResumeSeries.isVisible = true
|
|
||||||
|
|
||||||
focusPlayButton()
|
focusPlayButton()
|
||||||
|
// Stops last button right focus if it is a movie
|
||||||
|
if (resume.isMovie)
|
||||||
|
resultSearchButton.nextFocusRightId = R.id.result_search_Button
|
||||||
|
|
||||||
resultResumeSeries.text =
|
resultResumeSeriesText.text =
|
||||||
if (resume.isMovie) context?.getString(R.string.play_movie_button) else context?.getNameFull(
|
when {
|
||||||
null, // resume.result.name, we don't want episode title
|
resume.isMovie -> context?.getString(R.string.resume)
|
||||||
resume.result.episode,
|
resume.result.season != null ->
|
||||||
resume.result.season
|
"${getString(R.string.season_short)}${resume.result.season}:${getString(R.string.episode_short)}${resume.result.episode}"
|
||||||
)
|
else -> "${getString(R.string.episode)} ${resume.result.episode}"
|
||||||
|
}
|
||||||
|
|
||||||
resultResumeSeries.setOnClickListener {
|
resultResumeSeriesButton.setOnClickListener {
|
||||||
viewModel.handleAction(
|
viewModel.handleAction(
|
||||||
EpisodeClickEvent(
|
EpisodeClickEvent(
|
||||||
storedData.playerAction, //?: ACTION_PLAY_EPISODE_IN_PLAYER,
|
storedData.playerAction, //?: ACTION_PLAY_EPISODE_IN_PLAYER,
|
||||||
|
@ -495,7 +499,7 @@ class ResultFragmentTv : Fragment() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
resultResumeSeries.setOnLongClickListener {
|
resultResumeSeriesButton.setOnLongClickListener {
|
||||||
viewModel.handleAction(
|
viewModel.handleAction(
|
||||||
EpisodeClickEvent(ACTION_SHOW_OPTIONS, resume.result)
|
EpisodeClickEvent(ACTION_SHOW_OPTIONS, resume.result)
|
||||||
)
|
)
|
||||||
|
@ -509,9 +513,9 @@ class ResultFragmentTv : Fragment() {
|
||||||
context?.updateHasTrailers()
|
context?.updateHasTrailers()
|
||||||
if (!LoadResponse.isTrailersEnabled) return@observe
|
if (!LoadResponse.isTrailersEnabled) return@observe
|
||||||
val trailers = trailersLinks.flatMap { it.mirros }
|
val trailers = trailersLinks.flatMap { it.mirros }
|
||||||
binding?.resultPlayTrailer?.apply {
|
binding?.apply {
|
||||||
isGone = trailers.isEmpty()
|
resultPlayTrailer.isGone = trailers.isEmpty()
|
||||||
setOnClickListener {
|
resultPlayTrailerButton.setOnClickListener {
|
||||||
if (trailers.isEmpty()) return@setOnClickListener
|
if (trailers.isEmpty()) return@setOnClickListener
|
||||||
activity.navigate(
|
activity.navigate(
|
||||||
R.id.global_to_navigation_player, GeneratorPlayer.newInstance(
|
R.id.global_to_navigation_player, GeneratorPlayer.newInstance(
|
||||||
|
@ -526,24 +530,38 @@ class ResultFragmentTv : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
observe(viewModel.watchStatus) { watchType ->
|
observe(viewModel.watchStatus) { watchType ->
|
||||||
binding?.resultBookmarkButton?.apply {
|
binding?.apply {
|
||||||
setText(watchType.stringRes)
|
resultBookmarkText.setText(watchType.stringRes)
|
||||||
|
|
||||||
|
resultBookmarkButton.apply {
|
||||||
|
|
||||||
|
val drawable = if (watchType.stringRes == R.string.type_none) {
|
||||||
|
R.drawable.outline_bookmark_add_24
|
||||||
|
} else {
|
||||||
|
R.drawable.ic_baseline_bookmark_24
|
||||||
|
}
|
||||||
|
setIconResource(drawable)
|
||||||
|
|
||||||
setOnClickListener { view ->
|
setOnClickListener { view ->
|
||||||
activity?.showBottomDialog(
|
activity?.showBottomDialog(
|
||||||
WatchType.values().map { view.context.getString(it.stringRes) }.toList(),
|
WatchType.entries.map { view.context.getString(it.stringRes) }.toList(),
|
||||||
watchType.ordinal,
|
watchType.ordinal,
|
||||||
view.context.getString(R.string.action_add_to_bookmarks),
|
view.context.getString(R.string.action_add_to_bookmarks),
|
||||||
showApply = false,
|
showApply = false,
|
||||||
{}) {
|
{}) {
|
||||||
viewModel.updateWatchStatus(WatchType.values()[it], context)
|
viewModel.updateWatchStatus(WatchType.entries[it], context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
observeNullable(viewModel.favoriteStatus) { isFavorite ->
|
observeNullable(viewModel.favoriteStatus) { isFavorite ->
|
||||||
|
|
||||||
|
binding?.resultFavorite?.isVisible = isFavorite != null
|
||||||
|
|
||||||
binding?.resultFavoriteButton?.apply {
|
binding?.resultFavoriteButton?.apply {
|
||||||
isVisible = isFavorite != null
|
|
||||||
if (isFavorite == null) return@observeNullable
|
if (isFavorite == null) return@observeNullable
|
||||||
|
|
||||||
val drawable = if (isFavorite) {
|
val drawable = if (isFavorite) {
|
||||||
|
@ -552,14 +570,8 @@ class ResultFragmentTv : Fragment() {
|
||||||
R.drawable.ic_baseline_favorite_border_24
|
R.drawable.ic_baseline_favorite_border_24
|
||||||
}
|
}
|
||||||
|
|
||||||
val text = if (isFavorite) {
|
|
||||||
R.string.action_remove_from_favorites
|
|
||||||
} else {
|
|
||||||
R.string.action_add_to_favorites
|
|
||||||
}
|
|
||||||
|
|
||||||
setIconResource(drawable)
|
setIconResource(drawable)
|
||||||
setText(text)
|
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
viewModel.toggleFavoriteStatus(context) { newStatus: Boolean? ->
|
viewModel.toggleFavoriteStatus(context) { newStatus: Boolean? ->
|
||||||
if (newStatus == null) return@toggleFavoriteStatus
|
if (newStatus == null) return@toggleFavoriteStatus
|
||||||
|
@ -576,11 +588,21 @@ class ResultFragmentTv : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding?.resultFavoriteText?.apply {
|
||||||
|
val text = if (isFavorite == true) {
|
||||||
|
R.string.unfavorite
|
||||||
|
} else {
|
||||||
|
R.string.favorite
|
||||||
|
}
|
||||||
|
setText(text)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
observeNullable(viewModel.subscribeStatus) { isSubscribed ->
|
observeNullable(viewModel.subscribeStatus) { isSubscribed ->
|
||||||
|
binding?.resultSubscribe?.isVisible = isSubscribed != null && isLayout(EMULATOR)
|
||||||
binding?.resultSubscribeButton?.apply {
|
binding?.resultSubscribeButton?.apply {
|
||||||
isVisible = isSubscribed != null && context.isEmulatorSettings()
|
|
||||||
if (isSubscribed == null) return@observeNullable
|
if (isSubscribed == null) return@observeNullable
|
||||||
|
|
||||||
val drawable = if (isSubscribed) {
|
val drawable = if (isSubscribed) {
|
||||||
|
@ -589,14 +611,8 @@ class ResultFragmentTv : Fragment() {
|
||||||
R.drawable.baseline_notifications_none_24
|
R.drawable.baseline_notifications_none_24
|
||||||
}
|
}
|
||||||
|
|
||||||
val text = if (isSubscribed) {
|
|
||||||
R.string.action_unsubscribe
|
|
||||||
} else {
|
|
||||||
R.string.action_subscribe
|
|
||||||
}
|
|
||||||
|
|
||||||
setIconResource(drawable)
|
setIconResource(drawable)
|
||||||
setText(text)
|
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
viewModel.toggleSubscriptionStatus(context) { newStatus: Boolean? ->
|
viewModel.toggleSubscriptionStatus(context) { newStatus: Boolean? ->
|
||||||
if (newStatus == null) return@toggleSubscriptionStatus
|
if (newStatus == null) return@toggleSubscriptionStatus
|
||||||
|
@ -614,30 +630,47 @@ class ResultFragmentTv : Fragment() {
|
||||||
CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
|
CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding?.resultSubscribeText?.apply {
|
||||||
|
val text = if (isSubscribed) {
|
||||||
|
R.string.action_unsubscribe
|
||||||
|
} else {
|
||||||
|
R.string.action_subscribe
|
||||||
|
}
|
||||||
|
setText(text)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
observeNullable(viewModel.movie) { data ->
|
observeNullable(viewModel.movie) { data ->
|
||||||
|
if (data == null ) {
|
||||||
|
return@observeNullable
|
||||||
|
}
|
||||||
|
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
resultPlayMovie.isVisible = data is Resource.Success
|
|
||||||
resultPlaySeries.isVisible = data == null
|
|
||||||
seriesHolder.isVisible = data == null
|
|
||||||
resultEpisodesShow.isVisible = data == null
|
|
||||||
|
|
||||||
(data as? Resource.Success)?.value?.let { (text, ep) ->
|
(data as? Resource.Success)?.value?.let { (text, ep) ->
|
||||||
resultPlayMovie.setText(text)
|
|
||||||
resultPlayMovie.setOnClickListener {
|
resultPlayMovieButton.setOnClickListener {
|
||||||
viewModel.handleAction(
|
viewModel.handleAction(
|
||||||
EpisodeClickEvent(ACTION_CLICK_DEFAULT, ep)
|
EpisodeClickEvent(ACTION_CLICK_DEFAULT, ep)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
resultPlayMovie.setOnLongClickListener {
|
resultPlayMovieButton.setOnLongClickListener {
|
||||||
viewModel.handleAction(
|
viewModel.handleAction(
|
||||||
EpisodeClickEvent(ACTION_SHOW_OPTIONS, ep)
|
EpisodeClickEvent(ACTION_SHOW_OPTIONS, ep)
|
||||||
)
|
)
|
||||||
return@setOnLongClickListener true
|
return@setOnLongClickListener true
|
||||||
}
|
}
|
||||||
focusPlayButton()
|
|
||||||
|
resultPlayMovie.isVisible = !comingSoon && resultResumeSeries.isGone
|
||||||
|
if (comingSoon)
|
||||||
|
resultBookmarkButton.requestFocus()
|
||||||
|
else
|
||||||
|
resultPlayMovieButton.requestFocus()
|
||||||
|
|
||||||
|
// Stops last button right focus
|
||||||
|
resultSearchButton.nextFocusRightId = R.id.result_search_Button
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -720,6 +753,8 @@ class ResultFragmentTv : Fragment() {
|
||||||
observe(viewModel.recommendations) { recommendations ->
|
observe(viewModel.recommendations) { recommendations ->
|
||||||
setRecommendations(recommendations, null)
|
setRecommendations(recommendations, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isLayout(TV)) {
|
||||||
observe(viewModel.episodeSynopsis) { description ->
|
observe(viewModel.episodeSynopsis) { description ->
|
||||||
view.context?.let { ctx ->
|
view.context?.let { ctx ->
|
||||||
val builder: AlertDialog.Builder =
|
val builder: AlertDialog.Builder =
|
||||||
|
@ -732,23 +767,29 @@ class ResultFragmentTv : Fragment() {
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Used to request focus the first time the episodes are loaded.
|
// Used to request focus the first time the episodes are loaded.
|
||||||
var hasLoadedEpisodesOnce = false
|
var hasLoadedEpisodesOnce = false
|
||||||
observeNullable(viewModel.episodes) { episodes ->
|
observeNullable(viewModel.episodes) { episodes ->
|
||||||
|
if (episodes == null) return@observeNullable
|
||||||
|
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
resultEpisodes.isVisible = episodes is Resource.Success
|
|
||||||
|
if (comingSoon)
|
||||||
|
resultBookmarkButton.requestFocus()
|
||||||
|
|
||||||
// resultEpisodeLoading.isVisible = episodes is Resource.Loading
|
// resultEpisodeLoading.isVisible = episodes is Resource.Loading
|
||||||
if (episodes is Resource.Success) {
|
if (episodes is Resource.Success) {
|
||||||
val first = episodes.value.firstOrNull()
|
val first = episodes.value.firstOrNull()
|
||||||
if (first != null) {
|
if (first != null) {
|
||||||
resultPlaySeries.text = context?.getNameFull(
|
resultPlaySeriesText.text =
|
||||||
null, // resume.result.name, we don't want episode title
|
when {
|
||||||
first.episode,
|
first.season != null ->
|
||||||
first.season
|
"${getString(R.string.season_short)}${first.season}:${getString(R.string.episode_short)}${first.episode}"
|
||||||
)
|
else -> "${getString(R.string.episode)} ${first.episode}"
|
||||||
|
}
|
||||||
resultPlaySeries.setOnClickListener {
|
resultPlaySeriesButton.setOnClickListener {
|
||||||
viewModel.handleAction(
|
viewModel.handleAction(
|
||||||
EpisodeClickEvent(
|
EpisodeClickEvent(
|
||||||
ACTION_CLICK_DEFAULT,
|
ACTION_CLICK_DEFAULT,
|
||||||
|
@ -756,7 +797,7 @@ class ResultFragmentTv : Fragment() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
resultPlaySeries.setOnLongClickListener {
|
resultPlaySeriesButton.setOnLongClickListener {
|
||||||
viewModel.handleAction(
|
viewModel.handleAction(
|
||||||
EpisodeClickEvent(ACTION_SHOW_OPTIONS, first)
|
EpisodeClickEvent(ACTION_SHOW_OPTIONS, first)
|
||||||
)
|
)
|
||||||
|
@ -764,7 +805,9 @@ class ResultFragmentTv : Fragment() {
|
||||||
}
|
}
|
||||||
if (!hasLoadedEpisodesOnce) {
|
if (!hasLoadedEpisodesOnce) {
|
||||||
hasLoadedEpisodesOnce = true
|
hasLoadedEpisodesOnce = true
|
||||||
focusPlayButton()
|
resultPlaySeries.isVisible = resultResumeSeries.isGone && !comingSoon
|
||||||
|
resultEpisodesShow.isVisible = true && !comingSoon
|
||||||
|
resultPlaySeriesButton.requestFocus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -826,13 +869,23 @@ class ResultFragmentTv : Fragment() {
|
||||||
resultMetaYear.setText(d.yearText)
|
resultMetaYear.setText(d.yearText)
|
||||||
resultMetaDuration.setText(d.durationText)
|
resultMetaDuration.setText(d.durationText)
|
||||||
resultMetaRating.setText(d.ratingText)
|
resultMetaRating.setText(d.ratingText)
|
||||||
|
resultMetaStatus.setText(d.onGoingText)
|
||||||
resultMetaContentRating.setText(d.contentRatingText)
|
resultMetaContentRating.setText(d.contentRatingText)
|
||||||
resultCastText.setText(d.actorsText)
|
resultCastText.setText(d.actorsText)
|
||||||
resultNextAiring.setText(d.nextAiringEpisode)
|
resultNextAiring.setText(d.nextAiringEpisode)
|
||||||
resultNextAiringTime.setText(d.nextAiringDate)
|
resultNextAiringTime.setText(d.nextAiringDate)
|
||||||
resultPoster.setImage(d.posterImage)
|
resultPoster.setImage(d.posterImage)
|
||||||
resultDescription.setTextHtml(d.plotText)
|
|
||||||
resultDescription.setOnClickListener { view ->
|
var isExpanded = false
|
||||||
|
resultDescription.apply {
|
||||||
|
setTextHtml(d.plotText)
|
||||||
|
setOnClickListener {
|
||||||
|
if (isLayout(EMULATOR)) {
|
||||||
|
isExpanded = !isExpanded
|
||||||
|
maxLines = if (isExpanded) {
|
||||||
|
Integer.MAX_VALUE
|
||||||
|
} else 10
|
||||||
|
} else {
|
||||||
view.context?.let { ctx ->
|
view.context?.let { ctx ->
|
||||||
val builder: AlertDialog.Builder =
|
val builder: AlertDialog.Builder =
|
||||||
AlertDialog.Builder(ctx, R.style.AlertDialogCustom)
|
AlertDialog.Builder(ctx, R.style.AlertDialogCustom)
|
||||||
|
@ -841,6 +894,8 @@ class ResultFragmentTv : Fragment() {
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val error = listOf(
|
val error = listOf(
|
||||||
R.drawable.profile_bg_dark_blue,
|
R.drawable.profile_bg_dark_blue,
|
||||||
|
@ -859,8 +914,9 @@ class ResultFragmentTv : Fragment() {
|
||||||
radius = 0,
|
radius = 0,
|
||||||
errorImageDrawable = error
|
errorImageDrawable = error
|
||||||
)
|
)
|
||||||
resultComingSoon.isVisible = d.comingSoon
|
comingSoon = d.comingSoon
|
||||||
resultDataHolder.isGone = d.comingSoon
|
resultTvComingSoon.isVisible = d.comingSoon
|
||||||
|
|
||||||
UIHelper.populateChips(resultTag, d.tags)
|
UIHelper.populateChips(resultTag, d.tags)
|
||||||
resultCastItems.isGone = d.actors.isNullOrEmpty()
|
resultCastItems.isGone = d.actors.isNullOrEmpty()
|
||||||
(resultCastItems.adapter as? ActorAdaptor)?.updateList(
|
(resultCastItems.adapter as? ActorAdaptor)?.updateList(
|
||||||
|
@ -871,6 +927,10 @@ class ResultFragmentTv : Fragment() {
|
||||||
// If there is no rating to display, we don't want an empty gap
|
// If there is no rating to display, we don't want an empty gap
|
||||||
resultMetaContentRating.width = 0
|
resultMetaContentRating.width = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resultSearchButton.setOnClickListener {
|
||||||
|
QuickSearchFragment.pushSearch(activity, d.title)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is Resource.Loading -> {
|
is Resource.Loading -> {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import com.lagradost.cloudstream3.APIHolder.apis
|
||||||
import com.lagradost.cloudstream3.APIHolder.getId
|
import com.lagradost.cloudstream3.APIHolder.getId
|
||||||
import com.lagradost.cloudstream3.APIHolder.unixTime
|
import com.lagradost.cloudstream3.APIHolder.unixTime
|
||||||
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.context
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
import com.lagradost.cloudstream3.CommonActivity.activity
|
import com.lagradost.cloudstream3.CommonActivity.activity
|
||||||
import com.lagradost.cloudstream3.CommonActivity.getCastSession
|
import com.lagradost.cloudstream3.CommonActivity.getCastSession
|
||||||
|
@ -31,6 +32,7 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.isMovie
|
||||||
import com.lagradost.cloudstream3.metaproviders.SyncRedirector
|
import com.lagradost.cloudstream3.metaproviders.SyncRedirector
|
||||||
import com.lagradost.cloudstream3.mvvm.*
|
import com.lagradost.cloudstream3.mvvm.*
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.secondsToReadable
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
|
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.SimklApi
|
import com.lagradost.cloudstream3.syncproviders.providers.SimklApi
|
||||||
|
@ -79,12 +81,12 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.setSubscribedData
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setSubscribedData
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.setVideoWatchState
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setVideoWatchState
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.updateSubscribedData
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.updateSubscribedData
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
|
||||||
/** This starts at 1 */
|
/** This starts at 1 */
|
||||||
data class EpisodeRange(
|
data class EpisodeRange(
|
||||||
// used to index data
|
// used to index data
|
||||||
|
@ -261,8 +263,7 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData {
|
||||||
metaText =
|
metaText =
|
||||||
if (repo.providerType == ProviderType.MetaProvider) txt(R.string.provider_info_meta) else null,
|
if (repo.providerType == ProviderType.MetaProvider) txt(R.string.provider_info_meta) else null,
|
||||||
durationText = if (dur == null || dur <= 0) null else txt(
|
durationText = if (dur == null || dur <= 0) null else txt(
|
||||||
R.string.duration_format,
|
secondsToReadable(dur * 60, "0 mins")
|
||||||
dur
|
|
||||||
),
|
),
|
||||||
onGoingText = if (this is EpisodeResponse) {
|
onGoingText = if (this is EpisodeResponse) {
|
||||||
txt(
|
txt(
|
||||||
|
@ -927,15 +928,20 @@ class ResultViewModel2 : ViewModel() {
|
||||||
) {
|
) {
|
||||||
val isSubscribed = _subscribeStatus.value ?: return
|
val isSubscribed = _subscribeStatus.value ?: return
|
||||||
val response = currentResponse ?: return
|
val response = currentResponse ?: return
|
||||||
if (response !is EpisodeResponse) return
|
|
||||||
|
|
||||||
val currentId = currentId ?: 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) {
|
if (isSubscribed) {
|
||||||
removeSubscribedData(currentId)
|
removeSubscribedData(currentId)
|
||||||
statusChangedCallback?.invoke(false)
|
statusChangedCallback?.invoke(false)
|
||||||
_subscribeStatus.postValue(false)
|
_subscribeStatus.postValue(if (response is EpisodeResponse) false else null)
|
||||||
|
MainActivity.reloadLibraryEvent(true)
|
||||||
} else {
|
} else {
|
||||||
|
if (response !is EpisodeResponse) {
|
||||||
|
return
|
||||||
|
}
|
||||||
checkAndWarnDuplicates(
|
checkAndWarnDuplicates(
|
||||||
context,
|
context,
|
||||||
LibraryListType.SUBSCRIPTIONS,
|
LibraryListType.SUBSCRIPTIONS,
|
||||||
|
@ -980,8 +986,8 @@ class ResultViewModel2 : ViewModel() {
|
||||||
)
|
)
|
||||||
|
|
||||||
_subscribeStatus.postValue(true)
|
_subscribeStatus.postValue(true)
|
||||||
|
|
||||||
statusChangedCallback?.invoke(true)
|
statusChangedCallback?.invoke(true)
|
||||||
|
MainActivity.reloadLibraryEvent(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1692,14 +1698,8 @@ class ResultViewModel2 : ViewModel() {
|
||||||
LoadType.ExternalApp,
|
LoadType.ExternalApp,
|
||||||
txt(R.string.episode_action_copy_link)
|
txt(R.string.episode_action_copy_link)
|
||||||
) { (result, index) ->
|
) { (result, index) ->
|
||||||
val act = activity ?: return@acquireSingleLink
|
|
||||||
val serviceClipboard =
|
|
||||||
(act.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager?)
|
|
||||||
?: return@acquireSingleLink
|
|
||||||
val link = result.links[index]
|
val link = result.links[index]
|
||||||
val clip = ClipData.newPlainText(link.name, link.url)
|
clipboardHelper(txt(link.name), link.url)
|
||||||
serviceClipboard.setPrimaryClip(clip)
|
|
||||||
showToast(R.string.copy_link_toast, Toast.LENGTH_SHORT)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2051,12 +2051,15 @@ class ResultViewModel2 : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun postSubscription(loadResponse: LoadResponse) {
|
private fun postSubscription(loadResponse: LoadResponse) {
|
||||||
if (loadResponse.isEpisodeBased()) {
|
|
||||||
val id = loadResponse.getId()
|
val id = loadResponse.getId()
|
||||||
val data = getSubscribedData(id)
|
val data = getSubscribedData(id)
|
||||||
|
if (loadResponse.isEpisodeBased()) {
|
||||||
updateSubscribedData(id, data, loadResponse as? EpisodeResponse)
|
updateSubscribedData(id, data, loadResponse as? EpisodeResponse)
|
||||||
val isSubscribed = data != null
|
_subscribeStatus.postValue(data != null)
|
||||||
_subscribeStatus.postValue(isSubscribed)
|
}
|
||||||
|
// lets say that we have subscribed, then we must be able to unsubscribe no matter what
|
||||||
|
else if (data != null) {
|
||||||
|
_subscribeStatus.postValue(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2464,7 +2467,7 @@ class ResultViewModel2 : ViewModel() {
|
||||||
ResumeProgress(
|
ResumeProgress(
|
||||||
progress = (viewPos.position / 1000).toInt(),
|
progress = (viewPos.position / 1000).toInt(),
|
||||||
maxProgress = (viewPos.duration / 1000).toInt(),
|
maxProgress = (viewPos.duration / 1000).toInt(),
|
||||||
txt(R.string.resume_time_left, (viewPos.duration - viewPos.position) / (60_000))
|
txt(R.string.resume_remaining, secondsToReadable(((viewPos.duration - viewPos.position) / 1_000).toInt(), "0 mins"))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2590,6 +2593,7 @@ class ResultViewModel2 : ViewModel() {
|
||||||
override var posterHeaders: Map<String, String>? = null,
|
override var posterHeaders: Map<String, String>? = null,
|
||||||
override var backgroundPosterUrl: String? = null,
|
override var backgroundPosterUrl: String? = null,
|
||||||
override var contentRating: String? = null,
|
override var contentRating: String? = null,
|
||||||
|
val id : Int?,
|
||||||
) : LoadResponse
|
) : LoadResponse
|
||||||
|
|
||||||
fun loadSmall(activity: Activity?, searchResponse : SearchResponse) = ioSafe {
|
fun loadSmall(activity: Activity?, searchResponse : SearchResponse) = ioSafe {
|
||||||
|
@ -2599,7 +2603,7 @@ class ResultViewModel2 : ViewModel() {
|
||||||
val api = APIHolder.getApiFromNameNull(searchResponse.apiName) ?: APIHolder.getApiFromUrlNull(searchResponse.url) ?: APIRepository.noneApi
|
val api = APIHolder.getApiFromNameNull(searchResponse.apiName) ?: APIHolder.getApiFromUrlNull(searchResponse.url) ?: APIRepository.noneApi
|
||||||
val repo = APIRepository(api)
|
val repo = APIRepository(api)
|
||||||
val response = LoadResponseFromSearch(name = searchResponse.name, url = searchResponse.url, apiName = api.name, type = searchResponse.type ?: TvType.Others,
|
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) {
|
if (searchResponse is SyncAPI.LibraryItem) {
|
||||||
this.plot = searchResponse.plot
|
this.plot = searchResponse.plot
|
||||||
this.rating = searchResponse.personalRating?.times(100) ?: searchResponse.rating
|
this.rating = searchResponse.personalRating?.times(100) ?: searchResponse.rating
|
||||||
|
@ -2611,12 +2615,14 @@ class ResultViewModel2 : ViewModel() {
|
||||||
this.tags = searchResponse.tags
|
this.tags = searchResponse.tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val mainId = searchResponse.id ?: response.getId()
|
val mainId = response.getId()
|
||||||
|
|
||||||
postSuccessful(
|
postSuccessful(
|
||||||
loadResponse = response,
|
loadResponse = response,
|
||||||
mainId = mainId,
|
mainId = mainId,
|
||||||
apiRepository = repo, updateEpisodes = false, updateFillers = false)
|
apiRepository = repo,
|
||||||
|
updateEpisodes = false,
|
||||||
|
updateFillers = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun load(
|
fun load(
|
||||||
|
|
|
@ -6,7 +6,8 @@ import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.lagradost.cloudstream3.databinding.ResultSelectionBinding
|
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<UiText?, Any>
|
typealias SelectData = Pair<UiText?, Any>
|
||||||
|
|
||||||
|
@ -72,8 +73,7 @@ class SelectAdaptor(val callback: (Any) -> Unit) : RecyclerView.Adapter<Recycler
|
||||||
fun bind(
|
fun bind(
|
||||||
data: SelectData, isSelected: Boolean, callback: (Any) -> Unit
|
data: SelectData, isSelected: Boolean, callback: (Any) -> Unit
|
||||||
) {
|
) {
|
||||||
val isTrueTv = isTrueTvSettings()
|
if (isLayout(TV)) {
|
||||||
if (isTrueTv) {
|
|
||||||
item.isFocusable = true
|
item.isFocusable = true
|
||||||
item.isFocusableInTouchMode = true
|
item.isFocusableInTouchMode = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.home.ParentItemAdapter
|
||||||
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
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.ownHide
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.ownShow
|
import com.lagradost.cloudstream3.utils.AppUtils.ownShow
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
||||||
|
@ -107,13 +108,16 @@ class SearchFragment : Fragment() {
|
||||||
)
|
)
|
||||||
bottomSheetDialog?.ownShow()
|
bottomSheetDialog?.ownShow()
|
||||||
|
|
||||||
val layout = if (isTvSettings()) R.layout.fragment_search_tv else R.layout.fragment_search
|
|
||||||
|
|
||||||
|
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)
|
val root = inflater.inflate(layout, container, false)
|
||||||
// TODO TRYCATCH
|
FragmentSearchBinding.bind(root)
|
||||||
binding = FragmentSearchBinding.bind(root)
|
} catch (t : Throwable) {
|
||||||
|
FragmentSearchBinding.inflate(inflater)
|
||||||
|
}
|
||||||
|
|
||||||
return root
|
return binding?.root
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fixGrid() {
|
private fun fixGrid() {
|
||||||
|
@ -369,7 +373,7 @@ class SearchFragment : Fragment() {
|
||||||
|
|
||||||
selectedSearchTypes = DataStoreHelper.searchPreferenceTags.toMutableList()
|
selectedSearchTypes = DataStoreHelper.searchPreferenceTags.toMutableList()
|
||||||
|
|
||||||
if (isTrueTvSettings()) {
|
if (isLayout(TV)) {
|
||||||
binding?.searchFilter?.isFocusable = true
|
binding?.searchFilter?.isFocusable = true
|
||||||
binding?.searchFilter?.isFocusableInTouchMode = true
|
binding?.searchFilter?.isFocusableInTouchMode = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.lagradost.cloudstream3.ui.search
|
package com.lagradost.cloudstream3.ui.search
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.lagradost.cloudstream3.CommonActivity.activity
|
import com.lagradost.cloudstream3.CommonActivity.activity
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
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.DownloadButtonSetup.handleDownloadClick
|
||||||
import com.lagradost.cloudstream3.ui.download.DownloadClickEvent
|
import com.lagradost.cloudstream3.ui.download.DownloadClickEvent
|
||||||
import com.lagradost.cloudstream3.ui.result.START_ACTION_LOAD_EP
|
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.AppUtils.loadSearchResult
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||||
|
@ -56,7 +56,7 @@ object SearchHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SEARCH_ACTION_SHOW_METADATA -> {
|
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 {
|
(activity as? MainActivity?)?.apply {
|
||||||
loadPopup(callback.card)
|
loadPopup(callback.card)
|
||||||
} ?: kotlin.run {
|
} ?: kotlin.run {
|
||||||
|
|
|
@ -17,7 +17,8 @@ import com.lagradost.cloudstream3.SearchQuality
|
||||||
import com.lagradost.cloudstream3.SearchResponse
|
import com.lagradost.cloudstream3.SearchResponse
|
||||||
import com.lagradost.cloudstream3.isMovieType
|
import com.lagradost.cloudstream3.isMovieType
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
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.AppUtils.getNameFull
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual
|
||||||
|
@ -164,7 +165,7 @@ object SearchResultBuilder {
|
||||||
|
|
||||||
bg.isFocusable = false
|
bg.isFocusable = false
|
||||||
bg.isFocusableInTouchMode = false
|
bg.isFocusableInTouchMode = false
|
||||||
if(!isTrueTvSettings()) {
|
if(!isLayout(TV)) {
|
||||||
bg.setOnClickListener {
|
bg.setOnClickListener {
|
||||||
click(it)
|
click(it)
|
||||||
}
|
}
|
||||||
|
@ -207,7 +208,7 @@ object SearchResultBuilder {
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (isTrueTvSettings()) {
|
if (isLayout(TV)) {
|
||||||
// bg.isFocusable = true
|
// bg.isFocusable = true
|
||||||
// bg.isFocusableInTouchMode = true
|
// bg.isFocusableInTouchMode = true
|
||||||
// bg.touchscreenBlocksFocus = false
|
// bg.touchscreenBlocksFocus = false
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,8 +11,10 @@ import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
|
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
|
||||||
|
import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.databinding.AccountManagmentBinding
|
import com.lagradost.cloudstream3.databinding.AccountManagmentBinding
|
||||||
|
@ -27,12 +29,17 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.simklAp
|
||||||
import com.lagradost.cloudstream3.syncproviders.AuthAPI
|
import com.lagradost.cloudstream3.syncproviders.AuthAPI
|
||||||
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI
|
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI
|
||||||
import com.lagradost.cloudstream3.syncproviders.OAuth2API
|
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.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.setPaddingBottom
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
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.setUpToolbar
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.html
|
||||||
|
import com.lagradost.cloudstream3.utils.BackupUtils
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogText
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
|
@ -71,7 +78,7 @@ class SettingsAccount : PreferenceFragmentCompat() {
|
||||||
showAccountSwitch(activity, api)
|
showAccountSwitch(activity, api)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTvSettings()) {
|
if (isLayout(TV or EMULATOR)) {
|
||||||
binding.accountSwitchAccount.requestFocus()
|
binding.accountSwitchAccount.requestFocus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,7 +142,7 @@ class SettingsAccount : PreferenceFragmentCompat() {
|
||||||
binding.loginUsernameInput to api.requiresUsername
|
binding.loginUsernameInput to api.requiresUsername
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isTvSettings()) {
|
if (isLayout(TV or EMULATOR)) {
|
||||||
visibilityMap.forEach { (input, isVisible) ->
|
visibilityMap.forEach { (input, isVisible) ->
|
||||||
input.isVisible = isVisible
|
input.isVisible = isVisible
|
||||||
|
|
||||||
|
@ -256,6 +263,24 @@ class SettingsAccount : PreferenceFragmentCompat() {
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
setPreferencesFromResource(R.xml.settings_account, rootKey)
|
setPreferencesFromResource(R.xml.settings_account, rootKey)
|
||||||
|
|
||||||
|
getPref(R.string.biometric_key)?.setOnPreferenceClickListener {
|
||||||
|
val authEnabled = PreferenceManager.getDefaultSharedPreferences(
|
||||||
|
context ?: return@setOnPreferenceClickListener false
|
||||||
|
)
|
||||||
|
.getBoolean(getString(R.string.biometric_key), false)
|
||||||
|
|
||||||
|
if (authEnabled) {
|
||||||
|
BackupUtils.backup(activity)
|
||||||
|
val title = activity?.getString(R.string.biometric_setting)
|
||||||
|
val warning = activity?.getString(R.string.biometric_warning)
|
||||||
|
activity?.showBottomDialogText(
|
||||||
|
title as String,
|
||||||
|
warning.html()
|
||||||
|
) { onDialogDismissedEvent }
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
val syncApis =
|
val syncApis =
|
||||||
listOf(
|
listOf(
|
||||||
R.string.mal_key to malApi,
|
R.string.mal_key to malApi,
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
package com.lagradost.cloudstream3.ui.settings
|
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.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -16,7 +12,6 @@ import androidx.core.view.updateLayoutParams
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
|
@ -24,7 +19,12 @@ import com.lagradost.cloudstream3.databinding.MainSettingsBinding
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.accountManagers
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.accountManagers
|
||||||
import com.lagradost.cloudstream3.ui.home.HomeFragment
|
import com.lagradost.cloudstream3.ui.home.HomeFragment
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
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.navigate
|
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
||||||
|
@ -32,10 +32,6 @@ import java.io.File
|
||||||
|
|
||||||
class SettingsFragment : Fragment() {
|
class SettingsFragment : Fragment() {
|
||||||
companion object {
|
companion object {
|
||||||
var beneneCount = 0
|
|
||||||
|
|
||||||
private var isTv: Boolean = false
|
|
||||||
private var isTrueTv: Boolean = false
|
|
||||||
|
|
||||||
fun PreferenceFragmentCompat?.getPref(id: Int): Preference? {
|
fun PreferenceFragmentCompat?.getPref(id: Int): Preference? {
|
||||||
if (this == null) return null
|
if (this == null) return null
|
||||||
|
@ -52,12 +48,12 @@ class SettingsFragment : Fragment() {
|
||||||
* On TV you cannot properly scroll to the bottom of settings, this fixes that.
|
* On TV you cannot properly scroll to the bottom of settings, this fixes that.
|
||||||
* */
|
* */
|
||||||
fun PreferenceFragmentCompat.setPaddingBottom() {
|
fun PreferenceFragmentCompat.setPaddingBottom() {
|
||||||
if (isTvSettings()) {
|
if (isLayout(TV or EMULATOR)) {
|
||||||
listView?.setPadding(0, 0, 0, 100.toPx)
|
listView?.setPadding(0, 0, 0, 100.toPx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun PreferenceFragmentCompat.setToolBarScrollFlags() {
|
fun PreferenceFragmentCompat.setToolBarScrollFlags() {
|
||||||
if (isTvSettings()) {
|
if (isLayout(TV or EMULATOR)) {
|
||||||
val settingsAppbar = view?.findViewById<MaterialToolbar>(R.id.settings_toolbar)
|
val settingsAppbar = view?.findViewById<MaterialToolbar>(R.id.settings_toolbar)
|
||||||
|
|
||||||
settingsAppbar?.updateLayoutParams<AppBarLayout.LayoutParams> {
|
settingsAppbar?.updateLayoutParams<AppBarLayout.LayoutParams> {
|
||||||
|
@ -66,7 +62,7 @@ class SettingsFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun Fragment?.setToolBarScrollFlags() {
|
fun Fragment?.setToolBarScrollFlags() {
|
||||||
if (isTvSettings()) {
|
if (isLayout(TV or EMULATOR)) {
|
||||||
val settingsAppbar = this?.view?.findViewById<MaterialToolbar>(R.id.settings_toolbar)
|
val settingsAppbar = this?.view?.findViewById<MaterialToolbar>(R.id.settings_toolbar)
|
||||||
|
|
||||||
settingsAppbar?.updateLayoutParams<AppBarLayout.LayoutParams> {
|
settingsAppbar?.updateLayoutParams<AppBarLayout.LayoutParams> {
|
||||||
|
@ -85,7 +81,7 @@ class SettingsFragment : Fragment() {
|
||||||
activity?.onBackPressedDispatcher?.onBackPressed()
|
activity?.onBackPressedDispatcher?.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fixPaddingStatusbar(settingsToolbar)
|
UIHelper.fixPaddingStatusbar(settingsToolbar)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Fragment?.setUpToolbar(@StringRes title: Int) {
|
fun Fragment?.setUpToolbar(@StringRes title: Int) {
|
||||||
|
@ -100,7 +96,7 @@ class SettingsFragment : Fragment() {
|
||||||
activity?.onBackPressedDispatcher?.onBackPressed()
|
activity?.onBackPressedDispatcher?.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fixPaddingStatusbar(settingsToolbar)
|
UIHelper.fixPaddingStatusbar(settingsToolbar)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFolderSize(dir: File): Long {
|
fun getFolderSize(dir: File): Long {
|
||||||
|
@ -116,55 +112,7 @@ class SettingsFragment : Fragment() {
|
||||||
|
|
||||||
return size
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
override fun onDestroyView() {
|
||||||
binding = null
|
binding = null
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
@ -189,8 +137,6 @@ class SettingsFragment : Fragment() {
|
||||||
|
|
||||||
// used to debug leaks showToast(activity,"${VideoDownloadManager.downloadStatusEvent.size} : ${VideoDownloadManager.downloadProgressEvent.size}")
|
// used to debug leaks showToast(activity,"${VideoDownloadManager.downloadStatusEvent.size} : ${VideoDownloadManager.downloadProgressEvent.size}")
|
||||||
|
|
||||||
val isTrueTv = isTrueTvSettings()
|
|
||||||
|
|
||||||
for (syncApi in accountManagers) {
|
for (syncApi in accountManagers) {
|
||||||
val login = syncApi.loginInfo()
|
val login = syncApi.loginInfo()
|
||||||
val pic = login?.profilePicture ?: continue
|
val pic = login?.profilePicture ?: continue
|
||||||
|
@ -218,7 +164,7 @@ class SettingsFragment : Fragment() {
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
navigate(navigationId)
|
navigate(navigationId)
|
||||||
}
|
}
|
||||||
if (isTrueTv) {
|
if (isLayout(TV)) {
|
||||||
isFocusable = true
|
isFocusable = true
|
||||||
isFocusableInTouchMode = true
|
isFocusableInTouchMode = true
|
||||||
}
|
}
|
||||||
|
@ -226,9 +172,17 @@ class SettingsFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default focus on TV
|
// Default focus on TV
|
||||||
if (isTrueTv) {
|
if (isLayout(TV)) {
|
||||||
settingsGeneral.requestFocus()
|
settingsGeneral.requestFocus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val appVersion = getString(R.string.app_version)
|
||||||
|
val commitInfo = getString(R.string.commit_hash)
|
||||||
|
|
||||||
|
binding?.appVersionInfo?.setOnLongClickListener{
|
||||||
|
clipboardHelper(txt(R.string.extension_version), "$appVersion $commitInfo")
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
import com.lagradost.cloudstream3.network.initClient
|
import com.lagradost.cloudstream3.network.initClient
|
||||||
import com.lagradost.cloudstream3.ui.EasterEggMonke
|
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.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
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.setToolBarScrollFlags
|
||||||
|
@ -378,30 +379,30 @@ class SettingsGeneral : PreferenceFragmentCompat() {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SettingsFragment.beneneCount =
|
beneneCount =
|
||||||
settingsManager.getInt(getString(R.string.benene_count), 0)
|
settingsManager.getInt(getString(R.string.benene_count), 0)
|
||||||
getPref(R.string.benene_count)?.let { pref ->
|
getPref(R.string.benene_count)?.let { pref ->
|
||||||
pref.summary =
|
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
|
R.string.benene_count_text
|
||||||
).format(
|
).format(
|
||||||
SettingsFragment.beneneCount
|
beneneCount
|
||||||
)
|
)
|
||||||
|
|
||||||
pref.setOnPreferenceClickListener {
|
pref.setOnPreferenceClickListener {
|
||||||
try {
|
try {
|
||||||
SettingsFragment.beneneCount++
|
beneneCount++
|
||||||
if (SettingsFragment.beneneCount%20 == 0) {
|
if (beneneCount%20 == 0) {
|
||||||
val intent = Intent(context, EasterEggMonke::class.java)
|
val intent = Intent(context, EasterEggMonke::class.java)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
settingsManager.edit().putInt(
|
settingsManager.edit().putInt(
|
||||||
getString(R.string.benene_count),
|
getString(R.string.benene_count),
|
||||||
SettingsFragment.beneneCount
|
beneneCount
|
||||||
)
|
)
|
||||||
.apply()
|
.apply()
|
||||||
it.summary =
|
it.summary =
|
||||||
getString(R.string.benene_count_text).format(SettingsFragment.beneneCount)
|
getString(R.string.benene_count_text).format(beneneCount)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logError(e)
|
logError(e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,11 @@ import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.SearchQuality
|
import com.lagradost.cloudstream3.SearchQuality
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
|
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.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
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.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
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.showBottomDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
package com.lagradost.cloudstream3.ui.settings
|
package com.lagradost.cloudstream3.ui.settings
|
||||||
|
|
||||||
import android.content.ClipData
|
|
||||||
import android.content.ClipboardManager
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.TransactionTooLargeException
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
@ -20,6 +16,7 @@ import com.lagradost.cloudstream3.databinding.LogcatBinding
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.network.initClient
|
import com.lagradost.cloudstream3.network.initClient
|
||||||
import com.lagradost.cloudstream3.services.BackupWorkManager
|
import com.lagradost.cloudstream3.services.BackupWorkManager
|
||||||
|
import com.lagradost.cloudstream3.ui.result.txt
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
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.setPaddingBottom
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
|
@ -30,6 +27,7 @@ import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate
|
import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
||||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager
|
import com.lagradost.cloudstream3.utils.VideoDownloadManager
|
||||||
|
@ -117,22 +115,15 @@ class SettingsUpdates : PreferenceFragmentCompat() {
|
||||||
binding.text1.text = text
|
binding.text1.text = text
|
||||||
|
|
||||||
binding.copyBtt.setOnClickListener {
|
binding.copyBtt.setOnClickListener {
|
||||||
// Can crash on too much text
|
clipboardHelper(txt("Logcat"), text)
|
||||||
try {
|
|
||||||
val serviceClipboard =
|
|
||||||
(activity?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?)
|
|
||||||
?: return@setOnClickListener
|
|
||||||
val clip = ClipData.newPlainText("logcat", text)
|
|
||||||
serviceClipboard.setPrimaryClip(clip)
|
|
||||||
dialog.dismissSafe(activity)
|
dialog.dismissSafe(activity)
|
||||||
} catch (e: TransactionTooLargeException) {
|
|
||||||
showToast(R.string.clipboard_too_large)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.clearBtt.setOnClickListener {
|
binding.clearBtt.setOnClickListener {
|
||||||
Runtime.getRuntime().exec("logcat -c")
|
Runtime.getRuntime().exec("logcat -c")
|
||||||
dialog.dismissSafe(activity)
|
dialog.dismissSafe(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.saveBtt.setOnClickListener {
|
binding.saveBtt.setOnClickListener {
|
||||||
var fileStream: OutputStream? = null
|
var fileStream: OutputStream? = null
|
||||||
try {
|
try {
|
||||||
|
@ -153,9 +144,11 @@ class SettingsUpdates : PreferenceFragmentCompat() {
|
||||||
fileStream?.closeQuietly()
|
fileStream?.closeQuietly()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.closeBtt.setOnClickListener {
|
binding.closeBtt.setOnClickListener {
|
||||||
dialog.dismissSafe(activity)
|
dialog.dismissSafe(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
return@setOnPreferenceClickListener true
|
return@setOnPreferenceClickListener true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,8 @@ import com.lagradost.cloudstream3.plugins.RepositoryManager
|
||||||
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||||
import com.lagradost.cloudstream3.ui.result.setText
|
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.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.downloadAllPluginsDialog
|
import com.lagradost.cloudstream3.utils.AppUtils.downloadAllPluginsDialog
|
||||||
|
@ -97,7 +98,7 @@ class ExtensionsFragment : Fragment() {
|
||||||
nextLeft = R.id.nav_rail_view
|
nextLeft = R.id.nav_rail_view
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!isTrueTvSettings())
|
if (!isLayout(TV))
|
||||||
binding?.addRepoButton?.let { button ->
|
binding?.addRepoButton?.let { button ->
|
||||||
button.post {
|
button.post {
|
||||||
setPadding(
|
setPadding(
|
||||||
|
@ -286,7 +287,7 @@ class ExtensionsFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val isTv = isTrueTvSettings()
|
val isTv = isLayout(TV)
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
addRepoButton.isGone = isTv
|
addRepoButton.isGone = isTv
|
||||||
addRepoButtonImageviewHolder.isVisible = isTv
|
addRepoButtonImageviewHolder.isVisible = isTv
|
||||||
|
|
|
@ -17,7 +17,8 @@ import com.lagradost.cloudstream3.plugins.PluginManager
|
||||||
import com.lagradost.cloudstream3.plugins.VotingApi.getVotes
|
import com.lagradost.cloudstream3.plugins.VotingApi.getVotes
|
||||||
import com.lagradost.cloudstream3.ui.result.setText
|
import com.lagradost.cloudstream3.ui.result.setText
|
||||||
import com.lagradost.cloudstream3.ui.result.txt
|
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.AppUtils.html
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
|
@ -44,7 +45,7 @@ class PluginAdapter(
|
||||||
private val plugins: MutableList<PluginViewData> = mutableListOf()
|
private val plugins: MutableList<PluginViewData> = mutableListOf()
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
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)
|
val inflated = LayoutInflater.from(parent.context).inflate(layout, parent, false)
|
||||||
|
|
||||||
return PluginViewHolder(
|
return PluginViewHolder(
|
||||||
|
|
|
@ -17,7 +17,9 @@ import com.lagradost.cloudstream3.mvvm.observe
|
||||||
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips
|
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips
|
||||||
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
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.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import com.lagradost.cloudstream3.ui.settings.appLanguages
|
import com.lagradost.cloudstream3.ui.settings.appLanguages
|
||||||
|
@ -155,7 +157,7 @@ class PluginsFragment : Fragment() {
|
||||||
pluginViewModel.handlePluginAction(activity, url, it, isLocal)
|
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.
|
// Scrolling down does not reveal the whole RecyclerView on TV, add to bypass that.
|
||||||
binding?.pluginRecyclerView?.setPadding(0, 0, 0, 200.toPx)
|
binding?.pluginRecyclerView?.setPadding(0, 0, 0, 200.toPx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
package com.lagradost.cloudstream3.ui.settings.extensions
|
package com.lagradost.cloudstream3.ui.settings.extensions
|
||||||
|
|
||||||
import android.content.ClipData
|
|
||||||
import android.content.ClipboardManager
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import com.lagradost.cloudstream3.CommonActivity.activity
|
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.databinding.RepositoryItemBinding
|
import com.lagradost.cloudstream3.databinding.RepositoryItemBinding
|
||||||
import com.lagradost.cloudstream3.databinding.RepositoryItemTvBinding
|
import com.lagradost.cloudstream3.databinding.RepositoryItemTvBinding
|
||||||
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
|
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
import com.lagradost.cloudstream3.ui.result.txt
|
||||||
|
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(
|
class RepoAdapter(
|
||||||
val isSetup: Boolean,
|
val isSetup: Boolean,
|
||||||
|
@ -28,7 +24,7 @@ class RepoAdapter(
|
||||||
private val repositories: MutableList<RepositoryData> = mutableListOf()
|
private val repositories: MutableList<RepositoryData> = mutableListOf()
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
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),
|
LayoutInflater.from(parent.context),
|
||||||
parent,
|
parent,
|
||||||
false
|
false
|
||||||
|
@ -121,13 +117,9 @@ class RepoAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
repositoryItemRoot.setOnLongClickListener {
|
repositoryItemRoot.setOnLongClickListener {
|
||||||
val clipboardManager =
|
val shareableRepoData = "${repositoryData.name} : \n ${repositoryData.url}"
|
||||||
activity?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager?
|
clipboardHelper(txt(R.string.repo_copy_label), shareableRepoData)
|
||||||
clipboardManager?.setPrimaryClip(ClipData.newPlainText("RepoUrl", repositoryData.url))
|
true
|
||||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
|
|
||||||
showToast(R.string.copyRepoUrl, Toast.LENGTH_SHORT)
|
|
||||||
}
|
|
||||||
return@setOnLongClickListener true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mainText.text = repositoryData.name
|
mainText.text = repositoryData.name
|
||||||
|
|
|
@ -11,7 +11,8 @@ import com.lagradost.cloudstream3.databinding.FragmentTestingBinding
|
||||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
import com.lagradost.cloudstream3.mvvm.observeNullable
|
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.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
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?.isFocusableInTouchMode = true
|
||||||
providerTest.playPauseButton?.requestFocus()
|
providerTest.playPauseButton?.requestFocus()
|
||||||
}
|
}
|
||||||
|
@ -75,7 +76,7 @@ class TestFragment : Fragment() {
|
||||||
|
|
||||||
fun focusRecyclerView() {
|
fun focusRecyclerView() {
|
||||||
// Hack to make it possible to focus the recyclerview.
|
// Hack to make it possible to focus the recyclerview.
|
||||||
if (isTrueTvSettings()) {
|
if (isLayout(TV)) {
|
||||||
providerTestRecyclerView.requestFocus()
|
providerTestRecyclerView.requestFocus()
|
||||||
providerTestAppbar.setExpanded(false, true)
|
providerTestAppbar.setExpanded(false, true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ import android.view.ViewGroup
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import androidx.media3.common.text.Cue
|
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.google.android.gms.cast.TextTrackStyle.*
|
import com.google.android.gms.cast.TextTrackStyle.*
|
||||||
import com.jaredrummler.android.colorpicker.ColorPickerDialog
|
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.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.databinding.ChromecastSubtitleSettingsBinding
|
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.DataStore.setKey
|
||||||
import com.lagradost.cloudstream3.utils.Event
|
import com.lagradost.cloudstream3.utils.Event
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||||
|
@ -173,7 +175,7 @@ class ChromecastSubtitlesFragment : Fragment() {
|
||||||
state = getCurrentSavedStyle()
|
state = getCurrentSavedStyle()
|
||||||
context?.updateState()
|
context?.updateState()
|
||||||
|
|
||||||
val isTvSettings = isTvSettings()
|
val isTvSettings = isLayout(TV or EMULATOR)
|
||||||
|
|
||||||
fun View.setFocusableInTv() {
|
fun View.setFocusableInTv() {
|
||||||
this.isFocusableInTouchMode = isTvSettings
|
this.isFocusableInTouchMode = isTvSettings
|
||||||
|
|
|
@ -28,7 +28,9 @@ import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.databinding.SubtitleSettingsBinding
|
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.DataStore.setKey
|
||||||
import com.lagradost.cloudstream3.utils.Event
|
import com.lagradost.cloudstream3.utils.Event
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||||
|
@ -252,7 +254,7 @@ class SubtitlesFragment : Fragment() {
|
||||||
state = getCurrentSavedStyle()
|
state = getCurrentSavedStyle()
|
||||||
context?.updateState()
|
context?.updateState()
|
||||||
|
|
||||||
val isTvTrueSettings = isTrueTvSettings()
|
val isTvTrueSettings = isLayout(TV)
|
||||||
|
|
||||||
fun View.setFocusableInTv() {
|
fun View.setFocusableInTv() {
|
||||||
this.isFocusableInTouchMode = isTvTrueSettings
|
this.isFocusableInTouchMode = isTvTrueSettings
|
||||||
|
|
|
@ -61,8 +61,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStri
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
|
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
|
||||||
import com.lagradost.cloudstream3.ui.WebviewFragment
|
import com.lagradost.cloudstream3.ui.WebviewFragment
|
||||||
import com.lagradost.cloudstream3.ui.result.ResultFragment
|
import com.lagradost.cloudstream3.ui.result.ResultFragment
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
import com.lagradost.cloudstream3.ui.settings.Globals
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
|
||||||
import com.lagradost.cloudstream3.ui.settings.extensions.PluginsViewModel.Companion.downloadAll
|
import com.lagradost.cloudstream3.ui.settings.extensions.PluginsViewModel.Companion.downloadAll
|
||||||
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
|
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
|
@ -78,7 +77,6 @@ import okhttp3.Cache
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import kotlin.system.measureTimeMillis
|
|
||||||
|
|
||||||
object AppUtils {
|
object AppUtils {
|
||||||
fun RecyclerView.setMaxViewPoolSize(maxViewTypeId: Int, maxPoolSize: Int) {
|
fun RecyclerView.setMaxViewPoolSize(maxViewTypeId: Int, maxPoolSize: Int) {
|
||||||
|
@ -583,7 +581,7 @@ object AppUtils {
|
||||||
//private val viewModel: ResultViewModel by activityViewModels()
|
//private val viewModel: ResultViewModel by activityViewModels()
|
||||||
|
|
||||||
private fun getResultsId(): Int {
|
private fun getResultsId(): Int {
|
||||||
return if (isTvSettings()) {
|
return if (Globals.isLayout(Globals.TV or Globals.EMULATOR)) {
|
||||||
R.id.global_to_navigation_results_tv
|
R.id.global_to_navigation_results_tv
|
||||||
} else {
|
} else {
|
||||||
R.id.global_to_navigation_results_phone
|
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.
|
* Sets the focus to the negative button when in TV and Emulator layout.
|
||||||
**/
|
**/
|
||||||
fun AlertDialog.setDefaultFocus(buttonFocus: Int = DialogInterface.BUTTON_NEGATIVE) {
|
fun AlertDialog.setDefaultFocus(buttonFocus: Int = DialogInterface.BUTTON_NEGATIVE) {
|
||||||
if (!isTvSettings()) return
|
if (!Globals.isLayout(Globals.TV or Globals.EMULATOR)) return
|
||||||
this.getButton(buttonFocus).run {
|
this.getButton(buttonFocus).run {
|
||||||
isFocusableInTouchMode = true
|
isFocusableInTouchMode = true
|
||||||
requestFocus()
|
requestFocus()
|
||||||
|
|
|
@ -32,7 +32,6 @@ import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.getDefaultSharedPrefs
|
import com.lagradost.cloudstream3.utils.DataStore.getDefaultSharedPrefs
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.getSharedPrefs
|
import com.lagradost.cloudstream3.utils.DataStore.getSharedPrefs
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.mapper
|
import com.lagradost.cloudstream3.utils.DataStore.mapper
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.setKeyRaw
|
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.checkWrite
|
import com.lagradost.cloudstream3.utils.UIHelper.checkWrite
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.requestRW
|
import com.lagradost.cloudstream3.utils.UIHelper.requestRW
|
||||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.setupStream
|
import com.lagradost.cloudstream3.utils.VideoDownloadManager.setupStream
|
||||||
|
@ -65,12 +64,16 @@ object BackupUtils {
|
||||||
PLUGINS_KEY_LOCAL,
|
PLUGINS_KEY_LOCAL,
|
||||||
|
|
||||||
OPEN_SUBTITLES_USER_KEY,
|
OPEN_SUBTITLES_USER_KEY,
|
||||||
"nginx_user", // Nginx user key
|
|
||||||
|
DOWNLOAD_EPISODE_CACHE,
|
||||||
|
|
||||||
|
"biometric_key", // can lock down users if backup is shared on a incompatible device
|
||||||
|
"nginx_user" // Nginx user key
|
||||||
)
|
)
|
||||||
|
|
||||||
/** false if blacklisted key */
|
/** false if key should not be contained in backup */
|
||||||
private fun String.isTransferable(): Boolean {
|
private fun String.isTransferable(): Boolean {
|
||||||
return !nonTransferableKeys.contains(this)
|
return !nonTransferableKeys.any { this.contains(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private var restoreFileSelector: ActivityResultLauncher<Array<String>>? = null
|
private var restoreFileSelector: ActivityResultLauncher<Array<String>>? = null
|
||||||
|
@ -252,8 +255,12 @@ object BackupUtils {
|
||||||
map: Map<String, T>?,
|
map: Map<String, T>?,
|
||||||
isEditingAppSettings: Boolean = false
|
isEditingAppSettings: Boolean = false
|
||||||
) {
|
) {
|
||||||
map?.filter { it.key.isTransferable() }?.forEach {
|
val editor = DataStore.editor(this, isEditingAppSettings)
|
||||||
setKeyRaw(it.key, it.value, isEditingAppSettings)
|
map?.forEach {
|
||||||
|
if (it.key.isTransferable()) {
|
||||||
|
editor.setKeyRaw(it.key, it.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
editor.apply()
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,177 @@
|
||||||
|
package com.lagradost.cloudstream3.utils
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.KeyguardManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.biometric.BiometricManager
|
||||||
|
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
|
||||||
|
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK
|
||||||
|
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
|
||||||
|
import androidx.biometric.BiometricPrompt
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
|
|
||||||
|
object BiometricAuthenticator {
|
||||||
|
|
||||||
|
private const val MAX_FAILED_ATTEMPTS = 3
|
||||||
|
private var failedAttempts = 0
|
||||||
|
const val TAG = "cs3Auth"
|
||||||
|
|
||||||
|
private var biometricManager: BiometricManager? = null
|
||||||
|
var biometricPrompt: BiometricPrompt? = null
|
||||||
|
var promptInfo: BiometricPrompt.PromptInfo? = null
|
||||||
|
|
||||||
|
var authCallback: BiometricAuthCallback? = null // listen to authentication success
|
||||||
|
|
||||||
|
private fun initializeBiometrics(activity: Activity) {
|
||||||
|
val executor = ContextCompat.getMainExecutor(activity)
|
||||||
|
|
||||||
|
biometricManager = BiometricManager.from(activity)
|
||||||
|
|
||||||
|
biometricPrompt = BiometricPrompt(
|
||||||
|
activity as FragmentActivity,
|
||||||
|
executor,
|
||||||
|
object : BiometricPrompt.AuthenticationCallback() {
|
||||||
|
|
||||||
|
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
||||||
|
super.onAuthenticationError(errorCode, errString)
|
||||||
|
showToast("$errString")
|
||||||
|
Log.e(TAG, "$errorCode")
|
||||||
|
failedAttempts++
|
||||||
|
|
||||||
|
if (failedAttempts >= MAX_FAILED_ATTEMPTS) {
|
||||||
|
failedAttempts = 0
|
||||||
|
activity.finish()
|
||||||
|
} else {
|
||||||
|
failedAttempts = 0
|
||||||
|
activity.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||||
|
super.onAuthenticationSucceeded(result)
|
||||||
|
failedAttempts = 0
|
||||||
|
authCallback?.onAuthenticationSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAuthenticationFailed() {
|
||||||
|
super.onAuthenticationFailed()
|
||||||
|
failedAttempts++
|
||||||
|
if (failedAttempts >= MAX_FAILED_ATTEMPTS) {
|
||||||
|
failedAttempts = 0
|
||||||
|
activity.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
// authentication dialog prompt builder
|
||||||
|
private fun authenticationDialog(
|
||||||
|
activity: Activity,
|
||||||
|
title: Int,
|
||||||
|
setDeviceCred: Boolean,
|
||||||
|
) {
|
||||||
|
val description = activity.getString(R.string.biometric_prompt_description)
|
||||||
|
|
||||||
|
if (setDeviceCred) {
|
||||||
|
// For API level > 30, Newer API setAllowedAuthenticators is used
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
|
||||||
|
val authFlag = DEVICE_CREDENTIAL or BIOMETRIC_WEAK or BIOMETRIC_STRONG
|
||||||
|
promptInfo = BiometricPrompt.PromptInfo.Builder()
|
||||||
|
.setTitle(activity.getString(title))
|
||||||
|
.setDescription(description)
|
||||||
|
.setAllowedAuthenticators(authFlag)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// for apis < 30
|
||||||
|
promptInfo = BiometricPrompt.PromptInfo.Builder()
|
||||||
|
.setTitle(activity.getString(title))
|
||||||
|
.setDescription(description)
|
||||||
|
.setDeviceCredentialAllowed(true)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// fallback for A12+ when both fingerprint & Face unlock is absent but PIN is set
|
||||||
|
promptInfo = BiometricPrompt.PromptInfo.Builder()
|
||||||
|
.setTitle(activity.getString(title))
|
||||||
|
.setDescription(description)
|
||||||
|
.setDeviceCredentialAllowed(true)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isBiometricHardWareAvailable(): Boolean {
|
||||||
|
// authentication occurs only when this is true and device is truly capable
|
||||||
|
var result = false
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
|
||||||
|
when (biometricManager?.canAuthenticate(
|
||||||
|
DEVICE_CREDENTIAL or BIOMETRIC_STRONG or BIOMETRIC_WEAK
|
||||||
|
)) {
|
||||||
|
BiometricManager.BIOMETRIC_SUCCESS -> result = true
|
||||||
|
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> result = false
|
||||||
|
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> result = false
|
||||||
|
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> result = false
|
||||||
|
BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> result = true
|
||||||
|
BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> result = true
|
||||||
|
BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> result = false
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
when (biometricManager?.canAuthenticate()) {
|
||||||
|
BiometricManager.BIOMETRIC_SUCCESS -> result = true
|
||||||
|
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> result = false
|
||||||
|
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> result = false
|
||||||
|
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> result = false
|
||||||
|
BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> result = true
|
||||||
|
BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> result = true
|
||||||
|
BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> result = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if device is secured i.e has at least some type of lock
|
||||||
|
fun deviceHasPasswordPinLock(context: Context?): Boolean {
|
||||||
|
val keyMgr =
|
||||||
|
context?.getSystemService(AppCompatActivity.KEYGUARD_SERVICE) as? KeyguardManager
|
||||||
|
return keyMgr?.isKeyguardSecure ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to start authentication in any fragment or activity
|
||||||
|
fun startBiometricAuthentication(activity: Activity, title: Int, setDeviceCred: Boolean) {
|
||||||
|
initializeBiometrics(activity)
|
||||||
|
|
||||||
|
if (isBiometricHardWareAvailable()) {
|
||||||
|
authCallback = activity as? BiometricAuthCallback
|
||||||
|
authenticationDialog(activity, title, setDeviceCred)
|
||||||
|
promptInfo?.let { biometricPrompt?.authenticate(it) }
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (deviceHasPasswordPinLock(activity)) {
|
||||||
|
authCallback = activity as? BiometricAuthCallback
|
||||||
|
authenticationDialog(activity, R.string.password_pin_authentication_title, true)
|
||||||
|
promptInfo?.let { biometricPrompt?.authenticate(it) }
|
||||||
|
|
||||||
|
} else {
|
||||||
|
showToast(R.string.biometric_unsupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BiometricAuthCallback {
|
||||||
|
fun onAuthenticationSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,6 +50,28 @@ class PreferenceDelegate<T : Any>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** When inserting many keys use this function, this is because apply for every key is very expensive on memory */
|
||||||
|
data class Editor(
|
||||||
|
val editor : SharedPreferences.Editor
|
||||||
|
) {
|
||||||
|
/** Always remember to call apply after */
|
||||||
|
fun<T> setKeyRaw(path: String, value: T) {
|
||||||
|
when (value) {
|
||||||
|
is Boolean -> editor.putBoolean(path, value)
|
||||||
|
is Int -> editor.putInt(path, value)
|
||||||
|
is String -> editor.putString(path, value)
|
||||||
|
is Float -> editor.putFloat(path, value)
|
||||||
|
is Long -> editor.putLong(path, value)
|
||||||
|
(value as? Set<String> != null) -> editor.putStringSet(path, value as Set<String>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun apply() {
|
||||||
|
editor.apply()
|
||||||
|
System.gc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object DataStore {
|
object DataStore {
|
||||||
val mapper: JsonMapper = JsonMapper.builder().addModule(kotlinModule())
|
val mapper: JsonMapper = JsonMapper.builder().addModule(kotlinModule())
|
||||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()
|
||||||
|
@ -66,22 +88,10 @@ object DataStore {
|
||||||
return "${folder}/${path}"
|
return "${folder}/${path}"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> Context.setKeyRaw(path: String, value: T, isEditingAppSettings: Boolean = false) {
|
fun editor(context : Context, isEditingAppSettings: Boolean = false) : Editor {
|
||||||
try {
|
|
||||||
val editor: SharedPreferences.Editor =
|
val editor: SharedPreferences.Editor =
|
||||||
if (isEditingAppSettings) getDefaultSharedPrefs().edit() else getSharedPrefs().edit()
|
if (isEditingAppSettings) context.getDefaultSharedPrefs().edit() else context.getSharedPrefs().edit()
|
||||||
when (value) {
|
return Editor(editor)
|
||||||
is Boolean -> editor.putBoolean(path, value)
|
|
||||||
is Int -> editor.putInt(path, value)
|
|
||||||
is String -> editor.putString(path, value)
|
|
||||||
is Float -> editor.putFloat(path, value)
|
|
||||||
is Long -> editor.putLong(path, value)
|
|
||||||
(value as? Set<String> != null) -> editor.putStringSet(path, value as Set<String>)
|
|
||||||
}
|
|
||||||
editor.apply()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logError(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.getDefaultSharedPrefs(): SharedPreferences {
|
fun Context.getDefaultSharedPrefs(): SharedPreferences {
|
||||||
|
|
|
@ -21,7 +21,9 @@ import com.lagradost.cloudstream3.databinding.BottomInputDialogBinding
|
||||||
import com.lagradost.cloudstream3.databinding.BottomSelectionDialogBinding
|
import com.lagradost.cloudstream3.databinding.BottomSelectionDialogBinding
|
||||||
import com.lagradost.cloudstream3.databinding.BottomTextDialogBinding
|
import com.lagradost.cloudstream3.databinding.BottomTextDialogBinding
|
||||||
import com.lagradost.cloudstream3.databinding.OptionsPopupTvBinding
|
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.dismissSafe
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
|
@ -54,7 +56,7 @@ object SingleSelectionHelper {
|
||||||
) {
|
) {
|
||||||
if (this == null) return
|
if (this == null) return
|
||||||
|
|
||||||
if (isTvSettings()) {
|
if (isLayout(TV or EMULATOR)) {
|
||||||
val binding = OptionsPopupTvBinding.inflate(layoutInflater)
|
val binding = OptionsPopupTvBinding.inflate(layoutInflater)
|
||||||
val dialog = AlertDialog.Builder(this, R.style.AlertDialogCustom)
|
val dialog = AlertDialog.Builder(this, R.style.AlertDialogCustom)
|
||||||
.setView(binding.root)
|
.setView(binding.root)
|
||||||
|
|
|
@ -5,6 +5,8 @@ import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.AppOpsManager
|
import android.app.AppOpsManager
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
@ -14,12 +16,15 @@ import android.graphics.Color
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.TransactionTooLargeException
|
||||||
|
import android.util.Log
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.view.ViewGroup.MarginLayoutParams
|
import android.view.ViewGroup.MarginLayoutParams
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.ListAdapter
|
import android.widget.ListAdapter
|
||||||
import android.widget.ListView
|
import android.widget.ListView
|
||||||
|
import android.widget.Toast.LENGTH_LONG
|
||||||
import androidx.annotation.AttrRes
|
import androidx.annotation.AttrRes
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
|
@ -30,14 +35,12 @@ import androidx.appcompat.view.menu.MenuBuilder
|
||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.content.getSystemService
|
||||||
import androidx.core.graphics.alpha
|
import androidx.core.graphics.alpha
|
||||||
import androidx.core.graphics.blue
|
import androidx.core.graphics.blue
|
||||||
import androidx.core.graphics.drawable.toBitmapOrNull
|
import androidx.core.graphics.drawable.toBitmapOrNull
|
||||||
import androidx.core.graphics.green
|
import androidx.core.graphics.green
|
||||||
import androidx.core.graphics.red
|
import androidx.core.graphics.red
|
||||||
import androidx.core.view.WindowCompat
|
|
||||||
import androidx.core.view.WindowInsetsCompat
|
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
|
||||||
import androidx.core.view.marginBottom
|
import androidx.core.view.marginBottom
|
||||||
import androidx.core.view.marginLeft
|
import androidx.core.view.marginLeft
|
||||||
import androidx.core.view.marginRight
|
import androidx.core.view.marginRight
|
||||||
|
@ -58,17 +61,20 @@ import com.bumptech.glide.request.target.Target
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
import com.google.android.material.chip.ChipDrawable
|
import com.google.android.material.chip.ChipDrawable
|
||||||
import com.google.android.material.chip.ChipGroup
|
import com.google.android.material.chip.ChipGroup
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.context
|
||||||
import com.lagradost.cloudstream3.CommonActivity.activity
|
import com.lagradost.cloudstream3.CommonActivity.activity
|
||||||
import com.lagradost.cloudstream3.MainActivity
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.ui.result.UiImage
|
import com.lagradost.cloudstream3.ui.result.UiImage
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
|
import com.lagradost.cloudstream3.ui.result.UiText
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
import com.lagradost.cloudstream3.ui.result.txt
|
||||||
|
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 jp.wasabeef.glide.transformations.BlurTransformation
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
|
||||||
object UIHelper {
|
object UIHelper {
|
||||||
val Int.toPx: Int get() = (this * Resources.getSystem().displayMetrics.density).toInt()
|
val Int.toPx: Int get() = (this * Resources.getSystem().displayMetrics.density).toInt()
|
||||||
val Float.toPx: Float get() = (this * Resources.getSystem().displayMetrics.density)
|
val Float.toPx: Float get() = (this * Resources.getSystem().displayMetrics.density)
|
||||||
|
@ -123,6 +129,35 @@ object UIHelper {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clipboardHelper(label: UiText, text: CharSequence) {
|
||||||
|
val ctx = context ?: return
|
||||||
|
try {
|
||||||
|
ctx.let {
|
||||||
|
val clip = ClipData.newPlainText(label.asString(ctx), text)
|
||||||
|
val labelSuffix = txt(R.string.toast_copied).asString(ctx)
|
||||||
|
ctx.getSystemService<ClipboardManager>()?.setPrimaryClip(clip)
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
|
||||||
|
showToast("${label.asString(ctx)} $labelSuffix")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Log.e("ClipboardService", "$t")
|
||||||
|
when (t) {
|
||||||
|
is SecurityException -> {
|
||||||
|
showToast(R.string.clipboard_permission_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
is TransactionTooLargeException -> {
|
||||||
|
showToast(R.string.clipboard_too_large)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
showToast(R.string.clipboard_unknown_error, LENGTH_LONG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets ListView height dynamically based on the height of the items.
|
* Sets ListView height dynamically based on the height of the items.
|
||||||
|
@ -434,7 +469,7 @@ object UIHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.getStatusBarHeight(): Int {
|
fun Context.getStatusBarHeight(): Int {
|
||||||
if (isTvSettings()) {
|
if (isLayout(Globals.TV or EMULATOR)) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,12 +567,11 @@ object UIHelper {
|
||||||
WindowInsetsControllerCompat(window, View(this)).show(WindowInsetsCompat.Type.systemBars())
|
WindowInsetsControllerCompat(window, View(this)).show(WindowInsetsCompat.Type.systemBars())
|
||||||
|
|
||||||
} else {*/ /** WINDOW COMPAT IS BUGGY DUE TO FU*KED UP PLAYER AND TRAILERS **/
|
} else {*/ /** WINDOW COMPAT IS BUGGY DUE TO FU*KED UP PLAYER AND TRAILERS **/
|
||||||
Suppress("DEPRECATION")
|
|
||||||
window.decorView.systemUiVisibility =
|
window.decorView.systemUiVisibility =
|
||||||
(View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
|
(View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
changeStatusBarState(isEmulatorSettings())
|
changeStatusBarState(isLayout(EMULATOR))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.shouldShowPIPMode(isInPlayer: Boolean): Boolean {
|
fun Context.shouldShowPIPMode(isInPlayer: Boolean): Boolean {
|
||||||
|
|
10
app/src/main/res/drawable/ic_baseline_film_roll_24.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_film_roll_24.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M240,760L240,800Q240,817 228.5,828.5Q217,840 200,840Q183,840 171.5,828.5Q160,817 160,800L160,160Q160,143 171.5,131.5Q183,120 200,120Q217,120 228.5,131.5Q240,143 240,160L240,200L320,200L320,160Q320,143 331.5,131.5Q343,120 360,120L600,120Q617,120 628.5,131.5Q640,143 640,160L640,200L720,200L720,160Q720,143 731.5,131.5Q743,120 760,120Q777,120 788.5,131.5Q800,143 800,160L800,800Q800,817 788.5,828.5Q777,840 760,840Q743,840 731.5,828.5Q720,817 720,800L720,760L640,760L640,800Q640,817 628.5,828.5Q617,840 600,840L360,840Q343,840 331.5,828.5Q320,817 320,800L320,760L240,760ZM240,680L320,680L320,600L240,600L240,680ZM240,520L320,520L320,440L240,440L240,520ZM240,360L320,360L320,280L240,280L240,360ZM640,680L720,680L720,600L640,600L640,680ZM640,520L720,520L720,440L640,440L640,520ZM640,360L720,360L720,280L640,280L640,360Z"/>
|
||||||
|
</vector>
|
11
app/src/main/res/drawable/ic_baseline_resume_arrow.xml
Normal file
11
app/src/main/res/drawable/ic_baseline_resume_arrow.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="m8.46,5l0,14l11,-7l-11,-7z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="0.006"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
12
app/src/main/res/drawable/ic_baseline_resume_arrow2.xml
Normal file
12
app/src/main/res/drawable/ic_baseline_resume_arrow2.xml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="m8.46,5l0,14l11,-7l-11,-7z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M4.92,5.04h2.31v13.98h-2.31z"/>
|
||||||
|
</vector>
|
11
app/src/main/res/drawable/ic_fingerprint.xml
Normal file
11
app/src/main/res/drawable/ic_fingerprint.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/white"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:width="24dp">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M17.81,4.47c-0.08,0 -0.16,-0.02 -0.23,-0.06C15.66,3.42 14,3 12.01,3c-1.98,0 -3.86,0.47 -5.57,1.41 -0.24,0.13 -0.54,0.04 -0.68,-0.2 -0.13,-0.24 -0.04,-0.55 0.2,-0.68C7.82,2.52 9.86,2 12.01,2c2.13,0 3.99,0.47 6.03,1.52 0.25,0.13 0.34,0.43 0.21,0.67 -0.09,0.18 -0.26,0.28 -0.44,0.28zM3.5,9.72c-0.1,0 -0.2,-0.03 -0.29,-0.09 -0.23,-0.16 -0.28,-0.47 -0.12,-0.7 0.99,-1.4 2.25,-2.5 3.75,-3.27C9.98,4.04 14,4.03 17.15,5.65c1.5,0.77 2.76,1.86 3.75,3.25 0.16,0.22 0.11,0.54 -0.12,0.7 -0.23,0.16 -0.54,0.11 -0.7,-0.12 -0.9,-1.26 -2.04,-2.25 -3.39,-2.94 -2.87,-1.47 -6.54,-1.47 -9.4,0.01 -1.36,0.7 -2.5,1.7 -3.4,2.96 -0.08,0.14 -0.23,0.21 -0.39,0.21zM9.75,21.79c-0.13,0 -0.26,-0.05 -0.35,-0.15 -0.87,-0.87 -1.34,-1.43 -2.01,-2.64 -0.69,-1.23 -1.05,-2.73 -1.05,-4.34 0,-2.97 2.54,-5.39 5.66,-5.39s5.66,2.42 5.66,5.39c0,0.28 -0.22,0.5 -0.5,0.5s-0.5,-0.22 -0.5,-0.5c0,-2.42 -2.09,-4.39 -4.66,-4.39s-4.66,1.97 -4.66,4.39c0,1.44 0.32,2.77 0.93,3.85 0.64,1.15 1.08,1.64 1.85,2.42 0.19,0.2 0.19,0.51 0,0.71 -0.11,0.1 -0.24,0.15 -0.37,0.15zM16.92,19.94c-1.19,0 -2.24,-0.3 -3.1,-0.89 -1.49,-1.01 -2.38,-2.65 -2.38,-4.39 0,-0.28 0.22,-0.5 0.5,-0.5s0.5,0.22 0.5,0.5c0,1.41 0.72,2.74 1.94,3.56 0.71,0.48 1.54,0.71 2.54,0.71 0.24,0 0.64,-0.03 1.04,-0.1 0.27,-0.05 0.53,0.13 0.58,0.41 0.05,0.27 -0.13,0.53 -0.41,0.58 -0.57,0.11 -1.07,0.12 -1.21,0.12zM14.91,22c-0.04,0 -0.09,-0.01 -0.13,-0.02 -1.59,-0.44 -2.63,-1.03 -3.72,-2.1 -1.4,-1.39 -2.17,-3.24 -2.17,-5.22 0,-1.62 1.38,-2.94 3.08,-2.94s3.08,1.32 3.08,2.94c0,1.07 0.93,1.94 2.08,1.94s2.08,-0.87 2.08,-1.94c0,-3.77 -3.25,-6.83 -7.25,-6.83 -2.84,0 -5.44,1.58 -6.61,4.03 -0.39,0.81 -0.59,1.76 -0.59,2.8 0,0.78 0.07,2.01 0.67,3.61 0.1,0.26 -0.03,0.55 -0.29,0.64 -0.26,0.1 -0.55,-0.04 -0.64,-0.29 -0.49,-1.31 -0.73,-2.61 -0.73,-3.96 0,-1.2 0.23,-2.29 0.68,-3.24 1.33,-2.79 4.28,-4.6 7.51,-4.6 4.55,0 8.25,3.51 8.25,7.83 0,1.62 -1.38,2.94 -3.08,2.94s-3.08,-1.32 -3.08,-2.94c0,-1.07 -0.93,-1.94 -2.08,-1.94s-2.08,0.87 -2.08,1.94c0,1.71 0.66,3.31 1.87,4.51 0.95,0.94 1.86,1.46 3.27,1.85 0.27,0.07 0.42,0.35 0.35,0.61 -0.05,0.23 -0.26,0.38 -0.47,0.38z"/>
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/library_icon.xml
Normal file
10
app/src/main/res/drawable/library_icon.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/white">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M160,800Q127,800 103.5,776.5Q80,753 80,720L80,240Q80,207 103.5,183.5Q127,160 160,160L400,160L480,240L800,240Q833,240 856.5,263.5Q880,287 880,320L447,320L367,240L160,240Q160,240 160,240Q160,240 160,240L160,720Q160,720 160,720Q160,720 160,720L256,400L940,400L837,743Q829,769 807.5,784.5Q786,800 760,800L160,800ZM244,720L760,720L832,480L316,480L244,720ZM244,720L316,480L316,480L244,720L244,720ZM160,320L160,240Q160,240 160,240Q160,240 160,240L160,240L160,320L160,320Z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/outline_bookmark_add_24.xml
Normal file
5
app/src/main/res/drawable/outline_bookmark_add_24.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M17,11v6.97l-5,-2.14l-5,2.14V5h6V3H7C5.9,3 5,3.9 5,5v16l7,-3l7,3V11H17zM21,7h-2v2h-2V7h-2V5h2V3h2v2h2V7z"/>
|
||||||
|
</vector>
|
|
@ -3,13 +3,13 @@
|
||||||
<item android:state_focused="true">
|
<item android:state_focused="true">
|
||||||
<shape android:shape="rectangle">
|
<shape android:shape="rectangle">
|
||||||
<solid android:color="?attr/white" />
|
<solid android:color="?attr/white" />
|
||||||
<corners android:radius="3dp"/>
|
<corners android:radius="@dimen/rounded_image_radius"/>
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<shape android:shape="rectangle">
|
<shape android:shape="rectangle">
|
||||||
<solid android:color="@color/white_attr_20" />
|
<solid android:color="@color/white_attr_20" />
|
||||||
<corners android:radius="3dp"/>
|
<corners android:radius="@dimen/rounded_image_radius"/>
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
</selector>
|
</selector>
|
|
@ -3,7 +3,7 @@
|
||||||
<item android:state_focused="true">
|
<item android:state_focused="true">
|
||||||
<shape android:shape="rectangle">
|
<shape android:shape="rectangle">
|
||||||
<solid android:color="?attr/white" />
|
<solid android:color="?attr/white" />
|
||||||
<corners android:radius="3dp"/>
|
<corners android:radius="@dimen/rounded_image_radius"/>
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
</selector>
|
</selector>
|
|
@ -41,22 +41,34 @@
|
||||||
android:layout_marginStart="10dp"
|
android:layout_marginStart="10dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<FrameLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/resultview_preview_title"
|
android:id="@+id/resultview_preview_title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start|center_vertical"
|
||||||
|
android:layout_weight="1"
|
||||||
android:textColor="?attr/textColor"
|
android:textColor="?attr/textColor"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:layout_gravity="start|center_vertical"
|
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:layout_marginEnd="25dp"
|
tools:text="The Perfect Run" />
|
||||||
tools:text="The Perfect Run">
|
|
||||||
|
|
||||||
</TextView>
|
<ImageView
|
||||||
|
android:id="@+id/resultview_preview_subscribe"
|
||||||
|
android:layout_width="25dp"
|
||||||
|
android:layout_height="25dp"
|
||||||
|
android:layout_gravity="end|center_vertical"
|
||||||
|
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/subscribe_tooltip"
|
||||||
|
android:elevation="10dp"
|
||||||
|
android:nextFocusDown="@id/resultview_preview_bookmark"
|
||||||
|
android:src="@drawable/baseline_notifications_none_24"
|
||||||
|
app:tint="?attr/textColor" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/resultview_preview_favorite"
|
android:id="@+id/resultview_preview_favorite"
|
||||||
|
@ -66,11 +78,13 @@
|
||||||
|
|
||||||
android:layout_margin="5dp"
|
android:layout_margin="5dp"
|
||||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/favorite"
|
||||||
android:elevation="10dp"
|
android:elevation="10dp"
|
||||||
android:nextFocusDown="@id/resultview_preview_bookmark"
|
android:nextFocusDown="@id/resultview_preview_bookmark"
|
||||||
android:src="@drawable/ic_baseline_favorite_border_24"
|
android:src="@drawable/ic_baseline_favorite_border_24"
|
||||||
app:tint="?attr/textColor" />
|
app:tint="?attr/textColor" />
|
||||||
</FrameLayout>
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<com.lagradost.cloudstream3.widget.FlowLayout
|
<com.lagradost.cloudstream3.widget.FlowLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -134,38 +148,38 @@
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:padding="7dp"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="7dp">
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/resultview_preview_bookmark"
|
android:id="@+id/resultview_preview_bookmark"
|
||||||
|
style="@style/BlackButton"
|
||||||
|
android:layout_width="50dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:nextFocusUp="@id/resultview_preview_favorite"
|
|
||||||
android:nextFocusRight="@id/resultview_preview_more_info"
|
android:nextFocusRight="@id/resultview_preview_more_info"
|
||||||
|
|
||||||
tools:visibility="visible"
|
android:nextFocusUp="@id/resultview_preview_favorite"
|
||||||
|
|
||||||
app:icon="@drawable/ic_baseline_bookmark_24"
|
app:icon="@drawable/ic_baseline_bookmark_24"
|
||||||
tools:text="Bookmark"
|
tools:text="Bookmark"
|
||||||
style="@style/BlackButton"
|
|
||||||
|
|
||||||
android:layout_width="50dp" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/resultview_preview_more_info"
|
android:id="@+id/resultview_preview_more_info"
|
||||||
|
style="@style/WhiteButton"
|
||||||
|
android:layout_width="50dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:nextFocusUp="@id/resultview_preview_favorite"
|
|
||||||
android:nextFocusLeft="@id/resultview_preview_bookmark"
|
android:nextFocusLeft="@id/resultview_preview_bookmark"
|
||||||
|
|
||||||
tools:visibility="visible"
|
android:nextFocusUp="@id/resultview_preview_favorite"
|
||||||
|
|
||||||
app:icon="@drawable/ic_baseline_open_in_new_24"
|
|
||||||
android:text="@string/home_more_info"
|
android:text="@string/home_more_info"
|
||||||
style="@style/WhiteButton"
|
app:icon="@drawable/ic_baseline_open_in_new_24"
|
||||||
|
|
||||||
android:layout_width="50dp" />
|
tools:visibility="visible" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
android:layout_width="100dp"
|
android:layout_width="100dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:focusable="true"
|
|
||||||
android:padding="5dp">
|
android:padding="5dp">
|
||||||
<!--app:cardCornerRadius="@dimen/roundedImageRadius"-->
|
<!--app:cardCornerRadius="@dimen/roundedImageRadius"-->
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
|
|
|
@ -157,6 +157,7 @@
|
||||||
android:id="@+id/home_preview_hidden_prev_focus"
|
android:id="@+id/home_preview_hidden_prev_focus"
|
||||||
android:layout_width="1dp"
|
android:layout_width="1dp"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
|
android:tag="@string/tv_no_focus_tag"
|
||||||
android:focusable="false" />
|
android:focusable="false" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
|
@ -189,6 +190,7 @@
|
||||||
android:id="@+id/home_preview_hidden_next_focus"
|
android:id="@+id/home_preview_hidden_next_focus"
|
||||||
android:layout_width="1dp"
|
android:layout_width="1dp"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
|
android:tag="@string/tv_no_focus_tag"
|
||||||
android:focusable="false" />
|
android:focusable="false" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -409,10 +409,10 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
|
android:maxLines="10"
|
||||||
android:foreground="@drawable/outline_drawable"
|
android:foreground="@drawable/outline_drawable"
|
||||||
android:maxLength="1000"
|
|
||||||
android:nextFocusUp="@id/result_back"
|
android:nextFocusUp="@id/result_back"
|
||||||
android:nextFocusDown="@id/result_bookmark_button"
|
android:nextFocusDown="@id/result_bookmark_Button"
|
||||||
android:paddingTop="5dp"
|
android:paddingTop="5dp"
|
||||||
android:textColor="?attr/textColor"
|
android:textColor="?attr/textColor"
|
||||||
android:textSize="15sp"
|
android:textSize="15sp"
|
||||||
|
@ -474,7 +474,7 @@
|
||||||
android:fadingEdge="horizontal"
|
android:fadingEdge="horizontal"
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
android:focusableInTouchMode="false"
|
android:focusableInTouchMode="false"
|
||||||
android:nextFocusUp="@id/result_bookmark_button"
|
android:nextFocusUp="@id/result_bookmark_Button"
|
||||||
android:nextFocusDown="@id/result_play_movie"
|
android:nextFocusDown="@id/result_play_movie"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingTop="5dp"
|
android:paddingTop="5dp"
|
||||||
|
@ -580,7 +580,7 @@
|
||||||
android:layout_marginStart="0dp"
|
android:layout_marginStart="0dp"
|
||||||
android:layout_marginEnd="0dp"
|
android:layout_marginEnd="0dp"
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:nextFocusUp="@id/result_bookmark_button"
|
android:nextFocusUp="@id/result_bookmark_Button"
|
||||||
android:nextFocusDown="@id/result_download_movie"
|
android:nextFocusDown="@id/result_download_movie"
|
||||||
android:text="@string/play_movie_button"
|
android:text="@string/play_movie_button"
|
||||||
android:visibility="visible"
|
android:visibility="visible"
|
||||||
|
@ -658,7 +658,7 @@
|
||||||
android:layout_marginStart="0dp"
|
android:layout_marginStart="0dp"
|
||||||
android:layout_marginEnd="0dp"
|
android:layout_marginEnd="0dp"
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:nextFocusUp="@id/result_bookmark_button"
|
android:nextFocusUp="@id/result_bookmark_Button"
|
||||||
android:nextFocusDown="@id/result_download_movie"
|
android:nextFocusDown="@id/result_download_movie"
|
||||||
android:text="@string/resume"
|
android:text="@string/resume"
|
||||||
android:visibility="visible"
|
android:visibility="visible"
|
||||||
|
@ -674,7 +674,7 @@
|
||||||
android:layout_marginStart="0dp"
|
android:layout_marginStart="0dp"
|
||||||
android:layout_marginEnd="0dp"
|
android:layout_marginEnd="0dp"
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:nextFocusUp="@id/result_bookmark_button"
|
android:nextFocusUp="@id/result_bookmark_Button"
|
||||||
android:nextFocusDown="@id/result_download_movie"
|
android:nextFocusDown="@id/result_download_movie"
|
||||||
android:text="@string/next_episode"
|
android:text="@string/next_episode"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
|
|
@ -78,6 +78,30 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/background_poster_holder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="275dp"
|
||||||
|
android:visibility="visible">
|
||||||
|
|
||||||
|
<com.lagradost.cloudstream3.utils.PercentageCropImageView
|
||||||
|
android:id="@+id/background_poster"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="275dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:alpha="0.8"
|
||||||
|
android:scaleType="matrix"
|
||||||
|
tools:src="@drawable/profile_bg_dark_blue" >
|
||||||
|
</com.lagradost.cloudstream3.utils.PercentageCropImageView>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:src="@drawable/background_shadow">
|
||||||
|
</ImageView>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/result_loading_error"
|
android:id="@+id/result_loading_error"
|
||||||
|
|
||||||
|
@ -124,31 +148,6 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:textColor="?attr/textColor" />
|
android:textColor="?attr/textColor" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/background_poster_holder"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="250dp"
|
|
||||||
android:visibility="visible">
|
|
||||||
|
|
||||||
<com.lagradost.cloudstream3.utils.PercentageCropImageView
|
|
||||||
android:id="@+id/background_poster"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="275dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:alpha="0.8"
|
|
||||||
android:scaleType="matrix"
|
|
||||||
tools:src="@drawable/profile_bg_dark_blue" >
|
|
||||||
</com.lagradost.cloudstream3.utils.PercentageCropImageView>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="120dp"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:src="@drawable/background_shadow">
|
|
||||||
</ImageView>
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:id="@+id/result_finish_loading"
|
android:id="@+id/result_finish_loading"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -165,7 +164,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_marginTop="175dp">
|
android:layout_marginTop="225dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/result_title"
|
android:id="@+id/result_title"
|
||||||
|
@ -175,7 +174,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="?attr/textColor"
|
android:textColor="?attr/textColor"
|
||||||
android:textSize="20sp"
|
android:textSize="25sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
tools:text="The Perfect Run The Perfect Run" />
|
tools:text="The Perfect Run The Perfect Run" />
|
||||||
|
|
||||||
|
@ -221,157 +220,293 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:textStyle="normal"
|
android:textStyle="normal"
|
||||||
tools:text="5d 3h 30m" />
|
tools:text="5d 3h 30m" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/result_resume_progress_holder"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
android:id="@+id/result_resume_series_progress"
|
||||||
|
app:trackCornerRadius="50dp"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_gravity="end|center_vertical"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:indeterminate="false"
|
||||||
|
android:max="100"
|
||||||
|
android:paddingEnd="10dp"
|
||||||
|
android:progress="0"
|
||||||
|
android:progressBackgroundTint="?attr/colorPrimary"
|
||||||
|
tools:progress="50"
|
||||||
|
tools:visibility="visible"
|
||||||
|
tools:ignore="RtlSymmetry" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_resume_series_progress_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?attr/grayTextColor"
|
||||||
|
tools:ignore="RtlSymmetry"
|
||||||
|
tools:text="69m remaining" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/result_play_parent"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_marginBottom="5dp"
|
||||||
|
android:descendantFocusability="afterDescendants"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:id="@+id/result_play_movie"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/result_play_movie_button"
|
||||||
|
android:focusable="true"
|
||||||
|
style="@style/ResultSmallButtonTV"
|
||||||
|
app:iconPadding="0dp"
|
||||||
|
app:icon="@drawable/ic_baseline_play_arrow_24"
|
||||||
|
android:nextFocusUp="@id/result_play_movie_button"
|
||||||
|
android:nextFocusDown="@id/result_description"
|
||||||
|
android:tag="@string/tv_no_focus_tag">
|
||||||
|
</com.google.android.material.button.MaterialButton>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_play_movie_text"
|
||||||
|
style="@style/ResultMarqueeButtonText"
|
||||||
|
android:text="@string/movies_singular" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:id="@+id/result_play_series"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/result_play_series_button"
|
||||||
|
android:focusable="true"
|
||||||
|
style="@style/ResultSmallButtonTV"
|
||||||
|
app:iconPadding="0dp"
|
||||||
|
app:icon="@drawable/ic_baseline_play_arrow_24"
|
||||||
|
android:nextFocusUp="@id/result_play_series_button"
|
||||||
|
android:nextFocusDown="@id/result_description"
|
||||||
|
android:tag="@string/tv_no_focus_tag">
|
||||||
|
</com.google.android.material.button.MaterialButton>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_play_series_text"
|
||||||
|
style="@style/ResultMarqueeButtonText"
|
||||||
|
android:text="@string/episode" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/result_resume_series"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/result_resume_series_button"
|
||||||
|
android:focusable="true"
|
||||||
|
style="@style/ResultSmallButtonTV"
|
||||||
|
app:iconPadding="0dp"
|
||||||
|
app:icon="@drawable/ic_baseline_resume_arrow2"
|
||||||
|
android:nextFocusUp="@id/result_resume_series_button"
|
||||||
|
android:nextFocusDown="@id/result_description"
|
||||||
|
android:tag="@string/tv_no_focus_tag">
|
||||||
|
</com.google.android.material.button.MaterialButton>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_resume_series_text"
|
||||||
|
style="@style/ResultMarqueeButtonText"
|
||||||
|
android:text="@string/resume" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/result_play_trailer"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/result_play_trailer_button"
|
||||||
|
android:focusable="true"
|
||||||
|
style="@style/ResultSmallButtonTV"
|
||||||
|
app:iconPadding="0dp"
|
||||||
|
app:icon="@drawable/ic_baseline_film_roll_24"
|
||||||
|
android:nextFocusUp="@id/result_play_trailer_button"
|
||||||
|
android:nextFocusDown="@id/result_description"
|
||||||
|
android:tag="@string/tv_no_focus_tag">
|
||||||
|
</com.google.android.material.button.MaterialButton>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_play_trailer_text"
|
||||||
|
style="@style/ResultMarqueeButtonText"
|
||||||
|
android:text="@string/play_trailer_button" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/result_bookmark"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/result_bookmark_Button"
|
||||||
|
android:focusable="true"
|
||||||
|
style="@style/ResultSmallButtonTV"
|
||||||
|
app:iconPadding="0dp"
|
||||||
|
app:icon="@drawable/outline_bookmark_add_24"
|
||||||
|
android:nextFocusUp="@id/result_bookmark_Button"
|
||||||
|
android:nextFocusDown="@id/result_description"
|
||||||
|
android:tag="@string/tv_no_focus_tag">
|
||||||
|
</com.google.android.material.button.MaterialButton>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_bookmark_text"
|
||||||
|
style="@style/ResultMarqueeButtonText"
|
||||||
|
android:text="@string/type_none" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/result_favorite"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/result_favorite_Button"
|
||||||
|
android:focusable="true"
|
||||||
|
style="@style/ResultSmallButtonTV"
|
||||||
|
app:iconPadding="0dp"
|
||||||
|
app:icon="@drawable/ic_baseline_favorite_border_24"
|
||||||
|
android:nextFocusUp="@id/result_favorite_Button"
|
||||||
|
android:nextFocusDown="@id/result_description"
|
||||||
|
android:tag="@string/tv_no_focus_tag">
|
||||||
|
</com.google.android.material.button.MaterialButton>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_favorite_Text"
|
||||||
|
style="@style/ResultMarqueeButtonText"
|
||||||
|
android:text="@string/favorite" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:id="@+id/result_subscribe"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/result_subscribe_Button"
|
||||||
|
android:focusable="true"
|
||||||
|
style="@style/ResultSmallButtonTV"
|
||||||
|
app:iconPadding="0dp"
|
||||||
|
app:icon="@drawable/baseline_notifications_none_24"
|
||||||
|
android:nextFocusUp="@id/result_subscribe_Button"
|
||||||
|
android:nextFocusDown="@id/result_description"
|
||||||
|
android:tag="@string/tv_no_focus_tag">
|
||||||
|
</com.google.android.material.button.MaterialButton>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_subscribe_text"
|
||||||
|
style="@style/ResultMarqueeButtonText"
|
||||||
|
android:text="@string/action_subscribe" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:id="@+id/result_search"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/result_search_Button"
|
||||||
|
android:focusable="true"
|
||||||
|
style="@style/ResultSmallButtonTV"
|
||||||
|
app:iconPadding="0dp"
|
||||||
|
app:icon="@drawable/search_icon"
|
||||||
|
android:nextFocusUp="@id/result_search_Button"
|
||||||
|
android:nextFocusDown="@id/result_description"
|
||||||
|
android:tag="@string/tv_no_focus_tag">
|
||||||
|
</com.google.android.material.button.MaterialButton>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_search_text"
|
||||||
|
style="@style/ResultMarqueeButtonText"
|
||||||
|
android:text="@string/title_search" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:id="@+id/result_episodes_show"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/result_episodes_show_button"
|
||||||
|
android:focusable="true"
|
||||||
|
style="@style/ResultSmallButtonTV"
|
||||||
|
app:iconPadding="0dp"
|
||||||
|
app:icon="@drawable/ic_baseline_sort_24"
|
||||||
|
android:nextFocusUp="@id/result_episodes_show_button"
|
||||||
|
android:nextFocusRight="@id/redirect_to_episodes"
|
||||||
|
android:nextFocusDown="@id/result_description"
|
||||||
|
android:tag="@string/tv_no_focus_tag">
|
||||||
|
</com.google.android.material.button.MaterialButton>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_episodes_show_text"
|
||||||
|
style="@style/ResultMarqueeButtonText"
|
||||||
|
android:text="@string/episodes" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_marginTop="10dp">
|
android:layout_marginTop="10dp"
|
||||||
|
android:baselineAligned="false">
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="250dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="10dp"
|
|
||||||
android:layout_weight="0"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/result_movie_parent"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="start"
|
|
||||||
android:animateLayoutChanges="true"
|
|
||||||
android:orientation="vertical"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_play_movie"
|
|
||||||
style="@style/ResultButtonTV"
|
|
||||||
|
|
||||||
android:nextFocusRight="@id/result_description"
|
|
||||||
android:nextFocusUp="@id/result_play_movie"
|
|
||||||
android:nextFocusDown="@id/result_play_series"
|
|
||||||
android:text="@string/play_movie_button"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:icon="@drawable/ic_baseline_play_arrow_24" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/series_holder"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_play_series"
|
|
||||||
style="@style/ResultButtonTV"
|
|
||||||
|
|
||||||
android:nextFocusRight="@id/result_description"
|
|
||||||
android:nextFocusUp="@id/result_play_movie"
|
|
||||||
android:nextFocusDown="@id/result_resume_series"
|
|
||||||
android:text="@string/play_episode"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:icon="@drawable/ic_baseline_play_arrow_24" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_resume_series"
|
|
||||||
style="@style/ResultButtonTV"
|
|
||||||
|
|
||||||
android:nextFocusRight="@id/result_description"
|
|
||||||
android:nextFocusUp="@id/result_play_series"
|
|
||||||
android:nextFocusDown="@id/result_play_trailer"
|
|
||||||
android:text="@string/resume"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:icon="@drawable/ic_baseline_play_arrow_24" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_play_trailer"
|
|
||||||
style="@style/ResultButtonTV"
|
|
||||||
|
|
||||||
android:nextFocusRight="@id/result_description"
|
|
||||||
android:nextFocusUp="@id/result_resume_series"
|
|
||||||
android:nextFocusDown="@id/result_bookmark_button"
|
|
||||||
android:text="@string/play_trailer_button"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:icon="@drawable/ic_baseline_play_arrow_24"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <com.lagradost.cloudstream3.ui.download.button.DownloadButton
|
|
||||||
android:visibility="gone"
|
|
||||||
android:id="@+id/download_button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:minWidth="250dp"
|
|
||||||
app:download_layout="@layout/download_button_layout" />
|
|
||||||
-->
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_bookmark_button"
|
|
||||||
style="@style/ResultButtonTV"
|
|
||||||
android:nextFocusRight="@id/result_description"
|
|
||||||
android:nextFocusUp="@id/result_play_trailer"
|
|
||||||
android:nextFocusDown="@id/result_favorite_button"
|
|
||||||
|
|
||||||
android:text="@string/type_none"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:icon="@drawable/ic_baseline_bookmark_24" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_favorite_button"
|
|
||||||
style="@style/ResultButtonTV"
|
|
||||||
android:nextFocusRight="@id/result_description"
|
|
||||||
android:nextFocusUp="@id/result_bookmark_button"
|
|
||||||
android:nextFocusDown="@id/result_subscribe_button"
|
|
||||||
|
|
||||||
android:text="@string/action_add_to_favorites"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:icon="@drawable/ic_baseline_favorite_border_24" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_subscribe_button"
|
|
||||||
style="@style/ResultButtonTV"
|
|
||||||
android:nextFocusRight="@id/result_description"
|
|
||||||
android:nextFocusUp="@id/result_favorite_button"
|
|
||||||
android:nextFocusDown="@id/result_episodes_show"
|
|
||||||
|
|
||||||
android:text="@string/action_subscribe"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:icon="@drawable/ic_baseline_favorite_border_24" />
|
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_episodes_show"
|
|
||||||
style="@style/ResultButtonTV"
|
|
||||||
|
|
||||||
android:nextFocusRight="@id/redirect_to_episodes"
|
|
||||||
android:nextFocusUp="@id/result_subscribe_button"
|
|
||||||
android:nextFocusDown="@id/result_cast_items"
|
|
||||||
|
|
||||||
android:text="@string/episodes"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:icon="@drawable/ic_baseline_sort_24"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:id="@+id/redirect_to_episodes"
|
|
||||||
android:layout_width="1dp"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true" />
|
|
||||||
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:id="@+id/redirect_to_play"
|
|
||||||
android:layout_width="1dp"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true" />
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/right_layout"
|
android:id="@+id/right_layout"
|
||||||
|
@ -382,7 +517,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/linearLayout2"
|
app:layout_constraintStart_toEndOf="@+id/linearLayout2"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:ignore="UselessParent">
|
||||||
|
|
||||||
<com.lagradost.cloudstream3.widget.FlowLayout
|
<com.lagradost.cloudstream3.widget.FlowLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -439,8 +575,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:foreground="@drawable/outline_drawable"
|
android:foreground="@drawable/outline_drawable"
|
||||||
android:maxLines="7"
|
android:maxLines="7"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:nextFocusUp="@id/result_back"
|
android:nextFocusUp="@id/result_play_parent"
|
||||||
android:nextFocusDown="@id/result_bookmark_button"
|
android:nextFocusDown="@id/result_cast_items"
|
||||||
android:padding="5dp"
|
android:padding="5dp"
|
||||||
android:requiresFadingEdge="vertical"
|
android:requiresFadingEdge="vertical"
|
||||||
android:textColor="?attr/textColor"
|
android:textColor="?attr/textColor"
|
||||||
|
@ -450,59 +586,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
<com.google.android.material.chip.ChipGroup
|
<com.google.android.material.chip.ChipGroup
|
||||||
android:id="@+id/result_tag"
|
android:id="@+id/result_tag"
|
||||||
style="@style/ChipParent"
|
style="@style/ChipParent"
|
||||||
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/result_resume_progress_holder"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="10dp"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<androidx.core.widget.ContentLoadingProgressBar
|
|
||||||
android:id="@+id/result_resume_series_progress"
|
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="20dp"
|
|
||||||
android:layout_gravity="end|center_vertical"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:indeterminate="false"
|
|
||||||
android:max="100"
|
|
||||||
|
|
||||||
android:paddingEnd="10dp"
|
|
||||||
android:progress="0"
|
|
||||||
android:progressBackgroundTint="?attr/colorPrimary"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:progress="50"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/result_resume_series_progress_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_weight="0"
|
|
||||||
android:gravity="center"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:paddingEnd="5dp"
|
|
||||||
android:textColor="?attr/grayTextColor"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:ignore="RtlSymmetry"
|
|
||||||
tools:text="69m remaining" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -513,10 +598,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:descendantFocusability="afterDescendants"
|
android:descendantFocusability="afterDescendants"
|
||||||
|
|
||||||
android:fadingEdge="horizontal"
|
android:fadingEdge="horizontal"
|
||||||
android:focusable="false"
|
android:nextFocusUp="@id/result_description"
|
||||||
android:focusableInTouchMode="false"
|
android:nextFocusDown="@id/result_recommendations_list"
|
||||||
android:nextFocusUp="@id/result_episodes_show"
|
|
||||||
android:nextFocusDown="@id/result_recommendations_filter_selection"
|
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingTop="5dp"
|
android:paddingTop="5dp"
|
||||||
android:requiresFadingEdge="horizontal"
|
android:requiresFadingEdge="horizontal"
|
||||||
|
@ -525,8 +608,23 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
tools:listitem="@layout/cast_item"
|
tools:listitem="@layout/cast_item"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_tv_coming_soon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingTop="50dp"
|
||||||
|
android:text="@string/coming_soon"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:visibility="visible"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/result_recommendations_holder"
|
android:id="@+id/result_recommendations_holder"
|
||||||
|
android:descendantFocusability="afterDescendants"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
@ -540,7 +638,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:layout_marginEnd="10dp"
|
android:layout_marginEnd="10dp"
|
||||||
android:nextFocusUp="@id/result_cast_items"
|
android:nextFocusUp="@id/result_cast_items"
|
||||||
android:nextFocusDown="@id/result_recommendations_list"
|
android:nextFocusDown="@id/result_recommendations_list"
|
||||||
|
android:descendantFocusability="afterDescendants"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
tools:itemCount="2"
|
tools:itemCount="2"
|
||||||
|
@ -563,7 +661,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:descendantFocusability="afterDescendants"
|
android:descendantFocusability="afterDescendants"
|
||||||
android:nextFocusUp="@id/result_recommendations_filter_selection"
|
android:nextFocusUp="@id/result_cast_items"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
app:spanCount="8"
|
app:spanCount="8"
|
||||||
tools:listitem="@layout/search_result_grid" />
|
tools:listitem="@layout/search_result_grid" />
|
||||||
|
@ -576,7 +674,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible">
|
tools:visibility="invisible">
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
@ -765,152 +863,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
<!--
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/result_movie_parent"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="5dp"
|
|
||||||
android:animateLayoutChanges="true"
|
|
||||||
android:orientation="vertical"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_play_movie"
|
|
||||||
style="@style/RegularButtonTV"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
|
|
||||||
android:layout_marginStart="0dp"
|
|
||||||
android:layout_marginEnd="5dp"
|
|
||||||
android:layout_marginBottom="10dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:minWidth="250dp"
|
|
||||||
android:nextFocusUp="@id/result_back"
|
|
||||||
android:nextFocusDown="@id/result_play_series"
|
|
||||||
android:text="@string/play_movie_button"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:icon="@drawable/ic_baseline_play_arrow_24">
|
|
||||||
|
|
||||||
</com.google.android.material.button.MaterialButton>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/series_holder"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_play_series"
|
|
||||||
style="@style/RegularButtonTV"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_marginStart="0dp"
|
|
||||||
|
|
||||||
android:layout_marginEnd="5dp"
|
|
||||||
android:layout_marginBottom="10dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:minWidth="250dp"
|
|
||||||
android:nextFocusUp="@id/result_play_movie"
|
|
||||||
android:nextFocusDown="@id/result_resume_series"
|
|
||||||
android:text="@string/play_episode"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:icon="@drawable/ic_baseline_play_arrow_24"
|
|
||||||
tools:visibility="gone" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_resume_series"
|
|
||||||
style="@style/RegularButtonTV"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_marginStart="0dp"
|
|
||||||
|
|
||||||
android:layout_marginEnd="5dp"
|
|
||||||
android:layout_marginBottom="10dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:minWidth="250dp"
|
|
||||||
android:nextFocusUp="@id/result_play_series"
|
|
||||||
android:nextFocusDown="@id/result_play_trailer"
|
|
||||||
android:text="@string/resume"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:icon="@drawable/ic_baseline_play_arrow_24"
|
|
||||||
tools:visibility="gone" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_play_trailer"
|
|
||||||
style="@style/RegularButtonTV"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_marginStart="0dp"
|
|
||||||
android:layout_marginEnd="5dp"
|
|
||||||
android:layout_marginBottom="10dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:minWidth="250dp"
|
|
||||||
android:nextFocusUp="@id/result_resume_series"
|
|
||||||
android:nextFocusDown="@id/result_bookmark_button"
|
|
||||||
android:text="@string/play_trailer_button"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:icon="@drawable/ic_baseline_play_arrow_24"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
</com.google.android.material.button.MaterialButton>
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_bookmark_button"
|
|
||||||
style="@style/RegularButtonTV"
|
|
||||||
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_marginStart="0dp"
|
|
||||||
android:layout_marginEnd="5dp"
|
|
||||||
android:layout_marginBottom="10dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:minWidth="250dp"
|
|
||||||
android:nextFocusUp="@id/result_play_trailer"
|
|
||||||
android:nextFocusDown="@id/result_resume_series_button"
|
|
||||||
android:text="@string/type_none"
|
|
||||||
android:visibility="visible" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/result_resume_progress_holder"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="10dp"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<androidx.core.widget.ContentLoadingProgressBar
|
|
||||||
android:id="@+id/result_resume_series_progress"
|
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="20dp"
|
|
||||||
android:layout_gravity="end|center_vertical"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:indeterminate="false"
|
|
||||||
android:max="100"
|
|
||||||
android:paddingEnd="10dp"
|
|
||||||
android:progress="0"
|
|
||||||
android:progressBackgroundTint="?attr/colorPrimary"
|
|
||||||
android:visibility="visible"
|
|
||||||
tools:progress="50"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/result_resume_series_progress_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_weight="0"
|
|
||||||
android:gravity="center"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:paddingEnd="5dp"
|
|
||||||
android:textColor="?attr/grayTextColor"
|
|
||||||
tools:ignore="RtlSymmetry"
|
|
||||||
tools:text="69m remaining" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
-->
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/result_poster_holder"
|
android:id="@+id/result_poster_holder"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -1067,70 +1020,47 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:icon="@drawable/ic_baseline_add_24" />
|
app:icon="@drawable/ic_baseline_add_24" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
<!--<LinearLayout
|
|
||||||
|
|
||||||
android:id="@+id/result_resume_parent"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="5dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="gone">
|
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/result_next_series_button"
|
|
||||||
style="@style/WhiteButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginStart="0dp"
|
|
||||||
android:layout_marginEnd="0dp"
|
|
||||||
android:layout_marginBottom="10dp"
|
|
||||||
android:nextFocusUp="@id/result_bookmark_button"
|
|
||||||
android:nextFocusDown="@id/result_download_movie"
|
|
||||||
android:text="@string/next_episode"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:icon="@drawable/cast_ic_mini_controller_skip_next" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:layout_width="250dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/result_movie_parent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:layout_gravity="start"
|
||||||
|
android:animateLayoutChanges="true"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
<ImageView
|
<LinearLayout
|
||||||
android:id="@+id/result_resume_series_button"
|
android:id="@+id/series_holder"
|
||||||
android:layout_width="30dp"
|
|
||||||
android:layout_height="30dp"
|
|
||||||
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:background="?selectableItemBackgroundBorderless"
|
|
||||||
android:contentDescription="@string/download"
|
|
||||||
|
|
||||||
android:nextFocusUp="@id/result_play_movie"
|
|
||||||
android:nextFocusDown="@id/result_season_selection"
|
|
||||||
android:src="@drawable/ic_baseline_play_arrow_24"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:tint="?attr/white" />
|
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/result_resume_series_title"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:orientation="vertical">
|
||||||
android:gravity="center"
|
|
||||||
android:paddingStart="10dp"
|
|
||||||
android:paddingEnd="10dp"
|
|
||||||
android:textColor="?attr/textColor"
|
|
||||||
android:textSize="17sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
tools:text="S1E1 Episode 1" />
|
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/redirect_to_episodes"
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/redirect_to_play"
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>-->
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
|
@ -105,9 +105,10 @@
|
||||||
android:text="@string/extensions" />
|
android:text="@string/extensions" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/app_version_info"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:gravity="center_horizontal"
|
android:layout_gravity="center"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
|
|
@ -533,18 +533,14 @@
|
||||||
android:id="@id/exo_position"
|
android:id="@id/exo_position"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="30dp"
|
android:layout_height="30dp"
|
||||||
android:layout_gravity="center"
|
android:gravity="center"
|
||||||
android:layout_marginStart="20dp"
|
|
||||||
android:gravity="end|center_vertical"
|
|
||||||
android:includeFontPadding="false"
|
android:includeFontPadding="false"
|
||||||
android:minWidth="50dp"
|
android:minWidth="50dp"
|
||||||
android:paddingLeft="4dp"
|
|
||||||
android:paddingRight="4dp"
|
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:textStyle="normal"
|
android:textStyle="normal"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="@id/player_pause_play"
|
app:layout_constraintStart_toEndOf="@id/player_pause_play"
|
||||||
tools:text="15:30" />
|
tools:text="15:30" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
android:title="@string/title_search" />
|
android:title="@string/title_search" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/navigation_library"
|
android:id="@+id/navigation_library"
|
||||||
android:icon="@drawable/ic_outline_account_circle_24"
|
android:icon="@drawable/library_icon"
|
||||||
android:title="@string/library" />
|
android:title="@string/library" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/navigation_downloads"
|
android:id="@+id/navigation_downloads"
|
||||||
|
|
|
@ -212,6 +212,7 @@
|
||||||
<item>Banana</item>
|
<item>Banana</item>
|
||||||
<item>Fiesta</item>
|
<item>Fiesta</item>
|
||||||
<item>Dolor rosa</item>
|
<item>Dolor rosa</item>
|
||||||
|
<item>Lavanda</item>
|
||||||
<item>Material You</item>
|
<item>Material You</item>
|
||||||
<item>Material You (Secondary)</item>
|
<item>Material You (Secondary)</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
@ -235,6 +236,7 @@
|
||||||
<item>Banana</item>
|
<item>Banana</item>
|
||||||
<item>Party</item>
|
<item>Party</item>
|
||||||
<item>Pink</item>
|
<item>Pink</item>
|
||||||
|
<item>Lavender</item>
|
||||||
<item>Monet</item>
|
<item>Monet</item>
|
||||||
<item>Monet2</item>
|
<item>Monet2</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
|
@ -221,6 +221,7 @@
|
||||||
<item>Bananowy</item>
|
<item>Bananowy</item>
|
||||||
<item>Łososiowy</item>
|
<item>Łososiowy</item>
|
||||||
<item>Świnko peppowy</item>
|
<item>Świnko peppowy</item>
|
||||||
|
<item>Lawenda</item>
|
||||||
<item>Material You</item>
|
<item>Material You</item>
|
||||||
<item>Material You (drugorzędny)</item>
|
<item>Material You (drugorzędny)</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
@ -244,6 +245,7 @@
|
||||||
<item>Banana</item>
|
<item>Banana</item>
|
||||||
<item>Party</item>
|
<item>Party</item>
|
||||||
<item>Pink</item>
|
<item>Pink</item>
|
||||||
|
<item>Lavender</item>
|
||||||
<item>Monet</item>
|
<item>Monet</item>
|
||||||
<item>Monet2</item>
|
<item>Monet2</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
|
@ -247,6 +247,7 @@
|
||||||
<item>Muz</item>
|
<item>Muz</item>
|
||||||
<item>Parti</item>
|
<item>Parti</item>
|
||||||
<item>Pembe</item>
|
<item>Pembe</item>
|
||||||
|
<item>Lavanta</item>
|
||||||
<item>Material You</item>
|
<item>Material You</item>
|
||||||
<item>Material You (İkincil)</item>
|
<item>Material You (İkincil)</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
@ -270,6 +271,7 @@
|
||||||
<item>Banana</item>
|
<item>Banana</item>
|
||||||
<item>Party</item>
|
<item>Party</item>
|
||||||
<item>Pink</item>
|
<item>Pink</item>
|
||||||
|
<item>Lavender</item>
|
||||||
<item>Monet</item>
|
<item>Monet</item>
|
||||||
<item>Monet2</item>
|
<item>Monet2</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
|
@ -213,6 +213,7 @@
|
||||||
<item>Vàng</item>
|
<item>Vàng</item>
|
||||||
<item>Hồng</item>
|
<item>Hồng</item>
|
||||||
<item>Hồng đậm</item>
|
<item>Hồng đậm</item>
|
||||||
|
<item>Hoa oải hương</item>
|
||||||
<item>Material You</item>
|
<item>Material You</item>
|
||||||
<item>Material You (Secondary)</item>
|
<item>Material You (Secondary)</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
@ -236,6 +237,7 @@
|
||||||
<item>Banana</item>
|
<item>Banana</item>
|
||||||
<item>Party</item>
|
<item>Party</item>
|
||||||
<item>Pink</item>
|
<item>Pink</item>
|
||||||
|
<item>Lavender</item>
|
||||||
<item>Monet</item>
|
<item>Monet</item>
|
||||||
<item>Monet2</item>
|
<item>Monet2</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
|
@ -284,6 +284,7 @@
|
||||||
<item>Banana</item>
|
<item>Banana</item>
|
||||||
<item>Party</item>
|
<item>Party</item>
|
||||||
<item>Pink Pain</item>
|
<item>Pink Pain</item>
|
||||||
|
<item>Lavender</item>
|
||||||
<item>Material You</item>
|
<item>Material You</item>
|
||||||
<item>Material You (Secondary)</item>
|
<item>Material You (Secondary)</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
@ -307,6 +308,7 @@
|
||||||
<item>Banana</item>
|
<item>Banana</item>
|
||||||
<item>Party</item>
|
<item>Party</item>
|
||||||
<item>Pink</item>
|
<item>Pink</item>
|
||||||
|
<item>Lavender</item>
|
||||||
<item>Monet</item>
|
<item>Monet</item>
|
||||||
<item>Monet2</item>
|
<item>Monet2</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
|
@ -84,6 +84,7 @@
|
||||||
<color name="colorPrimaryOrange">#CE8500</color>
|
<color name="colorPrimaryOrange">#CE8500</color>
|
||||||
<color name="colorPrimaryDandelionYellow">#F5BB00</color>
|
<color name="colorPrimaryDandelionYellow">#F5BB00</color>
|
||||||
<color name="colorPrimaryCoolBlue">#408cac</color>
|
<color name="colorPrimaryCoolBlue">#408cac</color>
|
||||||
|
<color name="colorPrimaryLavender">#6F55AF</color>
|
||||||
|
|
||||||
<color name="colorTestPass">#48E484</color>
|
<color name="colorTestPass">#48E484</color>
|
||||||
<color name="colorTestFail">#ea596e</color>
|
<color name="colorTestFail">#ea596e</color>
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
<string name="enable_skip_op_from_database" translatable="false">enable_skip_op_from_database</string>
|
<string name="enable_skip_op_from_database" translatable="false">enable_skip_op_from_database</string>
|
||||||
<string name="rotate_video_key" translatable="false">rotate_video_key</string>
|
<string name="rotate_video_key" translatable="false">rotate_video_key</string>
|
||||||
<string name="auto_rotate_video_key" translatable="false">auto_rotate_video_key</string>
|
<string name="auto_rotate_video_key" translatable="false">auto_rotate_video_key</string>
|
||||||
|
<string name="biometric_key" translatable="false">biometric_key</string>
|
||||||
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
|
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
|
||||||
<string name="extra_info_format" formatted="true" translatable="false">%d %s | %s</string>
|
<string name="extra_info_format" formatted="true" translatable="false">%d %s | %s</string>
|
||||||
<string name="storage_size_format" formatted="true" translatable="false">%s • %s</string>
|
<string name="storage_size_format" formatted="true" translatable="false">%s • %s</string>
|
||||||
|
@ -173,8 +174,8 @@
|
||||||
<string name="sort_close">Close</string>
|
<string name="sort_close">Close</string>
|
||||||
<string name="sort_clear">Clear</string>
|
<string name="sort_clear">Clear</string>
|
||||||
<string name="sort_save">Save</string>
|
<string name="sort_save">Save</string>
|
||||||
<string name="copyTitle">Title copied!</string>
|
<string name="repo_copy_label">Repository name and URL</string>
|
||||||
<string name="copyRepoUrl">Repo URL copied!</string>
|
<string name="toast_copied">copied!</string>
|
||||||
<string name="subscribe_tooltip">New episode notification</string>
|
<string name="subscribe_tooltip">New episode notification</string>
|
||||||
<string name="result_search_tooltip">Search in other extensions</string>
|
<string name="result_search_tooltip">Search in other extensions</string>
|
||||||
<string name="recommendations_tooltip">Show recommendations</string>
|
<string name="recommendations_tooltip">Show recommendations</string>
|
||||||
|
@ -247,7 +248,7 @@
|
||||||
<string name="backup_failed_error_format">Error backing up %s</string>
|
<string name="backup_failed_error_format">Error backing up %s</string>
|
||||||
<string name="search">Search</string>
|
<string name="search">Search</string>
|
||||||
<string name="library">Library</string>
|
<string name="library">Library</string>
|
||||||
<string name="category_account">Accounts</string>
|
<string name="category_account">Accounts and Security</string>
|
||||||
<string name="category_updates">Updates and backup</string>
|
<string name="category_updates">Updates and backup</string>
|
||||||
<string name="settings_info">Info</string>
|
<string name="settings_info">Info</string>
|
||||||
<string name="advanced_search">Advanced Search</string>
|
<string name="advanced_search">Advanced Search</string>
|
||||||
|
@ -306,6 +307,7 @@
|
||||||
<string name="go_forward_30">+30</string>
|
<string name="go_forward_30">+30</string>
|
||||||
<string name="delete_message" formatted="true">This will permanently delete %s\nAre you sure?</string>
|
<string name="delete_message" formatted="true">This will permanently delete %s\nAre you sure?</string>
|
||||||
<string name="resume_time_left" formatted="true">%dm\nremaining</string>
|
<string name="resume_time_left" formatted="true">%dm\nremaining</string>
|
||||||
|
<string name="resume_remaining" formatted="true">%s\nremaining</string>
|
||||||
<string name="status_ongoing">Ongoing</string>
|
<string name="status_ongoing">Ongoing</string>
|
||||||
<string name="status_completed">Completed</string>
|
<string name="status_completed">Completed</string>
|
||||||
<string name="status">Status</string>
|
<string name="status">Status</string>
|
||||||
|
@ -645,6 +647,8 @@
|
||||||
<string name="history">History</string>
|
<string name="history">History</string>
|
||||||
<string name="enable_skip_op_from_database_des">Show skip popups for opening/ending</string>
|
<string name="enable_skip_op_from_database_des">Show skip popups for opening/ending</string>
|
||||||
<string name="clipboard_too_large">Too much text. Unable to save to clipboard.</string>
|
<string name="clipboard_too_large">Too much text. Unable to save to clipboard.</string>
|
||||||
|
<string name="clipboard_permission_error">Error accessing Clipboard, Please try again.</string>
|
||||||
|
<string name="clipboard_unknown_error">Error copying, Please copy logcat and contact app support.</string>
|
||||||
<string name="action_mark_as_watched">Mark as watched</string>
|
<string name="action_mark_as_watched">Mark as watched</string>
|
||||||
<string name="action_remove_from_watched">Remove from watched</string>
|
<string name="action_remove_from_watched">Remove from watched</string>
|
||||||
<string name="confirm_exit_dialog">Are you sure you want to exit\?</string>
|
<string name="confirm_exit_dialog">Are you sure you want to exit\?</string>
|
||||||
|
@ -745,4 +749,17 @@
|
||||||
<string name="rotate_video_desc">Display a toggle button for screen orientation</string>
|
<string name="rotate_video_desc">Display a toggle button for screen orientation</string>
|
||||||
<string name="auto_rotate_video_desc">Enable automatic switching of screen orientation based on video orientation</string>
|
<string name="auto_rotate_video_desc">Enable automatic switching of screen orientation based on video orientation</string>
|
||||||
<string name="auto_rotate_video">Auto rotate</string>
|
<string name="auto_rotate_video">Auto rotate</string>
|
||||||
|
<string name="favorite">Favorite</string>
|
||||||
|
<string name="unfavorite">Unfavorite</string>
|
||||||
|
<!-- For Biometrics -->
|
||||||
|
<string name="biometric_authentication_title">Unlock CloudStream</string>
|
||||||
|
<string name="biometric_setting">Lock with Biometrics</string>
|
||||||
|
<string name="password_pin_authentication_title">Password/PIN Authentication</string>
|
||||||
|
<string name="biometric_unsupported">Biometric authentication is not supported on this device</string>
|
||||||
|
<string name="biometric_setting_summary">Unlock the app with Fingerprint, Face ID, PIN, Pattern and Password.</string>
|
||||||
|
<string name="biometric_prompt_description">This window will close after few failed attempts. You\'ll have to restart the App.</string>
|
||||||
|
<string name="biometric_warning">Your CloudStream data has been backed up now, although probability of this rare case is very low but all
|
||||||
|
devices behave differently, in case you get locked down from accessing the app in worst case scenario,
|
||||||
|
Clear the app data wholly and restore the backup. Any inconvenience if arrived is deeply regretted.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -383,6 +383,16 @@
|
||||||
<item name="android:colorAccent">@color/colorPrimaryCoolBlue</item>
|
<item name="android:colorAccent">@color/colorPrimaryCoolBlue</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="OverlayPrimaryColorLavender">
|
||||||
|
<item name="colorPrimary">@color/colorPrimaryLavender</item>
|
||||||
|
<item name="android:colorPrimary">@color/colorPrimaryLavender</item>
|
||||||
|
<item name="colorPrimaryDark">#6B51AB</item>
|
||||||
|
<item name="colorAccent">#7961B4</item>
|
||||||
|
<item name="colorOnPrimary">@color/whiteText</item>
|
||||||
|
<!-- Needed for leanback fuckery -->
|
||||||
|
<item name="android:colorAccent">@color/colorPrimaryLavender</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="customRatingBar" parent="@style/Widget.AppCompat.RatingBar">
|
<style name="customRatingBar" parent="@style/Widget.AppCompat.RatingBar">
|
||||||
|
|
||||||
<item name="android:progressDrawable">@drawable/abc_ratingbar_indicator_material</item>
|
<item name="android:progressDrawable">@drawable/abc_ratingbar_indicator_material</item>
|
||||||
|
@ -804,6 +814,35 @@
|
||||||
<item name="android:insetTop">0dp</item>
|
<item name="android:insetTop">0dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="ResultSmallButtonTV">
|
||||||
|
<item name="android:tag">@string/tv_no_focus_tag</item>
|
||||||
|
<item name="android:stateListAnimator">@null</item>
|
||||||
|
<item name="strokeColor">@color/transparent</item>
|
||||||
|
<item name="backgroundTint">@null</item>
|
||||||
|
<item name="android:background">@drawable/player_button_tv_attr</item>
|
||||||
|
<item name="rippleColor">@color/white</item>
|
||||||
|
<item name="android:shadowColor">@color/transparent</item>
|
||||||
|
<item name="iconTint">@color/player_on_button_tv_attr</item>
|
||||||
|
<item name="iconGravity">textStart</item>
|
||||||
|
<item name="android:layout_width">60dp</item>
|
||||||
|
<item name="android:layout_height">40dp</item>
|
||||||
|
<item name="android:gravity">center</item>
|
||||||
|
<item name="android:layout_gravity">center</item>
|
||||||
|
<item name="android:layout_marginStart">4dp</item>
|
||||||
|
<item name="android:layout_marginEnd">4dp</item>
|
||||||
|
<item name="android:layout_marginBottom">4dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ResultMarqueeButtonText">
|
||||||
|
<item name="android:layout_width">match_parent</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:gravity">top|center_horizontal</item>
|
||||||
|
<item name="android:singleLine">true</item>
|
||||||
|
<item name="android:scrollHorizontally">true</item>
|
||||||
|
<item name="android:marqueeRepeatLimit">marquee_forever</item>
|
||||||
|
<item name="android:ellipsize">marquee</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="VideoButtonTV">
|
<style name="VideoButtonTV">
|
||||||
<item name="android:tag">@string/tv_no_focus_tag</item>
|
<item name="android:tag">@string/tv_no_focus_tag</item>
|
||||||
<item name="android:stateListAnimator">@null</item>
|
<item name="android:stateListAnimator">@null</item>
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
<SwitchPreference
|
|
||||||
android:defaultValue="false"
|
|
||||||
android:icon="@drawable/ic_outline_account_circle_24"
|
|
||||||
android:key="@string/skip_startup_account_select_key"
|
|
||||||
android:title="@string/skip_startup_account_select_pref" />
|
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:icon="@drawable/mal_logo"
|
android:icon="@drawable/mal_logo"
|
||||||
|
@ -22,17 +16,18 @@
|
||||||
<Preference
|
<Preference
|
||||||
android:icon="@drawable/open_subtitles_icon"
|
android:icon="@drawable/open_subtitles_icon"
|
||||||
android:key="@string/opensubtitles_key" />
|
android:key="@string/opensubtitles_key" />
|
||||||
<!-- <Preference-->
|
|
||||||
<!-- android:key="@string/nginx_key"-->
|
|
||||||
<!-- android:icon="@drawable/nginx" />-->
|
|
||||||
|
|
||||||
<!-- <Preference-->
|
<SwitchPreference
|
||||||
<!-- android:title="@string/nginx_info_title"-->
|
android:defaultValue="false"
|
||||||
<!-- android:icon="@drawable/nginx_question"-->
|
android:icon="@drawable/ic_outline_account_circle_24"
|
||||||
<!-- android:summary="@string/nginx_info_summary">-->
|
android:key="@string/skip_startup_account_select_key"
|
||||||
<!-- <intent-->
|
android:title="@string/skip_startup_account_select_pref" />
|
||||||
<!-- android:action="android.intent.action.VIEW"-->
|
|
||||||
<!-- android:data="https://www.sarlays.com/use-nginx-with-cloudstream/" />-->
|
<SwitchPreferenceCompat
|
||||||
<!-- </Preference>-->
|
android:key="@string/biometric_key"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:summary="@string/biometric_setting_summary"
|
||||||
|
android:icon="@drawable/ic_fingerprint"
|
||||||
|
android:title="@string/biometric_setting" />
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
|
@ -4,6 +4,7 @@ buildscript {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("com.android.tools.build:gradle:8.3.0")
|
classpath("com.android.tools.build:gradle:8.3.0")
|
||||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue