NowPlayingCarScreen.kt
package dev.stave.cartemplate
import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.annotations.ExperimentalCarApi
import androidx.car.app.media.model.MediaPlaybackTemplate
import androidx.car.app.model.Action
import androidx.car.app.model.Header
import androidx.car.app.model.Template
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
/**
* Primary Car App screen — uses [MediaPlaybackTemplate] which lets the
* Auto host render album art, track metadata, and playback controls
* directly from the app's MediaSession. The host controls the layout
* — large album art, transport controls positioned by the host — so
* we get the optimal rendering for each head unit without fighting
* the template system.
*
* Header actions allow navigation to the album detail view (tap the
* "Album" action → push [AlbumDetailCarScreen]).
*/
@OptIn(ExperimentalCarApi::class)
class NowPlayingCarScreen(
carContext: CarContext,
private val bridge: CarSessionMediaBridge,
) : Screen(carContext) {
@Volatile private var connected: Boolean = false
init {
lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
lifecycleScope.launch {
bridge.isConnected.combine(bridge.currentItem) { c, _ -> c }
.collect { c ->
connected = c
invalidate()
}
}
}
})
}
override fun onGetTemplate(): Template {
android.util.Log.i("EarhornCar",
"NowPlayingCarScreen.onGetTemplate connected=$connected")
val headerBuilder = Header.Builder()
.setTitle("Earhorn")
.setStartHeaderAction(Action.APP_ICON)
// "Album" action — drill into album track listing
val currentItem = bridge.snapshotCurrentItem()
if (currentItem != null) {
val albumTitle = currentItem.mediaMetadata.albumTitle?.toString()
if (!albumTitle.isNullOrBlank()) {
headerBuilder.addEndHeaderAction(
Action.Builder()
.setTitle("Album")
.setOnClickListener {
screenManager.push(
AlbumDetailCarScreen(carContext, bridge, currentItem)
)
}
.build()
)
}
}
return MediaPlaybackTemplate.Builder()
.setHeader(headerBuilder.build())
.build()
}
}