From cc4dfe772193eba28784ede1548a43a9527a097b Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 6 Aug 2024 17:35:50 +0200 Subject: [PATCH] rearange gesture UI code --- .../java/net/newpipe/newplayer/NewPlayer.kt | 12 +- .../newplayer/ui/videoplayer/GestureUI.kt | 238 ++---------------- .../gesture_ui/FastSeekVisualFeedback.kt | 165 ++++++++++++ .../ui/videoplayer/gesture_ui/TouchSurface.kt | 115 +++++++++ .../videoplayer/gesture_ui/TouchedPosition.kt | 25 ++ 5 files changed, 337 insertions(+), 218 deletions(-) create mode 100644 new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/FastSeekVisualFeedback.kt create mode 100644 new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/TouchSurface.kt create mode 100644 new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/TouchedPosition.kt diff --git a/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt b/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt index a5494c8..949eb29 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt @@ -52,8 +52,8 @@ interface NewPlayer { fun prepare() fun play() fun pause() - fun fastSeekForward() - fun fastSeekBackward() + fun fastSeekForward(ntimes: Int = 1) + fun fastSeekBackward(ntimes: Int = 1) fun seekTo(millisecond: Long) fun addToPlaylist(newItem: String) fun addListener(callbackListener: Listener) @@ -115,14 +115,14 @@ class NewPlayerImpl(override val internal_player: Player, override val repositor internal_player.pause() } - override fun fastSeekForward() { + override fun fastSeekForward(ntimes: Int) { val currentPosition = internal_player.currentPosition - internal_player.seekTo(currentPosition + fastSeekAmountSec * 1000) + internal_player.seekTo(currentPosition + fastSeekAmountSec * 1000 * ntimes) } - override fun fastSeekBackward() { + override fun fastSeekBackward(ntimes: Int) { val currentPosition = internal_player.currentPosition - internal_player.seekTo(currentPosition - fastSeekAmountSec * 1000) + internal_player.seekTo(currentPosition - fastSeekAmountSec * 1000 * ntimes) } override fun seekTo(millisecond: Long) { diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/GestureUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/GestureUI.kt index f8bf429..846a557 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/GestureUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/GestureUI.kt @@ -59,16 +59,17 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import net.newpipe.newplayer.R import net.newpipe.newplayer.ui.theme.VideoPlayerTheme +import net.newpipe.newplayer.ui.videoplayer.gesture_ui.FastSeekVisualFeedback +import net.newpipe.newplayer.ui.videoplayer.gesture_ui.TouchSurface +import net.newpipe.newplayer.ui.videoplayer.gesture_ui.TouchedPosition private const val TAG = "TouchUi" -private data class TouchedPosition(val x: Float, val y: Float) { - operator fun minus(other: TouchedPosition) = TouchedPosition(this.x - other.x, this.y - other.y) -} + const val DELAY_UNTIL_SHOWING_UI_AFTER_TOUCH_IN_MS: Long = 200 const val SEEK_ANIMATION_DURATION_IN_MS = 400 -const val SEEK_ANIMATION_VISSIBLE_IN_MS = 500L +const val FAST_SEEKMODE_DURATION = 500L const val SEEK_ANIMATION_FADE_IN = 200 const val SEEK_ANIMATION_FADE_OUT = 500 @@ -94,34 +95,39 @@ fun GestureUI( } } - var showFastSeekBack by remember { + var fastSeekModeBackward by remember { mutableStateOf(false) } - var showFastSeekForward by remember { + var fastSeekModeForward by remember { mutableStateOf(false) } val composeScope = rememberCoroutineScope() val doForwardSeek = { - showFastSeekForward = true + fastSeekModeForward = true composeScope.launch { - delay(SEEK_ANIMATION_VISSIBLE_IN_MS) - showFastSeekForward = false + delay(FAST_SEEKMODE_DURATION) + fastSeekModeForward = false } fastSeekForward() } - val doBackwardSeek = { - showFastSeekBack = true - composeScope.launch { - delay(SEEK_ANIMATION_VISSIBLE_IN_MS) - showFastSeekBack = false + var fastSeekModeTimeout: Job? = null + val resetFastSeekModeEnd = { + fastSeekModeTimeout?.cancel() + fastSeekModeTimeout = composeScope.launch { + delay(FAST_SEEKMODE_DURATION) + fastSeekModeBackward = false } - fastSeekBackward() } + val doBackwardSeek = { + fastSeekModeBackward = true + resetFastSeekModeEnd() + fastSeekBackward() + } if (fullscreen) { Row(modifier = modifier) { @@ -131,7 +137,7 @@ fun GestureUI( onRegularTap = defaultOnRegularTap, onDoubleTab = doBackwardSeek ) { - FadedAnimationForSeekFeedback(visible = showFastSeekBack) { + FadedAnimationForSeekFeedback(visible = fastSeekModeBackward) { Box(modifier = Modifier.fillMaxSize()) { FastSeekVisualFeedback( seconds = fastSeekSeconds, @@ -157,7 +163,7 @@ fun GestureUI( onRegularTap = defaultOnRegularTap, onDoubleTab = doForwardSeek ) { - FadedAnimationForSeekFeedback(visible = showFastSeekForward) { + FadedAnimationForSeekFeedback(visible = fastSeekModeForward) { Box(modifier = Modifier.fillMaxSize()) { FastSeekVisualFeedback( modifier = Modifier.align(Alignment.CenterStart), @@ -186,7 +192,7 @@ fun GestureUI( onRegularTap = defaultOnRegularTap, onMovement = handleDownwardMovement ) { - FadedAnimationForSeekFeedback(visible = showFastSeekBack) { + FadedAnimationForSeekFeedback(visible = fastSeekModeBackward) { Box(modifier = Modifier.fillMaxSize()) { FastSeekVisualFeedback( modifier = Modifier.align(Alignment.Center), @@ -203,7 +209,7 @@ fun GestureUI( onRegularTap = defaultOnRegularTap, onMovement = handleDownwardMovement ) { - FadedAnimationForSeekFeedback(visible = showFastSeekForward) { + FadedAnimationForSeekFeedback(visible = fastSeekModeForward) { Box(modifier = Modifier.fillMaxSize()) { FastSeekVisualFeedback( modifier = Modifier.align(Alignment.Center), @@ -217,6 +223,7 @@ fun GestureUI( } } + @Composable fun FadedAnimationForSeekFeedback(visible: Boolean, content: @Composable () -> Unit) { AnimatedVisibility( @@ -228,181 +235,7 @@ fun FadedAnimationForSeekFeedback(visible: Boolean, content: @Composable () -> U } } -@Composable -@OptIn(ExperimentalComposeUiApi::class) -private fun TouchSurface( - modifier: Modifier, - color: Color = Color.Transparent, - onDoubleTab: () -> Unit = {}, - onRegularTap: () -> Unit = {}, - onMovement: (TouchedPosition) -> Unit = {}, - content: @Composable () -> Unit = {} -) { - var moveOccured by remember { - mutableStateOf(false) - } - var lastTouchedPosition by remember { - mutableStateOf(TouchedPosition(0f, 0f)) - } - - var lastTouchTime by remember { - mutableStateOf(System.currentTimeMillis()) - } - - val composableScope = rememberCoroutineScope() - var regularTabJob: Job? by remember { - mutableStateOf(null) - } - - val defaultActionDown = { event: MotionEvent -> - lastTouchedPosition = TouchedPosition(event.x, event.y) - moveOccured = false - true - } - - - val defaultActionUp = { onDoubleTap: () -> Unit, onRegularTap: () -> Unit -> - val currentTime = System.currentTimeMillis() - if (!moveOccured) { - val timeSinceLastTouch = currentTime - lastTouchTime - if (timeSinceLastTouch <= DELAY_UNTIL_SHOWING_UI_AFTER_TOUCH_IN_MS) { - regularTabJob?.cancel() - onDoubleTap() - } else { - regularTabJob = composableScope.launch { - delay(DELAY_UNTIL_SHOWING_UI_AFTER_TOUCH_IN_MS) - onRegularTap() - } - } - } - moveOccured = false - lastTouchTime = currentTime - true - } - - val handleMove = { event: MotionEvent, lambda: (movement: TouchedPosition) -> Unit -> - val currentTouchedPosition = TouchedPosition(event.x, event.y) - val movement = currentTouchedPosition - lastTouchedPosition - lastTouchedPosition = currentTouchedPosition - moveOccured = true - lambda(movement) - true - } - - Box(modifier = modifier.pointerInteropFilter { - when (it.action) { - MotionEvent.ACTION_DOWN -> defaultActionDown(it) - MotionEvent.ACTION_UP -> defaultActionUp(onDoubleTab, onRegularTap) - MotionEvent.ACTION_MOVE -> handleMove(it, onMovement) - - else -> false - } - }) { - content() - Surface(color = color, modifier = Modifier.fillMaxSize()) {} - } -} - -@Composable -fun FastSeekVisualFeedback(modifier: Modifier = Modifier, seconds: Int, backwards: Boolean) { - - val contentDescription = String.format( - if (backwards) { - "Fast seeking backward by %d seconds." - //stringResource(id = R.string.fast_seeking_backward) - } else { - "Fast seeking forward by %d seconds." - //stringResource(id = R.string.fast_seeking_forward) - }, seconds - ) - - val infiniteTransition = rememberInfiniteTransition() - - val animatedColor1 by infiniteTransition.animateColor( - initialValue = Color.White, - targetValue = Color.Transparent, - animationSpec = infiniteRepeatable( - animation = keyframes { - durationMillis = SEEK_ANIMATION_DURATION_IN_MS - Color.White.copy(alpha = 1f) at 0 with LinearEasing - Color.White.copy(alpha = 0f) at SEEK_ANIMATION_DURATION_IN_MS with LinearEasing - }, - repeatMode = RepeatMode.Restart - ), label = "Arrow1 animation" - ) - - val animatedColor2 by infiniteTransition.animateColor( - initialValue = Color.White, - targetValue = Color.Transparent, - animationSpec = infiniteRepeatable( - animation = keyframes { - durationMillis = SEEK_ANIMATION_DURATION_IN_MS - Color.White.copy(alpha = 1f / 3f) at 0 with LinearEasing - Color.White.copy(alpha = 0f) at SEEK_ANIMATION_DURATION_IN_MS / 3 with LinearEasing - Color.White.copy(alpha = 1f) at SEEK_ANIMATION_DURATION_IN_MS / 3 + 1 with LinearEasing - Color.White.copy(alpha = 2f / 3f) at SEEK_ANIMATION_DURATION_IN_MS with LinearEasing - }, - repeatMode = RepeatMode.Restart - ), label = "Arrow2 animation" - ) - - val animatedColor3 by infiniteTransition.animateColor( - initialValue = Color.White, - targetValue = Color.Transparent, - animationSpec = infiniteRepeatable( - animation = keyframes { - durationMillis = SEEK_ANIMATION_DURATION_IN_MS - Color.White.copy(alpha = 2f / 3f) at 0 with LinearEasing - Color.White.copy(alpha = 0f) at 2 * SEEK_ANIMATION_DURATION_IN_MS / 3 with LinearEasing - Color.White.copy(alpha = 1f) at 2 * SEEK_ANIMATION_DURATION_IN_MS / 3 + 1 with LinearEasing - Color.White.copy(alpha = 2f / 3f) at SEEK_ANIMATION_DURATION_IN_MS with LinearEasing - }, - repeatMode = RepeatMode.Restart - ), label = "Arrow3 animation" - ) - - - //val secondsString = stringResource(id = R.string.seconds) - val secondsString = "Seconds" - - Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) { - Row { - SeekerIcon( - backwards = backwards, - description = contentDescription, - color = if (backwards) animatedColor3 else animatedColor1 - ) - SeekerIcon( - backwards = backwards, - description = contentDescription, - color = animatedColor2 - ) - SeekerIcon( - backwards = backwards, - description = contentDescription, - color = if (backwards) animatedColor1 else animatedColor3 - ) - } - Text(text = "$seconds $secondsString") - } - -} - - -@Composable -fun SeekerIcon(backwards: Boolean, description: String, color: Color) { - Icon( - modifier = if (backwards) { - Modifier.scale(-1f, 1f) - } else { - Modifier - }, - tint = color, - painter = painterResource(id = R.drawable.ic_play_seek_triangle), - contentDescription = description - ) -} @Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") @Composable @@ -446,22 +279,3 @@ fun EmbeddedGestureUIPreview() { } } -@Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") -@Composable -fun FastSeekVisualFeedbackPreviewBackwards() { - VideoPlayerTheme { - Surface(modifier = Modifier.wrapContentSize(), color = Color.Black) { - FastSeekVisualFeedback(seconds = 10, backwards = true) - } - } -} - -@Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") -@Composable -fun FastSeekVisualFeedbackPreview() { - VideoPlayerTheme { - Surface(modifier = Modifier.wrapContentSize(), color = Color.Black) { - FastSeekVisualFeedback(seconds = 10, backwards = false) - } - } -} \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/FastSeekVisualFeedback.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/FastSeekVisualFeedback.kt new file mode 100644 index 0000000..7b75797 --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/FastSeekVisualFeedback.kt @@ -0,0 +1,165 @@ +/* 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.gesture_ui + +import androidx.compose.animation.animateColor +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.keyframes +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.material3.Icon +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.scale +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import net.newpipe.newplayer.R +import net.newpipe.newplayer.ui.theme.VideoPlayerTheme +import net.newpipe.newplayer.ui.videoplayer.SEEK_ANIMATION_DURATION_IN_MS + +@Composable +fun FastSeekVisualFeedback(modifier: Modifier = Modifier, seconds: Int, backwards: Boolean) { + + val contentDescription = String.format( + if (backwards) { + "Fast seeking backward by %d seconds." + //stringResource(id = R.string.fast_seeking_backward) + } else { + "Fast seeking forward by %d seconds." + //stringResource(id = R.string.fast_seeking_forward) + }, seconds + ) + + val infiniteTransition = rememberInfiniteTransition() + + val animatedColor1 by infiniteTransition.animateColor( + initialValue = Color.White, + targetValue = Color.Transparent, + animationSpec = infiniteRepeatable( + animation = keyframes { + durationMillis = SEEK_ANIMATION_DURATION_IN_MS + Color.White.copy(alpha = 1f) at 0 with LinearEasing + Color.White.copy(alpha = 0f) at SEEK_ANIMATION_DURATION_IN_MS with LinearEasing + }, + repeatMode = RepeatMode.Restart + ), label = "Arrow1 animation" + ) + + val animatedColor2 by infiniteTransition.animateColor( + initialValue = Color.White, + targetValue = Color.Transparent, + animationSpec = infiniteRepeatable( + animation = keyframes { + durationMillis = SEEK_ANIMATION_DURATION_IN_MS + Color.White.copy(alpha = 1f / 3f) at 0 with LinearEasing + Color.White.copy(alpha = 0f) at SEEK_ANIMATION_DURATION_IN_MS / 3 with LinearEasing + Color.White.copy(alpha = 1f) at SEEK_ANIMATION_DURATION_IN_MS / 3 + 1 with LinearEasing + Color.White.copy(alpha = 2f / 3f) at SEEK_ANIMATION_DURATION_IN_MS with LinearEasing + }, + repeatMode = RepeatMode.Restart + ), label = "Arrow2 animation" + ) + + val animatedColor3 by infiniteTransition.animateColor( + initialValue = Color.White, + targetValue = Color.Transparent, + animationSpec = infiniteRepeatable( + animation = keyframes { + durationMillis = SEEK_ANIMATION_DURATION_IN_MS + Color.White.copy(alpha = 2f / 3f) at 0 with LinearEasing + Color.White.copy(alpha = 0f) at 2 * SEEK_ANIMATION_DURATION_IN_MS / 3 with LinearEasing + Color.White.copy(alpha = 1f) at 2 * SEEK_ANIMATION_DURATION_IN_MS / 3 + 1 with LinearEasing + Color.White.copy(alpha = 2f / 3f) at SEEK_ANIMATION_DURATION_IN_MS with LinearEasing + }, + repeatMode = RepeatMode.Restart + ), label = "Arrow3 animation" + ) + + + //val secondsString = stringResource(id = R.string.seconds) + val secondsString = "Seconds" + + Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) { + Row { + SeekerIcon( + backwards = backwards, + description = contentDescription, + color = if (backwards) animatedColor3 else animatedColor1 + ) + SeekerIcon( + backwards = backwards, + description = contentDescription, + color = animatedColor2 + ) + SeekerIcon( + backwards = backwards, + description = contentDescription, + color = if (backwards) animatedColor1 else animatedColor3 + ) + } + Text(text = "$seconds $secondsString") + } + +} + + +@Composable +private fun SeekerIcon(backwards: Boolean, description: String, color: Color) { + Icon( + modifier = if (backwards) { + Modifier.scale(-1f, 1f) + } else { + Modifier + }, + tint = color, + painter = painterResource(id = R.drawable.ic_play_seek_triangle), + contentDescription = description + ) +} + +@Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") +@Composable +fun FastSeekVisualFeedbackPreviewBackwards() { + VideoPlayerTheme { + Surface(modifier = Modifier.wrapContentSize(), color = Color.Black) { + FastSeekVisualFeedback(seconds = 10, backwards = true) + } + } +} + +@Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") +@Composable +fun FastSeekVisualFeedbackPreview() { + VideoPlayerTheme { + Surface(modifier = Modifier.wrapContentSize(), color = Color.Black) { + FastSeekVisualFeedback(seconds = 10, backwards = false) + } + } +} \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/TouchSurface.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/TouchSurface.kt new file mode 100644 index 0000000..33d2dea --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/TouchSurface.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.gesture_ui + +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.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInteropFilter +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import net.newpipe.newplayer.ui.videoplayer.DELAY_UNTIL_SHOWING_UI_AFTER_TOUCH_IN_MS + +@Composable +@OptIn(ExperimentalComposeUiApi::class) +fun TouchSurface( + modifier: Modifier, + color: Color = Color.Transparent, + onDoubleTab: () -> Unit = {}, + onRegularTap: () -> Unit = {}, + onMovement: (TouchedPosition) -> Unit = {}, + content: @Composable () -> Unit = {} +) { + var moveOccured by remember { + mutableStateOf(false) + } + + var lastTouchedPosition by remember { + mutableStateOf(TouchedPosition(0f, 0f)) + } + + var lastTouchTime by remember { + mutableStateOf(System.currentTimeMillis()) + } + + val composableScope = rememberCoroutineScope() + var regularTabJob: Job? by remember { + mutableStateOf(null) + } + + val defaultActionDown = { event: MotionEvent -> + lastTouchedPosition = TouchedPosition(event.x, event.y) + moveOccured = false + true + } + + val defaultActionUp = { onDoubleTap: () -> Unit, onRegularTap: () -> Unit -> + val currentTime = System.currentTimeMillis() + if (!moveOccured) { + val timeSinceLastTouch = currentTime - lastTouchTime + if (timeSinceLastTouch <= DELAY_UNTIL_SHOWING_UI_AFTER_TOUCH_IN_MS) { + regularTabJob?.cancel() + onDoubleTap() + } else { + regularTabJob = composableScope.launch { + delay(DELAY_UNTIL_SHOWING_UI_AFTER_TOUCH_IN_MS) + onRegularTap() + } + } + } + moveOccured = false + lastTouchTime = currentTime + true + } + + val handleMove = { event: MotionEvent, lambda: (movement: TouchedPosition) -> Unit -> + val currentTouchedPosition = TouchedPosition(event.x, event.y) + val movement = currentTouchedPosition - lastTouchedPosition + lastTouchedPosition = currentTouchedPosition + moveOccured = true + lambda(movement) + true + } + + Box(modifier = modifier.pointerInteropFilter { + when (it.action) { + MotionEvent.ACTION_DOWN -> defaultActionDown(it) + MotionEvent.ACTION_UP -> defaultActionUp(onDoubleTab, onRegularTap) + MotionEvent.ACTION_MOVE -> handleMove(it, onMovement) + + else -> false + } + }) { + content() + Surface(color = color, modifier = Modifier.fillMaxSize()) {} + } +} \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/TouchedPosition.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/TouchedPosition.kt new file mode 100644 index 0000000..988e3ac --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/TouchedPosition.kt @@ -0,0 +1,25 @@ +/* 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.gesture_ui + +data class TouchedPosition(val x: Float, val y: Float) { + operator fun minus(other: TouchedPosition) = TouchedPosition(this.x - other.x, this.y - other.y) +} \ No newline at end of file