EARHORNMUSIC UPnP · Android TV · Phones · Android Auto · GNU/Linux
Release 0.5.0

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()
    }
}