This commit is contained in:
Marek
2026-04-05 19:58:14 +02:00
parent 6e96c5ee99
commit 6a6df85139
8 changed files with 131 additions and 78 deletions

View File

@@ -3,7 +3,11 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.software.leanback" android:required="false" />
<application <application
android:banner="@drawable/tv_banner"
android:allowBackup="true" android:allowBackup="true"
android:label="YouTube App" android:label="YouTube App"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
@@ -15,6 +19,7 @@
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
</application> </application>

View File

@@ -1,5 +1,7 @@
package com.youtubeapp.ui.components 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.Column
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@@ -8,7 +10,13 @@ import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable 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.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -17,9 +25,18 @@ import com.youtubeapp.data.Video
@Composable @Composable
fun VideoCard(video: Video, onClick: () -> Unit) { fun VideoCard(video: Video, onClick: () -> Unit) {
var isFocused by remember { mutableStateOf(false) }
Card( Card(
onClick = onClick, 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 { Column {
AsyncImage( AsyncImage(

View File

@@ -47,7 +47,7 @@ fun AllVideosScreen(viewModel: VideoViewModel, onVideoClick: (Int) -> Unit) {
} }
else -> { else -> {
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(2), columns = GridCells.Adaptive(minSize = 250.dp),
contentPadding = PaddingValues(8.dp), contentPadding = PaddingValues(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),

View File

@@ -52,7 +52,7 @@ fun DownloadedScreen(viewModel: VideoViewModel, onVideoClick: (Int) -> Unit) {
} }
else -> { else -> {
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(2), columns = GridCells.Adaptive(minSize = 250.dp),
contentPadding = PaddingValues(8.dp), contentPadding = PaddingValues(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),

View File

@@ -1,6 +1,7 @@
package com.youtubeapp.ui.screens package com.youtubeapp.ui.screens
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@@ -73,19 +74,38 @@ fun VideoDetailScreen(
if (video == null) { if (video == null) {
Text("Video nicht gefunden", modifier = Modifier.padding(innerPadding).padding(16.dp)) Text("Video nicht gefunden", modifier = Modifier.padding(innerPadding).padding(16.dp))
} else { } else {
Column( BoxWithConstraints(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(innerPadding) .padding(innerPadding)
) { ) {
val isWide = maxWidth > 600.dp
Column(modifier = Modifier.fillMaxSize()) {
AsyncImage( AsyncImage(
model = video.thumbnail_url, model = video.thumbnail_url,
contentDescription = video.title, contentDescription = video.title,
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth(if (isWide) 0.5f else 1f)
.aspectRatio(16f / 9f) .aspectRatio(16f / 9f)
) )
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)) { Column(modifier = Modifier.padding(16.dp)) {
Text( Text(
text = video.title, text = video.title,
@@ -119,7 +139,7 @@ fun VideoDetailScreen(
Icon(Icons.Default.PlayArrow, contentDescription = null) Icon(Icons.Default.PlayArrow, contentDescription = null)
Text(" Abspielen") Text(" Abspielen")
} }
if (state.isDownloading) { if (isDownloading) {
OutlinedButton( OutlinedButton(
onClick = {}, onClick = {},
enabled = false, enabled = false,
@@ -150,7 +170,4 @@ fun VideoDetailScreen(
} }
} }
} }
}
}
}
} }

View File

@@ -1,6 +1,7 @@
package com.youtubeapp.ui.screens package com.youtubeapp.ui.screens
import android.app.Activity import android.app.Activity
import android.view.LayoutInflater
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
@@ -14,6 +15,7 @@ import androidx.core.view.WindowInsetsControllerCompat
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView import androidx.media3.ui.PlayerView
import com.youtubeapp.R
import com.youtubeapp.ui.viewmodel.VideoViewModel import com.youtubeapp.ui.viewmodel.VideoViewModel
@Composable @Composable
@@ -49,10 +51,12 @@ fun VideoPlayerScreen(videoId: Int, viewModel: VideoViewModel) {
AndroidView( AndroidView(
factory = { ctx -> factory = { ctx ->
PlayerView(ctx).apply { val view = LayoutInflater.from(ctx).inflate(R.layout.player_view, null) as PlayerView
player = exoPlayer view.player = exoPlayer
useController = true view
} },
update = { view ->
view.player = exoPlayer
}, },
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) )

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.media3.ui.PlayerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:surface_type="texture_view"
app:show_buffering="when_playing"
app:use_controller="true" />