advance touch ui: make fullscreen and and embedded view switch possible
This commit is contained in:
parent
ce6ef8a8fd
commit
07a6b0a03f
|
@ -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) {}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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 = {})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,8 @@ fun VideoPlayerControllerUIPreviewEmbeddedColorPreview() {
|
|||
showUi = {},
|
||||
hideUi = {},
|
||||
seekPositionChanged = {},
|
||||
seekingFinished = {})
|
||||
seekingFinished = {},
|
||||
embeddedDraggedDownBy = {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()) {}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue