Auto-synced .srt subtitle overlay for Netflix on Android — and extensible
to other video apps.
Pick a subtitle file, start the overlay, and your subtitles are drawn on top of the video and locked to the player's real playback position — even after you seek or scrub. Currently tuned for the Netflix app; the same approach works for any Android player that exposes a seek bar (see Compatibility).
⚠️ Not affiliated with, endorsed by, or connected to Netflix or any streaming service. "Netflix" is a trademark of its respective owner.
- 🎯 Auto-sync — subtitles lock to the player's real position; seeks snap instantly
- 🪟 Draws over any app — works even with
FLAG_SECUREplayers (which black out screenshots but not overlays) - 🎚️ Fine control — nudge timing by ±0.1 s; adjustable idle opacity and auto-hide delay
- 🙈 Context-aware — the subtitle hides automatically when another app is in the foreground
- 🌗 Clean dark UI — near-black theme, English + Turkish
- 🔒 Private by design — no account, no ads, no analytics, no network; everything stays on your device
Two Android capabilities make this possible (and they are Android-only — this approach is not possible on iOS):
-
Overlay window — a foreground service draws the subtitle text and a small floating control in a
TYPE_APPLICATION_OVERLAYwindow over other apps (SYSTEM_ALERT_WINDOWpermission). The player usesFLAG_SECURE, which blocks screenshots but does not block an overlay drawn on top. -
Position via AccessibilityService — the player exposes its seek bar in the accessibility tree with a
RangeInfothat reports the absolute playback position in milliseconds. AnAccessibilityServicereads it and the app locks the subtitle clock to that position. Between reads, a monotonic clock interpolates; any seek/scrub snaps the subtitles to the new position.
No DRM is touched and no video is captured — only the app's own text is drawn, and only the public accessibility position is read.
Subly+ currently targets the Netflix Android app (com.netflix.mediaclient),
which is what it's been built and tested against. Nothing about the technique is
Netflix-specific, though — it works for any Android video app that exposes its
playback position as a seek bar in the accessibility tree.
Adding another app is small and a great first contribution:
- Add the app's package to
packageNamesinapp/src/main/res/xml/a11y_config.xml. - Point the package check in
SubtitleSyncService/OverlayServiceat it (or generalize it to a configurable list). - Confirm the player's seek-bar node reports a
RangeInfoin milliseconds.
PRs that add well-tested support for other players are welcome.
| Module | What |
|---|---|
:core |
Pure Kotlin/JVM, unit-tested: SRT parsing, charset detection (UTF-8 → Windows-1254 fallback), timeline lookup, the playback clock. |
:app |
Android: the overlay foreground service, the floating control, the accessibility sync service, and the setup screen. |
Requires JDK 17+ (the Android Studio bundled JBR works well) and the Android SDK.
# point Gradle at a JDK 17+
export JAVA_HOME="/path/to/jdk-17"
# tell Gradle where your Android SDK is
echo "sdk.dir=/path/to/Android/sdk" > local.properties
# run the unit tests
./gradlew :core:test
# build + install a debug APK on a connected device
./gradlew :app:installDebugminSdk 28,targetSdk 36,compileSdk 36(AGP 8.9.1 / Gradle 8.11.1).- Release build:
./gradlew :app:bundleRelease(R8 enabled). Provide a keystore via theRELEASE_STORE_FILE/RELEASE_STORE_PASSWORD/RELEASE_KEY_ALIAS/RELEASE_KEY_PASSWORDGradle properties to sign it.
- Open the app, tap Choose subtitle (.srt) and pick a file.
- Tap Start overlay — grant the "draw over other apps" permission if asked.
- Enable the accessibility service when prompted (a disclosure explains why).
- Play your video — the subtitles lock to the current position and follow along.
The floating control lets you fine-tune timing by ±0.1 s and close the overlay; the setup screen exposes idle opacity and auto-hide delay.
Subly+ uses an AccessibilityService to read the player's playback position.
This is the only way to auto-sync on stock Android. It reads only the
seek-bar position of the configured player and sends nothing off the device.
Because this is not a disability-assistance use of the accessibility API, before the service can be enabled the app shows a prominent in-app disclosure that explains why and how the API is used and asks for explicit consent. Google Play distribution requires review and may restrict accessibility-based tools; sideloading works without any of that.
MIT © CyberTKR