mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Merge branch 'recloudstream:master' into master
This commit is contained in:
commit
9c43ee73b4
39 changed files with 892 additions and 351 deletions
|
@ -1,4 +1,3 @@
|
|||
import com.android.build.gradle.api.BaseVariantOutput
|
||||
import org.jetbrains.dokka.gradle.DokkaTask
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.net.URL
|
||||
|
@ -52,7 +51,7 @@ android {
|
|||
targetSdk = 33
|
||||
|
||||
versionCode = 59
|
||||
versionName = "4.1.1"
|
||||
versionName = "4.1.2"
|
||||
|
||||
resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}")
|
||||
|
||||
|
@ -108,14 +107,19 @@ android {
|
|||
versionCode = (System.currentTimeMillis() / 60000).toInt()
|
||||
}
|
||||
}
|
||||
//toolchain {
|
||||
// languageVersion.set(JavaLanguageVersion.of(17))
|
||||
// }
|
||||
// jvmToolchain(17)
|
||||
|
||||
compileOptions {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = "17"
|
||||
freeCompilerArgs = listOf("-Xjvm-default=compatibility")
|
||||
}
|
||||
lint {
|
||||
|
@ -131,22 +135,22 @@ repositories {
|
|||
|
||||
dependencies {
|
||||
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")
|
||||
|
||||
implementation("androidx.core:core-ktx:1.8.0")
|
||||
implementation("androidx.appcompat:appcompat:1.4.2") // need target 32 for 1.5.0
|
||||
implementation("androidx.core:core-ktx:1.10.1")
|
||||
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
|
||||
implementation("com.google.android.material:material:1.5.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.5.1")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.5.1")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
androidTestImplementation("androidx.test:core")
|
||||
|
||||
//implementation("io.karn:khttp-android:0.1.2") //okhttp instead
|
||||
|
@ -165,7 +169,7 @@ dependencies {
|
|||
|
||||
// implementation("androidx.leanback:leanback-paging:1.1.0-alpha09")
|
||||
|
||||
// Exoplayer
|
||||
// Media 3
|
||||
implementation("androidx.media3:media3-common:1.1.0")
|
||||
implementation("androidx.media3:media3-exoplayer: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-exoplayer-hls:1.1.0")
|
||||
implementation("androidx.media3:media3-exoplayer-dash: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")
|
||||
// Custom ffmpeg extension for audio codecs
|
||||
implementation("com.github.recloudstream:media-ffmpeg:1.1.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")
|
||||
|
||||
// Downloading
|
||||
implementation("androidx.work:work-runtime:2.8.0")
|
||||
implementation("androidx.work:work-runtime-ktx:2.8.0")
|
||||
implementation("androidx.work:work-runtime:2.8.1")
|
||||
implementation("androidx.work:work-runtime-ktx:2.8.1")
|
||||
|
||||
// Networking
|
||||
// implementation("com.squareup.okhttp3:okhttp:4.9.2")
|
||||
|
|
|
@ -19,8 +19,11 @@ import androidx.annotation.StringRes
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.children
|
||||
import androidx.preference.PreferenceManager
|
||||
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.removeKey
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
|
@ -39,6 +42,13 @@ import org.schabi.newpipe.extractor.NewPipe
|
|||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
||||
enum class FocusDirection {
|
||||
Start,
|
||||
End,
|
||||
Up,
|
||||
Down,
|
||||
}
|
||||
|
||||
object CommonActivity {
|
||||
|
||||
private var _activity: WeakReference<Activity>? = null
|
||||
|
@ -301,7 +311,8 @@ object CommonActivity {
|
|||
private fun localLook(from: View, id: Int): View? {
|
||||
if (id == NO_ID) return null
|
||||
var currentLook: View = from
|
||||
while (true) {
|
||||
// limit to 15 look depth
|
||||
for (i in 0..15) {
|
||||
currentLook.findViewById<View?>(id)?.let { return it }
|
||||
currentLook = (currentLook.parent as? View) ?: break
|
||||
}
|
||||
|
@ -317,17 +328,70 @@ object CommonActivity {
|
|||
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,
|
||||
* 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*/
|
||||
private fun getNextFocus(
|
||||
act: Activity?,
|
||||
fun getNextFocus(
|
||||
root: Any?,
|
||||
view: View?,
|
||||
direction: FocusDirection,
|
||||
depth: Int = 0
|
||||
): View? {
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -359,38 +423,12 @@ object CommonActivity {
|
|||
// if not specified then use forward id
|
||||
nextId = view.nextFocusForwardId
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
return continueGetNextFocus(root, view, direction, nextId, depth)
|
||||
}
|
||||
|
||||
private enum class FocusDirection {
|
||||
Start,
|
||||
End,
|
||||
Up,
|
||||
Down,
|
||||
}
|
||||
|
||||
fun onKeyDown(act: Activity?, keyCode: Int, event: KeyEvent?) {
|
||||
//println("Keycode: $keyCode")
|
||||
|
@ -520,7 +558,7 @@ object CommonActivity {
|
|||
|
||||
else -> null
|
||||
}
|
||||
|
||||
// println("NEXT FOCUS : $nextView")
|
||||
if (nextView != null) {
|
||||
nextView.requestFocus()
|
||||
keyEventListener?.invoke(Pair(event, true))
|
||||
|
|
|
@ -27,6 +27,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
|
|||
import androidx.core.view.children
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.marginStart
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.NavController
|
||||
|
@ -37,6 +38,8 @@ import androidx.navigation.NavOptions
|
|||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.LinearSnapHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
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.GeneratorPlayer
|
||||
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.START_ACTION_RESUME_LATEST
|
||||
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.isLtr
|
||||
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.loadRepository
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
|
||||
|
@ -146,6 +151,7 @@ import java.lang.ref.WeakReference
|
|||
import java.net.URI
|
||||
import java.net.URLDecoder
|
||||
import java.nio.charset.Charset
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.system.exitProcess
|
||||
|
@ -848,6 +854,24 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
|
||||
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
|
||||
fun updateFocusView(newFocus: View?, same: Boolean = false) {
|
||||
val focusOutline = focusOutline.get() ?: return
|
||||
|
@ -867,17 +891,67 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
|
||||
if (newFocus != null) {
|
||||
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)
|
||||
newFocus.getLocationInWindow(out)
|
||||
val (screenX, screenY) = out
|
||||
var (x, y) = screenX.toFloat() to screenY.toFloat()
|
||||
val (currentX, currentY) = focusOutline.translationX to focusOutline.translationY
|
||||
// println(">><<< $x $y $currentX $currentY")
|
||||
|
||||
if (!newFocus.isLtr()) {
|
||||
x = x - focusOutline.rootView.width + newFocus.measuredWidth
|
||||
}
|
||||
x -= targetDx
|
||||
|
||||
// out of bounds = 0,0
|
||||
if (screenX == 0 && screenY == 0) {
|
||||
|
@ -1093,9 +1167,17 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
}
|
||||
|
||||
//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) {
|
||||
PluginManager.downloadNotExistingPluginsAndLoad(this@MainActivity, auto_download_plugin)
|
||||
PluginManager.downloadNotExistingPluginsAndLoad(
|
||||
this@MainActivity,
|
||||
auto_download_plugin
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,12 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.databinding.FragmentChildDownloadsBinding
|
||||
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.DataStore.getKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKeys
|
||||
|
@ -110,7 +111,11 @@ class DownloadChildFragment : Fragment() {
|
|||
downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent += it }
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -40,6 +40,8 @@ import com.lagradost.cloudstream3.databinding.FragmentDownloadsBinding
|
|||
import com.lagradost.cloudstream3.databinding.StreamInputBinding
|
||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||
import com.lagradost.cloudstream3.ui.player.BasicLink
|
||||
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
||||
import java.net.URI
|
||||
|
||||
|
@ -74,7 +76,7 @@ class DownloadFragment : Fragment() {
|
|||
super.onDestroyView()
|
||||
}
|
||||
|
||||
var binding : FragmentDownloadsBinding? = null
|
||||
var binding: FragmentDownloadsBinding? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
|
@ -151,6 +153,7 @@ class DownloadFragment : Fragment() {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
1 -> {
|
||||
(activity as AppCompatActivity?)?.loadResult(
|
||||
click.data.url,
|
||||
|
@ -187,7 +190,13 @@ class DownloadFragment : Fragment() {
|
|||
|
||||
binding?.downloadList?.apply {
|
||||
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
|
||||
|
|
|
@ -88,14 +88,14 @@ class HomeChildItemAdapter(
|
|||
private val nextFocusUp: Int? = null,
|
||||
private val nextFocusDown: Int? = null,
|
||||
private val isHorizontal: Boolean = false,
|
||||
private val isRtl : Boolean
|
||||
private val isRtl: Boolean
|
||||
) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(card: SearchResponse, position: Int) {
|
||||
|
||||
// TV focus fixing
|
||||
val nextFocusBehavior = when (position) {
|
||||
/*val nextFocusBehavior = when (position) {
|
||||
0 -> true
|
||||
itemCount - 1 -> false
|
||||
else -> null
|
||||
|
@ -113,7 +113,7 @@ class HomeChildItemAdapter(
|
|||
} else {
|
||||
itemView.nextFocusRightId = -1
|
||||
itemView.nextFocusLeftId = -1
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
when (binding) {
|
||||
|
@ -171,7 +171,7 @@ class HomeChildItemAdapter(
|
|||
card,
|
||||
position,
|
||||
itemView,
|
||||
nextFocusBehavior,
|
||||
null, // nextFocusBehavior,
|
||||
nextFocusUp,
|
||||
nextFocusDown
|
||||
)
|
||||
|
|
|
@ -310,6 +310,17 @@ class HomeFragment : Fragment() {
|
|||
selectedTypes: List<TvType>,
|
||||
validTypes: List<TvType>,
|
||||
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
|
||||
val pairList = getPairList(header)
|
||||
|
@ -317,6 +328,17 @@ class HomeFragment : Fragment() {
|
|||
val isValid = validTypes.any { types.contains(it) }
|
||||
button?.isVisible = isValid
|
||||
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 { _, _ ->
|
||||
val list = ArrayList<TvType>()
|
||||
for ((sbutton, vvalidTypes) in pairList) {
|
||||
|
@ -462,7 +484,7 @@ class HomeFragment : Fragment() {
|
|||
|
||||
private val apiChangeClickListener = View.OnClickListener { view ->
|
||||
view.context.selectHomepage(currentApiName) { api ->
|
||||
homeViewModel.loadAndCancel(api, forceReload = true,fromUI = true)
|
||||
homeViewModel.loadAndCancel(api, forceReload = true, fromUI = true)
|
||||
}
|
||||
/*val validAPIs = view.context?.filterProviderByPreferredMedia()?.toMutableList() ?: mutableListOf()
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.lagradost.cloudstream3.HomePageList
|
|||
import com.lagradost.cloudstream3.LoadResponse
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.databinding.HomepageParentBinding
|
||||
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
||||
import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse
|
||||
|
@ -154,7 +155,7 @@ open class ParentItemAdapter(
|
|||
class ParentViewHolder
|
||||
constructor(
|
||||
val binding: HomepageParentBinding,
|
||||
// val viewModel: HomeViewModel,
|
||||
// val viewModel: HomeViewModel,
|
||||
private val clickCallback: (SearchClickCallback) -> Unit,
|
||||
private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit,
|
||||
private val expandCallback: ((String) -> Unit)? = null,
|
||||
|
@ -162,7 +163,8 @@ open class ParentItemAdapter(
|
|||
RecyclerView.ViewHolder(binding.root) {
|
||||
val title: TextView = binding.homeChildMoreInfo
|
||||
private val recyclerView: RecyclerView = binding.homeChildRecyclerview
|
||||
|
||||
private val startFocus = R.id.nav_rail_view
|
||||
private val endFocus = FOCUS_SELF
|
||||
fun update(expand: HomeViewModel.ExpandableHomepageList) {
|
||||
val info = expand.list
|
||||
(recyclerView.adapter as? HomeChildItemAdapter?)?.apply {
|
||||
|
@ -176,8 +178,13 @@ open class ParentItemAdapter(
|
|||
nextFocusDown = recyclerView.nextFocusDownId,
|
||||
).apply {
|
||||
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
|
||||
hasNext = expand.hasNext
|
||||
}
|
||||
recyclerView.setLinearListLayout()
|
||||
recyclerView.setLinearListLayout(
|
||||
isHorizontal = true,
|
||||
nextLeft = startFocus,
|
||||
nextRight = endFocus,
|
||||
)
|
||||
title.text = info.name
|
||||
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
|
|
|
@ -14,6 +14,7 @@ import androidx.viewbinding.ViewBinding
|
|||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.chip.ChipDrawable
|
||||
import com.google.android.material.chip.ChipGroup
|
||||
import com.lagradost.cloudstream3.APIHolder.getId
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
|
||||
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.ui.WatchType
|
||||
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.START_ACTION_RESUME_LATEST
|
||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||
|
@ -416,6 +418,7 @@ class HomeParentItemAdapterPreview(
|
|||
isChecked = checked.contains(watch)
|
||||
}
|
||||
}
|
||||
toggleListHolder?.isGone = visible.isEmpty()
|
||||
}
|
||||
} ?: debugException { "Expected findViewTreeLifecycleOwner" }
|
||||
}
|
||||
|
@ -428,6 +431,8 @@ class HomeParentItemAdapterPreview(
|
|||
Pair(itemView.findViewById(R.id.home_plan_to_watch_btt), WatchType.PLANTOWATCH),
|
||||
)
|
||||
|
||||
private val toggleListHolder : ChipGroup? = itemView.findViewById(R.id.home_type_holder)
|
||||
|
||||
init {
|
||||
previewViewpager.setPageTransformer(HomeScrollTransformer())
|
||||
|
||||
|
@ -435,8 +440,8 @@ class HomeParentItemAdapterPreview(
|
|||
resumeRecyclerView.adapter = resumeAdapter
|
||||
bookmarkRecyclerView.adapter = bookmarkAdapter
|
||||
|
||||
resumeRecyclerView.setLinearListLayout()
|
||||
bookmarkRecyclerView.setLinearListLayout()
|
||||
resumeRecyclerView.setLinearListLayout(nextLeft = R.id.nav_rail_view, nextRight = FOCUS_SELF)
|
||||
bookmarkRecyclerView.setLinearListLayout(nextLeft = R.id.nav_rail_view, nextRight = FOCUS_SELF)
|
||||
|
||||
fixPaddingStatusbarMargin(topPadding)
|
||||
|
||||
|
|
|
@ -7,16 +7,14 @@ import android.os.Looper
|
|||
import android.util.Log
|
||||
import android.util.Rational
|
||||
import android.widget.FrameLayout
|
||||
import androidx.media3.common.C
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.media3.common.C.*
|
||||
import androidx.media3.common.Format
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.TrackSelectionOverride
|
||||
import androidx.media3.common.MimeTypes
|
||||
import androidx.media3.common.PlaybackException
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.TrackGroup
|
||||
import androidx.media3.common.TrackSelectionOverride
|
||||
import androidx.media3.common.Tracks
|
||||
import androidx.media3.common.VideoSize
|
||||
import androidx.media3.database.StandaloneDatabaseProvider
|
||||
|
@ -30,6 +28,7 @@ import androidx.media3.datasource.cache.SimpleCache
|
|||
import androidx.media3.datasource.okhttp.OkHttpDataSource
|
||||
import androidx.media3.exoplayer.DefaultLoadControl
|
||||
import androidx.media3.exoplayer.DefaultRenderersFactory
|
||||
import androidx.media3.exoplayer.DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.exoplayer.SeekParameters
|
||||
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.TrackSelector
|
||||
import androidx.media3.ui.SubtitleView
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||
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.normalSafeApiCall
|
||||
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.EpisodeSkip
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLinkPlayList
|
||||
import com.lagradost.cloudstream3.utils.ExtractorUri
|
||||
|
@ -397,7 +397,7 @@ class CS3IPlayer : IPlayer {
|
|||
if (subtitle == null) {
|
||||
trackSelector.setParameters(
|
||||
trackSelector.buildUponParameters()
|
||||
.setPreferredTextLanguage(null)
|
||||
.setTrackTypeDisabled(TRACK_TYPE_TEXT, true)
|
||||
.clearOverridesOfType(TRACK_TYPE_TEXT)
|
||||
)
|
||||
} else {
|
||||
|
@ -415,6 +415,7 @@ class CS3IPlayer : IPlayer {
|
|||
.apply {
|
||||
val track = getTextTrack(subtitle.getId())
|
||||
if (track != null) {
|
||||
setTrackTypeDisabled(TRACK_TYPE_TEXT, false)
|
||||
setOverrideForType(
|
||||
TrackSelectionOverride(
|
||||
track.first,
|
||||
|
@ -662,12 +663,7 @@ class CS3IPlayer : IPlayer {
|
|||
|
||||
private fun getTrackSelector(context: Context, maxVideoHeight: Int?): TrackSelector {
|
||||
val trackSelector = DefaultTrackSelector(context)
|
||||
trackSelector.parameters = DefaultTrackSelector.ParametersBuilder(context)
|
||||
// .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)
|
||||
trackSelector.parameters = trackSelector.buildUponParameters()
|
||||
// This will not force higher quality videos to fail
|
||||
// but will make the m3u8 pick the correct preferred
|
||||
.setMaxVideoSize(Int.MAX_VALUE, maxVideoHeight ?: Int.MAX_VALUE)
|
||||
|
@ -701,9 +697,9 @@ class CS3IPlayer : IPlayer {
|
|||
ExoPlayer.Builder(context)
|
||||
.setRenderersFactory { eventHandler, videoRendererEventListener, audioRendererEventListener, textRendererOutput, metadataRendererOutput ->
|
||||
DefaultRenderersFactory(context).apply {
|
||||
// setEnableDecoderFallback(true)
|
||||
setEnableDecoderFallback(true)
|
||||
// Enable Ffmpeg extension
|
||||
// setExtensionRendererMode(EXTENSION_RENDERER_MODE_ON)
|
||||
setExtensionRendererMode(EXTENSION_RENDERER_MODE_ON)
|
||||
}.createRenderers(
|
||||
eventHandler,
|
||||
videoRendererEventListener,
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.util.Rational
|
|||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.StringRes
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class PlayerPipHelper {
|
||||
|
@ -88,22 +89,25 @@ class PlayerPipHelper {
|
|||
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)
|
||||
val fixedRational = aspectRatio?.toFloat()?.coerceIn(mixAspectRatio, maxAspectRatio)?.let {
|
||||
Rational((it * ratioAccuracy).roundToInt(), ratioAccuracy)
|
||||
}
|
||||
val fixedRational =
|
||||
aspectRatio?.toFloat()?.coerceIn(mixAspectRatio, maxAspectRatio)?.let {
|
||||
Rational((it * ratioAccuracy).roundToInt(), ratioAccuracy)
|
||||
}
|
||||
|
||||
activity.setPictureInPictureParams(
|
||||
PictureInPictureParams.Builder()
|
||||
.apply {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
setSeamlessResizeEnabled(true)
|
||||
setAutoEnterEnabled(isPlaying)
|
||||
normalSafeApiCall {
|
||||
activity.setPictureInPictureParams(
|
||||
PictureInPictureParams.Builder()
|
||||
.apply {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
setSeamlessResizeEnabled(true)
|
||||
setAutoEnterEnabled(isPlaying)
|
||||
}
|
||||
}
|
||||
}
|
||||
.setAspectRatio(fixedRational)
|
||||
.setActions(actions)
|
||||
.build()
|
||||
)
|
||||
.setAspectRatio(fixedRational)
|
||||
.setActions(actions)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,19 +4,45 @@ import android.content.Context
|
|||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
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
|
||||
|
||||
fun RecyclerView?.setLinearListLayout(isHorizontal: Boolean = true) {
|
||||
if (this == null) return
|
||||
const val FOCUS_SELF = View.NO_ID - 1
|
||||
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.context?.let { LinearListLayout(it).apply { if (isHorizontal) setHorizontal() else setVertical() } }
|
||||
// ?: this.layoutManager
|
||||
LinearListLayout(ctx).apply {
|
||||
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?) :
|
||||
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() {
|
||||
orientation = HORIZONTAL
|
||||
}
|
||||
|
@ -56,8 +82,37 @@ open class LinearListLayout(context: Context?) :
|
|||
linearSmoothScroller.targetPosition = position
|
||||
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? {
|
||||
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) {
|
||||
// This scrolls the recyclerview before doing focus search, which
|
||||
// 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
|
||||
// only flip on horizontal layout
|
||||
if (this.isLayoutRTL) {
|
||||
if (isLayoutRTL) {
|
||||
ret = -ret
|
||||
}
|
||||
ret
|
||||
} 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
|
||||
}
|
||||
|
||||
return try {
|
||||
getPosition(getCorrectParent(focused))?.let { position ->
|
||||
val lookfor = dir + position
|
||||
//clamp(dir + position, 0, recyclerView.adapter?.itemCount ?: return null)
|
||||
try {
|
||||
val position = getPosition(getCorrectParent(focused)) ?: return null
|
||||
val lookFor = dir + position
|
||||
|
||||
// refocus on the same view if going out of bounds, note that we only do it
|
||||
// for out of bounds one way as we may override the start where item == -1
|
||||
if (lookfor >= itemCount) {
|
||||
return getViewFromPos(itemCount - 1) ?: focused
|
||||
}
|
||||
|
||||
getViewFromPos(lookfor) ?: run {
|
||||
scrollToPosition(lookfor)
|
||||
// if out of bounds then refocus as specified
|
||||
return if (lookFor >= itemCount) {
|
||||
getNextDirection(focused, if(orientation == HORIZONTAL) FocusDirection.End else FocusDirection.Down)
|
||||
} else if (lookFor < 0) {
|
||||
getNextDirection(focused, if(orientation == HORIZONTAL) FocusDirection.Start else FocusDirection.Up)
|
||||
} else {
|
||||
getViewFromPos(lookFor) ?: run {
|
||||
scrollToPosition(lookFor)
|
||||
null
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
null
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -294,7 +294,7 @@ open class ResultFragmentPhone : FullScreenPlayer(),
|
|||
super.onStop()
|
||||
}
|
||||
|
||||
private fun updateUI(id : Int?) {
|
||||
private fun updateUI(id: Int?) {
|
||||
syncModel.updateUserData()
|
||||
viewModel.reloadEpisodes()
|
||||
}
|
||||
|
@ -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(
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State,
|
||||
|
@ -356,7 +361,7 @@ open class ResultFragmentPhone : FullScreenPlayer(),
|
|||
}
|
||||
}.apply {
|
||||
this.orientation = RecyclerView.HORIZONTAL
|
||||
}
|
||||
}*/
|
||||
resultCastItems.adapter = ActorAdaptor()
|
||||
|
||||
resultEpisodes.adapter =
|
||||
|
@ -597,8 +602,14 @@ open class ResultFragmentPhone : FullScreenPlayer(),
|
|||
EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, ep)
|
||||
)
|
||||
}
|
||||
|
||||
DOWNLOAD_ACTION_LONG_CLICK -> {
|
||||
viewModel.handleAction(EpisodeClickEvent(ACTION_DOWNLOAD_MIRROR, ep))
|
||||
viewModel.handleAction(
|
||||
EpisodeClickEvent(
|
||||
ACTION_DOWNLOAD_MIRROR,
|
||||
ep
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
else -> DownloadButtonSetup.handleDownloadClick(click)
|
||||
|
|
|
@ -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 {
|
||||
setVertical()
|
||||
}*/
|
||||
|
@ -367,6 +389,11 @@ class ResultFragmentTv : Fragment() {
|
|||
)
|
||||
|
||||
resultCastItems.layoutManager = object : LinearListLayout(view.context) {
|
||||
|
||||
override fun onInterceptFocusSearch(focused: View, direction: Int): View? {
|
||||
return super.onInterceptFocusSearch(focused, direction)
|
||||
}
|
||||
|
||||
override fun onRequestChildFocus(
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State,
|
||||
|
@ -383,8 +410,9 @@ class ResultFragmentTv : Fragment() {
|
|||
}
|
||||
}
|
||||
}.apply {
|
||||
this.orientation = RecyclerView.HORIZONTAL
|
||||
setHorizontal()
|
||||
}
|
||||
|
||||
resultCastItems.adapter = ActorAdaptor {
|
||||
toggleEpisodes(false)
|
||||
}
|
||||
|
|
|
@ -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.updateChips
|
||||
import com.lagradost.cloudstream3.ui.home.ParentItemAdapter
|
||||
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.ownHide
|
||||
|
@ -519,9 +521,12 @@ class SearchFragment : Fragment() {
|
|||
|
||||
binding?.apply {
|
||||
searchHistoryRecycler.adapter = historyAdapter
|
||||
searchHistoryRecycler.layoutManager = GridLayoutManager(context, 1)
|
||||
searchHistoryRecycler.setLinearListLayout(isHorizontal = false, nextRight = FOCUS_SELF)
|
||||
//searchHistoryRecycler.layoutManager = GridLayoutManager(context, 1)
|
||||
|
||||
searchMasterRecycler.adapter = masterAdapter
|
||||
//searchMasterRecycler.setLinearListLayout(isHorizontal = false, nextRight = FOCUS_SELF)
|
||||
|
||||
searchMasterRecycler.layoutManager = GridLayoutManager(context, 1)
|
||||
|
||||
// Automatically search the specified query, this allows the app search to launch from intent
|
||||
|
|
|
@ -23,6 +23,8 @@ import com.lagradost.cloudstream3.databinding.FragmentExtensionsBinding
|
|||
import com.lagradost.cloudstream3.mvvm.observe
|
||||
import com.lagradost.cloudstream3.mvvm.observeNullable
|
||||
import com.lagradost.cloudstream3.plugins.RepositoryManager
|
||||
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||
import com.lagradost.cloudstream3.ui.result.setText
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||
|
@ -82,6 +84,14 @@ class ExtensionsFragment : Fragment() {
|
|||
|
||||
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, {
|
||||
findNavController().navigate(
|
||||
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?.apply {
|
||||
/*binding?.repoRecyclerView?.apply {
|
||||
context?.let { ctx ->
|
||||
layoutManager = LinearRecycleViewLayoutManager(ctx, nextFocusUpId, nextFocusDownId)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// list_repositories?.setOnClickListener {
|
||||
// // Open webview on tv if browser fails
|
||||
|
|
|
@ -62,100 +62,101 @@ class PluginDetailsFragment(val data: PluginViewData) : BottomSheetDialogFragmen
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
val metadata = data.plugin.second
|
||||
binding?.apply {
|
||||
if (!pluginIcon.setImage(//plugin_icon?.height ?:
|
||||
metadata.iconUrl?.replace(
|
||||
"%size%",
|
||||
"$iconSize"
|
||||
)?.replace(
|
||||
"%exact_size%",
|
||||
"$iconSizeExact"
|
||||
),
|
||||
null,
|
||||
errorImageDrawable = R.drawable.ic_baseline_extension_24
|
||||
)
|
||||
) {
|
||||
pluginIcon.setImageResource(R.drawable.ic_baseline_extension_24)
|
||||
}
|
||||
pluginName.text = metadata.name.removeSuffix("Provider")
|
||||
pluginVersion.text = metadata.version.toString()
|
||||
pluginDescription.text = metadata.description ?: getString(R.string.no_data)
|
||||
pluginSize.text =
|
||||
if (metadata.fileSize == null) getString(R.string.no_data) else formatFileSize(
|
||||
context,
|
||||
metadata.fileSize
|
||||
)
|
||||
pluginAuthor.text =
|
||||
if (metadata.authors.isEmpty()) getString(R.string.no_data) else metadata.authors.joinToString(
|
||||
", "
|
||||
)
|
||||
pluginStatus.text = resources.getStringArray(R.array.extension_statuses)[metadata.status]
|
||||
pluginTypes.text =
|
||||
if (metadata.tvTypes.isNullOrEmpty()) getString(R.string.no_data) else metadata.tvTypes.joinToString(
|
||||
", "
|
||||
)
|
||||
pluginLang.text = if (metadata.language == null)
|
||||
getString(R.string.no_data)
|
||||
else
|
||||
"${getFlagFromIso(metadata.language)} ${fromTwoLettersToLanguage(metadata.language)}"
|
||||
|
||||
githubBtn.setOnClickListener {
|
||||
if (metadata.repositoryUrl != null) {
|
||||
openBrowser(metadata.repositoryUrl)
|
||||
if (!pluginIcon.setImage(//plugin_icon?.height ?:
|
||||
metadata.iconUrl?.replace(
|
||||
"%size%",
|
||||
"$iconSize"
|
||||
)?.replace(
|
||||
"%exact_size%",
|
||||
"$iconSizeExact"
|
||||
),
|
||||
null,
|
||||
errorImageDrawable = R.drawable.ic_baseline_extension_24
|
||||
)
|
||||
) {
|
||||
pluginIcon.setImageResource(R.drawable.ic_baseline_extension_24)
|
||||
}
|
||||
}
|
||||
pluginName.text = metadata.name.removeSuffix("Provider")
|
||||
pluginVersion.text = metadata.version.toString()
|
||||
pluginDescription.text = metadata.description ?: getString(R.string.no_data)
|
||||
pluginSize.text =
|
||||
if (metadata.fileSize == null) getString(R.string.no_data) else formatFileSize(
|
||||
context,
|
||||
metadata.fileSize
|
||||
)
|
||||
pluginAuthor.text =
|
||||
if (metadata.authors.isEmpty()) getString(R.string.no_data) else metadata.authors.joinToString(
|
||||
", "
|
||||
)
|
||||
pluginStatus.text =
|
||||
resources.getStringArray(R.array.extension_statuses)[metadata.status]
|
||||
pluginTypes.text =
|
||||
if (metadata.tvTypes.isNullOrEmpty()) getString(R.string.no_data) else metadata.tvTypes.joinToString(
|
||||
", "
|
||||
)
|
||||
pluginLang.text = if (metadata.language == null)
|
||||
getString(R.string.no_data)
|
||||
else
|
||||
"${getFlagFromIso(metadata.language)} ${fromTwoLettersToLanguage(metadata.language)}"
|
||||
|
||||
if (!metadata.canVote()) {
|
||||
downvote.alpha = .6f
|
||||
upvote.alpha = .6f
|
||||
}
|
||||
githubBtn.setOnClickListener {
|
||||
if (metadata.repositoryUrl != null) {
|
||||
openBrowser(metadata.repositoryUrl)
|
||||
}
|
||||
}
|
||||
|
||||
if (data.isDownloaded) {
|
||||
// On local plugins page the filepath is provided instead of url.
|
||||
val plugin =
|
||||
PluginManager.urlPlugins[metadata.url] ?: PluginManager.plugins[metadata.url]
|
||||
if (plugin?.openSettings != null && context != null) {
|
||||
actionSettings.isVisible = true
|
||||
actionSettings.setOnClickListener {
|
||||
try {
|
||||
plugin.openSettings!!.invoke(requireContext())
|
||||
} catch (e: Throwable) {
|
||||
Log.e(
|
||||
"PluginAdapter",
|
||||
"Failed to open ${metadata.name} settings: ${
|
||||
Log.getStackTraceString(e)
|
||||
}"
|
||||
)
|
||||
if (!metadata.canVote()) {
|
||||
downvote.alpha = .6f
|
||||
upvote.alpha = .6f
|
||||
}
|
||||
|
||||
if (data.isDownloaded) {
|
||||
// On local plugins page the filepath is provided instead of url.
|
||||
val plugin =
|
||||
PluginManager.urlPlugins[metadata.url] ?: PluginManager.plugins[metadata.url]
|
||||
if (plugin?.openSettings != null && context != null) {
|
||||
actionSettings.isVisible = true
|
||||
actionSettings.setOnClickListener {
|
||||
try {
|
||||
plugin.openSettings!!.invoke(requireContext())
|
||||
} catch (e: Throwable) {
|
||||
Log.e(
|
||||
"PluginAdapter",
|
||||
"Failed to open ${metadata.name} settings: ${
|
||||
Log.getStackTraceString(e)
|
||||
}"
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
actionSettings.isVisible = false
|
||||
}
|
||||
} else {
|
||||
actionSettings.isVisible = false
|
||||
}
|
||||
} else {
|
||||
actionSettings.isVisible = false
|
||||
}
|
||||
|
||||
upvote.setOnClickListener {
|
||||
upvote.setOnClickListener {
|
||||
ioSafe {
|
||||
metadata.vote(VotingApi.VoteType.UPVOTE).main {
|
||||
updateVoting(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
downvote.setOnClickListener {
|
||||
ioSafe {
|
||||
metadata.vote(VotingApi.VoteType.DOWNVOTE).main {
|
||||
updateVoting(it)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ioSafe {
|
||||
metadata.vote(VotingApi.VoteType.UPVOTE).main {
|
||||
metadata.getVotes().main {
|
||||
updateVoting(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
downvote.setOnClickListener {
|
||||
ioSafe {
|
||||
metadata.vote(VotingApi.VoteType.DOWNVOTE).main {
|
||||
updateVoting(it)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ioSafe {
|
||||
metadata.getVotes().main {
|
||||
updateVoting(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateVoting(value: Int) {
|
||||
|
|
|
@ -15,6 +15,8 @@ import com.lagradost.cloudstream3.TvType
|
|||
import com.lagradost.cloudstream3.databinding.FragmentPluginsBinding
|
||||
import com.lagradost.cloudstream3.mvvm.observe
|
||||
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips
|
||||
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||
import com.lagradost.cloudstream3.ui.settings.appLanguages
|
||||
|
@ -32,7 +34,7 @@ class PluginsFragment : Fragment() {
|
|||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
val localBinding = FragmentPluginsBinding.inflate(inflater,container,false)
|
||||
val localBinding = FragmentPluginsBinding.inflate(inflater, container, false)
|
||||
binding = localBinding
|
||||
return localBinding.root//inflater.inflate(R.layout.fragment_plugins, container, false)
|
||||
}
|
||||
|
@ -73,48 +75,51 @@ class PluginsFragment : Fragment() {
|
|||
|
||||
setUpToolbar(name)
|
||||
binding?.settingsToolbar?.apply {
|
||||
setOnMenuItemClickListener { menuItem ->
|
||||
when (menuItem?.itemId) {
|
||||
R.id.download_all -> {
|
||||
PluginsViewModel.downloadAll(activity, url, pluginViewModel)
|
||||
}
|
||||
R.id.lang_filter -> {
|
||||
val tempLangs = appLanguages.toMutableList()
|
||||
val languageCodes = mutableListOf("none") + tempLangs.map { (_, _, iso) -> iso }
|
||||
val languageNames =
|
||||
mutableListOf(getString(R.string.no_data)) + tempLangs.map { (emoji, name, iso) ->
|
||||
val flag =
|
||||
emoji.ifBlank { SubtitleHelper.getFlagFromIso(iso) ?: "ERROR" }
|
||||
"$flag $name"
|
||||
}
|
||||
val selectedList =
|
||||
pluginViewModel.languages.map { it -> languageCodes.indexOf(it) }
|
||||
|
||||
activity?.showMultiDialog(
|
||||
languageNames,
|
||||
selectedList,
|
||||
getString(R.string.provider_lang_settings),
|
||||
{}) { newList ->
|
||||
pluginViewModel.languages = newList.map { it -> languageCodes[it] }
|
||||
pluginViewModel.updateFilteredPlugins()
|
||||
setOnMenuItemClickListener { menuItem ->
|
||||
when (menuItem?.itemId) {
|
||||
R.id.download_all -> {
|
||||
PluginsViewModel.downloadAll(activity, url, pluginViewModel)
|
||||
}
|
||||
|
||||
R.id.lang_filter -> {
|
||||
val tempLangs = appLanguages.toMutableList()
|
||||
val languageCodes =
|
||||
mutableListOf("none") + tempLangs.map { (_, _, iso) -> iso }
|
||||
val languageNames =
|
||||
mutableListOf(getString(R.string.no_data)) + tempLangs.map { (emoji, name, iso) ->
|
||||
val flag =
|
||||
emoji.ifBlank { SubtitleHelper.getFlagFromIso(iso) ?: "ERROR" }
|
||||
"$flag $name"
|
||||
}
|
||||
val selectedList =
|
||||
pluginViewModel.languages.map { languageCodes.indexOf(it) }
|
||||
|
||||
activity?.showMultiDialog(
|
||||
languageNames,
|
||||
selectedList,
|
||||
getString(R.string.provider_lang_settings),
|
||||
{}) { newList ->
|
||||
pluginViewModel.languages = newList.map { languageCodes[it] }
|
||||
pluginViewModel.updateFilteredPlugins()
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
else -> {}
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
val searchView =
|
||||
menu?.findItem(R.id.search_button)?.actionView as? SearchView
|
||||
val searchView =
|
||||
menu?.findItem(R.id.search_button)?.actionView as? SearchView
|
||||
|
||||
// Don't go back if active query
|
||||
setNavigationOnClickListener {
|
||||
if (searchView?.isIconified == false) {
|
||||
searchView.isIconified = true
|
||||
} else {
|
||||
activity?.onBackPressed()
|
||||
// Don't go back if active query
|
||||
setNavigationOnClickListener {
|
||||
if (searchView?.isIconified == false) {
|
||||
searchView.isIconified = true
|
||||
} else {
|
||||
activity?.onBackPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
searchView?.setOnQueryTextFocusChangeListener { _, hasFocus ->
|
||||
if (!hasFocus) pluginViewModel.search(null)
|
||||
}
|
||||
|
@ -137,7 +142,11 @@ class PluginsFragment : Fragment() {
|
|||
|
||||
// Because onActionViewCollapsed doesn't wanna work we need this workaround :(
|
||||
|
||||
|
||||
binding?.pluginRecyclerView?.setLinearListLayout(
|
||||
isHorizontal = false,
|
||||
nextDown = FOCUS_SELF,
|
||||
nextRight = FOCUS_SELF,
|
||||
)
|
||||
|
||||
binding?.pluginRecyclerView?.adapter =
|
||||
PluginAdapter {
|
||||
|
@ -167,11 +176,18 @@ class PluginsFragment : Fragment() {
|
|||
pluginViewModel.updatePluginList(context, url)
|
||||
binding?.tvtypesChipsScroll?.root?.isVisible = true
|
||||
|
||||
bindChips(binding?.tvtypesChipsScroll?.tvtypesChips, emptyList(), TvType.values().toList()) { list ->
|
||||
pluginViewModel.tvTypes.clear()
|
||||
pluginViewModel.tvTypes.addAll(list.map { it.name })
|
||||
pluginViewModel.updateFilteredPlugins()
|
||||
}
|
||||
bindChips(
|
||||
binding?.tvtypesChipsScroll?.tvtypesChips,
|
||||
emptyList(),
|
||||
TvType.values().toList(),
|
||||
callback = { list ->
|
||||
pluginViewModel.tvTypes.clear()
|
||||
pluginViewModel.tvTypes.addAll(list.map { it.name })
|
||||
pluginViewModel.updateFilteredPlugins()
|
||||
},
|
||||
nextFocusDown = R.id.plugin_recycler_view,
|
||||
nextFocusUp = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager
|
|||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
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.VideoWatchState
|
||||
import com.lagradost.cloudstream3.ui.result.setImage
|
||||
|
@ -194,7 +195,13 @@ object DataStoreHelper {
|
|||
builder.setContentView(binding.root)
|
||||
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(
|
||||
selectCallBack = { account ->
|
||||
setAccount(account)
|
||||
|
|
|
@ -1,39 +1,40 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:background="?attr/primaryGrayBackground"
|
||||
android:id="@+id/download_child_root"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.download.DownloadFragment">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/download_child_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/primaryGrayBackground"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.download.DownloadFragment">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:background="@android:color/transparent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/download_child_toolbar"
|
||||
android:paddingTop="@dimen/navbar_height"
|
||||
tools:title="Overlord"
|
||||
android:background="?attr/primaryGrayBackground"
|
||||
app:navigationIconTint="?attr/iconColor"
|
||||
app:titleTextColor="?attr/textColor"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
android:id="@+id/download_child_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/primaryGrayBackground"
|
||||
android:paddingTop="@dimen/navbar_height"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:navigationIconTint="?attr/iconColor"
|
||||
app:titleTextColor="?attr/textColor"
|
||||
tools:title="Overlord" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:nextFocusUp="@id/download_child_toolbar"
|
||||
android:id="@+id/download_child_list"
|
||||
|
||||
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:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_width="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>
|
|
@ -64,6 +64,7 @@
|
|||
android:focusable="true"
|
||||
android:foreground="@drawable/outline_drawable"
|
||||
android:nextFocusRight="@id/add_repo_button_imageview"
|
||||
android:nextFocusUp="@id/repo_recycler_view"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp"
|
||||
|
||||
|
@ -84,13 +85,13 @@
|
|||
android:textColor="?attr/textColor" />
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
app:cardElevation="0dp"
|
||||
android:elevation="0dp"
|
||||
app:cardMaxElevation="0dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
app:cardCornerRadius="@dimen/storage_radius">
|
||||
android:elevation="0dp"
|
||||
app:cardCornerRadius="@dimen/storage_radius"
|
||||
app:cardElevation="0dp"
|
||||
app:cardMaxElevation="0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -203,6 +204,8 @@
|
|||
android:contentDescription="@string/add_repository"
|
||||
android:focusable="true"
|
||||
android:nextFocusLeft="@id/plugin_storage_appbar"
|
||||
android:nextFocusUp="@id/repo_recycler_view"
|
||||
|
||||
android:src="@drawable/ic_baseline_add_24"
|
||||
app:tint="?attr/textColor" />
|
||||
</LinearLayout>
|
||||
|
|
|
@ -153,6 +153,7 @@
|
|||
android:layout_marginStart="@dimen/navbar_width"
|
||||
android:backgroundTint="@color/semiWhite"
|
||||
android:minWidth="150dp"
|
||||
android:nextFocusUp="@id/home_preview_play_btt"
|
||||
android:nextFocusLeft="@id/nav_rail_view"
|
||||
android:nextFocusDown="@id/home_watch_child_recyclerview" />
|
||||
</FrameLayout>
|
||||
|
@ -178,6 +179,8 @@
|
|||
|
||||
<androidx.recyclerview.widget.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_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
|
@ -204,9 +207,6 @@
|
|||
android:fadingEdge="horizontal"
|
||||
|
||||
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:paddingTop="5dp"
|
||||
android:paddingEnd="12dp"
|
||||
|
@ -215,6 +215,12 @@
|
|||
android:requiresFadingEdge="horizontal">
|
||||
|
||||
<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_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/navbar_width"
|
||||
|
@ -225,6 +231,8 @@
|
|||
style="@style/ChipFilled"
|
||||
android:layout_width="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:nextFocusRight="@id/home_plan_to_watch_btt"
|
||||
|
@ -235,6 +243,8 @@
|
|||
style="@style/ChipFilled"
|
||||
android:layout_width="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:nextFocusRight="@id/home_type_on_hold_btt"
|
||||
|
@ -245,6 +255,8 @@
|
|||
style="@style/ChipFilled"
|
||||
android:layout_width="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:nextFocusRight="@id/home_type_dropped_btt"
|
||||
|
@ -255,6 +267,8 @@
|
|||
style="@style/ChipFilled"
|
||||
android:layout_width="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:nextFocusRight="@id/home_type_completed_btt"
|
||||
|
@ -264,6 +278,8 @@
|
|||
android:id="@+id/home_type_completed_btt"
|
||||
style="@style/ChipFilled"
|
||||
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:nextFocusLeft="@id/home_type_dropped_btt"
|
||||
|
@ -273,6 +289,10 @@
|
|||
|
||||
<androidx.recyclerview.widget.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_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
|
|
|
@ -162,6 +162,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:nextFocusLeft="@id/nav_rail_view"
|
||||
android:nextFocusUp="@id/home_bookmarked_child_recyclerview"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/homepage_parent_tv" />
|
||||
|
||||
|
|
|
@ -25,18 +25,22 @@
|
|||
app:titleTextColor="?attr/textColor"
|
||||
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>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:clipToPadding="false"
|
||||
android:id="@+id/plugin_recycler_view"
|
||||
android:layout_width="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:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:listitem="@layout/repository_item" />
|
||||
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
|
|
|
@ -248,6 +248,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
|||
style="@style/ResultButtonTV"
|
||||
|
||||
android:nextFocusRight="@id/result_description"
|
||||
android:nextFocusUp="@id/result_play_movie"
|
||||
android:nextFocusDown="@id/result_play_series"
|
||||
android:text="@string/play_movie_button"
|
||||
android:visibility="visible"
|
||||
|
@ -537,10 +538,10 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
|||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<ImageView
|
||||
android:layout_gravity="end"
|
||||
android:id="@+id/episodes_shadow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
|
@ -552,8 +553,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
|||
<LinearLayout
|
||||
android:id="@+id/episode_holder_tv"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:layout_gravity="end"
|
||||
android:orientation="horizontal"
|
||||
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_constraintTop_toTopOf="parent"
|
||||
tools:ignore="RtlHardcoded"
|
||||
tools:visibility="visible">
|
||||
tools:visibility="gone">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:id="@+id/result_season_selection"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:clipToPadding="false"
|
||||
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:nextFocusLeft="@id/result_episodes_show"
|
||||
android:nextFocusRight="@id/result_range_selection"
|
||||
android:orientation="vertical"
|
||||
|
@ -580,11 +581,11 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
|||
tools:listitem="@layout/result_selection" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:id="@+id/result_range_selection"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
|
||||
android:nextFocusLeft="@id/result_season_selection"
|
||||
android:nextFocusRight="@id/result_dub_selection"
|
||||
|
@ -595,12 +596,12 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
|||
tools:visibility="visible" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:id="@+id/result_dub_selection"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:clipToPadding="false"
|
||||
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:nextFocusLeft="@id/result_range_selection"
|
||||
android:nextFocusRight="@id/result_episodes"
|
||||
android:orientation="vertical"
|
||||
|
@ -618,13 +619,12 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
|||
android:layout_height="50dp" />-->
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:id="@+id/result_episodes"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:nextFocusLeft="@id/result_dub_selection"
|
||||
android:nextFocusRight="@id/result_recommendations_filter_selection"
|
||||
android:orientation="vertical"
|
||||
android:paddingVertical="@dimen/result_padding"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
tools:context=".ui.search.SearchFragment">
|
||||
|
||||
<LinearLayout
|
||||
android:paddingBottom="10dp"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -88,7 +88,9 @@
|
|||
app:tint="?attr/textColor" />
|
||||
</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>
|
||||
|
||||
|
||||
|
@ -116,7 +118,10 @@
|
|||
|
||||
android:background="?attr/primaryBlackBackground"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
|
||||
android:nextFocusLeft="@id/nav_rail_view"
|
||||
android:nextFocusUp="@id/tvtypes_chips"
|
||||
android:nextFocusDown="@id/search_clear_call_history"
|
||||
android:visibility="gone"
|
||||
tools:listitem="@layout/homepage_parent" />
|
||||
|
||||
|
@ -134,20 +139,24 @@
|
|||
android:background="?attr/primaryBlackBackground"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
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:visibility="visible"
|
||||
tools:listitem="@layout/search_history_item" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/search_clear_call_history"
|
||||
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_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>
|
||||
</LinearLayout>
|
|
@ -11,11 +11,11 @@
|
|||
tools:context=".ui.search.SearchFragment">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_marginStart="@dimen/navbar_width"
|
||||
android:paddingBottom="10dp"
|
||||
android:orientation="vertical"
|
||||
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
|
||||
android:layout_width="match_parent"
|
||||
|
@ -89,7 +89,9 @@
|
|||
app:tint="?attr/textColor" />
|
||||
</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>
|
||||
|
||||
|
||||
|
@ -118,38 +120,43 @@
|
|||
android:background="?attr/primaryBlackBackground"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:nextFocusLeft="@id/nav_rail_view"
|
||||
android:nextFocusUp="@id/tvtypes_chips"
|
||||
android:nextFocusDown="@id/search_clear_call_history"
|
||||
android:visibility="gone"
|
||||
tools:listitem="@layout/homepage_parent" />
|
||||
|
||||
<FrameLayout
|
||||
android:background="?attr/primaryBlackBackground"
|
||||
android:id="@+id/search_history_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/primaryBlackBackground">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_marginStart="@dimen/navbar_width"
|
||||
android:id="@+id/search_history_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/navbar_width"
|
||||
|
||||
android:descendantFocusability="afterDescendants"
|
||||
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:visibility="visible"
|
||||
tools:listitem="@layout/search_history_item" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:layout_marginStart="@dimen/navbar_width"
|
||||
android:id="@+id/search_clear_call_history"
|
||||
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_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>
|
||||
</LinearLayout>
|
|
@ -1,19 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.appbar.AppBarLayout android:background="@android:color/transparent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<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_height="wrap_content"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/settings_toolbar"
|
||||
android:paddingTop="@dimen/navbar_height"
|
||||
tools:title="Overlord"
|
||||
android:background="?attr/primaryGrayBackground"
|
||||
app:navigationIconTint="?attr/iconColor"
|
||||
app:titleTextColor="?attr/textColor"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
android:id="@+id/settings_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/primaryGrayBackground"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:paddingTop="@dimen/navbar_height"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:navigationIconTint="?attr/iconColor"
|
||||
app:titleTextColor="?attr/textColor"
|
||||
tools:title="Overlord" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
|
@ -6,6 +6,7 @@
|
|||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:id="@+id/home_select_group"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
app:singleSelection="false"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
<?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_height="wrap_content"
|
||||
android:fadingEdge="horizontal"
|
||||
android:requiresFadingEdge="horizontal"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
android:requiresFadingEdge="horizontal">
|
||||
|
||||
<include layout="@layout/tvtypes_chips" android:id="@+id/tvtypes_chips" />
|
||||
<include
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:id="@+id/tvtypes_chips"
|
||||
layout="@layout/tvtypes_chips" />
|
||||
</HorizontalScrollView>
|
|
@ -579,4 +579,6 @@
|
|||
<string name="qualities">النوعيات</string>
|
||||
<string name="profile_background_des">خلفية الملف الشخصي</string>
|
||||
<string name="unable_to_inflate">تعذر إنشاء واجهة المستخدم بشكل صحيح ، وهذا خطأ كبير ويجب الإبلاغ عنه على الفور %s</string>
|
||||
<string name="automatic_plugin_download_mode_title">حدد الوضع لتصفية تنزيل المكونات الإضافية</string>
|
||||
<string name="disable">تعطيل</string>
|
||||
</resources>
|
||||
|
|
|
@ -571,4 +571,6 @@
|
|||
\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>
|
||||
<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>
|
||||
|
|
|
@ -547,4 +547,7 @@
|
|||
<string name="qualities">Calidades</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="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>
|
||||
|
|
|
@ -13,4 +13,158 @@
|
|||
<string name="episode_poster_img_des">Póster do Episodio</string>
|
||||
<string name="go_back_img_des">Regresar</string>
|
||||
<string name="home_change_provider_img_des">Cambiar provedor</string>
|
||||
<string name="new_update_format" formatted="true">Nova actualización atopada!
|
||||
\n%s -> %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>
|
||||
|
|
|
@ -570,4 +570,6 @@
|
|||
<string name="qualities">Kualitas</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="disable">Nonaktif</string>
|
||||
<string name="automatic_plugin_download_mode_title">Pilih mode untuk memfilter unduhan plugin</string>
|
||||
</resources>
|
||||
|
|
|
@ -204,4 +204,11 @@
|
|||
<string name="next_episode_format" formatted="true">エピソード %d は にリリースされます</string>
|
||||
<string name="type_re_watching">再視聴</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>
|
||||
|
|
|
@ -550,4 +550,7 @@
|
|||
<string name="qualities">Jakości</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="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>
|
||||
|
|
|
@ -5,8 +5,8 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:7.3.1")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20")
|
||||
classpath("com.android.tools.build:gradle:8.0.2")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20")
|
||||
classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.5.0")
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
|||
#Fri Apr 30 17:11:15 CEST 2021
|
||||
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
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue