Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Anything running **Android TV** or **Google TV**.
| Platform | What works |
|---|---|
| **Android TV, Google TV** | The debloat and the launcher. |
| **Fire TV** | The debloat half only, since Fire OS will not hand Home to a third-party launcher. And on the 2015 Fire TV Box (Fire OS 5) even that half fails: every `pm` route is refused. |
| **Fire TV** | The debloat half, since Fire OS will not hand Home to a third-party launcher. On a Fire TV Stick 4K (AFTKM, Fire OS 8) the launcher still runs on top of Amazon's Home with `stock_home_redirect`: see [its list](examples/amazon-fire-tv-stick-4k-2023/debloat.txt). And on the 2015 Fire TV Box (Fire OS 5) even that half fails: every `pm` route is refused. |
| **A few China-market TCL sets** | They refuse to install any launcher at all; their lists say so in the header. |
| **Samsung (Tizen), LG (webOS), Roku, Vizio, Hisense VIDAA** | Not supported (no ADB). Turn off the tracking instead: [cleanyour.tv/not-android](https://cleanyour.tv/not-android). See [SHOWCASE.md](SHOWCASE.md#other-platforms-fire-tv-and-lg-webos) for four webOS ports, and [docs/devices/samsung-tizen.md](docs/devices/samsung-tizen.md) for what a 2021 Samsung set answers over Wi-Fi (no ADB, so no `pm disable-user`) if you are weighing a Tizen port. |

Expand Down
9 changes: 8 additions & 1 deletion examples/amazon-fire-tv-stick-4k-2023/debloat.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
# Android 11 (SDK 30), Brazil. The first Fire OS 8 list here: it starts from amazon-fire-tv-stick-lite-2020/ and
# amazon-fire-tv-stick-4k-2018/, and every package new in Fire OS 8 was checked with `dumpsys package` before listing.
#
# FIRE OS: DEBLOAT ONLY. Home is com.amazon.tv.launcher/.ui.HomeActivity_vNext (priority 950); launcher/ does not apply.
# FIRE OS: Home stays com.amazon.tv.launcher/.ui.HomeActivity_vNext (priority 950): Amazon's launcher can't be disabled
# and outranks set-home-activity. The launcher runs on top of it with stock_home_redirect: set it to
# com.amazon.tv.launcher (config.xml or a theme), then install with
# STOCK_LAUNCHER=none HOME_KEY_SERVICE=1 TV_HOST=<stick ip> launcher/build-and-install.sh
# Seen 2026-11-04 on another AFTKM (Fire OS 8.1.8.2): Home from an app starts the launcher 0.14 to 0.36 s after
# Amazon's home (0.8 to 1 s on the first press after an install), and Amazon's home shows until then. Fire OS keeps
# the Home key itself from accessibility services, so the redirect reacts to the window. Settings and Wi-Fi open
# (MainSettingsActivity is started directly, so Amazon's home never shows on the way) and Back returns to the launcher.
#
# Apply: TV_HOST=<stick ip> tools/disable.sh examples/amazon-fire-tv-stick-4k-2023/debloat.txt
# Undo: TV_HOST=<stick ip> tools/restore.sh examples/amazon-fire-tv-stick-4k-2023/debloat.txt com.amazon.tv.launcher
Expand Down
2 changes: 1 addition & 1 deletion examples/amazon-fire-tv-stick-4k-2023/tv.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"tv": "Amazon Fire TV Stick 4K, 2nd gen 2023 (AFTKM, karat), Brazil",
"os": "Fire OS 8.1.8.5 / Android 11",
"brand": "Amazon Fire TV",
"notes": "First Fire OS 8 list, every new package checked with `dumpsys package`: Amazon telemetry, ACR, presence sensing (`ftvspatial`), ad/recommendation feeders, Live tab and Fire TV Channels, Shopping, Music/Photos/Silk/Luna/Kids, Ambient, Alexa extras incl. smart home, calling and Alexa+ skill connections; seven protected packages noted; voice search, Prime Video, casting and mirroring, Matter casting, OTA kept; leave animation scales at 1.0 (at 0.5 the ☰ side menu opens nothing); Pi-hole via static DNS; debloat only"
"notes": "First Fire OS 8 list, every new package checked with `dumpsys package`: Amazon telemetry, ACR, presence sensing (`ftvspatial`), ad/recommendation feeders, Live tab and Fire TV Channels, Shopping, Music/Photos/Silk/Luna/Kids, Ambient, Alexa extras incl. smart home, calling and Alexa+ skill connections; seven protected packages noted; voice search, Prime Video, casting and mirroring, Matter casting, OTA kept; leave animation scales at 1.0 (at 0.5 the ☰ side menu opens nothing); Pi-hole via static DNS; the launcher runs on top of Amazon's Home with stock_home_redirect"
}
8 changes: 6 additions & 2 deletions launcher/build-and-install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,17 @@ function Undo-Handover {
# Order matters, see build-and-install.sh: start the new launcher, disable the stock one, set the
# preference last, then check where Home ended up and put the stock launcher back if it is not ours.
$homeNow = Get-HomeNow
if ($homeNow -notlike 'com.gathin.tvhome/*') {
# -StockLauncher none with -HomeKeyService keeps the stock launcher as Home on purpose (stock_home_redirect sends
# its home screen here): Fire OS, where Amazon's launcher can't be disabled and outranks set-home-activity.
$keptHome = $homeNow -and $homeNow -notlike 'com.gathin.tvhome/*' -and $StockLauncher -eq 'none' -and $HomeKeyService
if ($keptHome) { Write-Host "Home stays $homeNow; HomeKeyService sends it here (stock_home_redirect)." }
elseif ($homeNow -notlike 'com.gathin.tvhome/*') {
$reason = if ($homeNow) { "Home resolves to '$homeNow', not to the new launcher" } else { 'the TV stopped answering while Home was being switched, so the outcome is unknown' }
$restored = Undo-Handover
$hint = if ($homeNow) { ' On Google TV this usually means com.google.android.tungsten.setupwraith also claims Home: add it to your debloat list (see README, Troubleshooting), apply it, then run this script again.' } else { '' }
throw "$reason; $(if ($restored) { "$StockLauncher was re-enabled." } else { 'recovery is incomplete (see above).' })$hint"
}
Write-Host "Home now resolves to $homeNow"
else { Write-Host "Home now resolves to $homeNow" }
# Optional accessibility services, appended to enabled_accessibility_services without dropping others.
function Enable-Service([string]$Name) {
$svc = "com.gathin.tvhome/.$Name"
Expand Down
29 changes: 19 additions & 10 deletions launcher/build-and-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -196,21 +196,30 @@ rollback_home() {
# on Google TV 14 the next candidate is the setup wizard's RecoveryActivity, which crash-loops
# system_server (see examples/xiaomi-tv-a-pro-2026). So check where Home ended up, and put the
# stock launcher back if it is not ours.
# STOCK_LAUNCHER=none with HOME_KEY_SERVICE=1 keeps the stock launcher as Home on purpose: nothing was disabled,
# and stock_home_redirect sends its home screen here. Fire OS needs this: Amazon's launcher can't be disabled and
# outranks set-home-activity, so Home never resolves to this launcher there.
KEPT_HOME=0
if [ "$STOCK_LAUNCHER" = none ] && [ "${HOME_KEY_SERVICE:-0}" = 1 ]; then KEPT_HOME=1; fi
HOME_NOW=$(query_home)
case "$HOME_NOW" in
com.gathin.tvhome/*) echo "Home now resolves to $HOME_NOW" ;;
*)
if [ -z "$HOME_NOW" ]; then
echo "ERROR: the TV stopped answering while Home was being switched, so the outcome is unknown." >&2
if [ -n "$HOME_NOW" ] && [ "$KEPT_HOME" = 1 ]; then
echo "Home stays $HOME_NOW; HomeKeyService sends it here (stock_home_redirect)."
else
echo "ERROR: Home resolves to $HOME_NOW, not to the new launcher." >&2
fi
rollback_home || true
if [ -n "$HOME_NOW" ]; then
echo "On Google TV this usually means com.google.android.tungsten.setupwraith also claims Home: add it to" >&2
echo "your debloat list (see README, Troubleshooting), apply the list, then run this script again." >&2
fi
exit 1 ;;
if [ -z "$HOME_NOW" ]; then
echo "ERROR: the TV stopped answering while Home was being switched, so the outcome is unknown." >&2
else
echo "ERROR: Home resolves to $HOME_NOW, not to the new launcher." >&2
fi
rollback_home || true
if [ -n "$HOME_NOW" ]; then
echo "On Google TV this usually means com.google.android.tungsten.setupwraith also claims Home: add it to" >&2
echo "your debloat list (see README, Troubleshooting), apply the list, then run this script again." >&2
fi
exit 1
fi ;;
esac

# Optional accessibility services. Off unless asked for, and Android switches them off again on
Expand Down
23 changes: 22 additions & 1 deletion tests/test_launcher_handover.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def setUp(self):
self.home = self.base / "home"
self.home.mkdir()

def handover(self, **overrides):
def handover(self, env_extra=None, **overrides):
state = {
"stock_home": STOCK_HOME,
"home": STOCK_HOME,
Expand All @@ -161,6 +161,7 @@ def handover(self, **overrides):
env.update(TV_HOST="192.168.1.50", FAKE_TV_STATE=str(self.state_path),
HOME=str(self.home), JAVA_HOME=str(self.base / "java"),
ANDROID_HOME=str(self.base / "android-sdk"))
env.update(env_extra or {})
if os.name != "posix" or not shutil.which("bash"):
raise unittest.SkipTest("build-and-install.sh is a POSIX script; it needs bash")
result = subprocess.run(
Expand Down Expand Up @@ -223,6 +224,26 @@ def test_failed_rollback_is_not_reported_as_recovered(self):
self.assertTrue(any("pm enable --user 0" in c for c in state["commands"]))
self.assertIsNotNone(self.record())

def test_kept_stock_home_with_redirect_installs(self):
# Fire OS: Amazon's launcher stays Home (priority 950, set-home-activity changes nothing) and can't be
# disabled, so STOCK_LAUNCHER=none and HomeKeyService's stock_home_redirect bring Home here instead.
result, state = self.handover(set_home_fails=True,
env_extra={"STOCK_LAUNCHER": "none", "HOME_KEY_SERVICE": "1"})
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn("Installed.", result.stdout)
self.assertIn("stock_home_redirect", result.stdout)
self.assertTrue(any("enabled_accessibility_services" in c and "HomeKeyService" in c
for c in state["commands"]), "HomeKeyService is enabled")
self.assertFalse(any("pm disable-user" in c for c in state["commands"]))
self.assertFalse(state["stock_disabled"])
self.assertIsNone(self.record())

def test_kept_stock_home_without_redirect_still_stops(self):
result, state = self.handover(set_home_fails=True, env_extra={"STOCK_LAUNCHER": "none"})
self.assertNotEqual(result.returncode, 0)
self.assertIn("not to the new launcher", result.stderr)
self.assertFalse(any("HomeKeyService" in c for c in state["commands"]))


if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion themes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ A new key needs one there in the same pull request.
| `key_filter` | bool | `false` makes the key helpers stop filtering keys while staying enabled. Use it on firmware where any key-filtering accessibility service breaks long-press repeat (TCL Google TV on Android 12): the remote buttons above stop working, and volume repeat comes back |
| `gamepad_passthrough` | bool | `true` makes the key helpers stop filtering keys while a game controller is connected, so games see its buttons (Steam Link with an Xbox controller, Moonlight with two controllers); the remote buttons above do nothing until it disconnects. Needs `key_filter` on. A voice remote that reports a joystick does not count. Off by default |
| `consume_volume_keys` | bool | `true` makes the key helpers swallow Volume Up/Down and repeat a held one themselves (after 500 ms, every 60 ms). Use it on firmware that applies a volume key both before the helper and when the helper hands it back, so one tap moves two steps (TCL G03 / BeyondTV4, Android 11, #1100). Needs `key_filter` on. Off by default |
| `stock_home_redirect` | string | Stock launcher package; if the firmware turns it back on (XGIMI), its home screen is sent straight back here (`HOME_KEY_SERVICE=1`). The Home-key half (a stock home already on screen when Home is pressed, #1116) needs `key_filter` on: with `key_filter: false` it stays on the stock home until another app opens (#2779) |
| `stock_home_redirect` | string | Stock launcher package; if the firmware turns it back on (XGIMI), its home screen is sent straight back here (`HOME_KEY_SERVICE=1`). On Fire OS, where Amazon's launcher stays Home, `com.amazon.tv.launcher` with `STOCK_LAUNCHER=none`. The Home-key half (a stock home already on screen when Home is pressed, #1116) needs `key_filter` on: with `key_filter: false` it stays on the stock home until another app opens (#2779) |
| `power_on_home` | bool | For firmware that opens the Live TV app (`live_tv_package`) by itself at every power-on (Changhong / Homwee): until the first key press after boot or screen-on, that Live TV window is sent back to Home (`HOME_KEY_SERVICE=1`). Ignored with `boot_to_live_tv`, or with Live TV as the Open on start app. Off by default |
| `home_long_press_home` | bool | Every Home press opens Home, a held one too. Android turns a Home press held past 400 ms into ALL_APPS for the system-image launcher, which nothing answers once the stock launcher is disabled, so a firm press on a slow remote did nothing (Philips TPM191E, Android 12, where `long_press_timeout` does not move that limit). Needs `key_filter` (`HOME_KEY_SERVICE=1`), so held keys such as Volume Up/Down stop repeating, confirmed on that Philips; single presses still step. Off by default |
| `power_on_home_always` | bool | Home at every power-on, whatever the TV put in front: the last HDMI input, Live TV or an app, for TVs that come back to the last source (Philips). Unlike `power_on_home`, it does not wait for a window to show: the Home screen comes up about a second after the screen turns on (`HOME_KEY_SERVICE=1`). A key pressed before that wins. Ignored with `boot_to_live_tv`, or with an Open on start app. Off by default |
Expand Down
Loading