feat(android): apka instaluje sie i chodzi na Android TV

Prosba z apki 2026-09-16 („make a tv app of this"). Tania wersja: to samo APK,
zeby dalo sie je zainstalowac, znalezc w launcherze telewizora i obslugiwac pilotem
w istniejacym interfejsie. Pelnego interfejsu 10-stopowego (podswietlenie fokusu,
wieksze napisy, obejscie WebView przy hosterach) to NIE daje.

- uses-feature leanback i touchscreen jako NIEwymagane, plus jawne
  screen.portrait=false: blokada pionu sama z siebie dokladala WYMAGANIE ekranu
  pionowego i telewizor uznawal apke za niezgodna
- osobne wejscie TvMainActivity z LEANBACK_LAUNCHER, bez blokady pionu
  (na telefonie nic sie nie zmienia, launcher TV pyta tylko o leanback)
- banner 320x180 w res/drawable (res/drawable-*/ jest w .gitignore)

Dwie slepe uliczki, stad osobna klasa: `setRequestedOrientation` w onCreate to zmiana
konfiguracji, wiec system relaunchowal aktywnosc i na TV zostawal bialy ekran (widok
Reacta nie wracal); orientacja z values-television nie przechodzi, bo manifest czyta
zasoby bez konfiguracji (lint ManifestResource).

Sprawdzone na emulatorze android-34 android-tv (1920x1080): apka jest na liscie
leanback launchera, startuje na caly ekran poziomo, a pilotem da sie przejsc bramke
wieku (fokus chodzi po elementach, CENTER zaznacza), pominac samouczek i przewijac
siatke scen. Odtwarzania na TV nie testowalem, emulator ma software'owe kodeki.
Wersji APK nie ruszam, zmiana pojedzie z najblizszym wydaniem.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
goon-foss 2026-09-17 10:59:19 +02:00
parent c8035c68fa
commit be0f2d7d37
4 changed files with 42 additions and 3 deletions

View file

@ -7,6 +7,16 @@
<uses-permission android:name="android.permission.USE_FINGERPRINT"/> <uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- Android TV (prosba z apki 2026-09-16). Obie cechy jako NIEwymagane: bez tego
telewizor uznaje apke za niezgodna (nie ma ekranu dotykowego), a telefon nie ma
leanbacka. Sam wpis nic nie wymusza, otwiera tylko instalacje na obu rodzajach
sprzetu. Pelnego interfejsu pod pilota to NIE daje, patrz MainActivity. -->
<uses-feature android:name="android.software.leanback" android:required="false"/>
<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
<!-- Blokada pionu w aktywnosci sama z siebie dokladala WYMAGANIE ekranu pionowego
(aapt: uses-feature android.hardware.screen.portrait), przez co telewizor uznawal
apke za niezgodna. Jawny wpis to odkreca. -->
<uses-feature android:name="android.hardware.screen.portrait" android:required="false"/>
<queries> <queries>
<intent> <intent>
<action android:name="android.intent.action.VIEW"/> <action android:name="android.intent.action.VIEW"/>
@ -14,7 +24,7 @@
<data android:scheme="https"/> <data android:scheme="https"/>
</intent> </intent>
</queries> </queries>
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:usesCleartextTraffic="false" android:networkSecurityConfig="@xml/network_security_config"> <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:banner="@drawable/tv_banner" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:usesCleartextTraffic="false" android:networkSecurityConfig="@xml/network_security_config">
<!-- <!--
Expo Updates — ENABLED 2026-05-22 dla public release. Manifest serwowany Expo Updates — ENABLED 2026-05-22 dla public release. Manifest serwowany
przez backend `/expo-updates/manifest` (api.goon-foss.org). Nowe JS-only przez backend `/expo-updates/manifest` (api.goon-foss.org). Nowe JS-only
@ -39,7 +49,7 @@
gdy Chromium probowal podpiac skaner kodow (GOON-3B, 3 crashe od lipca). --> gdy Chromium probowal podpiac skaner kodow (GOON-3B, 3 crashe od lipca). -->
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>
<activity android:name="com.reactnative.googlecast.RNGCExpandedControllerActivity"/> <activity android:name="com.reactnative.googlecast.RNGCExpandedControllerActivity"/>
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait"> <activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait" android:resizeableActivity="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
@ -51,5 +61,14 @@
<data android:scheme="com.goon.mobile"/> <data android:scheme="com.goon.mobile"/>
</intent-filter> </intent-filter>
</activity> </activity>
<!-- Android TV. Bez wpisu LEANBACK_LAUNCHER apka nie pojawia sie na ekranie glownym
telewizora, mimo ze instaluje sie bez bledu. Banner (320x180) jest w <application>.
Osobna aktywnosc, bo tu NIE MA blokady pionu, patrz TvMainActivity. -->
<activity android:name=".TvMainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:resizeableActivity="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
</application> </application>
</manifest> </manifest>

View file

@ -13,7 +13,8 @@ import com.reactnative.googlecast.api.RNGCCastContext
import expo.modules.ReactActivityDelegateWrapper import expo.modules.ReactActivityDelegateWrapper
class MainActivity : ReactActivity() { // open, bo dziedziczy z niej TvMainActivity (to samo, tylko bez blokady pionu).
open class MainActivity : ReactActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
// Set the theme to AppTheme BEFORE onCreate to support // Set the theme to AppTheme BEFORE onCreate to support
// coloring the background, status bar, and navigation bar. // coloring the background, status bar, and navigation bar.

View file

@ -0,0 +1,19 @@
package com.goon.mobile
/**
* Wejscie dla Android TV. Ta sama aktywnosc co na telefonie, rozni sie WYLACZNIE wpisem
* w manifescie: telefon ma blokade pionu, telewizor jej nie ma, bo ekran telewizora jest
* na stale poziomy i pionowa aplikacja wychodzi tam waskim paskiem na srodku.
*
* Dlaczego osobna klasa, a nie jedna aktywnosc:
* - `setRequestedOrientation` w `onCreate` to zmiana konfiguracji, wiec system
* relaunchuje aktywnosc tuz po starcie i na TV zostawal bialy ekran (widok Reacta nie
* wracal; emulator android-34 android-tv, 2026-09-17),
* - orientacja z zasobu (`values-television`) nie przechodzi, bo manifest czyta zasoby
* bez kontekstu konfiguracji (lint ManifestResource),
* - `activity-alias` nie pozwala nadpisac `screenOrientation`.
*
* Telewizorowy launcher pyta o `LEANBACK_LAUNCHER`, wiec na telefonie ta klasa jest
* niewidoczna, a na TV widoczna jest tylko ona.
*/
class TvMainActivity : MainActivity()

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB