mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
fixed tv focus issue
This commit is contained in:
parent
c5f6f36fc7
commit
7e6a28bb99
5 changed files with 118 additions and 14 deletions
|
@ -301,7 +301,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
|
||||
}
|
||||
|
@ -359,18 +360,14 @@ 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 ->
|
||||
|
@ -520,7 +517,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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -416,6 +417,7 @@ class HomeParentItemAdapterPreview(
|
|||
isChecked = checked.contains(watch)
|
||||
}
|
||||
}
|
||||
toggleListHolder?.isGone = visible.isEmpty()
|
||||
}
|
||||
} ?: debugException { "Expected findViewTreeLifecycleOwner" }
|
||||
}
|
||||
|
@ -428,6 +430,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())
|
||||
|
||||
|
|
|
@ -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" />
|
||||
|
||||
|
|
Loading…
Reference in a new issue