feat(android): obwodka fokusu pilota, tablet bez blokady pionu na starcie

RN 0.76 nie przekazuje onFocus/onBlur do JS na Androidzie, a Android TV nie
rysuje systemowego podswietlenia, wiec na Fire TV nie bylo widac, gdzie jest
pilot (zgloszenie 7768ae0c). Globalny nasluch zmiany fokusu w MainActivity
daje widokowi z fokusem foreground z jasna obwodka; w trybie dotykowym nic.
Tablet (sw >= 600 dp) nie dostaje juz pionu w onCreate. Wymaga nowego APK.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
goon-foss 2026-10-15 12:26:55 +02:00
parent ca9ccf29f1
commit bc674c2ec4

View file

@ -5,9 +5,14 @@ import android.content.Context
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.util.TypedValue
import android.view.View
import java.lang.ref.WeakReference
import com.facebook.react.ReactActivity import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate import com.facebook.react.ReactActivityDelegate
@ -37,7 +42,9 @@ import expo.modules.ReactActivityDelegateWrapper
*/ */
open class MainActivity : ReactActivity() { open class MainActivity : ReactActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
if (!isTelevision()) { // Tablet (krotszy bok >= 600 dp) zostaje bez blokady: pion na tablecie trzymanym w poziomie
// to waski pasek, a zdjecie blokady z JS (src/lib/tablet.ts) i tak przychodzi chwile pozniej.
if (!isTelevision() && resources.configuration.smallestScreenWidthDp < 600) {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
} }
// Set the theme to AppTheme BEFORE onCreate to support // Set the theme to AppTheme BEFORE onCreate to support
@ -55,6 +62,40 @@ open class MainActivity : ReactActivity() {
} catch (e: Throwable) { } catch (e: Throwable) {
Log.w("Goon", "Cast niedostepny na tym urzadzeniu: " + e.javaClass.simpleName) Log.w("Goon", "Cast niedostepny na tym urzadzeniu: " + e.javaClass.simpleName)
} }
installFocusRing()
}
// Obwodka wokol elementu z fokusem pilota (Fire TV, Android TV, klawiatura).
//
// Pilot chodzi po zwyklych przyciskach RN, ale nic nie pokazywalo, gdzie jest fokus
// (zgloszenie 7768ae0c, Fire TV): widoki RN maja wlasne tlo bez stanu "focused", Android TV
// nie rysuje systemowego podswietlenia, a RN 0.76 na Androidzie nie przekazuje onFocus/onBlur
// do JS (sprawdzone 2026-10-15 na emulatorze: przycisk ma fokus, zdarzenie nie przychodzi).
// Dlatego natywnie: przy kazdej zmianie fokusu nowy widok dostaje foreground z obwodka,
// a poprzedni odzyskuje swoj. W trybie dotykowym (telefon) nic nie rysujemy.
private var ringView: WeakReference<View>? = null
private var ringPrevForeground: Drawable? = null
private fun installFocusRing() {
window.decorView.viewTreeObserver.addOnGlobalFocusChangeListener { _, newFocus ->
ringView?.get()?.let { it.foreground = ringPrevForeground }
ringView = null
ringPrevForeground = null
if (newFocus == null || newFocus.isInTouchMode) return@addOnGlobalFocusChangeListener
ringPrevForeground = newFocus.foreground
newFocus.foreground = makeFocusRing()
ringView = WeakReference(newFocus)
}
}
private fun makeFocusRing(): Drawable {
val dp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f, resources.displayMetrics)
return GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
cornerRadius = 12 * dp
setStroke((3 * dp).toInt(), 0xFFF5EDE0.toInt())
}
} }
private fun isTelevision(): Boolean { private fun isTelevision(): Boolean {