ARTICLE AD BOX
I’m implementing an Android Auto navigation app using the Android for Cars App Library in Kotlin. The app runs on Android Auto and shows the NavigationTemplate with the map, but the routing / maneuver card in the top-left corner never appears.
What I’m trying to do
When I start navigation, I expect Android Auto to show the default routing card (with next turn, distance, etc.) in the top-left of the screen while my map is visible in the background.
However, only the map and my app bar/actions are shown — there is no routing card.
Relevant code
This is a simplified version of my Screen and NavigationTemplate setup:
@RequiresApi(Build.VERSION_CODES.O) class NavigationHomeScreen( carContext: CarContext, position: LatLng, polylineList: List<Map<String, Any?>>?, poiLocations: List<NavSurfaceCallback.PoiMarker> = emptyList() ) : Screen(carContext) { private val appMgr = carContext.getCarService(AppManager::class.java) private val surfaceCb = NavSurfaceCallback( carContext, position, polylineList, poiLocations, refreshPois = { fetchPoiLocations() } ) private val assetHelper: AssetHelper = AssetHelper(carContext) private val navMgr = carContext.getCarService(NavigationManager::class.java) private val mainHandler = Handler(Looper.getMainLooper()) private var navigationStarted = false private var routingInfo: RoutingInfo = placeholderRoutingInfo() init { appMgr.setSurfaceCallback(surfaceCb) // NavigationManager callback ve basit dummy rota yüklemesi mainHandler.post { navMgr.setNavigationManagerCallback(object : NavigationManagerCallback { override fun onStopNavigation() { Log.d("NavigationManagerCallback", "Navigation Stopped") } }) // Fake rota/step/estimate ile NavigationManager'ı başlat startNavigationIfNeeded() fetchPoiLocations() } } private fun actionStrip() = ActionStrip.Builder() .addAction( Action.Builder() .setTitle("+") .setOnClickListener { surfaceCb.zoomIn() } .build() ) .addAction( Action.Builder() .setTitle("-") .setOnClickListener { surfaceCb.zoomOut() } .build() ) .addAction( Action.Builder() .setIcon( assetHelper.getCarIconFromDrawable( R.drawable.my_location, null, false, null ) ) .setOnClickListener { surfaceCb.recenter() } .build() ) .build() private fun mapActionStrip() = ActionStrip.Builder() .addAction(Action.PAN) .build() private fun createStep(text: String, maneuverType: Int): Step = Step.Builder(CarText.create(text)) .setManeuver(Maneuver.Builder(maneuverType).build()) .build() private fun isRoutingInfoSupported(): Boolean { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O } private fun placeholderRoutingInfo(): RoutingInfo { val currentStep = createStep("Başlıyoruz, düz devam", Maneuver.TYPE_DEPART) val nextStep = createStep("300 m sonra sağa dön", Maneuver.TYPE_TURN_NORMAL_RIGHT) return RoutingInfo.Builder() .setCurrentStep(currentStep, Distance.create(300.0, UNIT_METERS)) .setNextStep(nextStep) .build() } private fun buildTempTrip(): Trip { val currentStep = createStep("D100/E5 yönüne devam et", Maneuver.TYPE_STRAIGHT) val nextStep = createStep("300 m sonra sağa dön", Maneuver.TYPE_TURN_NORMAL_RIGHT) routingInfo = RoutingInfo.Builder() .setCurrentStep(currentStep, Distance.create(500.0, UNIT_METERS)) .setNextStep(nextStep) .build() return Trip.Builder() .addStep( currentStep, travelEstimate() ) .setCurrentRoad("Road") .build() } private fun fetchPoiLocations() { AndroidAutoLocationPlugin.getLocations(object : MethodChannel.Result { override fun success(result: Any?) { val markers = mutableListOf<NavSurfaceCallback.PoiMarker>() if (result is List<*>) { result.forEach { raw -> try { if (raw is String) { val item = LocationItem.fromArgs(raw) markers.add( NavSurfaceCallback.PoiMarker( position = LatLng(item.latitude, item.longitude), isAc = item.isAc, isDc = item.isDc ) ) } } catch (_: Exception) { } } } surfaceCb.updatePoiMarkers(markers) } override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { // ignore } override fun notImplemented() { // ignore } }) } @RequiresApi(Build.VERSION_CODES.O) private fun travelEstimate():TravelEstimate { val arrivalTime = ZonedDateTime.now(ZoneId.systemDefault()).plusMinutes(18) val dtw = DateTimeWithZone.create(arrivalTime) return TravelEstimate.Builder( Distance.create(12450.0, Distance.UNIT_METERS), dtw // ETA, DateTimeWithZone olarak ).setRemainingTimeSeconds(18 * 60L) .build() } private fun debugRoutingSupport() { Log.d("RoutingSupport", "SDK: ${Build.VERSION.SDK_INT}") Log.d("RoutingSupport", "Supported: ${isRoutingInfoSupported()}") Log.d("RoutingSupport", "Navigation started: $navigationStarted") } private fun startNavigationIfNeeded() { if (navigationStarted) return navigationStarted = true navMgr.navigationStarted() navMgr.updateTrip(buildTempTrip()) invalidate() } @RequiresApi(Build.VERSION_CODES.O) override fun onGetTemplate(): Template { debugRoutingSupport() startNavigationIfNeeded() return try { val builder = NavigationTemplate.Builder() .setNavigationInfo(routingInfo) .setDestinationTravelEstimate(travelEstimate()) .setActionStrip(actionStrip()) .setMapActionStrip(mapActionStrip()) return builder.build() } catch (t: Throwable) { MessageTemplate.Builder("Hata: ${t::class.java.simpleName}\n${t.message}") .setTitle("Car Demo") .setHeaderAction(Action.APP_ICON) .build() } }CarAppService and Session are set up like in the official documentation, and the app launches correctly on Android Auto.
What I’ve checked
The app has the required androidx.car.app dependencies.
The app is declared as a navigation app in the manifest ().
The NavigationTemplate is being shown (title, actions, and map are visible).
Location permission is granted and I can update the map location.
Question
What exactly is required for the routing / maneuver card in the top-left corner to appear in an Android Auto navigation app?
Do I need to set any specific properties on the NavigationTemplate, Trip, or Route?
Is there additional API (e.g. turn-by-turn / guidance callbacks) that must be implemented?
Are there any restrictions in the emulator or head unit that could prevent the card from showing?
Any pointers or minimal example that shows the routing card correctly would be appreciated.
