advance touch ui: make fullscreen and and embedded view switch possible

This commit is contained in:
Christian Schabesberger 2024-08-05 18:07:17 +02:00
parent ce6ef8a8fd
commit 07a6b0a03f
6 changed files with 230 additions and 30 deletions

View File

@ -47,8 +47,13 @@ interface VideoPlayerViewModel {
fun hideUi()
fun seekPositionChanged(newValue: Float)
fun seekingFinished()
fun embeddedDraggedDown(offset: Float)
fun fastSeekForward()
fun fastSeekBackward()
interface Listener {
fun onFullscreenToggle(isFullscreen: Boolean)
fun onFullscreenToggle(isFullscreen: Boolean) {}
fun embeddedPlayerDraggedDown(offset: Float) {}
}
}

View File

@ -263,6 +263,18 @@ class VideoPlayerViewModelImpl @Inject constructor(
Log.i(TAG, "Seek to Ms: $seekPositionInMs")
}
override fun embeddedDraggedDown(offset: Float) {
callbackListeners.forEach { it?.embeddedPlayerDraggedDown(offset) }
}
override fun fastSeekForward() {
newPlayer?.fastSeekForward()
}
override fun fastSeekBackward() {
newPlayer?.fastSeekBackward()
}
override fun switchToEmbeddedView() {
callbackListeners.forEach { it?.onFullscreenToggle(false) }
uiVisibilityJob?.cancel()
@ -330,13 +342,25 @@ class VideoPlayerViewModelImpl @Inject constructor(
}
override fun seekPositionChanged(newValue: Float) {
println("dummy impl")
println("dymmy seekPositionChanged: newValue: ${newValue}")
}
override fun seekingFinished() {
println("dummy impl")
}
override fun embeddedDraggedDown(offset: Float) {
println("dymmy embeddedDraggedDown: offset: ${offset}")
}
override fun fastSeekForward() {
println("dummy impl")
}
override fun fastSeekBackward() {
println("dummy impl")
}
override fun pause() {
println("dummy pause")
}

View File

@ -71,7 +71,10 @@ fun VideoPlayerControllerUI(
showUi: () -> Unit,
hideUi: () -> Unit,
seekPositionChanged: (Float) -> Unit,
seekingFinished: () -> Unit
seekingFinished: () -> Unit,
embeddedDraggedDownBy: (Float) -> Unit,
fastSeekBackward: () -> Unit,
fastSeekForward: () -> Unit,
) {
if (fullscreen) {
@ -88,12 +91,17 @@ fun VideoPlayerControllerUI(
if (!uiVissible) {
TouchUi(
modifier = Modifier
.fillMaxSize()
.windowInsetsPadding(WindowInsets.systemGestures),
.fillMaxSize(),
// .windowInsetsPadding(WindowInsets.systemGestures),
hideUi = hideUi,
showUi = showUi,
uiVissible = uiVissible,
fullscreen = fullscreen
fullscreen = fullscreen,
switchToFullscreen = switchToFullscreen,
switchToEmbeddedView = switchToEmbeddedView,
embeddedDraggedDownBy = embeddedDraggedDownBy,
fastSeekForward = fastSeekForward,
fastSeekBackward = fastSeekBackward
)
}
@ -123,7 +131,12 @@ fun VideoPlayerControllerUI(
hideUi = hideUi,
showUi = showUi,
uiVissible = uiVissible,
fullscreen = fullscreen
fullscreen = fullscreen,
switchToFullscreen = switchToFullscreen,
switchToEmbeddedView = switchToEmbeddedView,
embeddedDraggedDownBy = embeddedDraggedDownBy,
fastSeekForward = fastSeekForward,
fastSeekBackward = fastSeekBackward
)
Box(modifier = Modifier.fillMaxSize()) {
@ -215,7 +228,10 @@ fun VideoPlayerControllerUIPreviewEmbedded() {
showUi = {},
hideUi = {},
seekPositionChanged = {},
seekingFinished = {})
seekingFinished = {},
embeddedDraggedDownBy = {},
fastSeekBackward = {},
fastSeekForward = {})
}
}
}
@ -242,7 +258,10 @@ fun VideoPlayerControllerUIPreviewLandscape() {
showUi = {},
hideUi = {},
seekPositionChanged = {},
seekingFinished = {})
seekingFinished = {},
embeddedDraggedDownBy = {},
fastSeekBackward = {},
fastSeekForward = {})
}
}
}
@ -270,7 +289,10 @@ fun VideoPlayerControllerUIPreviewPortrait() {
showUi = {},
hideUi = {},
seekPositionChanged = {},
seekingFinished = {})
seekingFinished = {},
embeddedDraggedDownBy = {},
fastSeekForward = {},
fastSeekBackward = {})
}
}
}

View File

@ -161,7 +161,10 @@ fun VideoPlayerUI(
showUi = viewModel::showUi,
hideUi = viewModel::hideUi,
seekPositionChanged = viewModel::seekPositionChanged,
seekingFinished = viewModel::seekingFinished
seekingFinished = viewModel::seekingFinished,
embeddedDraggedDownBy = viewModel::embeddedDraggedDown,
fastSeekForward = viewModel::fastSeekForward,
fastSeekBackward = viewModel::fastSeekBackward
)
}
}

