Live subway arrival times for New York City, on the Light Phone III. LightOS shows the tool as Subway Times.
Current release: v1.2.0 (tag v1.2.0-build.21). versionCode 3, tool id
com.thelightphone.lightnycsubway.
Part of the gi-os Light App collection.
- Reads the MTA GTFS-realtime feeds directly. There is no middle server and no API key.
- Opens on your saved station and shows two columns, Uptown and Downtown, soonest train first.
- Draws the real route bullets, so a
6reads as a6and not as a letter in a box. - Merges platforms by MTA Complex ID, so a transfer station appears once instead of four times.
- Searches the whole system from a bundled catalog of 445 stations, held in
assets/stations.json. - Sorts the station list by distance under Near me, and filters it by borough.
- Stores the chosen station on the device. The tool asks for
INTERNET, and for coarse and fine location so that Near me works. It asks for nothing else.
Turning the phone's wheel scrolls the board, the station page and the search results. A search for "st" returns most of the system, so that last one is the reason it is here.
Nothing else has to be installed for it. Light patched
/system/usr/keylayout/Generic.kl, so a notch is an ordinary key event that lands in
whichever app holds focus, and this tool reads it itself. No companion service, no
permission, no root.
A light-sdk tool has no activity of its own to override, but the SDK hands every key that
is not Back or Home to the screen on top of the back stack before it forwards anything to
LightOS. So each of the three screens claims the two wheel scancodes — LightOS relabels
them WHEEL_CCW and WHEEL_CW — in onKeyDown and onKeyUp, and the notch reaches the
scroll view through a flow rather than through the screen reaching into its own UI.
Claiming both halves of the notch is what stops the turn carrying on to the server as a
brightness change. Notches arrive faster than a frame, so each one becomes a debt that a
share of gets paid off per frame, and the first notch after a pause is held back, because
the wheel sits under a thumb.
The wheel click and the camera button are left alone. Those belong to LightControl, which is optional and owns them across the phone: hold the wheel in and turn for brightness, tap it for the flashlight, press the camera button for the camera. Each is rebindable, tap and hold separately, to any installed app, and apps that handle no wheel keys of their own get brightness or a synthetic-swipe scroll out of it. The long version is in LightNews.
Installing it changes nothing in here, deliberately. The tool ships as
com.thelightphone.lightnycsubway, and LightControl treats every com.thelightphone. id
as hands-off, because a key an SDK tool does not claim is forwarded to LightOS, which
already does the sensible thing with it — a bare turn becomes brightness. A service in the
middle would be taking that away rather than adding anything. The trade is that
LightControl's button bindings do not reach this tool: the click and the camera button keep
whatever LightOS does with them. Scrolling is unaffected either way, which is the part that
matters here.
If you want LightControl for the rest of the phone:
# Optional: LightControl, for brightness, the flashlight and the camera button
adb install -r LightControl-v1.0.x.apk
# The key service. NOTE: this setting is a list, and this command REPLACES it —
# if you also run LightVoice's push-to-talk, colon-join both components instead.
adb shell settings put secure enabled_accessibility_services \
com.gios.lightcontrol/com.gios.lightcontrol.keys.ControlService
adb shell settings put secure accessibility_enabled 1
# Brightness, and the level readout + opening apps from the service
adb shell appops set com.gios.lightcontrol WRITE_SETTINGS allow
adb shell appops set com.gios.lightcontrol SYSTEM_ALERT_WINDOW allowThe current build is at LightControl/releases/latest.
The station catalog parses once, off the main thread, and then caches process-wide. An
earlier build parsed it during composition and the phone showed a black screen at
launch. StationCatalog.load now takes an asset reader and is safe to call from any
thread, more than once.
Each station maps to the set of MTA feeds that serve it. ArrivalsRepository requests
those feeds at the same time, drops trains that already left, removes duplicates, and
sorts by time. A feed that fails returns an empty list rather than an error, so one bad
feed does not empty the board.
This is the light-sdk tree with one tool added. Upstream code stays where upstream put it, which keeps a rebase cheap.
| Path | What it is |
|---|---|
examples/LightNYCSubway/ |
The tool |
examples/LightNYCSubway/src/main/kotlin/.../ArrivalsRepository.kt |
Feed fetch and GTFS-realtime decode |
examples/LightNYCSubway/src/main/kotlin/.../StationCatalog.kt |
Cached station parse |
examples/LightNYCSubway/src/main/kotlin/.../hw/ |
Wheel keys and the notch-to-scroller bus |
examples/LightNYCSubway/src/main/kotlin/.../HomeScreen.kt |
Uptown and Downtown columns |
examples/LightNYCSubway/src/main/kotlin/.../StationScreen.kt |
Station search and selection |
examples/LightNYCSubway/src/main/assets/stations.json |
Station and stop catalog |
examples/LightNYCSubway/lighttool.toml |
Tool identity, version and permissions |
sdk/, plugin/, builder/, docs/, other examples/ |
Upstream light-sdk |
./gradlew :examples:LightNYCSubway:assembleDebugYou need JDK 17 and the Android SDK. The SDK client libraries come from GitHub Packages, so set a token with package-read access first:
export GITHUB_ACTOR=<your-username>
export GITHUB_TOKEN=<token-with-read:packages>You can put gpr.user and gpr.key in local.properties instead. Never commit either
one.
.github/workflows/build-apk.yml builds the tool on each push to main that touches
examples/LightNYCSubway/, and attaches the APK to a GitHub Release.
To run against the LightOS emulator, set serverPackage = "com.thelightphone.sdk.emulator"
in examples/LightNYCSubway/lighttool.toml. Set it back to com.lightos before a device
build. The SDK system-app guide covers the one-time emulator
setup.
![]() Starred stations, live arrivals |
![]() Browse by borough |
Taken on a Light Phone III against the live MTA feeds.
- lightphone/light-sdk is the base of this repository. The whole tree is a fork. The Light Phone team wrote the SDK client, the Gradle plugin, the lint rules, the builder and the emulator, and released all of it under MIT before the platform was even public. Thank you.
- MTA publishes the GTFS-realtime feeds and the station data as open data. The station catalog derives from it.
- Ktor and OkHttp move the bytes. A hand-rolled decoder reads the protobuf, so the tool stays inside the SDK dependency allowlist.
- The Light Phone, LightOS and Light Phone III are names of The Light Phone. This is an unofficial community tool.
This repo set the pattern the rest of the collection reuses. Fork light-sdk, write the tool into a module the SDK reserves for it, leave upstream alone, and let a GitHub Actions workflow build the APK. LightRSS and LightSolitaire both follow it.
Twelve tools for the Light Phone III, all open source, all built in one run.
| Tool | What it does | Built on |
|---|---|---|
| LightPass | Photograph a movie ticket, keep the stub | Plain Android |
| LightQR | QR scanner, plus a browser generator | Plain Android |
| LightRSS | RSS and Atom reader with images and QR subscribe | light-sdk, fork of zachattack323/LightRSS |
| LightNYCSubway (this repo) | Live MTA subway arrivals | light-sdk fork |
| chat | iMessage over a self-hosted BlueBubbles server | Fork of craigeley/chat |
| LightFog | Fog of World companion, GPS recorder and fog map | Fork of garado/light-topographic |
| LightNonogram | Picross, plus a generator that only ships solvable puzzles | Kotlin generator, light-sdk tool |
| LightSolitaire | Klondike, draw one, unlimited redeals | light-sdk |
| LightFastread | RSVP speed reader for EPUB and MOBI | Fork of fluffyspace/FastRead |
| LightTip | Tip calculator, plus a receipt splitter that reads the line items | Plain Android |
| LightNoise | Twelve synthesized sounds, a two-layer mixer and a sleep timer | Plain Android |
| LightPods | AirPods battery, in-ear and lid status | Plain Android, ports LibrePods |
The Light Phone does not sponsor or endorse any of these. Licences vary per repo.
CI (.github/workflows/build-apk.yml) builds and releases automatically on every push to main
that touches examples/LightNYCSubway/ or the workflow file itself. The tag is
v<versionName>-build.<run number>, with versionName read from lighttool.toml — a push that
only edits docs elsewhere in the repo does not cut a release.
- v1.2.0 (build 21, 2026-07-29) — Hardware wheel scrolls the board, the station list and search
results, with a bump guard against stray brushes (
ArmedNotches). - v1.1.0 (builds 10–19, 2026-07-28) — Local tab: browse stations by borough, or by GPS/IP-based distance under Near me; favorite lines and stations; real subway route bullets; adaptive launcher icon; SDK icons on the bottom bar in place of text.
- v1.0.0 (builds 3–8, 2026-07-27) — Initial release: live GTFS-realtime arrivals over direct MTA
feeds, stations merged by MTA Complex ID, Uptown/Downtown columns, renamed from
nyc-subwayto LightNYCSubway / "Subway Times".
MIT, the same as upstream light-sdk. See LICENSE.

