From 1c01609af95ef3529715321aa8991dfd2c48376b Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 9 Jul 2024 18:20:30 +0200 Subject: [PATCH] add exoplayer --- app/build.gradle.kts | 1 + app/src/main/AndroidManifest.xml | 2 +- .../newplayer/model/VideoPlayerViewModel.kt | 26 +++- .../newplayer/ui/VideoPlayerControllerUI.kt | 115 ++++++++---------- .../net/newpipe/newplayer/ui/VideoPlayerUI.kt | 79 +++++++++++- app/src/main/res/values/test_streams.xml | 5 + gradle/libs.versions.toml | 9 +- 7 files changed, 163 insertions(+), 74 deletions(-) create mode 100644 app/src/main/res/values/test_streams.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 62f0c2c..ef1e53d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -82,6 +82,7 @@ dependencies { implementation(libs.androidx.media3.ui) implementation(libs.hilt.android) implementation(libs.androidx.lifecycle.viewmodel.compose) + implementation(libs.androidx.foundation) ksp(libs.hilt.android.compiler) ksp(libs.androidx.hilt.compiler) implementation(libs.androidx.hilt.navigation.compose) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ab9c102..0a45fa4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,7 +21,7 @@ - + () init { player.prepare() + player.setMediaItem(MediaItem.fromUri(app.getString(R.string.ccc_6502_video))) } override fun onCleared() { player.release() } + + companion object { + val dummy = object : VideoPlayerViewModel { + override val player = null + } + } } \ No newline at end of file diff --git a/app/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt b/app/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt index cb5cd0e..964a3ab 100644 --- a/app/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt +++ b/app/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt @@ -80,11 +80,9 @@ import net.newpipe.newplayer.ui.theme.video_player_onSurface @Composable fun VideoPlayerControllerUI() { Surface( - modifier = Modifier - .fillMaxSize() - .background(Color.White) + modifier = Modifier.fillMaxSize(), color = Color.Transparent ) { - Box() { + Box(modifier = Modifier.background(Color.Transparent)) { TopUI( modifier = Modifier .align(Alignment.TopStart) @@ -127,23 +125,18 @@ private fun TopUI(modifier: Modifier) { onClick = { /*TODO*/ }, contentPadding = PaddingValues(0.dp), colors = ButtonDefaults.buttonColors( - containerColor = Color.Transparent, - contentColor = video_player_onSurface + containerColor = Color.Transparent, contentColor = video_player_onSurface ), ) { Text( - "1080p", - fontWeight = FontWeight.Bold, - modifier = Modifier.padding(0.dp) + "1080p", fontWeight = FontWeight.Bold, modifier = Modifier.padding(0.dp) ) } IconButton( onClick = { /*TODO*/ }, ) { Text( - "1x", - fontWeight = FontWeight.Bold, - modifier = Modifier.padding(0.dp) + "1x", fontWeight = FontWeight.Bold, modifier = Modifier.padding(0.dp) ) } IconButton( @@ -177,25 +170,22 @@ private fun MainMenu() { } Box { - IconButton(onClick = { showMainMenu = true }, - modifier = Modifier.onPlaced { - offsetY = with(pixel_density) { - it.size.height.toDp() - } + IconButton(onClick = { showMainMenu = true }, modifier = Modifier.onPlaced { + offsetY = with(pixel_density) { + it.size.height.toDp() + } - }) { + }) { Icon( imageVector = Icons.Filled.MoreVert, contentDescription = stringResource(R.string.menu_item_more_settings) ) } - DropdownMenu( - modifier = Modifier.align(Alignment.TopStart), + DropdownMenu(modifier = Modifier.align(Alignment.TopStart), offset = DpOffset(x = 0.dp, y = -offsetY), expanded = showMainMenu, onDismissRequest = { showMainMenu = false }) { - DropdownMenuItem( - text = { Text(stringResource(R.string.menu_item_open_in_browser)) }, + DropdownMenuItem(text = { Text(stringResource(R.string.menu_item_open_in_browser)) }, leadingIcon = { Icon( imageVector = Icons.Filled.Language, @@ -203,8 +193,7 @@ private fun MainMenu() { ) }, onClick = { /*TODO*/ showMainMenu = false }) - DropdownMenuItem( - text = { Text(stringResource(R.string.menu_item_share_timestamp)) }, + DropdownMenuItem(text = { Text(stringResource(R.string.menu_item_share_timestamp)) }, leadingIcon = { Icon( imageVector = Icons.Filled.Share, @@ -212,17 +201,13 @@ private fun MainMenu() { ) }, onClick = { /*TODO*/ showMainMenu = false }) - DropdownMenuItem( - text = { Text(stringResource(R.string.mute)) }, - leadingIcon = { - Icon( - imageVector = Icons.AutoMirrored.Filled.VolumeUp, - contentDescription = stringResource(R.string.mute) - ) - }, - onClick = { /*TODO*/ showMainMenu = false }) - DropdownMenuItem( - text = { Text(stringResource(R.string.menu_item_fit_screen)) }, + DropdownMenuItem(text = { Text(stringResource(R.string.mute)) }, leadingIcon = { + Icon( + imageVector = Icons.AutoMirrored.Filled.VolumeUp, + contentDescription = stringResource(R.string.mute) + ) + }, onClick = { /*TODO*/ showMainMenu = false }) + DropdownMenuItem(text = { Text(stringResource(R.string.menu_item_fit_screen)) }, leadingIcon = { Icon( imageVector = Icons.Filled.FitScreen, @@ -230,8 +215,7 @@ private fun MainMenu() { ) }, onClick = { /*TODO*/ showMainMenu = false }) - DropdownMenuItem( - text = { Text(stringResource(R.string.menu_item_sub_titles)) }, + DropdownMenuItem(text = { Text(stringResource(R.string.menu_item_sub_titles)) }, leadingIcon = { Icon( imageVector = Icons.Filled.Subtitles, @@ -239,8 +223,7 @@ private fun MainMenu() { ) }, onClick = { /*TODO*/ showMainMenu = false }) - DropdownMenuItem( - text = { Text(stringResource(R.string.menu_item_language)) }, + DropdownMenuItem(text = { Text(stringResource(R.string.menu_item_language)) }, leadingIcon = { Icon( imageVector = Icons.Filled.Translate, @@ -264,28 +247,22 @@ private fun CenterUI(modifier: Modifier) { horizontalArrangement = Arrangement.SpaceBetween, modifier = modifier ) { - CenterControllButton( - buttonModifier = Modifier.size(80.dp), + CenterControllButton(buttonModifier = Modifier.size(80.dp), iconModifier = Modifier.size(40.dp), icon = Icons.Filled.SkipPrevious, contentDescriptoion = stringResource(R.string.widget_description_previous_stream), - onClick = {} - ) + onClick = {}) - CenterControllButton( - buttonModifier = Modifier.size(80.dp), + CenterControllButton(buttonModifier = Modifier.size(80.dp), iconModifier = Modifier.size(60.dp), icon = Icons.Filled.PlayArrow, contentDescriptoion = stringResource(R.string.widget_description_play), - onClick = {} - ) - CenterControllButton( - buttonModifier = Modifier.size(80.dp), + onClick = {}) + CenterControllButton(buttonModifier = Modifier.size(80.dp), iconModifier = Modifier.size(40.dp), icon = Icons.Filled.SkipNext, contentDescriptoion = stringResource(R.string.widget_description_next_stream), - onClick = {} - ) + onClick = {}) } } @@ -301,15 +278,12 @@ private fun CenterControllButton( onClick = onClick, contentPadding = PaddingValues(0.dp), colors = ButtonDefaults.buttonColors( - containerColor = Color.Transparent, - contentColor = video_player_onSurface + containerColor = Color.Transparent, contentColor = video_player_onSurface ), modifier = buttonModifier ) { Icon( - imageVector = icon, - modifier = iconModifier, - contentDescription = contentDescriptoion + imageVector = icon, modifier = iconModifier, contentDescription = contentDescriptoion ) } } @@ -352,26 +326,43 @@ private fun ViewInFullScreen() { LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) } +@Composable +fun PreviewBackgroundSurface( + content: @Composable () -> Unit +) { + Surface( + modifier = Modifier.fillMaxSize(), color = Color.Black + ) { + content() + } +} + @Preview(device = "spec:width=1080px,height=700px,dpi=440,orientation=landscape") @Composable -fun PlayerUIPreviewEmbeded() { +fun VideoPlayerControllerUIPreviewEmbeded() { VideoPlayerTheme { - VideoPlayerControllerUI() + PreviewBackgroundSurface { + VideoPlayerControllerUI() + } } } @Preview(device = "spec:width=2340px,height=1080px,dpi=440,orientation=landscape") @Composable -fun PlayerUIPreviewLandscape() { +fun VideoPlayerControllerUIPreviewLandscape() { VideoPlayerTheme { - VideoPlayerControllerUI() + PreviewBackgroundSurface { + VideoPlayerControllerUI() + } } } @Preview(device = "spec:width=2340px,height=1080px,dpi=440,orientation=portrait") @Composable -fun PlayerUIPreviewPortrait() { +fun VideoPlayerControllerUIPreviewPortrait() { VideoPlayerTheme { - VideoPlayerControllerUI() + PreviewBackgroundSurface { + VideoPlayerControllerUI() + } } } \ No newline at end of file diff --git a/app/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt b/app/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt index 4584995..28ecf86 100644 --- a/app/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt +++ b/app/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt @@ -20,12 +20,85 @@ package net.newpipe.newplayer.ui +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Surface import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +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.graphics.Color +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.viewinterop.AndroidView import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.media3.ui.PlayerView import net.newpipe.newplayer.model.VideoPlayerViewModel +import net.newpipe.newplayer.model.VideoPlayerViewModelImpl +import net.newpipe.newplayer.ui.theme.VideoPlayerTheme + @Composable -fun VideoPlayerUI() { - val videoPlayerViewModel: VideoPlayerViewModel = hiltViewModel() - VideoPlayerControllerUI() +fun VideoPlayerUI( + viewModel: VideoPlayerViewModel = hiltViewModel() +) { + + var lifecycle by remember { + mutableStateOf(Lifecycle.Event.ON_CREATE) + } + val lifecycleOwner = LocalLifecycleOwner.current + DisposableEffect(lifecycleOwner) { + val observer = LifecycleEventObserver { _, event -> + lifecycle = event + } + lifecycleOwner.lifecycle.addObserver(observer) + + onDispose { + lifecycleOwner.lifecycle.removeObserver(observer) + } + } + + Surface( + modifier = Modifier.fillMaxSize(), + color = Color.Black + ) { + AndroidView( + modifier = Modifier.fillMaxSize(), + factory = { context -> + PlayerView(context).also { + it.player = viewModel.player + it.useController = false + } + }, update = { + when (lifecycle) { + Lifecycle.Event.ON_PAUSE -> { + + } + + Lifecycle.Event.ON_RESUME -> { + + } + + Lifecycle.Event.ON_START -> { + it.player?.play() + } + + else -> Unit + } + }) + + VideoPlayerControllerUI() + } +} + +@Preview(device = "spec:width=1080px,height=700px,dpi=440,orientation=landscape") +@Composable +fun PlayerUIPreviewEmbeded() { + VideoPlayerTheme { + VideoPlayerUI(viewModel = VideoPlayerViewModelImpl.dummy) + } } \ No newline at end of file diff --git a/app/src/main/res/values/test_streams.xml b/app/src/main/res/values/test_streams.xml new file mode 100644 index 0000000..094196f --- /dev/null +++ b/app/src/main/res/values/test_streams.xml @@ -0,0 +1,5 @@ + + + https://ftp.fau.de/cdn.media.ccc.de/congress/2010/mp4-h264-HQ/27c3-4159-en-reverse_engineering_mos_6502.mp4 + https://ftp.fau.de/cdn.media.ccc.de/congress/2010/ogg-audio-only/27c3-4159-en-reverse_engineering_mos_6502.ogg + \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e1e78da..0373e9b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,7 +26,7 @@ junitVersion = "1.2.1" espressoCore = "3.6.1" appcompat = "1.7.0" material = "1.12.0" -activity = "1.9.0" +androidx = "1.9.0" constraintlayout = "2.1.4" material3 = "1.2.1" uiTooling = "1.6.8" @@ -36,7 +36,7 @@ hiltAndroid = "2.51.1" hiltCompiler = "1.2.0" hiltNavigationCompose = "1.2.0" lifecycleViewmodelCompose = "2.8.3" -kspVersion = "1.9.10-1.0.13" +kspVersion = "1.9.0-1.0.13" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -45,9 +45,9 @@ androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "j androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } -androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } +androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "androidx" } androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } -androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity" } +androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidx" } androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "uiTooling" } androidx-material-icons-extended-android = { group = "androidx.compose.material", name = "material-icons-extended-android", version.ref = "materialIconsExtendedAndroid" } @@ -58,6 +58,7 @@ androidx-hilt-compiler = { group = "androidx.hilt", name = "hilt-compiler", vers androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigationCompose" } hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hiltAndroid" } androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" } +androidx-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "uiTooling" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }