mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
focus fix + rtl fix
This commit is contained in:
parent
a1824c86a3
commit
c98f35fd94
13 changed files with 284 additions and 126 deletions
|
@ -13,16 +13,15 @@ import android.os.Bundle
|
|||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.AnimationSet
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.core.animation.addListener
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
|
@ -35,22 +34,12 @@ import androidx.navigation.NavOptions
|
|||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.transition.ChangeBounds
|
||||
import androidx.transition.ChangeTransform
|
||||
import androidx.transition.Scene
|
||||
import androidx.transition.TransitionManager
|
||||
import androidx.transition.TransitionManager.beginDelayedTransition
|
||||
import androidx.transition.TransitionSet
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.google.android.gms.cast.framework.*
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.navigationrail.NavigationRailView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
|
||||
|
@ -108,6 +97,7 @@ import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions
|
|||
import com.lagradost.cloudstream3.utils.*
|
||||
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.loadCache
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.loadRepository
|
||||
|
@ -131,6 +121,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.getResourceColor
|
|||
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.requestRW
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
||||
import com.lagradost.nicehttp.Requests
|
||||
import com.lagradost.nicehttp.ResponseParser
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
|
@ -140,6 +131,7 @@ import java.lang.ref.WeakReference
|
|||
import java.net.URI
|
||||
import java.net.URLDecoder
|
||||
import java.nio.charset.Charset
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
|
@ -472,13 +464,24 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
|
||||
binding?.navHostFragment?.apply {
|
||||
val params = layoutParams as ConstraintLayout.LayoutParams
|
||||
val push = if (!dontPush && isTvSettings()) resources.getDimensionPixelSize(R.dimen.navbar_width) else 0
|
||||
|
||||
if(!this.isLtr()) {
|
||||
params.setMargins(
|
||||
params.leftMargin,
|
||||
params.topMargin,
|
||||
push,
|
||||
params.bottomMargin
|
||||
)
|
||||
} else {
|
||||
params.setMargins(
|
||||
push,
|
||||
params.topMargin,
|
||||
params.rightMargin,
|
||||
params.bottomMargin
|
||||
)
|
||||
}
|
||||
|
||||
params.setMargins(
|
||||
if (!dontPush && isTvSettings()) resources.getDimensionPixelSize(R.dimen.navbar_width) else 0,
|
||||
params.topMargin,
|
||||
params.rightMargin,
|
||||
params.bottomMargin
|
||||
)
|
||||
layoutParams = params
|
||||
}
|
||||
|
||||
|
@ -572,9 +575,22 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
}
|
||||
|
||||
override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
|
||||
CommonActivity.dispatchKeyEvent(this, event)?.let {
|
||||
return it
|
||||
val start = System.currentTimeMillis()
|
||||
try {
|
||||
val response = CommonActivity.dispatchKeyEvent(this, event)
|
||||
|
||||
if (response != null)
|
||||
return response
|
||||
} finally {
|
||||
debugAssert({
|
||||
val end = System.currentTimeMillis()
|
||||
val delta = end - start
|
||||
delta > 100
|
||||
}) {
|
||||
"Took over 100ms to navigate, smth is VERY wrong"
|
||||
}
|
||||
}
|
||||
|
||||
return super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
|
@ -745,85 +761,186 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
}
|
||||
|
||||
private var binding: ActivityMainBinding? = null
|
||||
private var focusOutline: WeakReference<View> = WeakReference(null)
|
||||
private var lastFocus: WeakReference<View> = WeakReference(null)
|
||||
private val layoutListener: View.OnLayoutChangeListener =
|
||||
View.OnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||
updateFocusView(
|
||||
v
|
||||
)
|
||||
}
|
||||
private val attachListener : View.OnAttachStateChangeListener = object : View.OnAttachStateChangeListener {
|
||||
override fun onViewAttachedToWindow(v: View) {
|
||||
updateFocusView(v)
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(v: View) {
|
||||
// removes the focus view but not the listener as updateFocusView(null) will remove the listener
|
||||
focusOutline.get()?.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFocusView(newFocus: View?) {
|
||||
val focusOutline = focusOutline.get() ?: return
|
||||
lastFocus.get()?.removeOnLayoutChangeListener(layoutListener)
|
||||
lastFocus.get()?.removeOnAttachStateChangeListener(attachListener)
|
||||
val wasGone = focusOutline.isGone
|
||||
|
||||
val visible =
|
||||
newFocus != null && newFocus.measuredHeight > 0 && newFocus.measuredWidth > 0 && newFocus.isShown && newFocus.tag != "tv_no_focus_tag"
|
||||
focusOutline.isVisible = visible
|
||||
if (newFocus != null) {
|
||||
lastFocus = WeakReference(newFocus)
|
||||
|
||||
val out = IntArray(2)
|
||||
newFocus.getLocationInWindow(out)
|
||||
val (x, y) = out
|
||||
// out of bounds = 0,0
|
||||
if(x == 0 && y == 0) {
|
||||
focusOutline.isVisible = false
|
||||
}
|
||||
/*(newFocus.parent as? RecyclerView)?.let { recycle ->
|
||||
println("PARET IS RECYLE")
|
||||
val position = recycle.getChildAdapterPosition(newFocus)
|
||||
recycle.scrollToPosition(position)
|
||||
|
||||
(recycle.layoutManager as? GridLayoutManager)?.let {
|
||||
if(it.orientation == LinearLayout.HORIZONTAL) {
|
||||
println("SCROLL")
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}*/
|
||||
newFocus.addOnLayoutChangeListener(layoutListener)
|
||||
newFocus.addOnAttachStateChangeListener(attachListener)
|
||||
|
||||
// val set = AnimationSet(true)
|
||||
if(!wasGone) {
|
||||
(focusOutline.parent as? ViewGroup)?.let {
|
||||
TransitionManager.endTransitions(it)
|
||||
TransitionManager.beginDelayedTransition(
|
||||
it,
|
||||
TransitionSet().addTransition(ChangeBounds())
|
||||
.addTransition(ChangeTransform())
|
||||
.setDuration(100)
|
||||
object TvFocus {
|
||||
data class FocusTarget(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val x: Float,
|
||||
val y: Float,
|
||||
) {
|
||||
companion object {
|
||||
fun lerp(a: FocusTarget, b: FocusTarget, lerp: Float): FocusTarget {
|
||||
val ilerp = 1 - lerp
|
||||
return FocusTarget(
|
||||
width = (a.width * ilerp + b.width * lerp).toInt(),
|
||||
height = (a.height * ilerp + b.height * lerp).toInt(),
|
||||
x = a.x * ilerp + b.x * lerp,
|
||||
y = a.y * ilerp + b.y * lerp
|
||||
)
|
||||
}
|
||||
}
|
||||
// ObjectAnimator.ofFloat(focusOutline, "translationX",focusOutline.translationX, x.toFloat()
|
||||
}
|
||||
|
||||
var last: FocusTarget = FocusTarget(0, 0, 0.0f, 0.0f)
|
||||
var current: FocusTarget = FocusTarget(0, 0, 0.0f, 0.0f)
|
||||
|
||||
focusOutline.layoutParams = focusOutline.layoutParams?.apply {
|
||||
width = newFocus.measuredWidth
|
||||
height = newFocus.measuredHeight
|
||||
var focusOutline: WeakReference<View> = WeakReference(null)
|
||||
var lastFocus: WeakReference<View> = WeakReference(null)
|
||||
private val layoutListener: View.OnLayoutChangeListener =
|
||||
View.OnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||
updateFocusView(
|
||||
v, same = true
|
||||
)
|
||||
}
|
||||
private val attachListener: View.OnAttachStateChangeListener =
|
||||
object : View.OnAttachStateChangeListener {
|
||||
override fun onViewAttachedToWindow(v: View) {
|
||||
updateFocusView(v)
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(v: View) {
|
||||
// removes the focus view but not the listener as updateFocusView(null) will remove the listener
|
||||
focusOutline.get()?.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun setTargetPosition(target: FocusTarget) {
|
||||
focusOutline.get()?.apply {
|
||||
layoutParams = layoutParams?.apply {
|
||||
width = target.width
|
||||
height = target.height
|
||||
}
|
||||
|
||||
translationX = target.x
|
||||
translationY = target.y
|
||||
}
|
||||
}
|
||||
|
||||
private var animator: ValueAnimator? = null
|
||||
|
||||
@MainThread
|
||||
fun updateFocusView(newFocus: View?, same: Boolean = false) {
|
||||
val focusOutline = focusOutline.get() ?: return
|
||||
lastFocus.get()?.apply {
|
||||
removeOnLayoutChangeListener(layoutListener)
|
||||
removeOnAttachStateChangeListener(attachListener)
|
||||
}
|
||||
|
||||
val wasGone = focusOutline.isGone
|
||||
|
||||
val visible =
|
||||
newFocus != null && newFocus.measuredHeight > 0 && newFocus.measuredWidth > 0 && newFocus.isShown && newFocus.tag != "tv_no_focus_tag"
|
||||
focusOutline.isVisible = visible
|
||||
|
||||
if (newFocus != null) {
|
||||
lastFocus = WeakReference(newFocus)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// out of bounds = 0,0
|
||||
if (screenX == 0 && screenY == 0) {
|
||||
focusOutline.isVisible = false
|
||||
}
|
||||
|
||||
newFocus.addOnLayoutChangeListener(layoutListener)
|
||||
newFocus.addOnAttachStateChangeListener(attachListener)
|
||||
|
||||
val start = FocusTarget(
|
||||
x = currentX,
|
||||
y = currentY,
|
||||
width = focusOutline.measuredWidth,
|
||||
height = focusOutline.measuredHeight
|
||||
)
|
||||
val end = FocusTarget(
|
||||
x = x,
|
||||
y = y,
|
||||
width = newFocus.measuredWidth,
|
||||
height = newFocus.measuredHeight
|
||||
)
|
||||
|
||||
// if they are the same within then snap, aka scrolling
|
||||
val deltaMin = 50.toPx
|
||||
if (start.width == end.width && start.height == end.height && (start.x - end.x).absoluteValue < deltaMin && (start.y - end.y).absoluteValue < deltaMin) {
|
||||
animator?.cancel()
|
||||
last = start
|
||||
current = end
|
||||
setTargetPosition(end)
|
||||
return
|
||||
}
|
||||
|
||||
// if running then "reuse"
|
||||
if (animator?.isRunning == true) {
|
||||
current = end
|
||||
return
|
||||
} else {
|
||||
animator?.cancel()
|
||||
}
|
||||
|
||||
|
||||
last = start
|
||||
current = end
|
||||
|
||||
// if previously gone, then tp
|
||||
if (wasGone) {
|
||||
setTargetPosition(current)
|
||||
return
|
||||
}
|
||||
|
||||
// animate between a and b
|
||||
animator = ValueAnimator.ofFloat(0.0f, 1.0f).apply {
|
||||
startDelay = 0
|
||||
duration = 100
|
||||
addUpdateListener { animation ->
|
||||
val animatedValue = animation.animatedValue as Float
|
||||
val target = FocusTarget.lerp(last, current, minOf(animatedValue, 1.0f))
|
||||
setTargetPosition(target)
|
||||
}
|
||||
start()
|
||||
}
|
||||
|
||||
// post check
|
||||
if (!same) {
|
||||
newFocus.postDelayed({
|
||||
updateFocusView(lastFocus.get(), same = true)
|
||||
}, 200)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
the following is working, but somewhat bad code code
|
||||
|
||||
if (!wasGone) {
|
||||
(focusOutline.parent as? ViewGroup)?.let {
|
||||
TransitionManager.endTransitions(it)
|
||||
TransitionManager.beginDelayedTransition(
|
||||
it,
|
||||
TransitionSet().addTransition(ChangeBounds())
|
||||
.addTransition(ChangeTransform())
|
||||
.setDuration(100)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
focusOutline.layoutParams = focusOutline.layoutParams?.apply {
|
||||
width = newFocus.measuredWidth
|
||||
height = newFocus.measuredHeight
|
||||
}
|
||||
focusOutline.translationX = x.toFloat()
|
||||
focusOutline.translationY = y.toFloat()*/
|
||||
}
|
||||
focusOutline.translationX = x.toFloat()
|
||||
focusOutline.translationY = y.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
app.initClient(this)
|
||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
@ -872,13 +989,13 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
if (isTvSettings()) {
|
||||
val newLocalBinding = ActivityMainTvBinding.inflate(layoutInflater, null, false)
|
||||
setContentView(newLocalBinding.root)
|
||||
focusOutline = WeakReference(newLocalBinding.focusOutline)
|
||||
TvFocus.focusOutline = WeakReference(newLocalBinding.focusOutline)
|
||||
newLocalBinding.root.viewTreeObserver.addOnGlobalFocusChangeListener { _, newFocus ->
|
||||
// println("refocus $oldFocus -> $newFocus")
|
||||
updateFocusView(newFocus)
|
||||
TvFocus.updateFocusView(newFocus)
|
||||
}
|
||||
newLocalBinding.root.viewTreeObserver.addOnScrollChangedListener {
|
||||
updateFocusView(lastFocus.get())
|
||||
TvFocus.updateFocusView(TvFocus.lastFocus.get(), same = true)
|
||||
}
|
||||
|
||||
ActivityMainBinding.bind(newLocalBinding.root) // this may crash
|
||||
|
@ -1230,16 +1347,21 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
}*/
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
var providersAndroidManifestString = "Current androidmanifest should be:\n"
|
||||
for (api in allProviders) {
|
||||
providersAndroidManifestString += "<data android:scheme=\"https\" android:host=\"${
|
||||
api.mainUrl.removePrefix(
|
||||
"https://"
|
||||
)
|
||||
}\" android:pathPrefix=\"/\"/>\n"
|
||||
try {
|
||||
var providersAndroidManifestString = "Current androidmanifest should be:\n"
|
||||
for (api in allProviders) {
|
||||
providersAndroidManifestString += "<data android:scheme=\"https\" android:host=\"${
|
||||
api.mainUrl.removePrefix(
|
||||
"https://"
|
||||
)
|
||||
}\" android:pathPrefix=\"/\"/>\n"
|
||||
}
|
||||
println(providersAndroidManifestString)
|
||||
|
||||
} catch (t: Throwable) {
|
||||
logError(t)
|
||||
}
|
||||
|
||||
println(providersAndroidManifestString)
|
||||
}
|
||||
|
||||
handleAppIntent(intent)
|
||||
|
|
|
@ -177,7 +177,7 @@ open class ParentItemAdapter(
|
|||
).apply {
|
||||
isHorizontal = info.isHorizontalImages
|
||||
}
|
||||
recyclerView.setLinearListLayout()
|
||||
//recyclerView.setLinearListLayout()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,7 +192,7 @@ open class ParentItemAdapter(
|
|||
isHorizontal = info.isHorizontalImages
|
||||
hasNext = expand.hasNext
|
||||
}
|
||||
recyclerView.setLinearListLayout()
|
||||
// recyclerView.setLinearListLayout()
|
||||
title.text = info.name
|
||||
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueT
|
|||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.html
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||
import java.util.*
|
||||
|
||||
|
@ -48,7 +49,8 @@ const val ACTION_PLAY_EPISODE_IN_WEB_VIDEO = 16
|
|||
const val ACTION_PLAY_EPISODE_IN_MPV = 17
|
||||
|
||||
const val ACTION_MARK_AS_WATCHED = 18
|
||||
|
||||
const val TV_EP_SIZE_LARGE = 400
|
||||
const val TV_EP_SIZE_SMALL = 300
|
||||
data class EpisodeClickEvent(val action: Int, val data: ResultEpisode)
|
||||
|
||||
class EpisodeAdapter(
|
||||
|
@ -172,7 +174,7 @@ class EpisodeAdapter(
|
|||
localCard = card
|
||||
|
||||
val setWidth =
|
||||
if (isTvSettings()) ViewGroup.LayoutParams.WRAP_CONTENT else ViewGroup.LayoutParams.MATCH_PARENT
|
||||
if (isTvSettings()) TV_EP_SIZE_LARGE.toPx else ViewGroup.LayoutParams.MATCH_PARENT
|
||||
|
||||
binding.episodeLinHolder.layoutParams.width = setWidth
|
||||
binding.episodeHolderLarge.layoutParams.width = setWidth
|
||||
|
@ -293,7 +295,7 @@ class EpisodeAdapter(
|
|||
|
||||
binding.episodeHolder.layoutParams.apply {
|
||||
width =
|
||||
if (isTvSettings()) ViewGroup.LayoutParams.WRAP_CONTENT else ViewGroup.LayoutParams.MATCH_PARENT
|
||||
if (isTvSettings()) TV_EP_SIZE_SMALL.toPx else ViewGroup.LayoutParams.MATCH_PARENT
|
||||
}
|
||||
|
||||
binding.apply {
|
||||
|
|
|
@ -2,15 +2,19 @@ package com.lagradost.cloudstream3.ui.result
|
|||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.View.LAYOUT_DIRECTION_LTR
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.isLtr
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.isRtl
|
||||
|
||||
fun RecyclerView?.setLinearListLayout(isHorizontal: Boolean = true) {
|
||||
if (this == null) return
|
||||
|
||||
this.layoutManager =
|
||||
this.context?.let { LinearListLayout(it).apply { if (isHorizontal) setHorizontal() else setVertical() } }
|
||||
?: this.layoutManager
|
||||
// ?: this.layoutManager
|
||||
}
|
||||
|
||||
open class LinearListLayout(context: Context?) :
|
||||
|
@ -56,7 +60,7 @@ open class LinearListLayout(context: Context?) :
|
|||
startSmoothScroll(linearSmoothScroller)
|
||||
}*/
|
||||
override fun onInterceptFocusSearch(focused: View, direction: Int): View? {
|
||||
val dir = if (orientation == HORIZONTAL) {
|
||||
var dir = if (orientation == HORIZONTAL) {
|
||||
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.
|
||||
|
@ -71,6 +75,9 @@ open class LinearListLayout(context: Context?) :
|
|||
if (direction == View.FOCUS_RIGHT || direction == View.FOCUS_LEFT) return null
|
||||
if (direction == View.FOCUS_DOWN) 1 else -1
|
||||
}
|
||||
if(this.isLayoutRTL) {
|
||||
dir = -dir
|
||||
}
|
||||
|
||||
return try {
|
||||
getPosition(getCorrectParent(focused))?.let { position ->
|
||||
|
|
|
@ -62,6 +62,8 @@ import com.lagradost.cloudstream3.ui.search.SearchHelper
|
|||
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.html
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.isLtr
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.isRtl
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
|
|
|
@ -14,6 +14,7 @@ import android.view.animation.DecelerateInterpolator
|
|||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -30,6 +31,7 @@ import com.lagradost.cloudstream3.mvvm.observe
|
|||
import com.lagradost.cloudstream3.mvvm.observeNullable
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup
|
||||
import com.lagradost.cloudstream3.ui.player.CSPlayerEvent
|
||||
import com.lagradost.cloudstream3.ui.player.ExtractorLinkGenerator
|
||||
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
|
||||
import com.lagradost.cloudstream3.ui.result.ResultFragment.getStoredData
|
||||
|
@ -38,6 +40,8 @@ import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
|||
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.html
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.isLtr
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.isRtl
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||
|
@ -254,6 +258,12 @@ class ResultFragmentTv : Fragment() {
|
|||
resultEpisodesShow.onFocusChangeListener = rightListener
|
||||
resultDescription.onFocusChangeListener = leftListener
|
||||
resultBookmarkButton.onFocusChangeListener = leftListener
|
||||
resultEpisodesShow.setOnClickListener {
|
||||
// toggle, to make it more touch accessable just in case someone thinks that a
|
||||
// tv layout is better but is using a touch device
|
||||
toggleEpisodes(!episodeHolderTv.isVisible)
|
||||
}
|
||||
|
||||
redirectToPlay.setOnFocusChangeListener { _, hasFocus ->
|
||||
if (!hasFocus) return@setOnFocusChangeListener
|
||||
toggleEpisodes(false)
|
||||
|
@ -272,6 +282,12 @@ class ResultFragmentTv : Fragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parallax on background
|
||||
resultFinishLoading.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||
backgroundPosterHolder.translationY = -scrollY.toFloat() * 0.8f
|
||||
})
|
||||
|
||||
redirectToEpisodes.setOnFocusChangeListener { _, hasFocus ->
|
||||
if (!hasFocus) return@setOnFocusChangeListener
|
||||
toggleEpisodes(true)
|
||||
|
@ -290,10 +306,10 @@ class ResultFragmentTv : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
resultEpisodes.layoutManager =
|
||||
LinearListLayout(resultEpisodes.context).apply {
|
||||
resultEpisodes.setLinearListLayout(false)/*.layoutManager =
|
||||
LinearListLayout(resultEpisodes.context, resultEpisodes.isRtl()).apply {
|
||||
setVertical()
|
||||
}
|
||||
}*/
|
||||
|
||||
resultReloadConnectionerror.setOnClickListener {
|
||||
viewModel.load(
|
||||
|
|
|
@ -20,6 +20,9 @@ import android.os.*
|
|||
import android.provider.MediaStore
|
||||
import android.text.Spanned
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.View.LAYOUT_DIRECTION_LTR
|
||||
import android.view.View.LAYOUT_DIRECTION_RTL
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
|
@ -90,6 +93,9 @@ object AppUtils {
|
|||
return if (layoutManager == null || adapter == null) false else layoutManager.findLastCompletelyVisibleItemPosition() < adapter.itemCount - 7 // bit more than 1 to make it more seamless
|
||||
}
|
||||
|
||||
fun View.isLtr() = this.layoutDirection == LAYOUT_DIRECTION_LTR
|
||||
fun View.isRtl() = this.layoutDirection == LAYOUT_DIRECTION_RTL
|
||||
|
||||
fun BottomSheetDialog?.ownHide() {
|
||||
this?.hide()
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_focused="true" android:drawable="@drawable/outline_less" /> <!-- focused -->
|
||||
|
||||
<!--<item android:state_focused="true" android:drawable="@drawable/outline_less" />--> <!-- focused -->
|
||||
</selector>
|
|
@ -83,6 +83,7 @@
|
|||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:elevation="999999999dp"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:clickable="false"
|
||||
|
|
|
@ -126,6 +126,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
|||
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/background_poster_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:visibility="visible">
|
||||
|
@ -152,12 +153,12 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
|||
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/result_finish_loading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/result_finish_loading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
|
@ -415,6 +416,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
|||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/result_tag"
|
||||
style="@style/ChipParent"
|
||||
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
|
@ -479,8 +481,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
|||
android:fadingEdge="horizontal"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:nextFocusUp="@id/result_back"
|
||||
android:nextFocusDown="@id/result_play_movie"
|
||||
android:nextFocusUp="@id/result_episodes_show"
|
||||
android:nextFocusDown="@id/result_recommendations_filter_selection"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="5dp"
|
||||
android:requiresFadingEdge="horizontal"
|
||||
|
@ -502,9 +504,9 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:nextFocusUp="@id/result_episodes"
|
||||
|
||||
android:nextFocusUp="@id/result_cast_items"
|
||||
android:nextFocusDown="@id/result_recommendations_list"
|
||||
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="2"
|
||||
|
@ -525,7 +527,6 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
|
|||
android:layout_width="match_parent"
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/primaryBlackBackground"
|
||||
android:clipToPadding="false"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:nextFocusUp="@id/result_recommendations_filter_selection"
|
||||
|
|
|
@ -122,18 +122,17 @@
|
|||
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">
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:paddingStart="@dimen/navbar_width"
|
||||
android:layout_marginStart="@dimen/navbar_width"
|
||||
android:id="@+id/search_history_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
android:background="?attr/primaryBlackBackground"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:nextFocusLeft="@id/nav_rail_view"
|
||||
android:visibility="visible"
|
||||
|
@ -141,6 +140,7 @@
|
|||
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"
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/outline_drawable_less"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
|
||||
android:nextFocusRight="@id/home_history_remove"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
-->
|
||||
<TextView
|
||||
android:id="@+id/home_history_title"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -29,7 +30,6 @@
|
|||
android:layout_gravity="center_vertical|end"
|
||||
|
||||
android:background="@drawable/outline_drawable_less"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
|
||||
android:nextFocusLeft="@id/home_history_tab"
|
||||
android:padding="10dp"
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
</style>
|
||||
|
||||
<style name="ChipParent">
|
||||
<item name="chipSpacingVertical">5dp</item>
|
||||
<item name="chipSpacingVertical">-5dp</item>
|
||||
<item name="chipSpacingHorizontal">5dp</item>
|
||||
<item name="textColor">?attr/textColor</item>
|
||||
</style>
|
||||
|
|
Loading…
Reference in a new issue