Started at the end of May 2025

A screenshot of the Metron't app, showing a full map of the Metro system with station icons and moving train icons.

What is it?

  • Platform: Android
  • UI Toolkit: Jetpack Compose
  • Language: Kotlin

A lot of people seem to think that the official Pop app for the Tyne and Wear Metro system isn’t the best.

I am one of those people. So, combining my special interest in trains with my programming skills, I decide to learn something entirely different to what I normally do and make my own as practice!

I started just last week at the time of writing, and in that time I have had to learn:

  • The Kotlin programming language
  • Android development practices and architectures
  • The Jetpack Compose library
  • How REST APIs work and how to use Nexus’s API
  • Material Design Guidelines

Do note that it is far from done, I started just last week at the time of writing, and that some icon assets are taken directly from Nexus currently. The latter is an issue that I would definitely sort out before releasing this as a download, if I ever get to that point.

A screenshot showing a screen where one half is a greyed out map and the other half is the schedule for the station shown on the map. A screenshot showing a fullscreen schedule, showing all the trains due to arrive at that Heworth.

Current capabilities:

  • Displays a map of the Tyne and Wear Metro system, drawing each line on an OSM map.
  • Shows live locations of trains currently running, updated from Nexus’s API every ten seconds.
  • Shows stations along the lines.

Tapping a station reveals a schedule window showing infomation for approaching trains:

  • Destination
  • Line colour
  • Train colour
  • Platform
  • Due time
  • An aproximate description of location

This is not an embedded webview of the official Nexus metro map. It is a reimplementation using native Android libraries like osmdroid and osmbonuspack. Using this library with Compose was hard to wrap my head around at first, as it is not built to be directly compatible with it. However, Compose does allow you to make Android Views, the old UI toolkit, inside of Compose. This helped me massively.

@Composable
fun MetroMapView(
    trainStatuses: String,
    onShowBottomSheetChange: (Boolean) -> Unit,
    onSelectedStationIdChange: (String) -> Unit
) {
    val context = LocalContext.current
    val mapView = remember { MapView(context) }

    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { context ->
            val linesKmlDocument = KmlDocument()
            linesKmlDocument.parseKMLStream(context.assets.open("metromap.kml"), null)
            val linesKmlOverlay = linesKmlDocument.mKmlRoot.buildOverlay(mapView, null, null, linesKmlDocument)

            val stationsKmlDocument = KmlDocument()
            stationsKmlDocument.parseKMLStream(context.assets.open("metrostations.kml"), null)
            val stationsKmlOverlay = stationsKmlDocument.mKmlRoot.buildOverlay(mapView, null, StationsKmlStyler(context), stationsKmlDocument)

            (stationsKmlOverlay as FolderOverlay).items.forEachIndexed { index, marker ->
                (marker as Marker).setOnMarkerClickListener { marker, mapView ->
                    onShowBottomSheetChange(true)
                    onSelectedStationIdChange(stationsKmlDocument.mKmlRoot.mItems[index].mId)

                    true
                }
            }

The other options on the navigation bar are currently placeholder and do not function. But I’ll get to that. Hopefully.

Credits