mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
testing follow focus on tv
This commit is contained in:
parent
51a6e917b5
commit
3e4a5bdf4c
7 changed files with 164 additions and 62 deletions
|
@ -83,7 +83,7 @@ object CommonActivity {
|
|||
val act = activity ?: return
|
||||
if (message == null) return
|
||||
act.runOnUiThread {
|
||||
showToast(act, message.asString(act), duration)
|
||||
showToast(act, message.asString(act), duration)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -443,63 +443,58 @@ object CommonActivity {
|
|||
|
||||
fun dispatchKeyEvent(act: Activity?, event: KeyEvent?): Boolean? {
|
||||
if (act == null) return null
|
||||
val currentFocus = act.currentFocus
|
||||
|
||||
event?.keyCode?.let { keyCode ->
|
||||
when (event.action) {
|
||||
KeyEvent.ACTION_DOWN -> {
|
||||
if (act.currentFocus != null) {
|
||||
val next = when (keyCode) {
|
||||
KeyEvent.KEYCODE_DPAD_LEFT -> getNextFocus(
|
||||
act,
|
||||
act.currentFocus,
|
||||
FocusDirection.Left
|
||||
)
|
||||
if (currentFocus == null || event.action != KeyEvent.ACTION_DOWN) return@let
|
||||
val next = when (keyCode) {
|
||||
KeyEvent.KEYCODE_DPAD_LEFT -> getNextFocus(
|
||||
act,
|
||||
currentFocus,
|
||||
FocusDirection.Left
|
||||
)
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT -> getNextFocus(
|
||||
act,
|
||||
act.currentFocus,
|
||||
FocusDirection.Right
|
||||
)
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT -> getNextFocus(
|
||||
act,
|
||||
currentFocus,
|
||||
FocusDirection.Right
|
||||
)
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_UP -> getNextFocus(
|
||||
act,
|
||||
act.currentFocus,
|
||||
FocusDirection.Up
|
||||
)
|
||||
KeyEvent.KEYCODE_DPAD_UP -> getNextFocus(
|
||||
act,
|
||||
currentFocus,
|
||||
FocusDirection.Up
|
||||
)
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_DOWN -> getNextFocus(
|
||||
act,
|
||||
act.currentFocus,
|
||||
FocusDirection.Down
|
||||
)
|
||||
KeyEvent.KEYCODE_DPAD_DOWN -> getNextFocus(
|
||||
act,
|
||||
currentFocus,
|
||||
FocusDirection.Down
|
||||
)
|
||||
|
||||
else -> null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (next != null && next != -1) {
|
||||
val nextView = act.findViewById<View?>(next)
|
||||
if (nextView != null) {
|
||||
nextView.requestFocus()
|
||||
keyEventListener?.invoke(Pair(event, true))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_DPAD_CENTER -> {
|
||||
if (act.currentFocus is SearchView || act.currentFocus is SearchView.SearchAutoComplete) {
|
||||
UIHelper.showInputMethod(act.currentFocus?.findFocus())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//println("Keycode: $keyCode")
|
||||
//showToast(
|
||||
// this,
|
||||
// "Got Keycode $keyCode | ${KeyEvent.keyCodeToString(keyCode)} \n ${event?.action}",
|
||||
// Toast.LENGTH_LONG
|
||||
//)
|
||||
if (next != null && next != -1) {
|
||||
val nextView = act.findViewById<View?>(next)
|
||||
if (nextView != null) {
|
||||
nextView.requestFocus()
|
||||
keyEventListener?.invoke(Pair(event, true))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER && (act.currentFocus is SearchView || act.currentFocus is SearchView.SearchAutoComplete)) {
|
||||
UIHelper.showInputMethod(act.currentFocus?.findFocus())
|
||||
}
|
||||
|
||||
//println("Keycode: $keyCode")
|
||||
//showToast(
|
||||
// this,
|
||||
// "Got Keycode $keyCode | ${KeyEvent.keyCodeToString(keyCode)} \n ${event?.action}",
|
||||
// Toast.LENGTH_LONG
|
||||
//)
|
||||
|
||||
}
|
||||
|
||||
if (keyEventListener?.invoke(Pair(event, false)) == true) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.lagradost.cloudstream3
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
@ -11,12 +13,17 @@ 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.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
|
@ -28,12 +35,22 @@ 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
|
||||
|
@ -119,6 +136,7 @@ import com.lagradost.nicehttp.ResponseParser
|
|||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import java.io.File
|
||||
import java.lang.ref.WeakReference
|
||||
import java.net.URI
|
||||
import java.net.URLDecoder
|
||||
import java.nio.charset.Charset
|
||||
|
@ -553,7 +571,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
|
||||
CommonActivity.dispatchKeyEvent(this, event)?.let {
|
||||
return it
|
||||
|
@ -728,6 +745,68 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
}
|
||||
|
||||
var binding: ActivityMainBinding? = null
|
||||
var focusOutline: WeakReference<View> = WeakReference(null)
|
||||
var lastFocus: WeakReference<View> = WeakReference(null)
|
||||
val layoutListener: View.OnLayoutChangeListener =
|
||||
View.OnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
|
||||
updateFocusView(
|
||||
v
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateFocusView(newFocus: View?) {
|
||||
val focusOutline = focusOutline.get() ?: return
|
||||
//lastFocus.get()?.removeOnLayoutChangeListener(layoutListener)
|
||||
val wasGone = focusOutline.isGone
|
||||
focusOutline.isVisible =
|
||||
newFocus != null && newFocus.measuredHeight > 0 && newFocus.measuredWidth > 0 && newFocus.isVisible && newFocus !is MaterialButton
|
||||
|
||||
if (newFocus != null) {
|
||||
lastFocus = WeakReference(newFocus)
|
||||
|
||||
val out = IntArray(2)
|
||||
newFocus.getLocationInWindow(out)
|
||||
val (x, y) = out
|
||||
/*(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)
|
||||
|
||||
|
||||
// val set = AnimationSet(true)
|
||||
if(!wasGone) {
|
||||
(focusOutline.parent as? ViewGroup)?.let {
|
||||
TransitionManager.endTransitions(it)
|
||||
TransitionManager.beginDelayedTransition(
|
||||
it,
|
||||
TransitionSet().addTransition(ChangeBounds())
|
||||
.addTransition(ChangeTransform())
|
||||
.setDuration(100)
|
||||
)
|
||||
}
|
||||
}
|
||||
// ObjectAnimator.ofFloat(focusOutline, "translationX",focusOutline.translationX, x.toFloat()
|
||||
|
||||
|
||||
focusOutline.layoutParams = focusOutline.layoutParams?.apply {
|
||||
width = newFocus.measuredWidth
|
||||
height = newFocus.measuredHeight
|
||||
}
|
||||
focusOutline.translationX = x.toFloat()
|
||||
focusOutline.translationY = y.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
app.initClient(this)
|
||||
|
@ -763,12 +842,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
// backup when we update the app, I don't trust myself to not boot lock users, might want to make this a setting?
|
||||
try {
|
||||
val appVer = BuildConfig.VERSION_NAME
|
||||
val lastAppAutoBackup : String = getKey("VERSION_NAME") ?: ""
|
||||
val lastAppAutoBackup: String = getKey("VERSION_NAME") ?: ""
|
||||
if (appVer != lastAppAutoBackup) {
|
||||
setKey("VERSION_NAME", BuildConfig.VERSION_NAME)
|
||||
backup()
|
||||
}
|
||||
} catch (t : Throwable) {
|
||||
} catch (t: Throwable) {
|
||||
logError(t)
|
||||
}
|
||||
|
||||
|
@ -777,6 +856,15 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
if (isTvSettings()) {
|
||||
val newLocalBinding = ActivityMainTvBinding.inflate(layoutInflater, null, false)
|
||||
setContentView(newLocalBinding.root)
|
||||
focusOutline = WeakReference(newLocalBinding.focusOutline)
|
||||
newLocalBinding.root.viewTreeObserver.addOnGlobalFocusChangeListener { _, newFocus ->
|
||||
// println("refocus $oldFocus -> $newFocus")
|
||||
updateFocusView(newFocus)
|
||||
}
|
||||
newLocalBinding.root.viewTreeObserver.addOnScrollChangedListener {
|
||||
updateFocusView(lastFocus.get())
|
||||
}
|
||||
|
||||
ActivityMainBinding.bind(newLocalBinding.root) // this may crash
|
||||
} else {
|
||||
val newLocalBinding = ActivityMainBinding.inflate(layoutInflater, null, false)
|
||||
|
@ -816,7 +904,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
|
||||
if (PluginManager.checkSafeModeFile()) {
|
||||
normalSafeApiCall {
|
||||
showToast( R.string.safe_mode_file, Toast.LENGTH_LONG)
|
||||
showToast(R.string.safe_mode_file, Toast.LENGTH_LONG)
|
||||
}
|
||||
} else if (lastError == null) {
|
||||
ioSafe {
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<stroke android:width="2dp"
|
||||
android:color="?attr/white"/>
|
||||
<corners
|
||||
<!-- <corners
|
||||
android:bottomLeftRadius="@dimen/rounded_image_radius"
|
||||
android:bottomRightRadius="@dimen/rounded_image_radius"
|
||||
android:topLeftRadius="@dimen/rounded_image_radius"
|
||||
android:topRightRadius="@dimen/rounded_image_radius" />
|
||||
-->
|
||||
|
||||
</shape>
|
|
@ -1,5 +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"/> <!-- focused -->
|
||||
<!--<item android:state_focused="true"
|
||||
android:drawable="@drawable/outline"/>--> <!-- focused -->
|
||||
</selector>
|
|
@ -77,4 +77,21 @@
|
|||
tools:ignore="FragmentTagUsage" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:clickable="false"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/focus_outline"
|
||||
android:src="@drawable/outline"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp">
|
||||
</ImageView>
|
||||
</FrameLayout>
|
||||
|
||||
</FrameLayout>
|
|
@ -35,11 +35,10 @@
|
|||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/home_preview_change_api"
|
||||
style="@style/BlackButton"
|
||||
style="@style/RegularButtonTV"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="top|start"
|
||||
android:layout_marginStart="@dimen/navbar_width"
|
||||
android:backgroundTint="@color/semiWhite"
|
||||
android:minWidth="150dp" />
|
||||
</FrameLayout>
|
||||
|
||||
|
@ -92,7 +91,7 @@
|
|||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/home_preview_play_btt"
|
||||
style="@style/WhiteButton"
|
||||
style="@style/RegularButtonTV"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_margin="0dp"
|
||||
android:minWidth="150dp"
|
||||
|
@ -105,7 +104,7 @@
|
|||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/home_preview_info_btt"
|
||||
style="@style/BlackButton"
|
||||
style="@style/RegularButtonTV"
|
||||
android:layout_width="wrap_content"
|
||||
android:minWidth="150dp"
|
||||
|
||||
|
|
|
@ -85,7 +85,8 @@
|
|||
|
||||
<style name="ChipFilled" parent="@style/Widget.Material3.Chip.Filter">
|
||||
<item name="chipBackgroundColor">@color/chip_color</item>
|
||||
<item name="chipStrokeColor">@color/white_transparent_toggle</item>
|
||||
<!-- <item name="chipStrokeColor">@color/white_transparent_toggle</item>-->
|
||||
<item name="chipStrokeColor">@color/transparent</item>
|
||||
<item name="chipStrokeWidth">2dp</item>
|
||||
<item name="textColor">@color/chip_color_text</item>
|
||||
<item name="android:textColor">@color/chip_color_text</item>
|
||||
|
|
Loading…
Reference in a new issue