From 6a6df8513916d43a54e8d6d4ea843705a25373a8 Mon Sep 17 00:00:00 2001 From: Marek Date: Sun, 5 Apr 2026 19:58:14 +0200 Subject: [PATCH] update --- app/frontend/src/main/AndroidManifest.xml | 5 + .../com/youtubeapp/ui/components/VideoCard.kt | 19 ++- .../youtubeapp/ui/screens/AllVideosScreen.kt | 2 +- .../youtubeapp/ui/screens/DownloadedScreen.kt | 2 +- .../ui/screens/VideoDetailScreen.kt | 159 ++++++++++-------- .../ui/screens/VideoPlayerScreen.kt | 12 +- .../src/main/res/drawable/tv_banner.png | Bin 0 -> 942 bytes .../src/main/res/layout/player_view.xml | 10 ++ 8 files changed, 131 insertions(+), 78 deletions(-) create mode 100644 app/frontend/src/main/res/drawable/tv_banner.png create mode 100644 app/frontend/src/main/res/layout/player_view.xml diff --git a/app/frontend/src/main/AndroidManifest.xml b/app/frontend/src/main/AndroidManifest.xml index 5218c6a..fc6e766 100644 --- a/app/frontend/src/main/AndroidManifest.xml +++ b/app/frontend/src/main/AndroidManifest.xml @@ -3,7 +3,11 @@ + + + + diff --git a/app/frontend/src/main/java/com/youtubeapp/ui/components/VideoCard.kt b/app/frontend/src/main/java/com/youtubeapp/ui/components/VideoCard.kt index a54c84e..ab90037 100644 --- a/app/frontend/src/main/java/com/youtubeapp/ui/components/VideoCard.kt +++ b/app/frontend/src/main/java/com/youtubeapp/ui/components/VideoCard.kt @@ -1,5 +1,7 @@ package com.youtubeapp.ui.components +import androidx.compose.foundation.border +import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxWidth @@ -8,7 +10,13 @@ import androidx.compose.material3.Card import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -17,9 +25,18 @@ import com.youtubeapp.data.Video @Composable fun VideoCard(video: Video, onClick: () -> Unit) { + var isFocused by remember { mutableStateOf(false) } + Card( onClick = onClick, - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() + .onFocusChanged { isFocused = it.isFocused } + .then( + if (isFocused) Modifier.border(3.dp, Color.White, MaterialTheme.shapes.medium) + else Modifier + ) + .focusable() ) { Column { AsyncImage( diff --git a/app/frontend/src/main/java/com/youtubeapp/ui/screens/AllVideosScreen.kt b/app/frontend/src/main/java/com/youtubeapp/ui/screens/AllVideosScreen.kt index e2754f9..95befbb 100644 --- a/app/frontend/src/main/java/com/youtubeapp/ui/screens/AllVideosScreen.kt +++ b/app/frontend/src/main/java/com/youtubeapp/ui/screens/AllVideosScreen.kt @@ -47,7 +47,7 @@ fun AllVideosScreen(viewModel: VideoViewModel, onVideoClick: (Int) -> Unit) { } else -> { LazyVerticalGrid( - columns = GridCells.Fixed(2), + columns = GridCells.Adaptive(minSize = 250.dp), contentPadding = PaddingValues(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), diff --git a/app/frontend/src/main/java/com/youtubeapp/ui/screens/DownloadedScreen.kt b/app/frontend/src/main/java/com/youtubeapp/ui/screens/DownloadedScreen.kt index 70c5e3c..589c8a2 100644 --- a/app/frontend/src/main/java/com/youtubeapp/ui/screens/DownloadedScreen.kt +++ b/app/frontend/src/main/java/com/youtubeapp/ui/screens/DownloadedScreen.kt @@ -52,7 +52,7 @@ fun DownloadedScreen(viewModel: VideoViewModel, onVideoClick: (Int) -> Unit) { } else -> { LazyVerticalGrid( - columns = GridCells.Fixed(2), + columns = GridCells.Adaptive(minSize = 250.dp), contentPadding = PaddingValues(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), diff --git a/app/frontend/src/main/java/com/youtubeapp/ui/screens/VideoDetailScreen.kt b/app/frontend/src/main/java/com/youtubeapp/ui/screens/VideoDetailScreen.kt index 256f535..05e73aa 100644 --- a/app/frontend/src/main/java/com/youtubeapp/ui/screens/VideoDetailScreen.kt +++ b/app/frontend/src/main/java/com/youtubeapp/ui/screens/VideoDetailScreen.kt @@ -1,6 +1,7 @@ package com.youtubeapp.ui.screens import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -73,82 +74,98 @@ fun VideoDetailScreen( if (video == null) { Text("Video nicht gefunden", modifier = Modifier.padding(innerPadding).padding(16.dp)) } else { - Column( + BoxWithConstraints( modifier = Modifier .fillMaxSize() .padding(innerPadding) ) { - AsyncImage( - model = video.thumbnail_url, - contentDescription = video.title, - contentScale = ContentScale.Crop, - modifier = Modifier - .fillMaxWidth() - .aspectRatio(16f / 9f) - ) - Column(modifier = Modifier.padding(16.dp)) { - Text( - text = video.title, - style = MaterialTheme.typography.headlineSmall + val isWide = maxWidth > 600.dp + + Column(modifier = Modifier.fillMaxSize()) { + AsyncImage( + model = video.thumbnail_url, + contentDescription = video.title, + contentScale = ContentScale.Crop, + modifier = Modifier + .fillMaxWidth(if (isWide) 0.5f else 1f) + .aspectRatio(16f / 9f) ) - Spacer(modifier = Modifier.height(4.dp)) - Text( - text = video.youtuber, - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - Spacer(modifier = Modifier.height(4.dp)) - Text( - text = when { - isLocal -> "Lokal gespeichert" - video.is_downloaded -> "Auf Server heruntergeladen" - else -> "Noch nicht heruntergeladen" - }, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - Spacer(modifier = Modifier.height(16.dp)) - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), - modifier = Modifier.fillMaxWidth() - ) { - Button( - onClick = onPlayClick, - modifier = Modifier.weight(1f) - ) { - Icon(Icons.Default.PlayArrow, contentDescription = null) - Text(" Abspielen") - } - if (state.isDownloading) { - OutlinedButton( - onClick = {}, - enabled = false, - modifier = Modifier.weight(1f) - ) { - CircularProgressIndicator( - modifier = Modifier.height(20.dp), - strokeWidth = 2.dp - ) - Text(" Download...") - } - } else if (isLocal) { - OutlinedButton( - onClick = { viewModel.deleteLocalVideo(videoId) }, - modifier = Modifier.weight(1f) - ) { - Icon(Icons.Default.Delete, contentDescription = null) - Text(" Loeschen") - } - } else { - OutlinedButton( - onClick = { viewModel.triggerDownload(videoId) }, - modifier = Modifier.weight(1f) - ) { - Icon(Icons.Default.Download, contentDescription = null) - Text(" Download") - } - } - } + VideoInfo(video, isLocal, state.isDownloading, viewModel, videoId, onPlayClick) + } + } + } + } +} + +@Composable +private fun VideoInfo( + video: com.youtubeapp.data.Video, + isLocal: Boolean, + isDownloading: Boolean, + viewModel: VideoViewModel, + videoId: Int, + onPlayClick: () -> Unit +) { + Column(modifier = Modifier.padding(16.dp)) { + Text( + text = video.title, + style = MaterialTheme.typography.headlineSmall + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = video.youtuber, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = when { + isLocal -> "Lokal gespeichert" + video.is_downloaded -> "Auf Server heruntergeladen" + else -> "Noch nicht heruntergeladen" + }, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + Spacer(modifier = Modifier.height(16.dp)) + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxWidth() + ) { + Button( + onClick = onPlayClick, + modifier = Modifier.weight(1f) + ) { + Icon(Icons.Default.PlayArrow, contentDescription = null) + Text(" Abspielen") + } + if (isDownloading) { + OutlinedButton( + onClick = {}, + enabled = false, + modifier = Modifier.weight(1f) + ) { + CircularProgressIndicator( + modifier = Modifier.height(20.dp), + strokeWidth = 2.dp + ) + Text(" Download...") + } + } else if (isLocal) { + OutlinedButton( + onClick = { viewModel.deleteLocalVideo(videoId) }, + modifier = Modifier.weight(1f) + ) { + Icon(Icons.Default.Delete, contentDescription = null) + Text(" Loeschen") + } + } else { + OutlinedButton( + onClick = { viewModel.triggerDownload(videoId) }, + modifier = Modifier.weight(1f) + ) { + Icon(Icons.Default.Download, contentDescription = null) + Text(" Download") } } } diff --git a/app/frontend/src/main/java/com/youtubeapp/ui/screens/VideoPlayerScreen.kt b/app/frontend/src/main/java/com/youtubeapp/ui/screens/VideoPlayerScreen.kt index a3ad770..622e2f6 100644 --- a/app/frontend/src/main/java/com/youtubeapp/ui/screens/VideoPlayerScreen.kt +++ b/app/frontend/src/main/java/com/youtubeapp/ui/screens/VideoPlayerScreen.kt @@ -1,6 +1,7 @@ package com.youtubeapp.ui.screens import android.app.Activity +import android.view.LayoutInflater import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -14,6 +15,7 @@ import androidx.core.view.WindowInsetsControllerCompat import androidx.media3.common.MediaItem import androidx.media3.exoplayer.ExoPlayer import androidx.media3.ui.PlayerView +import com.youtubeapp.R import com.youtubeapp.ui.viewmodel.VideoViewModel @Composable @@ -49,10 +51,12 @@ fun VideoPlayerScreen(videoId: Int, viewModel: VideoViewModel) { AndroidView( factory = { ctx -> - PlayerView(ctx).apply { - player = exoPlayer - useController = true - } + val view = LayoutInflater.from(ctx).inflate(R.layout.player_view, null) as PlayerView + view.player = exoPlayer + view + }, + update = { view -> + view.player = exoPlayer }, modifier = Modifier.fillMaxSize() ) diff --git a/app/frontend/src/main/res/drawable/tv_banner.png b/app/frontend/src/main/res/drawable/tv_banner.png new file mode 100644 index 0000000000000000000000000000000000000000..453b2a9551676f2b200cd2ab06e6eeb66272c2ff GIT binary patch literal 942 zcmeAS@N?(olHy`uVBq!ia0y~yU~~Yow{S24$*i^{HwFggQcoAhkcv5PFGm)IxJ$HL ztXG}YK5v2)=S`<=6Ta_f6LYq_^+7>;(awuu6HEP%L^?b>9PaMUtq@Wg&)}n$z=4LY zF~u=0eDx!Pvqi{&Mc9C4_2b8?0$v9Md=3b#Vco;z#3|7PL|0FK!(GFGxZi}Aj3Tl1CQTOa-_zu_Jmf5*6i`P%Nwe+7RF zeBj`VTi+JUVR8HalyyD9SH3>AU3hQ7y?Uv4f2X_`ELdf}I{ff{BS!bC$5p0(PG>RJ zEBGyM_$~ijl+j(tz?!K~C7~><@Moh%EYrJxv+9KlR2VlaKk#5mb3ZVV<%}Qqk4B5R zOz-OFgtN@?+N<-A@5iggfc)_M_seVg8#b>jf5u|N^zvMo?s_?i0|#Hf{nt^;Q6ckc z?n1E-`8^qb;pMsZhT}`_%>?@LiZwfv+{~(W^#>n4%M+O9v3~7a6?T2U zG5h*gZ}+t~3w-EVQEmf?0Ay14fxAM;+V%3V$k`fa!vsnXKooTVAsSi3f=CKm8|F8F z;&N-G9V1ljtGk~#AyM{^8Iic)=`+lHwLQ!RWZQvk-3RFkA$Ql!gGra}u498*wY9+> zW`5T0e=LZkkj=QC0c3I3?n+qty88PpHzd*i5f@msOP@h)^*io{ldFC+C2V@Nk733d cxX?ZZ^Ob8|SLzopr0ELokg8%>k literal 0 HcmV?d00001 diff --git a/app/frontend/src/main/res/layout/player_view.xml b/app/frontend/src/main/res/layout/player_view.xml new file mode 100644 index 0000000..73d40e6 --- /dev/null +++ b/app/frontend/src/main/res/layout/player_view.xml @@ -0,0 +1,10 @@ + +