NowPlayingCarScreen.kt
Each line, with the release that last changed it.
| 0.5.0 | 1 | package dev.stave.cartemplate |
| 2 | ||
| 3 | import androidx.car.app.CarContext | |
| 4 | import androidx.car.app.Screen | |
| 5 | import androidx.car.app.annotations.ExperimentalCarApi | |
| 6 | import androidx.car.app.media.model.MediaPlaybackTemplate | |
| 7 | import androidx.car.app.model.Action | |
| 8 | import androidx.car.app.model.Header | |
| 9 | import androidx.car.app.model.Template | |
| 10 | import androidx.lifecycle.DefaultLifecycleObserver | |
| 11 | import androidx.lifecycle.LifecycleOwner | |
| 12 | import androidx.lifecycle.lifecycleScope | |
| 13 | import kotlinx.coroutines.flow.combine | |
| 14 | import kotlinx.coroutines.launch | |
| 15 | ||
| 16 | /** | |
| 17 | * Primary Car App screen — uses [MediaPlaybackTemplate] which lets the | |
| 18 | * Auto host render album art, track metadata, and playback controls | |
| 19 | * directly from the app's MediaSession. The host controls the layout | |
| 20 | * — large album art, transport controls positioned by the host — so | |
| 21 | * we get the optimal rendering for each head unit without fighting | |
| 22 | * the template system. | |
| 23 | * | |
| 24 | * Header actions allow navigation to the album detail view (tap the | |
| 25 | * "Album" action → push [AlbumDetailCarScreen]). | |
| 26 | */ | |
| 27 | @OptIn(ExperimentalCarApi::class) | |
| 28 | class NowPlayingCarScreen( | |
| 29 | carContext: CarContext, | |
| 30 | private val bridge: CarSessionMediaBridge, | |
| 31 | ) : Screen(carContext) { | |
| 32 | ||
| 33 | @Volatile private var connected: Boolean = false | |
| 34 | ||
| 35 | init { | |
| 36 | lifecycle.addObserver(object : DefaultLifecycleObserver { | |
| 37 | override fun onCreate(owner: LifecycleOwner) { | |
| 38 | lifecycleScope.launch { | |
| 39 | bridge.isConnected.combine(bridge.currentItem) { c, _ -> c } | |
| 40 | .collect { c -> | |
| 41 | connected = c | |
| 42 | invalidate() | |
| 43 | } | |
| 44 | } | |
| 45 | } | |
| 46 | }) | |
| 47 | } | |
| 48 | ||
| 49 | override fun onGetTemplate(): Template { | |
| 50 | android.util.Log.i("EarhornCar", | |
| 51 | "NowPlayingCarScreen.onGetTemplate connected=$connected") | |
| 52 | ||
| 53 | val headerBuilder = Header.Builder() | |
| 54 | .setTitle("Earhorn") | |
| 55 | .setStartHeaderAction(Action.APP_ICON) | |
| 56 | ||
| 57 | // "Album" action — drill into album track listing | |
| 58 | val currentItem = bridge.snapshotCurrentItem() | |
| 59 | if (currentItem != null) { | |
| 60 | val albumTitle = currentItem.mediaMetadata.albumTitle?.toString() | |
| 61 | if (!albumTitle.isNullOrBlank()) { | |
| 62 | headerBuilder.addEndHeaderAction( | |
| 63 | Action.Builder() | |
| 64 | .setTitle("Album") | |
| 65 | .setOnClickListener { | |
| 66 | screenManager.push( | |
| 67 | AlbumDetailCarScreen(carContext, bridge, currentItem) | |
| 68 | ) | |
| 69 | } | |
| 70 | .build() | |
| 71 | ) | |
| 72 | } | |
| 73 | } | |
| 74 | ||
| 75 | return MediaPlaybackTemplate.Builder() | |
| 76 | .setHeader(headerBuilder.build()) | |
| 77 | .build() | |
| 78 | } | |
| 79 | } |


