From fdd55bf4a18abd9d10af6afd1945b62550c878a2 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 29 Jul 2024 14:31:03 +0200 Subject: [PATCH] brake videoplayer ui apart --- .../net/newpipe/newplayer/ui/ContentScale.kt | 21 +- .../newplayer/ui/VideoPlayerControllerUI.kt | 345 +----------------- .../net/newpipe/newplayer/ui/VideoPlayerUI.kt | 1 - .../newplayer/ui/videoplayer/BottomUI.kt | 101 +++++ .../newplayer/ui/videoplayer/CenterUI.kt | 134 +++++++ .../newpipe/newplayer/ui/videoplayer/Menu.kt | 126 +++++++ .../newpipe/newplayer/ui/videoplayer/TopUI.kt | 115 ++++++ .../newplayer/ui/videoplayer/TouchUI.kt | 69 ++++ 8 files changed, 575 insertions(+), 337 deletions(-) create mode 100644 new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/BottomUI.kt create mode 100644 new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/CenterUI.kt create mode 100644 new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/Menu.kt create mode 100644 new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/TopUI.kt create mode 100644 new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/TouchUI.kt diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/ContentScale.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/ContentScale.kt index 0d75852..1d4340d 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/ContentScale.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/ContentScale.kt @@ -1,5 +1,24 @@ -package net.newpipe.newplayer.ui +/* NewPlayer + * + * @author Christian Schabesberger + * + * Copyright (C) NewPipe e.V. 2024 + * + * NewPlayer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPlayer. If not, see . + */ +package net.newpipe.newplayer.ui enum class ContentScale { FILL, diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt index 5f4d206..f09e491 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt @@ -20,14 +20,10 @@ package net.newpipe.newplayer.ui -import android.view.MotionEvent + import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.displayCutout @@ -35,63 +31,26 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.systemGestures import androidx.compose.foundation.layout.union import androidx.compose.foundation.layout.waterfall import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.windowInsetsPadding -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.List -import androidx.compose.material.icons.automirrored.filled.MenuBook -import androidx.compose.material.icons.automirrored.filled.VolumeUp -import androidx.compose.material.icons.filled.FitScreen -import androidx.compose.material.icons.filled.Fullscreen -import androidx.compose.material.icons.filled.FullscreenExit -import androidx.compose.material.icons.filled.Language -import androidx.compose.material.icons.filled.MoreVert -import androidx.compose.material.icons.filled.Pause -import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.material.icons.filled.Share -import androidx.compose.material.icons.filled.SkipNext -import androidx.compose.material.icons.filled.SkipPrevious -import androidx.compose.material.icons.filled.Subtitles -import androidx.compose.material.icons.filled.Translate -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Slider import androidx.compose.material3.Surface -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.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.input.pointer.pointerInteropFilter -import androidx.compose.ui.layout.onPlaced -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import net.newpipe.newplayer.R -import net.newpipe.newplayer.ui.seeker.Seeker import net.newpipe.newplayer.ui.theme.VideoPlayerTheme -import net.newpipe.newplayer.ui.theme.video_player_onSurface +import net.newpipe.newplayer.ui.videoplayer.BottomUI +import net.newpipe.newplayer.ui.videoplayer.CenterUI +import net.newpipe.newplayer.ui.videoplayer.TopUI +import net.newpipe.newplayer.ui.videoplayer.TouchUi @Composable fun VideoPlayerControllerUI( @@ -204,294 +163,6 @@ fun VideoPlayerControllerUI( } } - -@OptIn(ExperimentalComposeUiApi::class) -@Composable -private fun TouchUi( - modifier: Modifier, - hideUi: () -> Unit, - showUi: () -> Unit, - uiVissible: Boolean, - fullscreen: Boolean, -) { - Box(modifier = Modifier - .pointerInteropFilter { - when (it.action) { - MotionEvent.ACTION_DOWN -> { - true - } - - MotionEvent.ACTION_UP -> { - if (uiVissible) { - hideUi() - } else { - showUi() - } - true - } - - MotionEvent.ACTION_MOVE -> { - true - } - - else -> false - } - }) { - Surface(color = Color.Transparent, modifier = Modifier.fillMaxSize()) { - - } - } -} - -/////////////////////////////////////////////////////////////////// -// TopUI -/////////////////////////////////////////////////////////////////// - -@Composable -private fun TopUI(modifier: Modifier) { - Row( - modifier = modifier, - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, - ) { - Column(horizontalAlignment = Alignment.Start, modifier = Modifier.weight(1F)) { - Text("The Title", fontSize = 15.sp, fontWeight = FontWeight.Bold) - Text( - "The Channel", - fontSize = 12.sp, - - ) - } - Button( - onClick = { /*TODO*/ }, - contentPadding = PaddingValues(0.dp), - colors = ButtonDefaults.buttonColors( - containerColor = Color.Transparent, contentColor = video_player_onSurface - ), - ) { - Text( - "1080p", fontWeight = FontWeight.Bold, modifier = Modifier.padding(0.dp) - ) - } - IconButton( - onClick = { /*TODO*/ }, - ) { - Text( - "1x", fontWeight = FontWeight.Bold, modifier = Modifier.padding(0.dp) - ) - } - IconButton( - onClick = { /*TODO*/ }, - ) { - Icon( - imageVector = Icons.AutoMirrored.Filled.MenuBook, - contentDescription = stringResource(R.string.widget_description_chapter_selection) - ) - } - IconButton( - onClick = { /*TODO*/ }, - ) { - Icon( - imageVector = Icons.AutoMirrored.Filled.List, - contentDescription = stringResource(R.string.widget_descriptoin_playlist_item_selection) - ) - } - MainMenu() - } -} - - -@Composable -private fun MainMenu() { - var showMainMenu: Boolean by remember { mutableStateOf(false) } - - var pixel_density = LocalDensity.current - - var offsetY by remember { - mutableStateOf(0.dp) - } - - Box { - 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), - offset = DpOffset(x = 0.dp, y = -offsetY), - expanded = showMainMenu, - onDismissRequest = { showMainMenu = false }) { - DropdownMenuItem(text = { Text(stringResource(R.string.menu_item_open_in_browser)) }, - leadingIcon = { - Icon( - imageVector = Icons.Filled.Language, - contentDescription = stringResource(R.string.menu_item_open_in_browser) - ) - }, - onClick = { /*TODO*/ showMainMenu = false }) - DropdownMenuItem(text = { Text(stringResource(R.string.menu_item_share_timestamp)) }, - leadingIcon = { - Icon( - imageVector = Icons.Filled.Share, - contentDescription = stringResource(R.string.menu_item_share_timestamp) - ) - }, - 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)) }, - leadingIcon = { - Icon( - imageVector = Icons.Filled.FitScreen, - contentDescription = stringResource(R.string.menu_item_fit_screen) - ) - }, - onClick = { /*TODO*/ showMainMenu = false }) - DropdownMenuItem(text = { Text(stringResource(R.string.menu_item_sub_titles)) }, - leadingIcon = { - Icon( - imageVector = Icons.Filled.Subtitles, - contentDescription = stringResource(R.string.menu_item_sub_titles) - ) - }, - onClick = { /*TODO*/ showMainMenu = false }) - DropdownMenuItem(text = { Text(stringResource(R.string.menu_item_language)) }, - leadingIcon = { - Icon( - imageVector = Icons.Filled.Translate, - contentDescription = stringResource(R.string.menu_item_language) - ) - }, - onClick = { /*TODO*/ showMainMenu = false }) - - } - } -} - -/////////////////////////////////////////////////////////////////// -// CenterUI -/////////////////////////////////////////////////////////////////// - -@Composable -private fun CenterUI( - modifier: Modifier, - isPlaying: Boolean, - isLoading: Boolean, - play: () -> Unit, - pause: () -> Unit, - nextStream: () -> Unit, - prevStream: () -> Unit -) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, - modifier = modifier, - ) { - if (!isLoading) { - CenterControllButton( - buttonModifier = Modifier.size(80.dp), - iconModifier = Modifier.size(40.dp), - icon = Icons.Filled.SkipPrevious, - contentDescriptoion = stringResource(R.string.widget_description_previous_stream), - onClick = prevStream - ) - - CenterControllButton( - buttonModifier = Modifier.size(80.dp), - iconModifier = Modifier.size(60.dp), - icon = if (isPlaying) Icons.Filled.Pause else Icons.Filled.PlayArrow, - contentDescriptoion = stringResource( - if (isPlaying) R.string.widget_description_pause - else R.string.widget_description_play - ), - onClick = if (isPlaying) pause else play - ) - - CenterControllButton( - buttonModifier = Modifier.size(80.dp), - iconModifier = Modifier.size(40.dp), - icon = Icons.Filled.SkipNext, - contentDescriptoion = stringResource(R.string.widget_description_next_stream), - onClick = nextStream - ) - } - } -} - -@Composable -private fun CenterControllButton( - buttonModifier: Modifier, - iconModifier: Modifier, - icon: ImageVector, - contentDescriptoion: String?, - onClick: () -> Unit -) { - Button( - onClick = onClick, - contentPadding = PaddingValues(0.dp), - colors = ButtonDefaults.buttonColors( - containerColor = Color.Transparent, - ), - modifier = buttonModifier - ) { - Icon( - imageVector = icon, modifier = iconModifier, contentDescription = contentDescriptoion - ) - } -} - -/////////////////////////////////////////////////////////////////// -// BottomUI -/////////////////////////////////////////////////////////////////// - -@Composable -private fun BottomUI( - modifier: Modifier, - isFullscreen: Boolean, - seekPosition: Float, - switchToFullscreen: () -> Unit, - switchToEmbeddedView: () -> Unit, - seekPositionChanged: (Float) -> Unit, - seekingFinished: () -> Unit -) { - - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, - modifier = modifier - ) { - Text("00:06:45") - Seeker( - Modifier.weight(1F), - value = seekPosition, - onValueChange = seekPositionChanged, - onValueChangeFinished = seekingFinished - ) - - //Slider(value = 0.4F, onValueChange = {}, modifier = Modifier.weight(1F)) - - Text("00:09:40") - - IconButton(onClick = if (isFullscreen) switchToEmbeddedView else switchToFullscreen) { - Icon( - imageVector = if (isFullscreen) Icons.Filled.FullscreenExit - else Icons.Filled.Fullscreen, - contentDescription = stringResource(R.string.widget_description_toggle_fullscreen) - ) - } - } -} - /////////////////////////////////////////////////////////////////// // Utils /////////////////////////////////////////////////////////////////// @@ -512,9 +183,13 @@ fun PreviewBackgroundSurface( } } +/////////////////////////////////////////////////////////////////// +// Preview +/////////////////////////////////////////////////////////////////// + @Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") @Composable -fun VideoPlayerControllerUIPreviewEmbeded() { +fun VideoPlayerControllerUIPreviewEmbedded() { VideoPlayerTheme { PreviewBackgroundSurface { VideoPlayerControllerUI(isPlaying = false, diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt index 798eb38..03fc780 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt @@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/BottomUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/BottomUI.kt new file mode 100644 index 0000000..585c1dd --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/BottomUI.kt @@ -0,0 +1,101 @@ +/* NewPlayer + * + * @author Christian Schabesberger + * + * Copyright (C) NewPipe e.V. 2024 + * + * NewPlayer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPlayer. If not, see . + */ + +package net.newpipe.newplayer.ui.videoplayer + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Fullscreen +import androidx.compose.material.icons.filled.FullscreenExit +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import net.newpipe.newplayer.R +import net.newpipe.newplayer.ui.seeker.Seeker +import net.newpipe.newplayer.ui.theme.VideoPlayerTheme + +@Composable +fun BottomUI( + modifier: Modifier, + isFullscreen: Boolean, + seekPosition: Float, + switchToFullscreen: () -> Unit, + switchToEmbeddedView: () -> Unit, + seekPositionChanged: (Float) -> Unit, + seekingFinished: () -> Unit +) { + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = modifier + ) { + Text("00:06:45") + Seeker( + Modifier.weight(1F), + value = seekPosition, + onValueChange = seekPositionChanged, + onValueChangeFinished = seekingFinished + ) + + //Slider(value = 0.4F, onValueChange = {}, modifier = Modifier.weight(1F)) + + Text("00:09:40") + + IconButton(onClick = if (isFullscreen) switchToEmbeddedView else switchToFullscreen) { + Icon( + imageVector = if (isFullscreen) Icons.Filled.FullscreenExit + else Icons.Filled.Fullscreen, + contentDescription = stringResource(R.string.widget_description_toggle_fullscreen) + ) + } + } +} + +/////////////////////////////////////////////////////////////////// +// Preview +/////////////////////////////////////////////////////////////////// + +@Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") +@Composable +fun VideoPlayerControllerBottomUIPreview() { + VideoPlayerTheme { + Surface(color = Color.Black) { + BottomUI( + modifier = Modifier, + isFullscreen = true, + seekPosition = 0.4F, + switchToFullscreen = { }, + switchToEmbeddedView = { }, + seekPositionChanged = {} + ) { + + } + } + } +} \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/CenterUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/CenterUI.kt new file mode 100644 index 0000000..465035a --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/CenterUI.kt @@ -0,0 +1,134 @@ +/* NewPlayer + * + * @author Christian Schabesberger + * + * Copyright (C) NewPipe e.V. 2024 + * + * NewPlayer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPlayer. If not, see . + */ + +package net.newpipe.newplayer.ui.videoplayer + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Pause +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material.icons.filled.SkipNext +import androidx.compose.material.icons.filled.SkipPrevious +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import net.newpipe.newplayer.R +import net.newpipe.newplayer.ui.theme.VideoPlayerTheme + +@Composable +fun CenterUI( + modifier: Modifier, + isPlaying: Boolean, + isLoading: Boolean, + play: () -> Unit, + pause: () -> Unit, + nextStream: () -> Unit, + prevStream: () -> Unit +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = modifier, + ) { + if (!isLoading) { + CenterControllButton( + buttonModifier = Modifier.size(80.dp), + iconModifier = Modifier.size(40.dp), + icon = Icons.Filled.SkipPrevious, + contentDescriptoion = stringResource(R.string.widget_description_previous_stream), + onClick = prevStream + ) + + CenterControllButton( + buttonModifier = Modifier.size(80.dp), + iconModifier = Modifier.size(60.dp), + icon = if (isPlaying) Icons.Filled.Pause else Icons.Filled.PlayArrow, + contentDescriptoion = stringResource( + if (isPlaying) R.string.widget_description_pause + else R.string.widget_description_play + ), + onClick = if (isPlaying) pause else play + ) + + CenterControllButton( + buttonModifier = Modifier.size(80.dp), + iconModifier = Modifier.size(40.dp), + icon = Icons.Filled.SkipNext, + contentDescriptoion = stringResource(R.string.widget_description_next_stream), + onClick = nextStream + ) + } + } +} + +@Composable +private fun CenterControllButton( + buttonModifier: Modifier, + iconModifier: Modifier, + icon: ImageVector, + contentDescriptoion: String?, + onClick: () -> Unit +) { + Button( + onClick = onClick, + contentPadding = PaddingValues(0.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + ), + modifier = buttonModifier + ) { + Icon( + imageVector = icon, modifier = iconModifier, contentDescription = contentDescriptoion + ) + } +} + +/////////////////////////////////////////////////////////////////// +// Preview +/////////////////////////////////////////////////////////////////// + +@Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") +@Composable +fun VideoPlayerControllerUICenterUIPreview() { + VideoPlayerTheme { + Surface(color = Color.Black) { + CenterUI( + modifier = Modifier, + isPlaying = true, + isLoading = false, + play = { }, + pause = { }, + nextStream = { }) { + } + } + } +} \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/Menu.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/Menu.kt new file mode 100644 index 0000000..72c2437 --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/Menu.kt @@ -0,0 +1,126 @@ +/* NewPlayer + * + * @author Christian Schabesberger + * + * Copyright (C) NewPipe e.V. 2024 + * + * NewPlayer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPlayer. If not, see . + */ + +package net.newpipe.newplayer.ui.videoplayer + +import androidx.compose.foundation.layout.Box +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.VolumeUp +import androidx.compose.material.icons.filled.FitScreen +import androidx.compose.material.icons.filled.Language +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material.icons.filled.Share +import androidx.compose.material.icons.filled.Subtitles +import androidx.compose.material.icons.filled.Translate +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +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.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.onPlaced +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.unit.dp +import net.newpipe.newplayer.R + +@Composable +fun DropDownMenu() { + var showMainMenu: Boolean by remember { mutableStateOf(false) } + + var pixel_density = LocalDensity.current + + var offsetY by remember { + mutableStateOf(0.dp) + } + + Box { + 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), + offset = DpOffset(x = 0.dp, y = -offsetY), + expanded = showMainMenu, + onDismissRequest = { showMainMenu = false }) { + DropdownMenuItem(text = { Text(stringResource(R.string.menu_item_open_in_browser)) }, + leadingIcon = { + Icon( + imageVector = Icons.Filled.Language, + contentDescription = stringResource(R.string.menu_item_open_in_browser) + ) + }, + onClick = { /*TODO*/ showMainMenu = false }) + DropdownMenuItem(text = { Text(stringResource(R.string.menu_item_share_timestamp)) }, + leadingIcon = { + Icon( + imageVector = Icons.Filled.Share, + contentDescription = stringResource(R.string.menu_item_share_timestamp) + ) + }, + 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)) }, + leadingIcon = { + Icon( + imageVector = Icons.Filled.FitScreen, + contentDescription = stringResource(R.string.menu_item_fit_screen) + ) + }, + onClick = { /*TODO*/ showMainMenu = false }) + DropdownMenuItem(text = { Text(stringResource(R.string.menu_item_sub_titles)) }, + leadingIcon = { + Icon( + imageVector = Icons.Filled.Subtitles, + contentDescription = stringResource(R.string.menu_item_sub_titles) + ) + }, + onClick = { /*TODO*/ showMainMenu = false }) + DropdownMenuItem(text = { Text(stringResource(R.string.menu_item_language)) }, + leadingIcon = { + Icon( + imageVector = Icons.Filled.Translate, + contentDescription = stringResource(R.string.menu_item_language) + ) + }, + onClick = { /*TODO*/ showMainMenu = false }) + + } + } +} \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/TopUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/TopUI.kt new file mode 100644 index 0000000..40f7f2f --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/TopUI.kt @@ -0,0 +1,115 @@ +/* NewPlayer + * + * @author Christian Schabesberger + * + * Copyright (C) NewPipe e.V. 2024 + * + * NewPlayer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPlayer. If not, see . + */ + +package net.newpipe.newplayer.ui.videoplayer + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.List +import androidx.compose.material.icons.automirrored.filled.MenuBook +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import net.newpipe.newplayer.R +import net.newpipe.newplayer.ui.theme.VideoPlayerTheme +import net.newpipe.newplayer.ui.theme.video_player_onSurface + +@Composable +fun TopUI(modifier: Modifier) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Column(horizontalAlignment = Alignment.Start, modifier = Modifier.weight(1F)) { + Text("The Title", fontSize = 15.sp, fontWeight = FontWeight.Bold) + Text( + "The Channel", + fontSize = 12.sp, + + ) + } + Button( + onClick = { /*TODO*/ }, + contentPadding = PaddingValues(0.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color.Transparent, contentColor = video_player_onSurface + ), + ) { + Text( + "1080p", fontWeight = FontWeight.Bold, modifier = Modifier.padding(0.dp) + ) + } + IconButton( + onClick = { /*TODO*/ }, + ) { + Text( + "1x", fontWeight = FontWeight.Bold, modifier = Modifier.padding(0.dp) + ) + } + IconButton( + onClick = { /*TODO*/ }, + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.MenuBook, + contentDescription = stringResource(R.string.widget_description_chapter_selection) + ) + } + IconButton( + onClick = { /*TODO*/ }, + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.List, + contentDescription = stringResource(R.string.widget_descriptoin_playlist_item_selection) + ) + } + DropDownMenu() + } +} + +/////////////////////////////////////////////////////////////////// +// Preview +/////////////////////////////////////////////////////////////////// + +@Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") +@Composable +fun VideoPlayerControllerTopUIPreview() { + VideoPlayerTheme { + Surface(color = Color.Black) { + TopUI(modifier = Modifier) + } + } +} \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/TouchUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/TouchUI.kt new file mode 100644 index 0000000..415ffed --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/TouchUI.kt @@ -0,0 +1,69 @@ +/* NewPlayer + * + * @author Christian Schabesberger + * + * Copyright (C) NewPipe e.V. 2024 + * + * NewPlayer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPlayer. If not, see . + */ + +package net.newpipe.newplayer.ui.videoplayer + +import android.view.MotionEvent +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInteropFilter + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun TouchUi( + modifier: Modifier, + hideUi: () -> Unit, + showUi: () -> Unit, + uiVissible: Boolean, + fullscreen: Boolean, +) { + Box(modifier = Modifier + .pointerInteropFilter { + when (it.action) { + MotionEvent.ACTION_DOWN -> { + true + } + + MotionEvent.ACTION_UP -> { + if (uiVissible) { + hideUi() + } else { + showUi() + } + true + } + + MotionEvent.ACTION_MOVE -> { + true + } + + else -> false + } + }) { + Surface(color = Color.Transparent, modifier = Modifier.fillMaxSize()) { + + } + } +}