|
| 1 | +<div align="center"> |
| 2 | + |
| 3 | +# 🔒 TV App Lock |
| 4 | + |
| 5 | +**Put a PIN on any app on your Android TV. No root. No cloud. No ads. No tracking.** |
| 6 | + |
| 7 | +[](LICENSE) |
| 8 | +[](https://github.com/alexeyklots/tv-app-lock/releases/latest) |
| 9 | +[](https://github.com/alexeyklots/tv-app-lock/actions) |
| 10 | +-3DDC84?logo=android&logoColor=white) |
| 11 | + |
| 12 | + |
| 13 | + |
| 14 | +</div> |
| 15 | + |
| 16 | +TV App Lock watches which app is on the foreground of your Android TV and puts |
| 17 | +a 4-digit PIN gate on top of the apps you choose — SmartTube, YouTube, Lampa, |
| 18 | +whatever you want to keep behind a password. Built for a real living-room |
| 19 | +remote: everything is operated with the D-pad. |
| 20 | + |
| 21 | +## How it works |
| 22 | + |
| 23 | +- A tiny foreground service polls the system usage stats every 250 ms. |
| 24 | +- When a **locked app enters the foreground**, the launcher is parked to Home |
| 25 | + and a PIN screen is raised over it. |
| 26 | +- Correct PIN → you land **inside the app**, and a configurable *grace period* |
| 27 | + (0–60 min or "until reboot") decides how long re-entries stay unlocked. |
| 28 | +- A watchdog re-arms the gate if Android silently refuses the overlay launch, |
| 29 | + and an alarm keep-alive resurrects the service if the firmware kills it. |
| 30 | +- Everything is local. No network permission at all. |
| 31 | + |
| 32 | +### Why not an accessibility service? |
| 33 | + |
| 34 | +Accessibility-based app lockers (most of the Play Store ones) intercept window |
| 35 | +events, but on Android TV they globally change how the remote's OK button is |
| 36 | +delivered — short taps start opening the long-press context menu in the |
| 37 | +launcher. TV App Lock deliberately uses polling instead of accessibility: zero |
| 38 | +interference with input, at the cost of a ~0.5 s window between an app's icon |
| 39 | +and the gate appearing. |
| 40 | + |
| 41 | +## Features |
| 42 | + |
| 43 | +| | | |
| 44 | +|---|---| |
| 45 | +| 🔢 PIN | 4 digits, typed with the remote, auto-saves | |
| 46 | +| 📱 App picker | pick any app visible on the TV home screen (LAUNCHER + LEANBACK_LAUNCHER) | |
| 47 | +| ⏱️ Grace period | 0 / 5 / 15 / 30 / 60 min / until reboot, cycled from the UI | |
| 48 | +| 🔓 State visible | the main screen always shows whether the lock is armed or open | |
| 49 | +| ♻️ Survives reboot | boot receiver + keep-alive; no root needed | |
| 50 | +| 🌍 UI | English & Ukrainian | |
| 51 | + |
| 52 | +## Requirements |
| 53 | + |
| 54 | +- Android TV 6.0+ (API 21), tested on Android 14 |
| 55 | +- ADB available for the one-time install and permission grant |
| 56 | + |
| 57 | +## Install |
| 58 | + |
| 59 | +1. Download `tvapplock-release.apk` from the |
| 60 | + [latest release](https://github.com/alexeyklots/tv-app-lock/releases/latest) |
| 61 | + and verify it against `SHA256SUMS.txt`. |
| 62 | +2. Enable ADB debugging on the TV (Settings → Device preferences → USB |
| 63 | + debugging; on some builds you must first tap "Build version" 7 times in |
| 64 | + About). |
| 65 | +3. From any computer on the same network: |
| 66 | + |
| 67 | +```bash |
| 68 | +adb connect <TV_IP>:5555 |
| 69 | +adb install tvapplock-release.apk |
| 70 | + |
| 71 | +# grant the two permissions the locker needs (one-time setup) |
| 72 | +adb shell appops set com.alexk.tvlock GET_USAGE_STATS allow |
| 73 | +adb shell appops set com.alexk.tvlock SYSTEM_ALERT_WINDOW allow |
| 74 | + |
| 75 | +# start the guardian service |
| 76 | +adb shell am start-foreground-service com.alexk.tvlock/.LockPollService |
| 77 | +``` |
| 78 | + |
| 79 | +4. Open **TV App Lock** on the TV → *Set PIN* → pick the apps to lock. |
| 80 | + |
| 81 | +## Known limitations (honest list) |
| 82 | + |
| 83 | +- **~0.5 s race**: polling is not a hook. The locked app may show its first |
| 84 | + frame before the gate rises. Without root/system signature this is not |
| 85 | + solvable completely. |
| 86 | +- **Bypassable by anyone who can reach Settings**: disabling Usage Access or |
| 87 | + force-stopping the app lifts the lock. This is child resistance (4–10 y.o.), |
| 88 | + not security against a determined adult. |
| 89 | +- **Grace period is a tradeoff**: `0` (ask every time) is the safest; longer |
| 90 | + windows leave the door open for everyone, not just for you. |
| 91 | +- The PIN is stored locally, hashed only by SharedPreferences — fine for a |
| 92 | + family TV, never reuse a real password. |
| 93 | + |
| 94 | +## Build from source |
| 95 | + |
| 96 | +Requires JDK 17 and an Android SDK with platform 34 (or just Docker): |
| 97 | + |
| 98 | +```bash |
| 99 | +./gradlew assembleRelease # with env vars RELEASE_KEYSTORE, RELEASE_STORE_PASSWORD, ... |
| 100 | +# or fully containerized: |
| 101 | +docker build -t tvlock-build . |
| 102 | +docker run --rm -v $PWD:/project -v $PWD/.android:/root/.android \ |
| 103 | + -v $PWD/secrets:/secrets tvlock-build \ |
| 104 | + ./gradlew assembleRelease |
| 105 | +``` |
| 106 | + |
| 107 | +## Privacy |
| 108 | + |
| 109 | +The app requests no internet permission. The usage-stats permission is used |
| 110 | +solely to determine the foreground app, locally and in real time. Nothing ever |
| 111 | +leaves the device. |
| 112 | + |
| 113 | +## License |
| 114 | + |
| 115 | +[MIT](LICENSE) |
0 commit comments