Merge branch 'recloudstream:master' into master

This commit is contained in:
self-similarity 2023-08-04 14:57:42 +00:00 committed by GitHub
commit 9c43ee73b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 892 additions and 351 deletions

View file

@ -1,4 +1,3 @@
import com.android.build.gradle.api.BaseVariantOutput
import org.jetbrains.dokka.gradle.DokkaTask import org.jetbrains.dokka.gradle.DokkaTask
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.net.URL import java.net.URL
@ -52,7 +51,7 @@ android {
targetSdk = 33 targetSdk = 33
versionCode = 59 versionCode = 59
versionName = "4.1.1" versionName = "4.1.2"
resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}") resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}")
@ -108,14 +107,19 @@ android {
versionCode = (System.currentTimeMillis() / 60000).toInt() versionCode = (System.currentTimeMillis() / 60000).toInt()
} }
} }
//toolchain {
// languageVersion.set(JavaLanguageVersion.of(17))
// }
// jvmToolchain(17)
compileOptions { compileOptions {
isCoreLibraryDesugaringEnabled = true isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_17
} }
kotlinOptions { kotlinOptions {
jvmTarget = "1.8" jvmTarget = "17"
freeCompilerArgs = listOf("-Xjvm-default=compatibility") freeCompilerArgs = listOf("-Xjvm-default=compatibility")
} }
lint { lint {
@ -131,22 +135,22 @@ repositories {
dependencies { dependencies {
implementation("com.google.android.mediahome:video:1.0.0") implementation("com.google.android.mediahome:video:1.0.0")
implementation("androidx.test.ext:junit-ktx:1.1.3") implementation("androidx.test.ext:junit-ktx:1.1.5")
testImplementation("org.json:json:20180813") testImplementation("org.json:json:20180813")
implementation("androidx.core:core-ktx:1.8.0") implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.appcompat:appcompat:1.4.2") // need target 32 for 1.5.0 implementation("androidx.appcompat:appcompat:1.6.1") // need target 32 for 1.5.0
// dont change this to 1.6.0 it looks ugly af // dont change this to 1.6.0 it looks ugly af
implementation("com.google.android.material:material:1.5.0") implementation("com.google.android.material:material:1.5.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.navigation:navigation-fragment-ktx:2.5.1") implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
implementation("androidx.navigation:navigation-ui-ktx:2.5.1") implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1") implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.3") androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation("androidx.test:core") androidTestImplementation("androidx.test:core")
//implementation("io.karn:khttp-android:0.1.2") //okhttp instead //implementation("io.karn:khttp-android:0.1.2") //okhttp instead
@ -165,7 +169,7 @@ dependencies {
// implementation("androidx.leanback:leanback-paging:1.1.0-alpha09") // implementation("androidx.leanback:leanback-paging:1.1.0-alpha09")
// Exoplayer // Media 3
implementation("androidx.media3:media3-common:1.1.0") implementation("androidx.media3:media3-common:1.1.0")
implementation("androidx.media3:media3-exoplayer:1.1.0") implementation("androidx.media3:media3-exoplayer:1.1.0")
implementation("androidx.media3:media3-datasource-okhttp:1.1.0") implementation("androidx.media3:media3-datasource-okhttp:1.1.0")
@ -174,10 +178,8 @@ dependencies {
implementation("androidx.media3:media3-cast:1.1.0") implementation("androidx.media3:media3-cast:1.1.0")
implementation("androidx.media3:media3-exoplayer-hls:1.1.0") implementation("androidx.media3:media3-exoplayer-hls:1.1.0")
implementation("androidx.media3:media3-exoplayer-dash:1.1.0") implementation("androidx.media3:media3-exoplayer-dash:1.1.0")
// Custom ffmpeg extension for audio codecs
implementation("com.github.recloudstream:media-ffmpeg:1.1.0")
// Use the Jellyfin ffmpeg extension for easy ffmpeg audio decoding in exoplayer. Thank you Jellyfin <3
// implementation("org.jellyfin.exoplayer:exoplayer-ffmpeg-extension:2.18.2+1")
//implementation("com.google.android.exoplayer:extension-leanback:2.14.0") //implementation("com.google.android.exoplayer:extension-leanback:2.14.0")
@ -203,8 +205,8 @@ dependencies {
//implementation("com.github.TorrentStream:TorrentStream-Android:2.7.0") //implementation("com.github.TorrentStream:TorrentStream-Android:2.7.0")
// Downloading // Downloading
implementation("androidx.work:work-runtime:2.8.0") implementation("androidx.work:work-runtime:2.8.1")
implementation("androidx.work:work-runtime-ktx:2.8.0") implementation("androidx.work:work-runtime-ktx:2.8.1")
// Networking // Networking
// implementation("com.squareup.okhttp3:okhttp:4.9.2") // implementation("com.squareup.okhttp3:okhttp:4.9.2")

View file

@ -19,8 +19,11 @@ import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.gms.cast.framework.CastSession import com.google.android.gms.cast.framework.CastSession
import com.google.android.material.chip.ChipGroup
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.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
@ -39,6 +42,13 @@ import org.schabi.newpipe.extractor.NewPipe
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.* import java.util.*
enum class FocusDirection {
Start,
End,
Up,
Down,
}
object CommonActivity { object CommonActivity {
private var _activity: WeakReference<Activity>? = null private var _activity: WeakReference<Activity>? = null
@ -301,7 +311,8 @@ object CommonActivity {
private fun localLook(from: View, id: Int): View? { private fun localLook(from: View, id: Int): View? {
if (id == NO_ID) return null if (id == NO_ID) return null
var currentLook: View = from var currentLook: View = from
while (true) { // limit to 15 look depth
for (i in 0..15) {
currentLook.findViewById<View?>(id)?.let { return it } currentLook.findViewById<View?>(id)?.let { return it }
currentLook = (currentLook.parent as? View) ?: break currentLook = (currentLook.parent as? View) ?: break
} }
@ -317,17 +328,70 @@ object CommonActivity {
currentLook = currentLook.parent as? View ?: break currentLook = currentLook.parent as? View ?: break
}*/ }*/
/** skips the initial stage of searching for an id using the view, see getNextFocus for specification */
fun continueGetNextFocus(
root: Any?,
view: View,
direction: FocusDirection,
nextId: Int,
depth: Int = 0
): View? {
if (nextId == NO_ID) return null
// do an initial search for the view, in case the localLook is too deep we can use this as
// an early break and backup view
var next =
when (root) {
is Activity -> root.findViewById(nextId)
is View -> root.rootView.findViewById<View?>(nextId)
else -> null
} ?: return null
next = localLook(view, nextId) ?: next
// if cant focus but visible then break and let android decide
// the exception if is the view is a parent and has children that wants focus
val hasChildrenThatWantsFocus = (next as? ViewGroup)?.let { parent ->
parent.descendantFocusability == ViewGroup.FOCUS_AFTER_DESCENDANTS && parent.childCount > 0
} ?: false
if (!next.isFocusable && next.isShown && !hasChildrenThatWantsFocus) return null
// if not shown then continue because we will "skip" over views to get to a replacement
if (!next.isShown) {
// we don't want a while true loop, so we let android decide if we find a recursive view
if (next == view) return null
return getNextFocus(root, next, direction, depth + 1)
}
(when (next) {
is ChipGroup -> {
next.children.firstOrNull { it.isFocusable && it.isShown }
}
is NavigationRailView -> {
next.findViewById(next.selectedItemId) ?: next.findViewById(R.id.navigation_home)
}
else -> null
})?.let {
return it
}
// nothing wrong with the view found, return it
return next
}
/** recursively looks for a next focus up to a depth of 10, /** recursively looks for a next focus up to a depth of 10,
* this is used to override the normal shit focus system * this is used to override the normal shit focus system
* because this application has a lot of invisible views that messes with some tv devices*/ * because this application has a lot of invisible views that messes with some tv devices*/
private fun getNextFocus( fun getNextFocus(
act: Activity?, root: Any?,
view: View?, view: View?,
direction: FocusDirection, direction: FocusDirection,
depth: Int = 0 depth: Int = 0
): View? { ): View? {
// if input is invalid let android decide + depth test to not crash if loop is found // if input is invalid let android decide + depth test to not crash if loop is found
if (view == null || depth >= 10 || act == null) { if (view == null || depth >= 10 || root == null) {
return null return null
} }
@ -359,38 +423,12 @@ object CommonActivity {
// if not specified then use forward id // if not specified then use forward id
nextId = view.nextFocusForwardId nextId = view.nextFocusForwardId
// if view is still not found to next focus then return and let android decide // if view is still not found to next focus then return and let android decide
if (nextId == NO_ID) return null if (nextId == NO_ID)
return null
}
return continueGetNextFocus(root, view, direction, nextId, depth)
} }
var next = act.findViewById<View?>(nextId) ?: return null
next = localLook(view, nextId) ?: next
var currentLook: View = view
while (currentLook.findViewById<View?>(nextId)?.also { next = it } == null) {
currentLook = (currentLook.parent as? View) ?: break
}
// if cant focus but visible then break and let android decide
// the exception if is the view is a parent and has children that wants focus
val hasChildrenThatWantsFocus = (next as? ViewGroup)?.let { parent ->
parent.descendantFocusability == ViewGroup.FOCUS_AFTER_DESCENDANTS && parent.childCount > 0
} ?: false
if (!next.isFocusable && next.isShown && !hasChildrenThatWantsFocus) return null
// if not shown then continue because we will "skip" over views to get to a replacement
if (!next.isShown) return getNextFocus(act, next, direction, depth + 1)
// nothing wrong with the view found, return it
return next
}
private enum class FocusDirection {
Start,
End,
Up,
Down,
}
fun onKeyDown(act: Activity?, keyCode: Int, event: KeyEvent?) { fun onKeyDown(act: Activity?, keyCode: Int, event: KeyEvent?) {
//println("Keycode: $keyCode") //println("Keycode: $keyCode")
@ -520,7 +558,7 @@ object CommonActivity {
else -> null else -> null
} }
// println("NEXT FOCUS : $nextView")
if (nextView != null) { if (nextView != null) {
nextView.requestFocus() nextView.requestFocus()
keyEventListener?.invoke(Pair(event, true)) keyEventListener?.invoke(Pair(event, true))

View file

@ -27,6 +27,7 @@ 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.isVisible import androidx.core.view.isVisible
import androidx.core.view.marginStart
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.NavController import androidx.navigation.NavController
@ -37,6 +38,8 @@ import androidx.navigation.NavOptions
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSnapHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
@ -91,6 +94,7 @@ import com.lagradost.cloudstream3.ui.home.HomeViewModel
import com.lagradost.cloudstream3.ui.player.BasicLink 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.LinearListLayout
import com.lagradost.cloudstream3.ui.result.ResultViewModel2 import com.lagradost.cloudstream3.ui.result.ResultViewModel2
import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST
import com.lagradost.cloudstream3.ui.result.setImage import com.lagradost.cloudstream3.ui.result.setImage
@ -110,6 +114,7 @@ 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.isLtr import com.lagradost.cloudstream3.utils.AppUtils.isLtr
import com.lagradost.cloudstream3.utils.AppUtils.isNetworkAvailable import com.lagradost.cloudstream3.utils.AppUtils.isNetworkAvailable
import com.lagradost.cloudstream3.utils.AppUtils.isRtl
import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.loadCache
import com.lagradost.cloudstream3.utils.AppUtils.loadRepository import com.lagradost.cloudstream3.utils.AppUtils.loadRepository
import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.AppUtils.loadResult
@ -146,6 +151,7 @@ import java.lang.ref.WeakReference
import java.net.URI import java.net.URI
import java.net.URLDecoder import java.net.URLDecoder
import java.nio.charset.Charset import java.nio.charset.Charset
import kotlin.math.abs
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -848,6 +854,24 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
private var animator: ValueAnimator? = null private var animator: ValueAnimator? = null
/** if this is enabled it will keep the focus unmoving
* during listview move */
private const val NO_MOVE_LIST: Boolean = false
/** If this is enabled then it will try to move the
* listview focus to the left instead of center */
private const val LEFTMOST_MOVE_LIST: Boolean = true
private val reflectedScroll by lazy {
try {
RecyclerView::class.java.declaredMethods.firstOrNull {
it.name == "scrollStep"
}?.also { it.isAccessible = true }
} catch (t : Throwable) {
null
}
}
@MainThread @MainThread
fun updateFocusView(newFocus: View?, same: Boolean = false) { fun updateFocusView(newFocus: View?, same: Boolean = false) {
val focusOutline = focusOutline.get() ?: return val focusOutline = focusOutline.get() ?: return
@ -867,17 +891,67 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
if (newFocus != null) { if (newFocus != null) {
lastFocus = WeakReference(newFocus) lastFocus = WeakReference(newFocus)
val parent = newFocus.parent
var targetDx = 0
if (parent is RecyclerView) {
val layoutManager = parent.layoutManager
if (layoutManager is LinearListLayout && layoutManager.orientation == LinearLayoutManager.HORIZONTAL) {
val dx =
LinearSnapHelper().calculateDistanceToFinalSnap(layoutManager, newFocus)
?.get(0)
if (dx != null) {
val rdx = if (LEFTMOST_MOVE_LIST) {
// this makes the item the leftmost in ltr, instead of center
val diff =
((layoutManager.width - layoutManager.paddingStart - newFocus.measuredWidth) / 2) - newFocus.marginStart
dx + if (parent.isRtl()) {
-diff
} else {
diff
}
} else {
if (dx > 0) dx else 0
}
if(!NO_MOVE_LIST) {
parent.smoothScrollBy(rdx, 0)
}else {
val smoothScroll = reflectedScroll
if(smoothScroll == null) {
parent.smoothScrollBy(rdx, 0)
} else {
try {
// this is very fucked but because it is a protected method to
// be able to compute the scroll I use reflection, scroll, then
// scroll back, then smooth scroll and set the no move
val out = IntArray(2)
smoothScroll.invoke(parent, rdx, 0, out)
val scrolledX = out[0]
if(abs(scrolledX) <= 0) { // newFocus.measuredWidth*2
smoothScroll.invoke(parent, -rdx, 0, out)
parent.smoothScrollBy(scrolledX, 0)
if (NO_MOVE_LIST) targetDx = scrolledX
}
} catch (t : Throwable) {
parent.smoothScrollBy(rdx, 0)
}
}
}
}
}
}
val out = IntArray(2) val out = IntArray(2)
newFocus.getLocationInWindow(out) newFocus.getLocationInWindow(out)
val (screenX, screenY) = out val (screenX, screenY) = out
var (x, y) = screenX.toFloat() to screenY.toFloat() var (x, y) = screenX.toFloat() to screenY.toFloat()
val (currentX, currentY) = focusOutline.translationX to focusOutline.translationY val (currentX, currentY) = focusOutline.translationX to focusOutline.translationY
// println(">><<< $x $y $currentX $currentY")
if (!newFocus.isLtr()) { if (!newFocus.isLtr()) {
x = x - focusOutline.rootView.width + newFocus.measuredWidth x = x - focusOutline.rootView.width + newFocus.measuredWidth
} }
x -= targetDx
// out of bounds = 0,0 // out of bounds = 0,0
if (screenX == 0 && screenY == 0) { if (screenX == 0 && screenY == 0) {
@ -1093,9 +1167,17 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
} }
//Automatically download not existing plugins, using mode specified. //Automatically download not existing plugins, using mode specified.
val auto_download_plugin = AutoDownloadMode.getEnum(settingsManager.getInt(getString(R.string.auto_download_plugins_key), 0)) ?: AutoDownloadMode.Disable val auto_download_plugin = AutoDownloadMode.getEnum(
settingsManager.getInt(
getString(R.string.auto_download_plugins_key),
0
)
) ?: AutoDownloadMode.Disable
if (auto_download_plugin != AutoDownloadMode.Disable) { if (auto_download_plugin != AutoDownloadMode.Disable) {
PluginManager.downloadNotExistingPluginsAndLoad(this@MainActivity, auto_download_plugin) PluginManager.downloadNotExistingPluginsAndLoad(
this@MainActivity,
auto_download_plugin
)
} }
} }

View file

@ -5,11 +5,12 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.FragmentChildDownloadsBinding import com.lagradost.cloudstream3.databinding.FragmentChildDownloadsBinding
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
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.getKeys import com.lagradost.cloudstream3.utils.DataStore.getKeys
@ -110,7 +111,11 @@ class DownloadChildFragment : Fragment() {
downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent += it } downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent += it }
binding?.downloadChildList?.adapter = adapter binding?.downloadChildList?.adapter = adapter
binding?.downloadChildList?.layoutManager = GridLayoutManager(context, 1) binding?.downloadChildList?.setLinearListLayout(
isHorizontal = false,
nextDown = FOCUS_SELF,
nextRight = FOCUS_SELF
)//layoutManager = GridLayoutManager(context, 1)
updateList(folder) updateList(folder)
} }

View file

@ -40,6 +40,8 @@ import com.lagradost.cloudstream3.databinding.FragmentDownloadsBinding
import com.lagradost.cloudstream3.databinding.StreamInputBinding import com.lagradost.cloudstream3.databinding.StreamInputBinding
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.ui.player.BasicLink 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 com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import java.net.URI import java.net.URI
@ -151,6 +153,7 @@ class DownloadFragment : Fragment() {
) )
} }
} }
1 -> { 1 -> {
(activity as AppCompatActivity?)?.loadResult( (activity as AppCompatActivity?)?.loadResult(
click.data.url, click.data.url,
@ -187,7 +190,13 @@ class DownloadFragment : Fragment() {
binding?.downloadList?.apply { binding?.downloadList?.apply {
this.adapter = adapter this.adapter = adapter
layoutManager = GridLayoutManager(context, 1) setLinearListLayout(
isHorizontal = false,
nextRight = FOCUS_SELF,
nextUp = FOCUS_SELF,
nextDown = FOCUS_SELF
)
//layoutManager = GridLayoutManager(context, 1)
} }
// Should be visible in emulator layout // Should be visible in emulator layout

View file

@ -95,7 +95,7 @@ class HomeChildItemAdapter(
fun bind(card: SearchResponse, position: Int) { fun bind(card: SearchResponse, position: Int) {
// TV focus fixing // TV focus fixing
val nextFocusBehavior = when (position) { /*val nextFocusBehavior = when (position) {
0 -> true 0 -> true
itemCount - 1 -> false itemCount - 1 -> false
else -> null else -> null
@ -113,7 +113,7 @@ class HomeChildItemAdapter(
} else { } else {
itemView.nextFocusRightId = -1 itemView.nextFocusRightId = -1
itemView.nextFocusLeftId = -1 itemView.nextFocusLeftId = -1
} }*/
when (binding) { when (binding) {
@ -171,7 +171,7 @@ class HomeChildItemAdapter(
card, card,
position, position,
itemView, itemView,
nextFocusBehavior, null, // nextFocusBehavior,
nextFocusUp, nextFocusUp,
nextFocusDown nextFocusDown
) )

View file

@ -310,6 +310,17 @@ class HomeFragment : Fragment() {
selectedTypes: List<TvType>, selectedTypes: List<TvType>,
validTypes: List<TvType>, validTypes: List<TvType>,
callback: (List<TvType>) -> Unit callback: (List<TvType>) -> Unit
) {
bindChips(header, selectedTypes, validTypes, callback, null, null)
}
fun bindChips(
header: TvtypesChipsBinding?,
selectedTypes: List<TvType>,
validTypes: List<TvType>,
callback: (List<TvType>) -> Unit,
nextFocusDown: Int?,
nextFocusUp: Int?
) { ) {
if (header == null) return if (header == null) return
val pairList = getPairList(header) val pairList = getPairList(header)
@ -317,6 +328,17 @@ class HomeFragment : Fragment() {
val isValid = validTypes.any { types.contains(it) } val isValid = validTypes.any { types.contains(it) }
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
if (isTrueTvSettings()) {
button?.isFocusableInTouchMode = true
}
if (nextFocusDown != null)
button?.nextFocusDownId = nextFocusDown
if (nextFocusUp != null)
button?.nextFocusUpId = nextFocusUp
button?.setOnCheckedChangeListener { _, _ -> button?.setOnCheckedChangeListener { _, _ ->
val list = ArrayList<TvType>() val list = ArrayList<TvType>()
for ((sbutton, vvalidTypes) in pairList) { for ((sbutton, vvalidTypes) in pairList) {

View file

@ -11,6 +11,7 @@ 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.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
@ -162,7 +163,8 @@ open class ParentItemAdapter(
RecyclerView.ViewHolder(binding.root) { RecyclerView.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 endFocus = FOCUS_SELF
fun update(expand: HomeViewModel.ExpandableHomepageList) { fun update(expand: HomeViewModel.ExpandableHomepageList) {
val info = expand.list val info = expand.list
(recyclerView.adapter as? HomeChildItemAdapter?)?.apply { (recyclerView.adapter as? HomeChildItemAdapter?)?.apply {
@ -176,8 +178,13 @@ open class ParentItemAdapter(
nextFocusDown = recyclerView.nextFocusDownId, nextFocusDown = recyclerView.nextFocusDownId,
).apply { ).apply {
isHorizontal = info.isHorizontalImages isHorizontal = info.isHorizontalImages
hasNext = expand.hasNext
} }
recyclerView.setLinearListLayout() recyclerView.setLinearListLayout(
isHorizontal = true,
nextLeft = startFocus,
nextRight = endFocus,
)
} }
} }
@ -192,7 +199,11 @@ open class ParentItemAdapter(
isHorizontal = info.isHorizontalImages isHorizontal = info.isHorizontalImages
hasNext = expand.hasNext hasNext = expand.hasNext
} }
recyclerView.setLinearListLayout() recyclerView.setLinearListLayout(
isHorizontal = true,
nextLeft = startFocus,
nextRight = endFocus,
)
title.text = info.name title.text = info.name
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {

View file

@ -14,6 +14,7 @@ import androidx.viewbinding.ViewBinding
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
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.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
import com.lagradost.cloudstream3.CommonActivity.activity import com.lagradost.cloudstream3.CommonActivity.activity
@ -29,6 +30,7 @@ import com.lagradost.cloudstream3.mvvm.debugException
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.selectHomepage import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.selectHomepage
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
import com.lagradost.cloudstream3.ui.result.ResultViewModel2 import com.lagradost.cloudstream3.ui.result.ResultViewModel2
import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST
import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.result.setLinearListLayout
@ -416,6 +418,7 @@ class HomeParentItemAdapterPreview(
isChecked = checked.contains(watch) isChecked = checked.contains(watch)
} }
} }
toggleListHolder?.isGone = visible.isEmpty()
} }
} ?: debugException { "Expected findViewTreeLifecycleOwner" } } ?: debugException { "Expected findViewTreeLifecycleOwner" }
} }
@ -428,6 +431,8 @@ class HomeParentItemAdapterPreview(
Pair(itemView.findViewById(R.id.home_plan_to_watch_btt), WatchType.PLANTOWATCH), Pair(itemView.findViewById(R.id.home_plan_to_watch_btt), WatchType.PLANTOWATCH),
) )
private val toggleListHolder : ChipGroup? = itemView.findViewById(R.id.home_type_holder)
init { init {
previewViewpager.setPageTransformer(HomeScrollTransformer()) previewViewpager.setPageTransformer(HomeScrollTransformer())
@ -435,8 +440,8 @@ class HomeParentItemAdapterPreview(
resumeRecyclerView.adapter = resumeAdapter resumeRecyclerView.adapter = resumeAdapter
bookmarkRecyclerView.adapter = bookmarkAdapter bookmarkRecyclerView.adapter = bookmarkAdapter
resumeRecyclerView.setLinearListLayout() resumeRecyclerView.setLinearListLayout(nextLeft = R.id.nav_rail_view, nextRight = FOCUS_SELF)
bookmarkRecyclerView.setLinearListLayout() bookmarkRecyclerView.setLinearListLayout(nextLeft = R.id.nav_rail_view, nextRight = FOCUS_SELF)
fixPaddingStatusbarMargin(topPadding) fixPaddingStatusbarMargin(topPadding)

View file

@ -7,16 +7,14 @@ import android.os.Looper
import android.util.Log import android.util.Log
import android.util.Rational import android.util.Rational
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.media3.common.C
import androidx.preference.PreferenceManager
import androidx.media3.common.C.* import androidx.media3.common.C.*
import androidx.media3.common.Format import androidx.media3.common.Format
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import androidx.media3.common.TrackSelectionOverride
import androidx.media3.common.MimeTypes import androidx.media3.common.MimeTypes
import androidx.media3.common.PlaybackException import androidx.media3.common.PlaybackException
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.common.TrackGroup import androidx.media3.common.TrackGroup
import androidx.media3.common.TrackSelectionOverride
import androidx.media3.common.Tracks import androidx.media3.common.Tracks
import androidx.media3.common.VideoSize import androidx.media3.common.VideoSize
import androidx.media3.database.StandaloneDatabaseProvider import androidx.media3.database.StandaloneDatabaseProvider
@ -30,6 +28,7 @@ import androidx.media3.datasource.cache.SimpleCache
import androidx.media3.datasource.okhttp.OkHttpDataSource import androidx.media3.datasource.okhttp.OkHttpDataSource
import androidx.media3.exoplayer.DefaultLoadControl import androidx.media3.exoplayer.DefaultLoadControl
import androidx.media3.exoplayer.DefaultRenderersFactory import androidx.media3.exoplayer.DefaultRenderersFactory
import androidx.media3.exoplayer.DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.SeekParameters import androidx.media3.exoplayer.SeekParameters
import androidx.media3.exoplayer.source.ClippingMediaSource import androidx.media3.exoplayer.source.ClippingMediaSource
@ -41,6 +40,7 @@ import androidx.media3.exoplayer.text.TextRenderer
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
import androidx.media3.exoplayer.trackselection.TrackSelector import androidx.media3.exoplayer.trackselection.TrackSelector
import androidx.media3.ui.SubtitleView import androidx.media3.ui.SubtitleView
import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
@ -49,8 +49,8 @@ import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle
import com.lagradost.cloudstream3.utils.EpisodeSkip
import com.lagradost.cloudstream3.utils.AppUtils.isUsingMobileData import com.lagradost.cloudstream3.utils.AppUtils.isUsingMobileData
import com.lagradost.cloudstream3.utils.EpisodeSkip
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.ExtractorLinkPlayList import com.lagradost.cloudstream3.utils.ExtractorLinkPlayList
import com.lagradost.cloudstream3.utils.ExtractorUri import com.lagradost.cloudstream3.utils.ExtractorUri
@ -397,7 +397,7 @@ class CS3IPlayer : IPlayer {
if (subtitle == null) { if (subtitle == null) {
trackSelector.setParameters( trackSelector.setParameters(
trackSelector.buildUponParameters() trackSelector.buildUponParameters()
.setPreferredTextLanguage(null) .setTrackTypeDisabled(TRACK_TYPE_TEXT, true)
.clearOverridesOfType(TRACK_TYPE_TEXT) .clearOverridesOfType(TRACK_TYPE_TEXT)
) )
} else { } else {
@ -415,6 +415,7 @@ class CS3IPlayer : IPlayer {
.apply { .apply {
val track = getTextTrack(subtitle.getId()) val track = getTextTrack(subtitle.getId())
if (track != null) { if (track != null) {
setTrackTypeDisabled(TRACK_TYPE_TEXT, false)
setOverrideForType( setOverrideForType(
TrackSelectionOverride( TrackSelectionOverride(
track.first, track.first,
@ -662,12 +663,7 @@ class CS3IPlayer : IPlayer {
private fun getTrackSelector(context: Context, maxVideoHeight: Int?): TrackSelector { private fun getTrackSelector(context: Context, maxVideoHeight: Int?): TrackSelector {
val trackSelector = DefaultTrackSelector(context) val trackSelector = DefaultTrackSelector(context)
trackSelector.parameters = DefaultTrackSelector.ParametersBuilder(context) trackSelector.parameters = trackSelector.buildUponParameters()
// .setRendererDisabled(C.TRACK_TYPE_VIDEO, true)
.setRendererDisabled(C.TRACK_TYPE_TEXT, true)
// Experimental, I think this causes issues with audio track init 5001
// .setTunnelingEnabled(true)
.setDisabledTextTrackSelectionFlags(C.TRACK_TYPE_TEXT)
// This will not force higher quality videos to fail // This will not force higher quality videos to fail
// but will make the m3u8 pick the correct preferred // but will make the m3u8 pick the correct preferred
.setMaxVideoSize(Int.MAX_VALUE, maxVideoHeight ?: Int.MAX_VALUE) .setMaxVideoSize(Int.MAX_VALUE, maxVideoHeight ?: Int.MAX_VALUE)
@ -701,9 +697,9 @@ class CS3IPlayer : IPlayer {
ExoPlayer.Builder(context) ExoPlayer.Builder(context)
.setRenderersFactory { eventHandler, videoRendererEventListener, audioRendererEventListener, textRendererOutput, metadataRendererOutput -> .setRenderersFactory { eventHandler, videoRendererEventListener, audioRendererEventListener, textRendererOutput, metadataRendererOutput ->
DefaultRenderersFactory(context).apply { DefaultRenderersFactory(context).apply {
// setEnableDecoderFallback(true) setEnableDecoderFallback(true)
// Enable Ffmpeg extension // Enable Ffmpeg extension
// setExtensionRendererMode(EXTENSION_RENDERER_MODE_ON) setExtensionRendererMode(EXTENSION_RENDERER_MODE_ON)
}.createRenderers( }.createRenderers(
eventHandler, eventHandler,
videoRendererEventListener, videoRendererEventListener,

View file

@ -11,6 +11,7 @@ import android.util.Rational
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import kotlin.math.roundToInt import kotlin.math.roundToInt
class PlayerPipHelper { class PlayerPipHelper {
@ -88,10 +89,12 @@ class PlayerPipHelper {
val ratioAccuracy = 100000 // To convert the float to int val ratioAccuracy = 100000 // To convert the float to int
// java.lang.IllegalArgumentException: setPictureInPictureParams: Aspect ratio is too extreme (must be between 0.418410 and 2.390000) // java.lang.IllegalArgumentException: setPictureInPictureParams: Aspect ratio is too extreme (must be between 0.418410 and 2.390000)
val fixedRational = aspectRatio?.toFloat()?.coerceIn(mixAspectRatio, maxAspectRatio)?.let { val fixedRational =
aspectRatio?.toFloat()?.coerceIn(mixAspectRatio, maxAspectRatio)?.let {
Rational((it * ratioAccuracy).roundToInt(), ratioAccuracy) Rational((it * ratioAccuracy).roundToInt(), ratioAccuracy)
} }
normalSafeApiCall {
activity.setPictureInPictureParams( activity.setPictureInPictureParams(
PictureInPictureParams.Builder() PictureInPictureParams.Builder()
.apply { .apply {
@ -107,3 +110,4 @@ class PlayerPipHelper {
} }
} }
} }
}

View file

@ -4,19 +4,45 @@ import android.content.Context
import android.view.View import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.FocusDirection
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
fun RecyclerView?.setLinearListLayout(isHorizontal: Boolean = true) { const val FOCUS_SELF = View.NO_ID - 1
if (this == null) return const val FOCUS_INHERIT = FOCUS_SELF - 1
fun RecyclerView?.setLinearListLayout(
isHorizontal: Boolean = true,
nextLeft: Int = FOCUS_INHERIT,
nextRight: Int = FOCUS_INHERIT,
nextUp: Int = FOCUS_INHERIT,
nextDown: Int = FOCUS_INHERIT
) {
if (this == null) return
val ctx = this.context ?: return
this.layoutManager = this.layoutManager =
this.context?.let { LinearListLayout(it).apply { if (isHorizontal) setHorizontal() else setVertical() } } LinearListLayout(ctx).apply {
// ?: this.layoutManager if (isHorizontal) setHorizontal() else setVertical()
nextFocusLeft =
if (nextLeft == FOCUS_INHERIT) this@setLinearListLayout.nextFocusLeftId else nextLeft
nextFocusRight =
if (nextRight == FOCUS_INHERIT) this@setLinearListLayout.nextFocusRightId else nextRight
nextFocusUp =
if (nextUp == FOCUS_INHERIT) this@setLinearListLayout.nextFocusUpId else nextUp
nextFocusDown =
if (nextDown == FOCUS_INHERIT) this@setLinearListLayout.nextFocusDownId else nextDown
}
} }
open class LinearListLayout(context: Context?) : open class LinearListLayout(context: Context?) :
LinearLayoutManager(context) { LinearLayoutManager(context) {
var nextFocusLeft: Int = View.NO_ID
var nextFocusRight: Int = View.NO_ID
var nextFocusUp: Int = View.NO_ID
var nextFocusDown: Int = View.NO_ID
fun setHorizontal() { fun setHorizontal() {
orientation = HORIZONTAL orientation = HORIZONTAL
} }
@ -56,8 +82,37 @@ open class LinearListLayout(context: Context?) :
linearSmoothScroller.targetPosition = position linearSmoothScroller.targetPosition = position
startSmoothScroll(linearSmoothScroller) startSmoothScroll(linearSmoothScroller)
}*/ }*/
/** from the current focus go to a direction */
private fun getNextDirection(focused: View?, direction: FocusDirection): View? {
val id = when (direction) {
FocusDirection.Start -> if (isLayoutRTL) nextFocusRight else nextFocusLeft
FocusDirection.End -> if (isLayoutRTL) nextFocusLeft else nextFocusRight
FocusDirection.Up -> nextFocusUp
FocusDirection.Down -> nextFocusDown
}
return when (id) {
View.NO_ID -> null
FOCUS_SELF -> focused
else -> CommonActivity.continueGetNextFocus(
activity ?: focused,
focused ?: return null,
direction,
id
)
}
}
override fun onInterceptFocusSearch(focused: View, direction: Int): View? { override fun onInterceptFocusSearch(focused: View, direction: Int): View? {
val dir = if (orientation == HORIZONTAL) { val dir = if (orientation == HORIZONTAL) {
if (direction == View.FOCUS_DOWN) getNextDirection(focused, FocusDirection.Down)?.let { newFocus ->
return newFocus
}
if (direction == View.FOCUS_UP) getNextDirection(focused, FocusDirection.Up)?.let { newFocus ->
return newFocus
}
if (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP) { if (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP) {
// This scrolls the recyclerview before doing focus search, which // This scrolls the recyclerview before doing focus search, which
// allows the focus search to work better. // allows the focus search to work better.
@ -69,34 +124,45 @@ open class LinearListLayout(context: Context?) :
} }
var ret = if (direction == View.FOCUS_RIGHT) 1 else -1 var ret = if (direction == View.FOCUS_RIGHT) 1 else -1
// only flip on horizontal layout // only flip on horizontal layout
if (this.isLayoutRTL) { if (isLayoutRTL) {
ret = -ret ret = -ret
} }
ret ret
} else { } else {
if (direction == View.FOCUS_RIGHT || direction == View.FOCUS_LEFT) return null if (direction == View.FOCUS_RIGHT) getNextDirection(focused, FocusDirection.End)?.let { newFocus ->
return newFocus
}
if (direction == View.FOCUS_LEFT) getNextDirection(focused, FocusDirection.Start)?.let { newFocus ->
return newFocus
}
if (direction == View.FOCUS_RIGHT || direction == View.FOCUS_LEFT) {
(focused.parent as? RecyclerView)?.focusSearch(direction)
return null
}
//if (direction == View.FOCUS_RIGHT || direction == View.FOCUS_LEFT) return null
if (direction == View.FOCUS_DOWN) 1 else -1 if (direction == View.FOCUS_DOWN) 1 else -1
} }
return try { try {
getPosition(getCorrectParent(focused))?.let { position -> val position = getPosition(getCorrectParent(focused)) ?: return null
val lookfor = dir + position val lookFor = dir + position
//clamp(dir + position, 0, recyclerView.adapter?.itemCount ?: return null)
// refocus on the same view if going out of bounds, note that we only do it // if out of bounds then refocus as specified
// for out of bounds one way as we may override the start where item == -1 return if (lookFor >= itemCount) {
if (lookfor >= itemCount) { getNextDirection(focused, if(orientation == HORIZONTAL) FocusDirection.End else FocusDirection.Down)
return getViewFromPos(itemCount - 1) ?: focused } else if (lookFor < 0) {
} getNextDirection(focused, if(orientation == HORIZONTAL) FocusDirection.Start else FocusDirection.Up)
} else {
getViewFromPos(lookfor) ?: run { getViewFromPos(lookFor) ?: run {
scrollToPosition(lookfor) scrollToPosition(lookFor)
null null
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)
null return null
} }
} }

View file

@ -338,7 +338,12 @@ open class ResultFragmentPhone : FullScreenPlayer(),
) )
} }
resultCastItems.layoutManager = object : LinearListLayout(view.context) { resultCastItems.setLinearListLayout(
isHorizontal = true,
nextLeft = FOCUS_SELF,
nextRight = FOCUS_SELF
)
/*resultCastItems.layoutManager = object : LinearListLayout(view.context) {
override fun onRequestChildFocus( override fun onRequestChildFocus(
parent: RecyclerView, parent: RecyclerView,
state: RecyclerView.State, state: RecyclerView.State,
@ -356,7 +361,7 @@ open class ResultFragmentPhone : FullScreenPlayer(),
} }
}.apply { }.apply {
this.orientation = RecyclerView.HORIZONTAL this.orientation = RecyclerView.HORIZONTAL
} }*/
resultCastItems.adapter = ActorAdaptor() resultCastItems.adapter = ActorAdaptor()
resultEpisodes.adapter = resultEpisodes.adapter =
@ -597,8 +602,14 @@ open class ResultFragmentPhone : FullScreenPlayer(),
EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, ep) EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, ep)
) )
} }
DOWNLOAD_ACTION_LONG_CLICK -> { DOWNLOAD_ACTION_LONG_CLICK -> {
viewModel.handleAction(EpisodeClickEvent(ACTION_DOWNLOAD_MIRROR, ep)) viewModel.handleAction(
EpisodeClickEvent(
ACTION_DOWNLOAD_MIRROR,
ep
)
)
} }
else -> DownloadButtonSetup.handleDownloadClick(click) else -> DownloadButtonSetup.handleDownloadClick(click)

View file

@ -307,7 +307,29 @@ class ResultFragmentTv : Fragment() {
} }
} }
resultEpisodes.setLinearListLayout(isHorizontal = false)/*.layoutManager = resultEpisodes.setLinearListLayout(
isHorizontal = false,
nextUp = FOCUS_SELF,
nextDown = FOCUS_SELF,
nextRight = FOCUS_SELF,
)
resultDubSelection.setLinearListLayout(
isHorizontal = false,
nextUp = FOCUS_SELF,
nextDown = FOCUS_SELF,
)
resultRangeSelection.setLinearListLayout(
isHorizontal = false,
nextUp = FOCUS_SELF,
nextDown = FOCUS_SELF,
)
resultSeasonSelection.setLinearListLayout(
isHorizontal = false,
nextUp = FOCUS_SELF,
nextDown = FOCUS_SELF,
)
/*.layoutManager =
LinearListLayout(resultEpisodes.context, resultEpisodes.isRtl()).apply { LinearListLayout(resultEpisodes.context, resultEpisodes.isRtl()).apply {
setVertical() setVertical()
}*/ }*/
@ -367,6 +389,11 @@ class ResultFragmentTv : Fragment() {
) )
resultCastItems.layoutManager = object : LinearListLayout(view.context) { resultCastItems.layoutManager = object : LinearListLayout(view.context) {
override fun onInterceptFocusSearch(focused: View, direction: Int): View? {
return super.onInterceptFocusSearch(focused, direction)
}
override fun onRequestChildFocus( override fun onRequestChildFocus(
parent: RecyclerView, parent: RecyclerView,
state: RecyclerView.State, state: RecyclerView.State,
@ -383,8 +410,9 @@ class ResultFragmentTv : Fragment() {
} }
} }
}.apply { }.apply {
this.orientation = RecyclerView.HORIZONTAL setHorizontal()
} }
resultCastItems.adapter = ActorAdaptor { resultCastItems.adapter = ActorAdaptor {
toggleEpisodes(false) toggleEpisodes(false)
} }

View file

@ -45,6 +45,8 @@ import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.currentSpan
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.updateChips 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.setLinearListLayout
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.AppUtils.ownHide import com.lagradost.cloudstream3.utils.AppUtils.ownHide
@ -519,9 +521,12 @@ class SearchFragment : Fragment() {
binding?.apply { binding?.apply {
searchHistoryRecycler.adapter = historyAdapter searchHistoryRecycler.adapter = historyAdapter
searchHistoryRecycler.layoutManager = GridLayoutManager(context, 1) searchHistoryRecycler.setLinearListLayout(isHorizontal = false, nextRight = FOCUS_SELF)
//searchHistoryRecycler.layoutManager = GridLayoutManager(context, 1)
searchMasterRecycler.adapter = masterAdapter searchMasterRecycler.adapter = masterAdapter
//searchMasterRecycler.setLinearListLayout(isHorizontal = false, nextRight = FOCUS_SELF)
searchMasterRecycler.layoutManager = GridLayoutManager(context, 1) searchMasterRecycler.layoutManager = GridLayoutManager(context, 1)
// Automatically search the specified query, this allows the app search to launch from intent // Automatically search the specified query, this allows the app search to launch from intent

View file

@ -23,6 +23,8 @@ import com.lagradost.cloudstream3.databinding.FragmentExtensionsBinding
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.plugins.RepositoryManager import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.result.setText
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
@ -82,6 +84,14 @@ class ExtensionsFragment : Fragment() {
setUpToolbar(R.string.extensions) setUpToolbar(R.string.extensions)
binding?.repoRecyclerView?.setLinearListLayout(
isHorizontal = false,
nextUp = R.id.settings_toolbar, //FOCUS_SELF, // back has no id so we cant :pensive:
nextDown = R.id.plugin_storage_appbar,
nextRight = FOCUS_SELF,
nextLeft = R.id.nav_rail_view
)
binding?.repoRecyclerView?.adapter = RepoAdapter(false, { binding?.repoRecyclerView?.adapter = RepoAdapter(false, {
findNavController().navigate( findNavController().navigate(
R.id.navigation_settings_extensions_to_navigation_settings_plugins, R.id.navigation_settings_extensions_to_navigation_settings_plugins,
@ -126,11 +136,11 @@ class ExtensionsFragment : Fragment() {
(binding?.repoRecyclerView?.adapter as? RepoAdapter)?.updateList(it) (binding?.repoRecyclerView?.adapter as? RepoAdapter)?.updateList(it)
} }
binding?.repoRecyclerView?.apply { /*binding?.repoRecyclerView?.apply {
context?.let { ctx -> context?.let { ctx ->
layoutManager = LinearRecycleViewLayoutManager(ctx, nextFocusUpId, nextFocusDownId) layoutManager = LinearRecycleViewLayoutManager(ctx, nextFocusUpId, nextFocusDownId)
} }
} }*/
// list_repositories?.setOnClickListener { // list_repositories?.setOnClickListener {
// // Open webview on tv if browser fails // // Open webview on tv if browser fails

View file

@ -88,7 +88,8 @@ class PluginDetailsFragment(val data: PluginViewData) : BottomSheetDialogFragmen
if (metadata.authors.isEmpty()) getString(R.string.no_data) else metadata.authors.joinToString( if (metadata.authors.isEmpty()) getString(R.string.no_data) else metadata.authors.joinToString(
", " ", "
) )
pluginStatus.text = resources.getStringArray(R.array.extension_statuses)[metadata.status] pluginStatus.text =
resources.getStringArray(R.array.extension_statuses)[metadata.status]
pluginTypes.text = pluginTypes.text =
if (metadata.tvTypes.isNullOrEmpty()) getString(R.string.no_data) else metadata.tvTypes.joinToString( if (metadata.tvTypes.isNullOrEmpty()) getString(R.string.no_data) else metadata.tvTypes.joinToString(
", " ", "

View file

@ -15,6 +15,8 @@ import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.databinding.FragmentPluginsBinding import com.lagradost.cloudstream3.databinding.FragmentPluginsBinding
import com.lagradost.cloudstream3.mvvm.observe 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.setLinearListLayout
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
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
@ -78,9 +80,11 @@ class PluginsFragment : Fragment() {
R.id.download_all -> { R.id.download_all -> {
PluginsViewModel.downloadAll(activity, url, pluginViewModel) PluginsViewModel.downloadAll(activity, url, pluginViewModel)
} }
R.id.lang_filter -> { R.id.lang_filter -> {
val tempLangs = appLanguages.toMutableList() val tempLangs = appLanguages.toMutableList()
val languageCodes = mutableListOf("none") + tempLangs.map { (_, _, iso) -> iso } val languageCodes =
mutableListOf("none") + tempLangs.map { (_, _, iso) -> iso }
val languageNames = val languageNames =
mutableListOf(getString(R.string.no_data)) + tempLangs.map { (emoji, name, iso) -> mutableListOf(getString(R.string.no_data)) + tempLangs.map { (emoji, name, iso) ->
val flag = val flag =
@ -88,17 +92,18 @@ class PluginsFragment : Fragment() {
"$flag $name" "$flag $name"
} }
val selectedList = val selectedList =
pluginViewModel.languages.map { it -> languageCodes.indexOf(it) } pluginViewModel.languages.map { languageCodes.indexOf(it) }
activity?.showMultiDialog( activity?.showMultiDialog(
languageNames, languageNames,
selectedList, selectedList,
getString(R.string.provider_lang_settings), getString(R.string.provider_lang_settings),
{}) { newList -> {}) { newList ->
pluginViewModel.languages = newList.map { it -> languageCodes[it] } pluginViewModel.languages = newList.map { languageCodes[it] }
pluginViewModel.updateFilteredPlugins() pluginViewModel.updateFilteredPlugins()
} }
} }
else -> {} else -> {}
} }
return@setOnMenuItemClickListener true return@setOnMenuItemClickListener true
@ -137,7 +142,11 @@ class PluginsFragment : Fragment() {
// Because onActionViewCollapsed doesn't wanna work we need this workaround :( // Because onActionViewCollapsed doesn't wanna work we need this workaround :(
binding?.pluginRecyclerView?.setLinearListLayout(
isHorizontal = false,
nextDown = FOCUS_SELF,
nextRight = FOCUS_SELF,
)
binding?.pluginRecyclerView?.adapter = binding?.pluginRecyclerView?.adapter =
PluginAdapter { PluginAdapter {
@ -167,11 +176,18 @@ class PluginsFragment : Fragment() {
pluginViewModel.updatePluginList(context, url) pluginViewModel.updatePluginList(context, url)
binding?.tvtypesChipsScroll?.root?.isVisible = true binding?.tvtypesChipsScroll?.root?.isVisible = true
bindChips(binding?.tvtypesChipsScroll?.tvtypesChips, emptyList(), TvType.values().toList()) { list -> bindChips(
binding?.tvtypesChipsScroll?.tvtypesChips,
emptyList(),
TvType.values().toList(),
callback = { list ->
pluginViewModel.tvTypes.clear() pluginViewModel.tvTypes.clear()
pluginViewModel.tvTypes.addAll(list.map { it.name }) pluginViewModel.tvTypes.addAll(list.map { it.name })
pluginViewModel.updateFilteredPlugins() pluginViewModel.updateFilteredPlugins()
} },
nextFocusDown = R.id.plugin_recycler_view,
nextFocusUp = null,
)
} }
} }

View file

@ -24,6 +24,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.WhoIsWatchingAdapter import com.lagradost.cloudstream3.ui.WhoIsWatchingAdapter
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
import com.lagradost.cloudstream3.ui.result.UiImage import com.lagradost.cloudstream3.ui.result.UiImage
import com.lagradost.cloudstream3.ui.result.VideoWatchState import com.lagradost.cloudstream3.ui.result.VideoWatchState
import com.lagradost.cloudstream3.ui.result.setImage import com.lagradost.cloudstream3.ui.result.setImage
@ -194,7 +195,13 @@ object DataStoreHelper {
builder.setContentView(binding.root) builder.setContentView(binding.root)
val accountName = context.getString(R.string.account) val accountName = context.getString(R.string.account)
binding.profilesRecyclerview.setLinearListLayout(isHorizontal = true) binding.profilesRecyclerview.setLinearListLayout(
isHorizontal = true,
nextUp = FOCUS_SELF,
nextDown = FOCUS_SELF,
nextLeft = FOCUS_SELF,
nextRight = FOCUS_SELF
)
binding.profilesRecyclerview.adapter = WhoIsWatchingAdapter( binding.profilesRecyclerview.adapter = WhoIsWatchingAdapter(
selectCallBack = { account -> selectCallBack = { account ->
setAccount(account) setAccount(account)

View file

@ -2,38 +2,39 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:background="?attr/primaryGrayBackground"
android:id="@+id/download_child_root" android:id="@+id/download_child_root"
android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/primaryGrayBackground"
android:orientation="vertical"
tools:context=".ui.download.DownloadFragment"> tools:context=".ui.download.DownloadFragment">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:background="@android:color/transparent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:background="@android:color/transparent">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/download_child_toolbar" android:id="@+id/download_child_toolbar"
android:paddingTop="@dimen/navbar_height" android:layout_width="match_parent"
tools:title="Overlord" android:layout_height="wrap_content"
android:background="?attr/primaryGrayBackground" android:background="?attr/primaryGrayBackground"
android:paddingTop="@dimen/navbar_height"
app:layout_scrollFlags="scroll|enterAlways"
app:navigationIconTint="?attr/iconColor" app:navigationIconTint="?attr/iconColor"
app:titleTextColor="?attr/textColor" app:titleTextColor="?attr/textColor"
app:layout_scrollFlags="scroll|enterAlways" tools:title="Overlord" />
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:nextFocusUp="@id/download_child_toolbar"
android:background="?attr/primaryBlackBackground"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:padding="10dp"
tools:listitem="@layout/download_child_episode"
android:id="@+id/download_child_list" android:id="@+id/download_child_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent"
android:background="?attr/primaryBlackBackground"
android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusUp="@id/download_child_toolbar"
android:padding="10dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:listitem="@layout/download_child_episode" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -64,6 +64,7 @@
android:focusable="true" android:focusable="true"
android:foreground="@drawable/outline_drawable" android:foreground="@drawable/outline_drawable"
android:nextFocusRight="@id/add_repo_button_imageview" android:nextFocusRight="@id/add_repo_button_imageview"
android:nextFocusUp="@id/repo_recycler_view"
android:orientation="horizontal" android:orientation="horizontal"
android:padding="10dp" android:padding="10dp"
@ -84,13 +85,13 @@
android:textColor="?attr/textColor" /> android:textColor="?attr/textColor" />
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
app:cardElevation="0dp"
android:elevation="0dp"
app:cardMaxElevation="0dp"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="12dp" android:layout_height="12dp"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
app:cardCornerRadius="@dimen/storage_radius"> android:elevation="0dp"
app:cardCornerRadius="@dimen/storage_radius"
app:cardElevation="0dp"
app:cardMaxElevation="0dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -203,6 +204,8 @@
android:contentDescription="@string/add_repository" android:contentDescription="@string/add_repository"
android:focusable="true" android:focusable="true"
android:nextFocusLeft="@id/plugin_storage_appbar" android:nextFocusLeft="@id/plugin_storage_appbar"
android:nextFocusUp="@id/repo_recycler_view"
android:src="@drawable/ic_baseline_add_24" android:src="@drawable/ic_baseline_add_24"
app:tint="?attr/textColor" /> app:tint="?attr/textColor" />
</LinearLayout> </LinearLayout>

View file

@ -153,6 +153,7 @@
android:layout_marginStart="@dimen/navbar_width" android:layout_marginStart="@dimen/navbar_width"
android:backgroundTint="@color/semiWhite" android:backgroundTint="@color/semiWhite"
android:minWidth="150dp" android:minWidth="150dp"
android:nextFocusUp="@id/home_preview_play_btt"
android:nextFocusLeft="@id/nav_rail_view" android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusDown="@id/home_watch_child_recyclerview" /> android:nextFocusDown="@id/home_watch_child_recyclerview" />
</FrameLayout> </FrameLayout>
@ -178,6 +179,8 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_watch_child_recyclerview" android:id="@+id/home_watch_child_recyclerview"
android:nextFocusUp="@id/home_preview_change_api2"
android:nextFocusDown="@id/home_type_holder"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
@ -204,9 +207,6 @@
android:fadingEdge="horizontal" android:fadingEdge="horizontal"
android:foreground="?android:attr/selectableItemBackgroundBorderless" android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusUp="@id/home_watch_child_recyclerview"
android:nextFocusForward="@id/home_bookmarked_child_recyclerview"
android:paddingStart="12dp" android:paddingStart="12dp"
android:paddingTop="5dp" android:paddingTop="5dp"
android:paddingEnd="12dp" android:paddingEnd="12dp"
@ -215,6 +215,12 @@
android:requiresFadingEdge="horizontal"> android:requiresFadingEdge="horizontal">
<com.google.android.material.chip.ChipGroup <com.google.android.material.chip.ChipGroup
android:id="@+id/home_type_holder"
android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusUp="@id/home_watch_child_recyclerview"
android:nextFocusDown="@id/home_bookmarked_child_recyclerview"
android:descendantFocusability="afterDescendants"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/navbar_width" android:layout_marginStart="@dimen/navbar_width"
@ -225,6 +231,8 @@
style="@style/ChipFilled" style="@style/ChipFilled"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nextFocusUp="@id/home_watch_child_recyclerview"
android:nextFocusDown="@id/home_bookmarked_child_recyclerview"
android:nextFocusLeft="@id/nav_rail_view" android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusRight="@id/home_plan_to_watch_btt" android:nextFocusRight="@id/home_plan_to_watch_btt"
@ -235,6 +243,8 @@
style="@style/ChipFilled" style="@style/ChipFilled"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nextFocusUp="@id/home_watch_child_recyclerview"
android:nextFocusDown="@id/home_bookmarked_child_recyclerview"
android:nextFocusLeft="@id/home_type_watching_btt" android:nextFocusLeft="@id/home_type_watching_btt"
android:nextFocusRight="@id/home_type_on_hold_btt" android:nextFocusRight="@id/home_type_on_hold_btt"
@ -245,6 +255,8 @@
style="@style/ChipFilled" style="@style/ChipFilled"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nextFocusUp="@id/home_watch_child_recyclerview"
android:nextFocusDown="@id/home_bookmarked_child_recyclerview"
android:nextFocusLeft="@id/home_plan_to_watch_btt" android:nextFocusLeft="@id/home_plan_to_watch_btt"
android:nextFocusRight="@id/home_type_dropped_btt" android:nextFocusRight="@id/home_type_dropped_btt"
@ -255,6 +267,8 @@
style="@style/ChipFilled" style="@style/ChipFilled"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nextFocusUp="@id/home_watch_child_recyclerview"
android:nextFocusDown="@id/home_bookmarked_child_recyclerview"
android:nextFocusLeft="@id/home_type_on_hold_btt" android:nextFocusLeft="@id/home_type_on_hold_btt"
android:nextFocusRight="@id/home_type_completed_btt" android:nextFocusRight="@id/home_type_completed_btt"
@ -264,6 +278,8 @@
android:id="@+id/home_type_completed_btt" android:id="@+id/home_type_completed_btt"
style="@style/ChipFilled" style="@style/ChipFilled"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:nextFocusUp="@id/home_watch_child_recyclerview"
android:nextFocusDown="@id/home_bookmarked_child_recyclerview"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nextFocusLeft="@id/home_type_dropped_btt" android:nextFocusLeft="@id/home_type_dropped_btt"
@ -273,6 +289,10 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_bookmarked_child_recyclerview" android:id="@+id/home_bookmarked_child_recyclerview"
android:nextFocusUp="@id/home_type_holder"
android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusDown="@id/home_child_recyclerview"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"

View file

@ -162,6 +162,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:descendantFocusability="afterDescendants" android:descendantFocusability="afterDescendants"
android:nextFocusLeft="@id/nav_rail_view" android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusUp="@id/home_bookmarked_child_recyclerview"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/homepage_parent_tv" /> tools:listitem="@layout/homepage_parent_tv" />

View file

@ -25,18 +25,22 @@
app:titleTextColor="?attr/textColor" app:titleTextColor="?attr/textColor"
tools:title="Overlord" /> tools:title="Overlord" />
<include layout="@layout/tvtypes_chips_scroll" android:id="@+id/tvtypes_chips_scroll" /> <include
android:id="@+id/tvtypes_chips_scroll"
layout="@layout/tvtypes_chips_scroll" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:clipToPadding="false"
android:id="@+id/plugin_recycler_view" android:id="@+id/plugin_recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusUp="@id/tvtypes_chips"
android:clipToPadding="false"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:listitem="@layout/repository_item" /> tools:listitem="@layout/repository_item" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -248,6 +248,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
style="@style/ResultButtonTV" style="@style/ResultButtonTV"
android:nextFocusRight="@id/result_description" android:nextFocusRight="@id/result_description"
android:nextFocusUp="@id/result_play_movie"
android:nextFocusDown="@id/result_play_series" android:nextFocusDown="@id/result_play_series"
android:text="@string/play_movie_button" android:text="@string/play_movie_button"
android:visibility="visible" android:visibility="visible"
@ -537,10 +538,10 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
<ImageView <ImageView
android:layout_gravity="end"
android:id="@+id/episodes_shadow" android:id="@+id/episodes_shadow"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="end"
android:clickable="false" android:clickable="false"
android:focusable="false" android:focusable="false"
android:focusableInTouchMode="false" android:focusableInTouchMode="false"
@ -552,8 +553,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
<LinearLayout <LinearLayout
android:id="@+id/episode_holder_tv" android:id="@+id/episode_holder_tv"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingStart="@dimen/result_padding" android:paddingStart="@dimen/result_padding"
@ -563,15 +564,15 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:ignore="RtlHardcoded" tools:ignore="RtlHardcoded"
tools:visibility="visible"> tools:visibility="gone">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:descendantFocusability="afterDescendants"
android:id="@+id/result_season_selection" android:id="@+id/result_season_selection"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
android:nextFocusLeft="@id/result_episodes_show" android:nextFocusLeft="@id/result_episodes_show"
android:nextFocusRight="@id/result_range_selection" android:nextFocusRight="@id/result_range_selection"
android:orientation="vertical" android:orientation="vertical"
@ -580,11 +581,11 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
tools:listitem="@layout/result_selection" /> tools:listitem="@layout/result_selection" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:descendantFocusability="afterDescendants"
android:id="@+id/result_range_selection" android:id="@+id/result_range_selection"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
android:nextFocusLeft="@id/result_season_selection" android:nextFocusLeft="@id/result_season_selection"
android:nextFocusRight="@id/result_dub_selection" android:nextFocusRight="@id/result_dub_selection"
@ -595,12 +596,12 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
tools:visibility="visible" /> tools:visibility="visible" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:descendantFocusability="afterDescendants"
android:id="@+id/result_dub_selection" android:id="@+id/result_dub_selection"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
android:nextFocusLeft="@id/result_range_selection" android:nextFocusLeft="@id/result_range_selection"
android:nextFocusRight="@id/result_episodes" android:nextFocusRight="@id/result_episodes"
android:orientation="vertical" android:orientation="vertical"
@ -618,13 +619,12 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:layout_height="50dp" />--> android:layout_height="50dp" />-->
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:descendantFocusability="afterDescendants"
android:id="@+id/result_episodes" android:id="@+id/result_episodes"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
android:nextFocusLeft="@id/result_dub_selection" android:nextFocusLeft="@id/result_dub_selection"
android:nextFocusRight="@id/result_recommendations_filter_selection"
android:orientation="vertical" android:orientation="vertical"
android:paddingVertical="@dimen/result_padding" android:paddingVertical="@dimen/result_padding"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"

View file

@ -11,10 +11,10 @@
tools:context=".ui.search.SearchFragment"> tools:context=".ui.search.SearchFragment">
<LinearLayout <LinearLayout
android:paddingBottom="10dp"
android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="10dp">
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -88,7 +88,9 @@
app:tint="?attr/textColor" /> app:tint="?attr/textColor" />
</FrameLayout> </FrameLayout>
<include layout="@layout/tvtypes_chips_scroll" android:id="@+id/tvtypes_chips_scroll" /> <include
android:id="@+id/tvtypes_chips_scroll"
layout="@layout/tvtypes_chips_scroll" />
</LinearLayout> </LinearLayout>
@ -116,7 +118,10 @@
android:background="?attr/primaryBlackBackground" android:background="?attr/primaryBlackBackground"
android:descendantFocusability="afterDescendants" android:descendantFocusability="afterDescendants"
android:nextFocusLeft="@id/nav_rail_view" android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusUp="@id/tvtypes_chips"
android:nextFocusDown="@id/search_clear_call_history"
android:visibility="gone" android:visibility="gone"
tools:listitem="@layout/homepage_parent" /> tools:listitem="@layout/homepage_parent" />
@ -134,20 +139,24 @@
android:background="?attr/primaryBlackBackground" android:background="?attr/primaryBlackBackground"
android:descendantFocusability="afterDescendants" android:descendantFocusability="afterDescendants"
android:nextFocusLeft="@id/nav_rail_view" android:nextFocusLeft="@id/nav_rail_view"
android:visibility="visible" android:nextFocusUp="@id/tvtypes_chips"
android:nextFocusDown="@id/search_clear_call_history"
android:paddingBottom="50dp" android:paddingBottom="50dp"
android:visibility="visible"
tools:listitem="@layout/search_history_item" /> tools:listitem="@layout/search_history_item" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/search_clear_call_history" android:id="@+id/search_clear_call_history"
style="@style/BlackButton" style="@style/BlackButton"
android:layout_gravity="bottom"
android:padding="0dp"
app:cornerRadius="0dp"
android:layout_margin="0dp"
android:text="@string/clear_history"
app:icon="@drawable/delete_all"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="50dp" /> android:layout_height="50dp"
android:layout_gravity="bottom"
android:layout_margin="0dp"
android:nextFocusUp="@id/search_history_recycler"
android:padding="0dp"
android:text="@string/clear_history"
app:cornerRadius="0dp"
app:icon="@drawable/delete_all" />
</FrameLayout> </FrameLayout>
</LinearLayout> </LinearLayout>

View file

@ -11,11 +11,11 @@
tools:context=".ui.search.SearchFragment"> tools:context=".ui.search.SearchFragment">
<LinearLayout <LinearLayout
android:layout_marginStart="@dimen/navbar_width"
android:paddingBottom="10dp"
android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:layout_marginStart="@dimen/navbar_width"
android:orientation="vertical"
android:paddingBottom="10dp">
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -89,7 +89,9 @@
app:tint="?attr/textColor" /> app:tint="?attr/textColor" />
</FrameLayout> </FrameLayout>
<include layout="@layout/tvtypes_chips_scroll" android:id="@+id/tvtypes_chips_scroll" /> <include
android:id="@+id/tvtypes_chips_scroll"
layout="@layout/tvtypes_chips_scroll" />
</LinearLayout> </LinearLayout>
@ -118,38 +120,43 @@
android:background="?attr/primaryBlackBackground" android:background="?attr/primaryBlackBackground"
android:descendantFocusability="afterDescendants" android:descendantFocusability="afterDescendants"
android:nextFocusLeft="@id/nav_rail_view" android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusUp="@id/tvtypes_chips"
android:nextFocusDown="@id/search_clear_call_history"
android:visibility="gone" android:visibility="gone"
tools:listitem="@layout/homepage_parent" /> tools:listitem="@layout/homepage_parent" />
<FrameLayout <FrameLayout
android:background="?attr/primaryBlackBackground"
android:id="@+id/search_history_holder" android:id="@+id/search_history_holder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:background="?attr/primaryBlackBackground">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:layout_marginStart="@dimen/navbar_width"
android:id="@+id/search_history_recycler" android:id="@+id/search_history_recycler"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginStart="@dimen/navbar_width"
android:descendantFocusability="afterDescendants" android:descendantFocusability="afterDescendants"
android:nextFocusLeft="@id/nav_rail_view" android:nextFocusLeft="@id/nav_rail_view"
android:visibility="visible" android:nextFocusUp="@id/tvtypes_chips"
android:nextFocusDown="@id/search_clear_call_history"
android:paddingBottom="50dp" android:paddingBottom="50dp"
android:visibility="visible"
tools:listitem="@layout/search_history_item" /> tools:listitem="@layout/search_history_item" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:layout_marginStart="@dimen/navbar_width"
android:id="@+id/search_clear_call_history" android:id="@+id/search_clear_call_history"
style="@style/BlackButton" style="@style/BlackButton"
android:layout_gravity="bottom"
android:padding="0dp"
app:cornerRadius="0dp"
android:layout_margin="0dp"
android:text="@string/clear_history"
app:icon="@drawable/delete_all"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="50dp" /> android:layout_height="50dp"
android:layout_gravity="bottom"
android:layout_margin="0dp"
android:layout_marginStart="@dimen/navbar_width"
android:nextFocusUp="@id/search_history_recycler"
android:padding="0dp"
android:text="@string/clear_history"
app:cornerRadius="0dp"
app:icon="@drawable/delete_all" />
</FrameLayout> </FrameLayout>
</LinearLayout> </LinearLayout>

View file

@ -1,19 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.appbar.AppBarLayout android:background="@android:color/transparent" <com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android" android:background="@android:color/transparent">
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/settings_toolbar" android:id="@+id/settings_toolbar"
android:paddingTop="@dimen/navbar_height" android:layout_width="match_parent"
tools:title="Overlord" android:layout_height="wrap_content"
android:background="?attr/primaryGrayBackground" android:background="?attr/primaryGrayBackground"
android:descendantFocusability="afterDescendants"
android:paddingTop="@dimen/navbar_height"
app:layout_scrollFlags="scroll|enterAlways"
app:navigationIconTint="?attr/iconColor" app:navigationIconTint="?attr/iconColor"
app:titleTextColor="?attr/textColor" app:titleTextColor="?attr/textColor"
app:layout_scrollFlags="scroll|enterAlways" tools:title="Overlord" />
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View file

@ -6,6 +6,7 @@
android:paddingStart="8dp" android:paddingStart="8dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:id="@+id/home_select_group" android:id="@+id/home_select_group"
android:descendantFocusability="afterDescendants"
app:singleSelection="false" app:singleSelection="false"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:android="http://schemas.android.com/apk/res/android">

View file

@ -1,10 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<HorizontalScrollView android:id="@+id/tv_types_scroll_view" <HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_types_scroll_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fadingEdge="horizontal" android:fadingEdge="horizontal"
android:requiresFadingEdge="horizontal" android:requiresFadingEdge="horizontal">
xmlns:android="http://schemas.android.com/apk/res/android">
<include layout="@layout/tvtypes_chips" android:id="@+id/tvtypes_chips" /> <include
android:descendantFocusability="afterDescendants"
android:id="@+id/tvtypes_chips"
layout="@layout/tvtypes_chips" />
</HorizontalScrollView> </HorizontalScrollView>

View file

@ -579,4 +579,6 @@
<string name="qualities">النوعيات</string> <string name="qualities">النوعيات</string>
<string name="profile_background_des">خلفية الملف الشخصي</string> <string name="profile_background_des">خلفية الملف الشخصي</string>
<string name="unable_to_inflate">تعذر إنشاء واجهة المستخدم بشكل صحيح ، وهذا خطأ كبير ويجب الإبلاغ عنه على الفور %s</string> <string name="unable_to_inflate">تعذر إنشاء واجهة المستخدم بشكل صحيح ، وهذا خطأ كبير ويجب الإبلاغ عنه على الفور %s</string>
<string name="automatic_plugin_download_mode_title">حدد الوضع لتصفية تنزيل المكونات الإضافية</string>
<string name="disable">تعطيل</string>
</resources> </resources>

View file

@ -571,4 +571,6 @@
\n \n
\nPOZNÁMKA: Pokud je součet 10 nebo vyšší, přehrávač automaticky přeskočí načítání při načtení daného odkazu!</string> \nPOZNÁMKA: Pokud je součet 10 nebo vyšší, přehrávač automaticky přeskočí načítání při načtení daného odkazu!</string>
<string name="unable_to_inflate">Nepodařilo se správně vytvořit rozhraní. Toto je VÁŽNÁ CHYBA, kterou je potřeba ihned nahlásit %s</string> <string name="unable_to_inflate">Nepodařilo se správně vytvořit rozhraní. Toto je VÁŽNÁ CHYBA, kterou je potřeba ihned nahlásit %s</string>
<string name="disable">Zakázat</string>
<string name="automatic_plugin_download_mode_title">Výběr režimu pro filtrování stahování doplňků</string>
</resources> </resources>

View file

@ -547,4 +547,7 @@
<string name="qualities">Calidades</string> <string name="qualities">Calidades</string>
<string name="profile_background_des">Perfil del fondo</string> <string name="profile_background_des">Perfil del fondo</string>
<string name="unable_to_inflate">La interfaz de usuario no se ha podido crear correctamente, se trata de un GRAN BUG y debe ser reportado inmediatamente %s</string> <string name="unable_to_inflate">La interfaz de usuario no se ha podido crear correctamente, se trata de un GRAN BUG y debe ser reportado inmediatamente %s</string>
<string name="automatic_plugin_download_mode_title">Selecciona el modo para filtrar la descarga de los plugins</string>
<string name="disable">Desactivar</string>
<string name="default_account">@string/default_subtitles</string>
</resources> </resources>

View file

@ -13,4 +13,158 @@
<string name="episode_poster_img_des">Póster do Episodio</string> <string name="episode_poster_img_des">Póster do Episodio</string>
<string name="go_back_img_des">Regresar</string> <string name="go_back_img_des">Regresar</string>
<string name="home_change_provider_img_des">Cambiar provedor</string> <string name="home_change_provider_img_des">Cambiar provedor</string>
<string name="new_update_format" formatted="true">Nova actualización atopada!
\n%s -&gt; %s</string>
<string name="filler" formatted="true">Recheo</string>
<string name="duration_format" formatted="true">%d min</string>
<string name="title_settings">Configuración</string>
<string name="search_hint">Procurar…</string>
<string name="search_hint_site" formatted="true">Procurar en %s…</string>
<string name="no_data">Sen datos</string>
<string name="episode_more_options_des">Máis opcións</string>
<string name="next_episode">Seguinte episodio</string>
<string name="result_tags">Xéneros</string>
<string name="type_watching">Mirando</string>
<string name="type_on_hold">En espera</string>
<string name="type_completed">Completado</string>
<string name="type_dropped">Descartado</string>
<string name="type_plan_to_watch">Planeando ver</string>
<string name="type_none">Ningún</string>
<string name="type_re_watching">Remirando</string>
<string name="error_bookmarks_text">Marcadores</string>
<string name="action_remove_from_bookmarks">Borrar</string>
<string name="action_add_to_bookmarks">Seleccionar estado de visualización</string>
<string name="sort_apply">Aplicar</string>
<string name="sort_cancel">Cancelar</string>
<string name="sort_copy">Copiar</string>
<string name="sort_close">Cerrar</string>
<string name="sort_clear">Limpar</string>
<string name="sort_save">Gardar</string>
<string name="player_speed">Velocidade do reproductor</string>
<string name="subtitles_settings">Configurar Subtítulos</string>
<string name="subs_background_color">Cor de Fondo</string>
<string name="subs_text_color">Cor de Texto</string>
<string name="subs_outline_color">Cor de Contorno</string>
<string name="subs_window_color">Cor de Ventá</string>
<string name="continue_watching">Continuar Vendo</string>
<string name="rated_format" formatted="true">Nota: %.1f</string>
<string name="app_name">CloudStream</string>
<string name="title_home">Inicio</string>
<string name="title_downloads">Descarga</string>
<string name="result_share">Compartir</string>
<string name="result_open_in_browser">Abrir no Navegador</string>
<string name="browser">Navegador</string>
<string name="skip_loading">Omitir carga</string>
<string name="loading">Cargando…</string>
<string name="play_movie_button">Reproducir o filme</string>
<string name="play_trailer_button">Reproducir Trailer</string>
<string name="play_livestream_button">Reproducir transmisión en vivo</string>
<string name="play_torrent_button">Transmitir Torrent</string>
<string name="pick_source">Fontes</string>
<string name="reload_error">Tentar de novo a conexión…</string>
<string name="go_back">Volver</string>
<string name="play_episode">Reproducir Episodio</string>
<string name="download">Descargar</string>
<string name="downloaded">Descargado</string>
<string name="downloading">Descargando</string>
<string name="download_paused">Descarga Pausada</string>
<string name="download_started">Descarga comezada</string>
<string name="download_failed">Descarga Fallida</string>
<string name="download_canceled">Descarga Cancelada</string>
<string name="download_done">Descarga rematada</string>
<string name="update_started">Actualización Comezada</string>
<string name="app_subbed_text">Subtitulado</string>
<string name="popup_delete_file">Borrar Arquivo</string>
<string name="popup_play_file">Reproducir Arquivo</string>
<string name="popup_resume_download">Continuar Descarga</string>
<string name="popup_pause_download">Pausar Descarga</string>
<string name="pref_disable_acra">Desactivar reporte automático de bugs</string>
<string name="home_more_info">Máis información</string>
<string name="home_expanded_hide">Agochar</string>
<string name="home_play">Reproducir</string>
<string name="subs_edge_type">Tipo de Borde</string>
<string name="subs_font">Fonte</string>
<string name="benene_count_text_none">Non se deron bananas</string>
<string name="subs_auto_select_language">Seleccionar idioma automáticamente</string>
<string name="subs_download_languages">Descargar Idiomas</string>
<string name="subs_subtitle_languages">Idioma do Subtítulo</string>
<string name="subs_hold_to_reset_to_default">Manteña premido para restablecer os valores predeterminados</string>
<string name="subs_import_text" formatted="true">Importar fontes colocandoas en %s</string>
<string name="action_remove_watching">Borrar</string>
<string name="action_open_watching">Máis Info</string>
<string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">Unha VPN pode ser necesaria para o correcto funcionamento deste provedor</string>
<string name="vpn_torrent">Este proveedor é un torrent, recomendase o uso dunha VPN</string>
<string name="provider_info_meta">Os metadatos non son proporcionados polo sitio, a carga do video fallará se non existe no sitio</string>
<string name="torrent_plot">Descripción</string>
<string name="normal_no_plot">Trama non atopada</string>
<string name="torrent_no_plot">Descripción non atopada</string>
<string name="show_log_cat">Amosar Logcat 🐈</string>
<string name="test_log">Rexistro</string>
<string name="picture_in_picture_des">Continúa a reprodución nun reproductor miniatura enriba doutras aplicacións</string>
<string name="play_with_app_name">Reproducir con CloudStream</string>
<string name="title_search">Procurar</string>
<string name="preview_background_img_des">Vista previa do fondo</string>
<string name="player_speed_text_format" formatted="true">Velocidade (%.2fx)</string>
<string name="pick_subtitle">Subtítulos</string>
<string name="stream">Transmitir</string>
<string name="error_loading_links_toast">Error Cargando Ligazón</string>
<string name="app_dubbed_text">Dobrado</string>
<string name="filter_bookmarks">Filtrar Marcadores</string>
<string name="download_storage_text">Almacenamento Interno</string>
<string name="subs_font_size">Tamaño de Fonte</string>
<string name="home_info">Info</string>
<string name="subs_subtitle_elevation">Elevación de Subtítulo</string>
<string name="search_provider_text_providers">Procurar usando proveedores</string>
<string name="benene_count_text">%d Bananas dadas aos desenvolvedores</string>
<string name="search_provider_text_types">Procurar por tipos</string>
<string name="picture_in_picture">Imaxe en Imaxe</string>
<string name="player_subtitles_settings_des">Configuración de subtítulos do reprodutor</string>
<string name="chromecast_subtitles_settings">Subtítulos de Chromecast</string>
<string name="chromecast_subtitles_settings_des">Configuración de subtítulos de Chromecast</string>
<string name="eigengraumode_settings">Modo Eigengravy</string>
<string name="eigengraumode_settings_des">Engadir a opción de velocidade no reprodutor</string>
<string name="swipe_to_seek_settings">Deslice para avanzar/retroceder</string>
<string name="swipe_to_seek_settings_des">Deslice o dedo de lado a lado para controlar a posición nun video</string>
<string name="swipe_to_change_settings">Deslice para cambiar a configuración</string>
<string name="swipe_to_change_settings_des">Deslice cara arriba ou cara abaixo no lado esquerdo ou dereito para cambiar o brillo ou o volumen</string>
<string name="autoplay_next_settings">Reproducir automáticamente episodio seguinte</string>
<string name="autoplay_next_settings_des">Comezar o seguinte episodio cando o actual remate</string>
<string name="double_tap_to_seek_settings">Toca dúas veces para procurar</string>
<string name="double_tap_to_pause_settings">Tocar dúas veces para pausar</string>
<string name="double_tap_to_seek_amount_settings">Tempo de rebobinado do reprodutor (segundos)</string>
<string name="double_tap_to_seek_settings_des">Toque dúas veces no lado dereito ou esquerdo para rebobinar cara adiante ou cara atrás</string>
<string name="double_tap_to_pause_settings_des">Toque dúas veces no medio para pausar</string>
<string name="use_system_brightness_settings">Usar brillo do sistema</string>
<string name="episode_sync_settings">Actualizar progreso do visto</string>
<string name="restore_settings">Restaurar datos dende o respaldo</string>
<string name="backup_settings">Crear respaldo de datos</string>
<string name="restore_success">Arquivo de respaldo cargado</string>
<string name="restore_failed_format" formatted="true">Fallou a restauración dos datos dende o arquivo %s</string>
<string name="backup_success">Datos gardados</string>
<string name="backup_failed">Faltan permisos de almacenamento. Por favor tenteo de novo.</string>
<string name="backup_failed_error_format">Error respaldando de %s</string>
<string name="search">Procurar</string>
<string name="library">Biblioteca</string>
<string name="category_account">Contas</string>
<string name="category_updates">Actualizacións e copias de seguridade</string>
<string name="settings_info">Info</string>
<string name="advanced_search">Procura Avanzada</string>
<string name="advanced_search_des">Da os resultados da procura separados por proveedor</string>
<string name="player_size_settings">Botón de cambio de tamaño do reprodutor</string>
<string name="player_size_settings_des">Eliminar bordes negros</string>
<string name="player_subtitles_settings">Subtítulos</string>
<string name="episode_sync_settings_des">Sincronizar automáticamente o progreso do episodio actual</string>
<string name="use_system_brightness_settings_des">Use o brillo do sistema no reprodutor da aplicación en lugar dunha superposición oscura</string>
<string name="bug_report_settings_off">Só envía datos se a aplicacción falla inesperadamente</string>
<string name="bug_report_settings_on">Non enviar datos</string>
<string name="show_fillers_settings">Mostrar episodio de recheo para Anime</string>
<string name="show_trailers_settings">Mostrar Trailers</string>
<string name="kitsu_settings">Mostrar pósters de Kitsu</string>
<string name="pref_filter_search_quality">Ocultar calidade de video nos resultados da procura</string>
<string name="automatic_plugin_updates">Actualicación automática de complementos</string>
<string name="automatic_plugin_download">Descarga automática de complementos</string>
<string name="automatic_plugin_download_mode_title">Selecciona o modo para filtrar a descarga dos complementos</string>
<string name="automatic_plugin_download_summary">Instala automáticamente todos os complementos aínda non instalados dos repositorios engadidos.</string>
<string name="updates_settings">Mostrar actualizacións da aplicación</string>
</resources> </resources>

View file

@ -570,4 +570,6 @@
<string name="qualities">Kualitas</string> <string name="qualities">Kualitas</string>
<string name="profile_background_des">Latar belakang profil</string> <string name="profile_background_des">Latar belakang profil</string>
<string name="unable_to_inflate">UI tidak dapat dibuat dengan benar, ini adalah BUG UTAMA dan harus segera dilaporkan %s</string> <string name="unable_to_inflate">UI tidak dapat dibuat dengan benar, ini adalah BUG UTAMA dan harus segera dilaporkan %s</string>
<string name="disable">Nonaktif</string>
<string name="automatic_plugin_download_mode_title">Pilih mode untuk memfilter unduhan plugin</string>
</resources> </resources>

View file

@ -204,4 +204,11 @@
<string name="next_episode_format" formatted="true">エピソード %d は にリリースされます</string> <string name="next_episode_format" formatted="true">エピソード %d は にリリースされます</string>
<string name="type_re_watching">再視聴</string> <string name="type_re_watching">再視聴</string>
<string name="play_torrent_button">ストリームトレント</string> <string name="play_torrent_button">ストリームトレント</string>
<string name="subs_text_color">文字の色</string>
<string name="popup_delete_file">ファイルを削除</string>
<string name="popup_play_file">再生ファイル</string>
<string name="subs_background_color">背景の色</string>
<string name="subs_window_color">窓の色</string>
<string name="subs_edge_type">エッジタイプ</string>
<string name="popup_pause_download">ダウンロードを一時停止する</string>
</resources> </resources>

View file

@ -550,4 +550,7 @@
<string name="qualities">Jakości</string> <string name="qualities">Jakości</string>
<string name="profile_background_des">Tło profilu</string> <string name="profile_background_des">Tło profilu</string>
<string name="unable_to_inflate">Nie można było poprawnie utworzyć interfejsu użytkownika, jest to POWAŻNY BŁĄD i należy go natychmiast zgłosić %s</string> <string name="unable_to_inflate">Nie można było poprawnie utworzyć interfejsu użytkownika, jest to POWAŻNY BŁĄD i należy go natychmiast zgłosić %s</string>
<string name="automatic_plugin_download_mode_title">Wybierz tryb filtrowania pobieranych rozszerzeń</string>
<string name="disable">Wyłączać</string>
<string name="default_account">@string/default_subtitles</string>
</resources> </resources>

View file

@ -5,8 +5,8 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath("com.android.tools.build:gradle:7.3.1") classpath("com.android.tools.build:gradle:8.0.2")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20")
classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.5.0") classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.5.0")
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong

View file

@ -1,6 +1,6 @@
#Fri Apr 30 17:11:15 CEST 2021 #Fri Apr 30 17:11:15 CEST 2021
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME