From 7e6a28bb996eec749c15a509d62f2516ab334733 Mon Sep 17 00:00:00 2001 From: LagradOst <11805592+LagradOst@users.noreply.github.com> Date: Wed, 2 Aug 2023 21:00:04 +0200 Subject: [PATCH] fixed tv focus issue --- .../lagradost/cloudstream3/CommonActivity.kt | 13 ++- .../lagradost/cloudstream3/MainActivity.kt | 88 ++++++++++++++++++- .../ui/home/HomeParentItemAdapterPreview.kt | 4 + .../main/res/layout/fragment_home_head_tv.xml | 26 +++++- app/src/main/res/layout/fragment_home_tv.xml | 1 + 5 files changed, 118 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt index 3cfde983..4d7afaba 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt @@ -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(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(nextId) ?: return null next = localLook(view, nextId) ?: next - var currentLook: View = view - while (currentLook.findViewById(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)) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index b1f60ad7..d6e275ed 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -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 + ) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt index eb7b6f74..8557d26f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt @@ -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()) diff --git a/app/src/main/res/layout/fragment_home_head_tv.xml b/app/src/main/res/layout/fragment_home_head_tv.xml index d7fbb9e9..d2c20bc4 100644 --- a/app/src/main/res/layout/fragment_home_head_tv.xml +++ b/app/src/main/res/layout/fragment_home_head_tv.xml @@ -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" /> @@ -178,6 +179,8 @@