add initial support for multitap gesture
This commit is contained in:
parent
cc4dfe7721
commit
628ba4db1b
|
@ -21,25 +21,15 @@
|
||||||
package net.newpipe.newplayer.ui.videoplayer
|
package net.newpipe.newplayer.ui.videoplayer
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.MotionEvent
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
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.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.wrapContentSize
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
@ -47,17 +37,9 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.scale
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.input.pointer.pointerInteropFilter
|
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
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.theme.VideoPlayerTheme
|
||||||
import net.newpipe.newplayer.ui.videoplayer.gesture_ui.FastSeekVisualFeedback
|
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.TouchSurface
|
||||||
|
@ -95,16 +77,17 @@ fun GestureUI(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fastSeekModeBackward by remember {
|
var fastSeekBackwardBy:Int by remember {
|
||||||
mutableStateOf(false)
|
mutableStateOf(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
var fastSeekModeForward by remember {
|
var fastSeekForwardBy:Int by remember {
|
||||||
mutableStateOf(false)
|
mutableStateOf(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
val composeScope = rememberCoroutineScope()
|
val composeScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
/*
|
||||||
val doForwardSeek = {
|
val doForwardSeek = {
|
||||||
fastSeekModeForward = true
|
fastSeekModeForward = true
|
||||||
composeScope.launch {
|
composeScope.launch {
|
||||||
|
@ -128,16 +111,26 @@ fun GestureUI(
|
||||||
resetFastSeekModeEnd()
|
resetFastSeekModeEnd()
|
||||||
fastSeekBackward()
|
fastSeekBackward()
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
val onMultitapBackward = { amount: Int ->
|
||||||
|
fastSeekBackwardBy = amount * fastSeekSeconds
|
||||||
|
}
|
||||||
|
|
||||||
|
val onMultitapForward = { amount: Int ->
|
||||||
|
fastSeekForwardBy = amount * fastSeekSeconds
|
||||||
|
}
|
||||||
|
|
||||||
if (fullscreen) {
|
if (fullscreen) {
|
||||||
Row(modifier = modifier) {
|
Row(modifier = modifier) {
|
||||||
TouchSurface(
|
TouchSurface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f),
|
.weight(1f),
|
||||||
|
multitapDurationInMs = FAST_SEEKMODE_DURATION,
|
||||||
onRegularTap = defaultOnRegularTap,
|
onRegularTap = defaultOnRegularTap,
|
||||||
onDoubleTab = doBackwardSeek
|
onMultiTap = onMultitapBackward
|
||||||
) {
|
) {
|
||||||
FadedAnimationForSeekFeedback(visible = fastSeekModeBackward) {
|
FadedAnimationForSeekFeedback(visible = fastSeekForwardBy != 0) {
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
FastSeekVisualFeedback(
|
FastSeekVisualFeedback(
|
||||||
seconds = fastSeekSeconds,
|
seconds = fastSeekSeconds,
|
||||||
|
@ -151,6 +144,7 @@ fun GestureUI(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f),
|
.weight(1f),
|
||||||
onRegularTap = defaultOnRegularTap,
|
onRegularTap = defaultOnRegularTap,
|
||||||
|
multitapDurationInMs = FAST_SEEKMODE_DURATION,
|
||||||
onMovement = { movement ->
|
onMovement = { movement ->
|
||||||
if (0 < movement.y) {
|
if (0 < movement.y) {
|
||||||
switchToEmbeddedView()
|
switchToEmbeddedView()
|
||||||
|
@ -161,9 +155,10 @@ fun GestureUI(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f),
|
.weight(1f),
|
||||||
onRegularTap = defaultOnRegularTap,
|
onRegularTap = defaultOnRegularTap,
|
||||||
onDoubleTab = doForwardSeek
|
multitapDurationInMs = FAST_SEEKMODE_DURATION,
|
||||||
|
onMultiTap = onMultitapForward
|
||||||
) {
|
) {
|
||||||
FadedAnimationForSeekFeedback(visible = fastSeekModeForward) {
|
FadedAnimationForSeekFeedback(visible = fastSeekBackwardBy != 0) {
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
FastSeekVisualFeedback(
|
FastSeekVisualFeedback(
|
||||||
modifier = Modifier.align(Alignment.CenterStart),
|
modifier = Modifier.align(Alignment.CenterStart),
|
||||||
|
@ -188,11 +183,12 @@ fun GestureUI(
|
||||||
TouchSurface(
|
TouchSurface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f),
|
.weight(1f),
|
||||||
onDoubleTab = doBackwardSeek,
|
multitapDurationInMs = FAST_SEEKMODE_DURATION,
|
||||||
onRegularTap = defaultOnRegularTap,
|
onRegularTap = defaultOnRegularTap,
|
||||||
|
onMultiTap = onMultitapBackward,
|
||||||
onMovement = handleDownwardMovement
|
onMovement = handleDownwardMovement
|
||||||
) {
|
) {
|
||||||
FadedAnimationForSeekFeedback(visible = fastSeekModeBackward) {
|
FadedAnimationForSeekFeedback(visible = fastSeekBackwardBy != 0) {
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
FastSeekVisualFeedback(
|
FastSeekVisualFeedback(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
@ -205,11 +201,12 @@ fun GestureUI(
|
||||||
TouchSurface(
|
TouchSurface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f),
|
.weight(1f),
|
||||||
onDoubleTab = doForwardSeek,
|
multitapDurationInMs = FAST_SEEKMODE_DURATION,
|
||||||
onRegularTap = defaultOnRegularTap,
|
onRegularTap = defaultOnRegularTap,
|
||||||
onMovement = handleDownwardMovement
|
onMovement = handleDownwardMovement,
|
||||||
|
onMultiTap = onMultitapForward
|
||||||
) {
|
) {
|
||||||
FadedAnimationForSeekFeedback(visible = fastSeekModeForward) {
|
FadedAnimationForSeekFeedback(visible = fastSeekForwardBy != 0) {
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
FastSeekVisualFeedback(
|
FastSeekVisualFeedback(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
@ -220,6 +217,7 @@ fun GestureUI(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,14 +37,15 @@ import androidx.compose.ui.input.pointer.pointerInteropFilter
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import net.newpipe.newplayer.ui.videoplayer.DELAY_UNTIL_SHOWING_UI_AFTER_TOUCH_IN_MS
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
fun TouchSurface(
|
fun TouchSurface(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
color: Color = Color.Transparent,
|
color: Color = Color.Transparent,
|
||||||
onDoubleTab: () -> Unit = {},
|
multitapDurationInMs: Long,
|
||||||
|
onMultiTap: (Int) -> Unit = {},
|
||||||
|
onMultiTapFinished: () -> Unit = {},
|
||||||
onRegularTap: () -> Unit = {},
|
onRegularTap: () -> Unit = {},
|
||||||
onMovement: (TouchedPosition) -> Unit = {},
|
onMovement: (TouchedPosition) -> Unit = {},
|
||||||
content: @Composable () -> Unit = {}
|
content: @Composable () -> Unit = {}
|
||||||
|
@ -72,16 +73,30 @@ fun TouchSurface(
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
val defaultActionUp = { onDoubleTap: () -> Unit, onRegularTap: () -> Unit ->
|
var multitapAmount:Int by remember {
|
||||||
|
mutableStateOf(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cancelMultitapJob: Job? by remember {
|
||||||
|
mutableStateOf(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
val defaultActionUp = { onMultiTap: (Int) -> Unit, onRegularTap: () -> Unit ->
|
||||||
val currentTime = System.currentTimeMillis()
|
val currentTime = System.currentTimeMillis()
|
||||||
if (!moveOccured) {
|
if (!moveOccured) {
|
||||||
val timeSinceLastTouch = currentTime - lastTouchTime
|
val timeSinceLastTouch = currentTime - lastTouchTime
|
||||||
if (timeSinceLastTouch <= DELAY_UNTIL_SHOWING_UI_AFTER_TOUCH_IN_MS) {
|
if (timeSinceLastTouch <= multitapDurationInMs) {
|
||||||
regularTabJob?.cancel()
|
regularTabJob?.cancel()
|
||||||
onDoubleTap()
|
cancelMultitapJob?.cancel()
|
||||||
|
multitapAmount++
|
||||||
|
onMultiTap(multitapAmount)
|
||||||
|
cancelMultitapJob = composableScope.launch {
|
||||||
|
delay(multitapDurationInMs)
|
||||||
|
onMultiTapFinished()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
regularTabJob = composableScope.launch {
|
regularTabJob = composableScope.launch {
|
||||||
delay(DELAY_UNTIL_SHOWING_UI_AFTER_TOUCH_IN_MS)
|
delay(multitapDurationInMs)
|
||||||
onRegularTap()
|
onRegularTap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +118,7 @@ fun TouchSurface(
|
||||||
Box(modifier = modifier.pointerInteropFilter {
|
Box(modifier = modifier.pointerInteropFilter {
|
||||||
when (it.action) {
|
when (it.action) {
|
||||||
MotionEvent.ACTION_DOWN -> defaultActionDown(it)
|
MotionEvent.ACTION_DOWN -> defaultActionDown(it)
|
||||||
MotionEvent.ACTION_UP -> defaultActionUp(onDoubleTab, onRegularTap)
|
MotionEvent.ACTION_UP -> defaultActionUp(onMultiTap, onRegularTap)
|
||||||
MotionEvent.ACTION_MOVE -> handleMove(it, onMovement)
|
MotionEvent.ACTION_MOVE -> handleMove(it, onMovement)
|
||||||
|
|
||||||
else -> false
|
else -> false
|
||||||
|
|
Loading…
Reference in New Issue