tv focus changes + gradle bump + pip crash fix

This commit is contained in:
LagradOst 2023-08-04 05:37:41 +02:00
parent 653982a6bd
commit f5c4864a3c
28 changed files with 590 additions and 320 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -11,6 +11,7 @@ import com.lagradost.cloudstream3.HomePageList
import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.HomepageParentBinding import com.lagradost.cloudstream3.databinding.HomepageParentBinding
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.result.setLinearListLayout
import com.lagradost.cloudstream3.ui.search.SearchClickCallback import com.lagradost.cloudstream3.ui.search.SearchClickCallback
import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse
@ -162,7 +163,8 @@ open class ParentItemAdapter(
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
val title: TextView = binding.homeChildMoreInfo val title: TextView = binding.homeChildMoreInfo
private val recyclerView: RecyclerView = binding.homeChildRecyclerview private val recyclerView: RecyclerView = binding.homeChildRecyclerview
private val startFocus = R.id.nav_rail_view
private val endFocus = FOCUS_SELF
fun update(expand: HomeViewModel.ExpandableHomepageList) { fun update(expand: HomeViewModel.ExpandableHomepageList) {
val info = expand.list val info = expand.list
(recyclerView.adapter as? HomeChildItemAdapter?)?.apply { (recyclerView.adapter as? HomeChildItemAdapter?)?.apply {
@ -176,8 +178,13 @@ open class ParentItemAdapter(
nextFocusDown = recyclerView.nextFocusDownId, nextFocusDown = recyclerView.nextFocusDownId,
).apply { ).apply {
isHorizontal = info.isHorizontalImages isHorizontal = info.isHorizontalImages
hasNext = expand.hasNext
} }
recyclerView.setLinearListLayout() recyclerView.setLinearListLayout(
isHorizontal = true,
nextLeft = startFocus,
nextRight = endFocus,
)
} }
} }
@ -192,7 +199,11 @@ open class ParentItemAdapter(
isHorizontal = info.isHorizontalImages isHorizontal = info.isHorizontalImages
hasNext = expand.hasNext hasNext = expand.hasNext
} }
recyclerView.setLinearListLayout() recyclerView.setLinearListLayout(
isHorizontal = true,
nextLeft = startFocus,
nextRight = endFocus,
)
title.text = info.name title.text = info.name
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {

View file

@ -30,6 +30,7 @@ import com.lagradost.cloudstream3.mvvm.debugException
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.selectHomepage import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.selectHomepage
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
import com.lagradost.cloudstream3.ui.result.ResultViewModel2 import com.lagradost.cloudstream3.ui.result.ResultViewModel2
import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST
import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.result.setLinearListLayout
@ -439,8 +440,8 @@ class HomeParentItemAdapterPreview(
resumeRecyclerView.adapter = resumeAdapter resumeRecyclerView.adapter = resumeAdapter
bookmarkRecyclerView.adapter = bookmarkAdapter bookmarkRecyclerView.adapter = bookmarkAdapter
resumeRecyclerView.setLinearListLayout() resumeRecyclerView.setLinearListLayout(nextLeft = R.id.nav_rail_view, nextRight = FOCUS_SELF)
bookmarkRecyclerView.setLinearListLayout() bookmarkRecyclerView.setLinearListLayout(nextLeft = R.id.nav_rail_view, nextRight = FOCUS_SELF)
fixPaddingStatusbarMargin(topPadding) fixPaddingStatusbarMargin(topPadding)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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