Skip to content

Customize Android notifications#

Android needs a persistent notification to keep the app alive during the navigation tracking.

By default a simple notification is shown, which has some basic configuration options.

Configure the basic notification#

Is possible to customize the icon, and the notification title, using the meta-data tag in the AndroidManifest.xml The available keys are:

  • com.citymapper.sdk.notification_title for the title of the notification
  • com.citymapper.sdk.notification_icon for the icon of the notification
    android:value="@string/notification_title" />

    android:resource="@drawable/ic_stat" />

From android oreo (API 26), notifications need a channel, you can change the channel name using the key com.citymapper.sdk.notification_channel

    android:value="@string/notification_channel" />

Show a completely custom notification#

For more control over the notification, implement a custom foreground Service which will run while navigation is active.

Specify the custom service class in the TrackingConfiguration object when starting navigation, or retrieving a NavigableRoute.

val trackingConfiguration = TrackingConfiguration(
  foregroundServiceClass =

It is strongly recommended that this service extends from BaseNavigationForegroundService which gives a simple API for building a notification from the latest RouteProgress information, and automatically handles the service lifecycle.

class CustomForegroundService : BaseNavigationForegroundService<CustomForegroundService.State>() {

  private val channel by lazy {
    val channel = NotificationChannelCompat.Builder(
      .setName("My custom navigation notification")
      .setSound(null, null)



  private val contentIntent by lazy {
      Intent(this, {
      PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE

  override fun createNotificationModel(routeProgress: RouteProgress): State {
    val secondsRemaining = routeProgress.durationSecondsRemaining
      ?: return State(title = "Loading...")

    return State(title = "${secondsRemaining / 60} minutes left!")

  override fun getNotification(notificationModel: State?): Notification {
    return NotificationCompat.Builder(this,
      .setContentTitle(notificationModel?.title ?: "Loading...")

  // To prevent unneccesary notification updates, this must be a data class
  // or correctly implement `equals`
  data class State(val title: String)