View File

@ -92,7 +92,8 @@ fun VideoPlayerControllerUIPreviewEmbeddedColorPreview() {
showUi = {},
hideUi = {},
seekPositionChanged = {},
seekingFinished = {})
seekingFinished = {},
embeddedDraggedDownBy = {})
}
}
}

View File

@ -20,15 +20,33 @@
package net.newpipe.newplayer.ui.videoplayer
import android.util.Log
import android.view.MotionEvent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
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
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 = 150
@OptIn(ExperimentalComposeUiApi::class)
@Composable
@ -38,32 +56,159 @@ fun TouchUi(
showUi: () -> Unit,
uiVissible: Boolean,
fullscreen: Boolean,
switchToFullscreen: () -> Unit,
switchToEmbeddedView: () -> Unit,
embeddedDraggedDownBy: (Float) -> Unit,
fastSeekBackward: () -> Unit,
fastSeekForward: () -> Unit,
) {
Box(modifier = Modifier
.pointerInteropFilter {
when (it.action) {
MotionEvent.ACTION_DOWN -> {
true
}
MotionEvent.ACTION_UP -> {
if (uiVissible) {
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 showUiJob: Job? by remember{
mutableStateOf(null)
}
val defaultActionDown = { event: MotionEvent ->
lastTouchedPosition = TouchedPosition(event.x, event.y)
moveOccured = false
true
}
val defaultActionUp = { onDoubleTap: () -> Unit ->
val currentTime = System.currentTimeMillis()
if (!moveOccured) {
val timeSinceLastTouch = currentTime - lastTouchTime
if(timeSinceLastTouch <= DELAY_UNTIL_SHOWING_UI_AFTER_TOUCH_IN_MS) {
showUiJob?.cancel()
onDoubleTap()
} else {
if (uiVissible) {
showUiJob = composableScope.launch {
delay(DELAY_UNTIL_SHOWING_UI_AFTER_TOUCH_IN_MS)
hideUi()
} else {
}
} else {
showUiJob = composableScope.launch {
delay(DELAY_UNTIL_SHOWING_UI_AFTER_TOUCH_IN_MS)
showUi()
}
true
}
MotionEvent.ACTION_MOVE -> {
true
}
else -> false
}
}) {
Surface(color = Color.Transparent, modifier = Modifier.fillMaxSize()) {
}
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
}
if (fullscreen) {
Row(modifier = modifier) {
TouchSurface(modifier = Modifier.weight(1f)
.pointerInteropFilter {
when (it.action) {
MotionEvent.ACTION_DOWN -> defaultActionDown(it)
MotionEvent.ACTION_UP -> defaultActionUp(fastSeekBackward)
MotionEvent.ACTION_MOVE -> handleMove(it) { movement ->
}
else -> false
}
})
TouchSurface(modifier = Modifier
.weight(1f)
.pointerInteropFilter {
when (it.action) {
MotionEvent.ACTION_DOWN -> defaultActionDown(it)
MotionEvent.ACTION_UP -> defaultActionUp({})
MotionEvent.ACTION_MOVE -> handleMove(it) { movement ->
if (0 < movement.y) {
switchToEmbeddedView()
}
}
else -> false
}
})
TouchSurface(modifier = Modifier.weight(1f)
.pointerInteropFilter {
when (it.action) {
MotionEvent.ACTION_DOWN -> defaultActionDown(it)
MotionEvent.ACTION_UP -> defaultActionUp(fastSeekForward)
MotionEvent.ACTION_MOVE -> handleMove(it) { movement ->
}
else -> false
}
})
}
} else {
Row(modifier = modifier) {
TouchSurface(modifier = Modifier
.weight(1f)
.pointerInteropFilter {
when (it.action) {
MotionEvent.ACTION_DOWN -> defaultActionDown(it)
MotionEvent.ACTION_UP -> defaultActionUp(fastSeekBackward)
MotionEvent.ACTION_MOVE -> handleMove(it) { movement ->
Log.d(TAG, "${it.x}:${it.y}")
if (0 < movement.y) {
embeddedDraggedDownBy(movement.y)
} else {
switchToFullscreen()
}
}
else -> false
}
})
TouchSurface(modifier = Modifier
.weight(1f)
.pointerInteropFilter {
when (it.action) {
MotionEvent.ACTION_DOWN -> defaultActionDown(it)
MotionEvent.ACTION_UP -> defaultActionUp(fastSeekForward)
MotionEvent.ACTION_MOVE -> handleMove(it) { movement ->
if (0 < movement.y) {
embeddedDraggedDownBy(movement.y)
} else {
switchToFullscreen()
}
}
else -> false
}
})
}
}
}
@Composable
private fun TouchSurface(modifier: Modifier, color: Color = Color.Transparent) {
Box(modifier = modifier) {
Surface(color = color, modifier = Modifier.fillMaxSize()) {}
}
}