next-stop

Next Stop – public transport for Android and Wear OS that rides along with you
[![Android](https://img.shields.io/badge/Android-12%2B-3DDC84?logo=android&logoColor=white)](#getting-started) [![Wear OS](https://img.shields.io/badge/Wear%20OS-3%2B-4285F4?logo=wearos&logoColor=white)](#trip-companion) [![Kotlin](https://img.shields.io/badge/Kotlin-2.2-7F52FF?logo=kotlin&logoColor=white)](https://kotlinlang.org) [![Jetpack Compose](https://img.shields.io/badge/Jetpack%20Compose-Material%203-4285F4?logo=jetpackcompose&logoColor=white)](https://developer.android.com/compose) [![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-1B4FD8)](LICENSE) [![No ads · no tracking](https://img.shields.io/badge/no%20ads%20%C2%B7%20no%20tracking-C8F16B)](#privacy) **Berlin first, all of Germany on board.**
Knows how long a change really takes, where to board, when a faster way turns up and when to get off –
on the phone, on the watch and on a page your friends can follow. [Highlights](#highlights) · [How it works](#how-it-works) · [Getting started](#getting-started) · [Data & attribution](#data-sources-and-attribution) · [Privacy](#privacy) · [Roadmap](#roadmap)

[!NOTE] In active development. The phone app, the Wear OS app, the live page and the route page work; there is no release yet.

Contents - [Highlights](#highlights) · [All features](#all-features) - [How it works](#how-it-works) - [Transfer levels](#transfer-levels) · [Chart view](#chart-view) · [Setup and settings](#setup-and-settings) · [Trip companion](#trip-companion) - [Live link and route link](#live-link-and-route-link) · [Little motions](#little-motions) · [Timetable sources](#timetable-sources) · [From OpenStreetMap to the app](#from-openstreetmap-to-the-app) - [Screenshots](#screenshots) - [Architecture](#architecture) - [Getting started](#getting-started) – build, keys, tests, station data, web pages - [Data sources and attribution](#data-sources-and-attribution) - [Privacy](#privacy) · [Roadmap](#roadmap) · [Contributing](#contributing) · [For developers](#for-developers) · [License](#license)

Highlights

Most journey planners stop caring the moment you pick a connection. Next Stop keeps going – built on open data and community services, with no account and no server of its own.


Transfer levels
Every change rated relaxed, normal, tight or sprint by the walking pace it needs – from live times and the real walk. You set how tight you go.

Exact transfer walks
Measured on the station's OpenStreetMap footways – stairs, lifts and platforms, not straight lines.

Trip companion
Next step, countdown and stops left with live times – in the app and as a live notification.

Boarding hints
Board at the front, middle or rear – and be first at the stairs for your connection.

Shortcut alert
Keeps searching while you ride and speaks up when a faster way turns up.

Get-off reminders
Sound and vibration one stop before you get off, at every change – on the watch too.

Live vehicles
Buses and trains around you on the map, placed from real-time data, with their direction.

Stations up close
Entrances, accesses through buildings, lifts and the stop positions of bus and tram stops.

Made for Wear OS
Tiles, a complication, companion pages, a map and standalone search on the wrist.

Live link
Friends follow your trip in any browser – or get a connection as a step-by-step page to travel themselves.

All of Germany
Station data for all 16 states from OpenStreetMap; timetables nationwide through Transitous.

Private by design
No account, no ads, no tracking – and no server of ours in between.

All features

Plan – transfer levels, through-runs, means of transport, bike combo, chart view | | | |---|---| | **Transfer levels** | Every change is rated by the live times and the walking pace it needs: *relaxed* (plenty of buffer, stroll), *normal* (normal walking pace), *tight* (works at a brisk pace) and *sprint* (only running or with luck, and every 0–1 min change). German UI: Entspannt, Normal, Knapp, Sprint. | | **Slower on the move** | With luggage, a pram or a walking aid every walk counts 1.3 times as long, at every level. | | **Exact transfer walks** | Change walks are measured on the OpenStreetMap footway network of the station. | | **Through-runs** | When your train continues as another line, the app tells you to *stay seated* and which line it continues as. | | **Means of transport** | S-Bahn, U-Bahn, tram, bus, ferry, regional – plus *Long-distance* (ICE, IC, EC, RJ …) and *Coach* (FlixBus …) as categories of their own, globally or per search. | | **Deutschlandticket** | One switch turns long-distance and coach off together (the ticket covers neither); off again turns them back on. | | **With bicycle** | Only show rides that take a bicycle along. | | **Bike combo** | Cycle the first and last mile, take the bike on S-Bahn, U-Bahn, tram and regional trains; buses and coaches only where they carry bikes, otherwise that stretch is cycled. | | **Chart or list** | Connections as columns on a shared time axis, one block per leg, the change minutes in the colour of their wait – or as a classic list. Via stops with an optional stay. | | **Delays** | Three ways to show a late departure: new time + delay (default, `12:09 +2`), planned time + delay (`12:07 +2`), or the coloured new time alone. |
Ride – companion, boarding hints, shortcut alert, reminders | | | |---|---| | **Trip companion** | Follows your trip with live times: the next step, the countdown to the departure, the stops left – in the app and as a live notification. | | **Boarding hints** | *Board at the front / middle / rear* from change-position data; estimated for changes to buses and trams. | | **Shortcut alert** | Searches in the background while you ride and alerts you when a faster way turns up – optionally only equally comfortable ones. | | **Every minute counts** | A per-trip mode: sprint changes allowed, shortcut alert on, searching again from the next stop you can reach. | | **Get-off reminders** | Sound and vibration one stop before you get off and shortly before arrival, at every change. | | **Walk rerouting** | If you leave the planned walk, the way is recomputed and tells you what it costs. |
Map – live vehicles, stations, stop positions, search | | | |---|---| | **Live vehicles** | Buses and trains around you, placed from real-time data, with a live dot and their direction; without a dot they run by the timetable. | | **One card at a time** | Tap a stop, a vehicle or a place and one card shows it – with distances, and a map style of your choice (light, colourful, dark or automatic). | | **Stations up close** | Entrances and exits of U- and S-Bahn stations, accesses through buildings and lifts – shown automatically by zoom, always, or only at the station you picked. | | **Stop positions** | The individual stop positions (*Pos.*) of bus and tram stops, with the lines that stop at each. | | **Search** | Place search over OpenStreetMap data, plus saved places, recent trips and contacts. |
Watch – tiles, complication, companion pages, map, search | | | |---|---| | **Tiles & complication** | *Next departure* and *Favourite* tiles and a complication on the watch face. | | **Companion pages** | Swipe through depart, ride, change and overview pages during a trip; get-off alerts with haptics. | | **Map** | A map of the stops and departures nearby. | | **Standalone search** | Type or speak a destination on the watch; it uses the phone's position or its own. |
Share, data and more – live link, route link, state packs, widgets, settings | | | |---|---| | **As text** | A clean text summary of the connection for any messenger. | | **Live link** | A static web page (`web/live`) where others watch your trip live in the browser. | | **Route link** | A static web page (`web/route`, "So kommst du hin") that turns a shared connection into steps to travel yourself, with live times. | | **State packs** | Station, entrance, change-position and transfer-walk data for all 16 German states, built from OpenStreetMap. | | **Line colours** | From the providers' feeds and OpenStreetMap route relations, picked by place – tram 1 in Mannheim is not tram 1 in Karlsruhe. | | **Many sources** | Berlin/Brandenburg through VBB and BVG, the rest of Germany (long-distance trains included) through Transitous. | | **Widgets** | Departures, favourite, current trip, mini, next appointment and an automatic one that shows what matters now. | | **Disruptions** | Notices for your saved routes, with an optional morning check. | | **Lift status** | accessibility.cloud and DB FaSta, with warnings for lifts out of order on a journey. | | **Fare zone hint** | A tag when a journey reaches VBB zone C. | | **Next appointment** | From your calendar, with when to leave for it. | | **Settings** | Everyday choices up front; data sources, API keys, an own server, the live page address and developer options under *Advanced*. |

How it works

Transfer levels

A change rated by walking pace: Relaxed with 5 or more minutes to spare, Normal with 2 to 4, Tight when it works at a brisk pace, Sprint only running or with luck

A change needs 1 min on the same platform, 3 to another, 5 at large rail stations, or its walk plus 1 min. Beyond that, 5 spare minutes are relaxed and 2 normal. If it still works at a brisk pace – the walk ×0.8 and no buffer, at least 2 min – it is tight; otherwise it is a sprint. A journey found only by a search without any minimum change time counts as tight at least. The rules live in Models.kt (changeNeed, paceTightness, journeyChangeRatings) and WalkPace.kt (brisk and slower walking).

Chart view

Five connections as columns on a time axis, one coloured block per ride, the change minutes between them in green, grey, red, magenta or amber

Setup and settings

Setup sketches: four mini maps for the map style, the transfer level bar, three ways to show a delay, and two vehicles on a loop track, one with a live dot

The same sketches move when you pick an option, and stand still when the system has animations turned off.

Trip companion

Phone and watch showing the next departure with a countdown ring and a boarding hint
Live page on a phone: arrival time, countdown, progress bar and the U8 on the map Route page on a phone: header with the start time, the route on a map, and the first steps

Both are static pages in web/; the trip or connection travels in the link fragment, and the viewer’s browser asks the public timetable APIs itself.

Little motions

A small train of three blue cars glides over a dashed track
Loading
A stop sign hops on the map and its stop positions pop in
Picking a stop
A check mark and a short burst of confetti on arrival
Arrival

Short, soft and never in the way – and off when the system asks for reduced motion.

Timetable sources

Fallback chain: VBB HAFAS ReST, BVG REST, public-transport-enabler, Transitous

The first three share VBB stop ids, so a stop found by one can be asked about at another. Details: SourceChain.kt and FamilySource.kt.

From OpenStreetMap to the app

Geofabrik extract, extract script, build scripts, state packs, app

The pipeline needs no public server: it reads local Geofabrik .osm.pbf files. See tools/README.md.

Screenshots

Screenshots are coming soon. How to take them: docs/screenshots/README.md.

Planned layout | Start | Connections | Companion | Map | Watch | |:---:|:---:|:---:|:---:|:---:| | `start.png` | `connections.png` | `companion.png` | `map.png` | `watch.png` |

Architecture

app/     Android phone app – Kotlin, Jetpack Compose, MapLibre, Glance widgets
wear/    Wear OS app – Compose for Wear OS, tiles, complication, Data Layer link to the phone
web/     Static pages: live tracking, route links and the info site – plain JS, no build step, offline tests
tools/   Python pipeline that turns OpenStreetMap data into the app's station assets
docs/    Design spec, key handling, licences, README images
Tech stack | | Version | |---|---| | Kotlin | 2.2.10 | | Android Gradle Plugin / Gradle | 9.5.1 / 9.6.0 | | Jetpack Compose BOM (Material 3) | 2026.02.01 | | Activity Compose | 1.13.0 | | Kotlin Coroutines | 1.10.2 | | MapLibre Android | 11.8.0 | | Glance (widgets) | 1.2.0 | | WorkManager | 2.12.0 | | OkHttp | 5.4.0 | | Compose for Wear OS | 1.7.1 | | Wear Tiles / ProtoLayout | 1.6.2 / 1.4.2 | | Play Services Wearable | 20.0.1 | | public-transport-enabler | pinned commit via JitPack | Phone: min SDK 31 (Android 12), target SDK 37. Watch: min SDK 30 (Wear OS 3), target SDK 36.

Getting started

You need a recent Android Studio, JDK 17 or newer (the bundled JetBrains Runtime works), Node.js for the web tests and Python 3 for the data tools.

git clone <this repository>
cd next-stop
./gradlew assembleDebug          # phone and watch debug APKs
./gradlew :app:installDebug      # install the phone app on a connected device

Keys (optional)

Some sources need an access key. Copy secrets.properties.example to secrets.properties (gitignored) and fill in what you have; the build turns the values into built-in keys. Without it, keyed sources stay off until a key is entered in the settings – and a key entered there always wins. Never commit keys and do not publish builds with keys built in. More: docs/keys.md.

Building without public-transport-enabler The library comes from JitPack, pinned to a commit. If JitPack fails to build it, set ```properties # gradle.properties nextstop.pte=false ``` The app then compiles `app/src/nopte/` instead of `app/src/pte/` and shows that source as "not included in this build".

Tests

./gradlew test                              # unit tests (phone and watch)
node web/test/live.test.js                  # live page, offline
node web/test/route.test.js                 # route page, offline
python3 tools/test_build_station_asset.py   # data pipeline self-tests
python3 tools/test_build_transfer_walks.py
python3 tools/test_extract_osm_pbf.py

Station data and web pages

Data sources and attribution

Data Source License
Map and station data © OpenStreetMap contributors ODbL 1.0
Map tiles OpenFreeMap OpenStreetMap data, ODbL
Place search Photon (komoot) OpenStreetMap data, ODbL
Walking and cycling paths OSRM (FOSSGIS e.V.) OpenStreetMap data, ODbL
Timetables, Germany Transitous – sources at transitous.org/sources per feed
Timetables, Berlin/Brandenburg VBB HAFAS ReST API, BVG REST (v6.bvg.transport.rest) per provider terms
Change positions vbb-change-positions ODbL
Lift status accessibility.cloud (brokenlifts.org), DB FaSta provider terms
Library public-transport-enabler GPL-3.0
Font Google Sans Flex SIL OFL 1.1 (text)

The app lists every source with its attribution under Settings › About Next Stop › Data sources & attribution, which the small “i” under the location button of the map opens too; “Map data © OpenStreetMap contributors” also stays at the end of the settings, below place search results and as “© OSM” on the watch maps, and the Transitous sources are linked there and in Advanced. Third-party components are listed in NOTICE and in the app (About Next Stop › Open-source licenses, from data/ThirdPartyNotices.kt, which a unit test holds against gradle/libs.versions.toml).

Privacy

The shared trip travels in the link fragment, which browsers never send to the server

Roadmap

Contributing

Bug reports, ideas and pull requests are welcome.

For developers

License

Next Stop is licensed under the GNU General Public License v3.0 – it links public-transport-enabler, which is GPL-3.0 as well. Third-party components and their licences are listed in NOTICE.

App store permission. Next Stop’s own code comes with an additional permission under GPLv3 section 7: it may also be distributed through app stores whose terms add restrictions for users (such as Apple’s App Store), as long as the full source stays freely available under GPLv3 and the app says where to find it. The permission covers only Next Stop’s own code – third-party components such as public-transport-enabler keep their plain licences. Contributions are accepted under GPLv3 with this permission.

Map data © OpenStreetMap contributors, available under the Open Database License.