From 095472cb7f09c8f7833bf8616fa3586c3d21b021 Mon Sep 17 00:00:00 2001 From: benjamin Date: Wed, 26 Aug 2026 00:10:05 +0200 Subject: [PATCH] Drawn transport icons instead of glyphs (Android renders those as emoji) Co-Authored-By: Claude Fable 5 --- app/build.gradle.kts | 4 +- .../main/java/dev/castarr/tv/ui/CastarrApp.kt | 2 +- .../java/dev/castarr/tv/ui/PlayerScreen.kt | 56 ++++++++++++++++--- 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0f6fae8..aff0440 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "dev.castarr.tv" minSdk = 26 targetSdk = 35 - versionCode = 9 - versionName = "0.4.1" + versionCode = 10 + versionName = "0.5.0" } // Release signing from environment (see ~/.keys/castarr-release.env on the diff --git a/app/src/main/java/dev/castarr/tv/ui/CastarrApp.kt b/app/src/main/java/dev/castarr/tv/ui/CastarrApp.kt index 80dca23..9a643bc 100644 --- a/app/src/main/java/dev/castarr/tv/ui/CastarrApp.kt +++ b/app/src/main/java/dev/castarr/tv/ui/CastarrApp.kt @@ -88,7 +88,7 @@ private fun TopBar(state: AppState) { Spacer(Modifier.width(18.dp)) } TabItem( - if (state.screen == AppState.Screen.LIVE) "⚙ Einstellungen" else "‹ Zurück zu Live", + if (state.screen == AppState.Screen.LIVE) "Einstellungen" else "Zurück zu Live", selected = false, ) { state.screen = if (state.screen == AppState.Screen.LIVE) AppState.Screen.SETTINGS diff --git a/app/src/main/java/dev/castarr/tv/ui/PlayerScreen.kt b/app/src/main/java/dev/castarr/tv/ui/PlayerScreen.kt index 8c9d831..f4d5a76 100644 --- a/app/src/main/java/dev/castarr/tv/ui/PlayerScreen.kt +++ b/app/src/main/java/dev/castarr/tv/ui/PlayerScreen.kt @@ -1,5 +1,6 @@ package dev.castarr.tv.ui +import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.Box @@ -24,6 +25,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Path import androidx.compose.ui.input.key.onKeyEvent import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.text.font.FontWeight @@ -32,6 +34,7 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.viewinterop.AndroidView import androidx.media3.ui.PlayerView import androidx.tv.material3.ClickableSurfaceDefaults +import androidx.tv.material3.LocalContentColor import androidx.tv.material3.Surface import androidx.tv.material3.Text import dev.castarr.tv.AppState @@ -234,11 +237,12 @@ private fun Overlay(state: AppState) { Spacer(Modifier.height(16.dp)) Row(verticalAlignment = Alignment.CenterVertically) { PillButton( - label = if (state.playerState == "paused") "▶ Weiter" else "⏸ Pause", + label = if (state.playerState == "paused") "Weiter" else "Pause", + icon = if (state.playerState == "paused") PillIcon.PLAY else PillIcon.PAUSE, focusRequester = playFocus, ) { state.player.toggle() } Spacer(Modifier.width(10.dp)) - PillButton(label = "■ Beenden") { state.stopPlayback() } + PillButton(label = "Beenden", icon = PillIcon.STOP) { state.stopPlayback() } if (state.audioTracks.size > 1) { Spacer(Modifier.width(10.dp)) val current = state.audioTracks.firstOrNull { it.selected } @@ -256,9 +260,12 @@ private fun Overlay(state: AppState) { } } +private enum class PillIcon { PLAY, PAUSE, STOP } + @Composable private fun PillButton( label: String, + icon: PillIcon? = null, focusRequester: FocusRequester? = null, onClick: () -> Unit, ) { @@ -273,12 +280,45 @@ private fun PillButton( focusedContentColor = CastarrColors.onAccent, ), ) { - Text( - label, - fontFamily = AppFont, - fontSize = 14.sp, - fontWeight = FontWeight.Medium, + Row( modifier = Modifier.padding(horizontal = 18.dp, vertical = 9.dp), - ) + verticalAlignment = Alignment.CenterVertically, + ) { + if (icon != null) { + // Drawn, not typed — glyphs like U+23F8 render as emoji on + // Android and have no business on this screen. + val color = LocalContentColor.current + Canvas(Modifier.size(11.dp)) { + when (icon) { + PillIcon.PLAY -> drawPath( + Path().apply { + moveTo(0f, 0f) + lineTo(size.width, size.height / 2f) + lineTo(0f, size.height) + close() + }, + color, + ) + PillIcon.PAUSE -> { + val bar = size.width * 0.32f + drawRect(color, size = androidx.compose.ui.geometry.Size(bar, size.height)) + drawRect( + color, + topLeft = androidx.compose.ui.geometry.Offset(size.width - bar, 0f), + size = androidx.compose.ui.geometry.Size(bar, size.height), + ) + } + PillIcon.STOP -> drawRect(color) + } + } + Spacer(Modifier.width(9.dp)) + } + Text( + label, + fontFamily = AppFont, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + ) + } } }