Skip to content

Commit

Permalink
feat: create SeekHandler component
Browse files Browse the repository at this point in the history
Closes #15
  • Loading branch information
Thalys Matias Carrara committed Jun 8, 2023
1 parent 892f3a0 commit 277569c
Showing 1 changed file with 143 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package com.profusion.androidenhancedvideoplayer.components.playerOverlay

import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
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.Modifier
import androidx.compose.ui.graphics.Color
import androidx.media3.exoplayer.ExoPlayer
import com.profusion.androidenhancedvideoplayer.styling.Colors
import com.profusion.androidenhancedvideoplayer.utils.JobsHolder
import com.profusion.androidenhancedvideoplayer.utils.executeAfterTimeout
import kotlin.math.max
import kotlin.math.min

private const val ICON_ANIMATION_DURATION_MS = 650
private const val ICON_INITIAL_SCALE = 0.8f
private const val ICON_TARGET_SCALE = 1.1f
private const val JOB_TIMEOUT = 650L

@Composable
fun SeekHandler(
exoPlayer: ExoPlayer,
setIsControlsVisible: (value: Boolean) -> Unit,
transformSeekIncrementRatio: (tapCount: Int) -> Long,
toggleIsControlsVisible: () -> Unit,
controlsCustomization: ControlsCustomization
) {
val jobs = JobsHolder
val scope = rememberCoroutineScope()
var forwardTapCount by remember { mutableStateOf(0) }
var rewindTapCount by remember { mutableStateOf(0) }
val isRewinding by remember { derivedStateOf { rewindTapCount > 0 } }
val isForwarding by remember { derivedStateOf { forwardTapCount > 0 } }

val transition = rememberInfiniteTransition("scaleSeekIcon")

val scale = transition.animateFloat(
initialValue = ICON_INITIAL_SCALE,
targetValue = ICON_TARGET_SCALE,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = ICON_ANIMATION_DURATION_MS,
easing = FastOutSlowInEasing
),
repeatMode = RepeatMode.Restart
)
)

LaunchedEffect(forwardTapCount) {
val timeToSeek = exoPlayer.currentPosition +
transformSeekIncrementRatio(forwardTapCount)
exoPlayer.seekTo(min(exoPlayer.duration, timeToSeek))
}

LaunchedEffect(rewindTapCount) {
val currentPosition = exoPlayer.currentPosition
val timeToSeek = currentPosition - transformSeekIncrementRatio(rewindTapCount)
exoPlayer.seekTo(max(0, timeToSeek))
}

fun checkIfCanToggleIsControlsVisible() {
if (!isRewinding && !isForwarding) {
toggleIsControlsVisible()
}
}

fun onForwardSingleTap() {
jobs.forwardJob = executeAfterTimeout(scope, jobs.forwardJob, JOB_TIMEOUT) {
forwardTapCount = 0
}
forwardTapCount++
}

fun onForwardDoubleTap() {
if (isRewinding) return
setIsControlsVisible(false)
onForwardSingleTap()
}

fun onRewindSingleTap() {
jobs.rewindJob = executeAfterTimeout(scope, jobs.rewindJob, JOB_TIMEOUT) {
rewindTapCount = 0
}
rewindTapCount++
}

fun onRewindDoubleTap() {
if (isForwarding) return
setIsControlsVisible(false)
onRewindSingleTap()
}

Row(
modifier = Modifier
.fillMaxSize()
.background(
if (isRewinding || isForwarding) {
Colors.controlsShadow
} else {
Color.Transparent
}
)
.clickable { toggleIsControlsVisible() }
) {
SeekClickableArea(
modifier = Modifier.weight(1f),
tapCount = rewindTapCount,
scaleAnimation = scale.value,
onSeekDoubleTap = ::onRewindDoubleTap,
onSeekSingleTap = ::onRewindSingleTap,
checkIfCanToggleIsControlsVisible = ::checkIfCanToggleIsControlsVisible,
getSeekTime = { (transformSeekIncrementRatio(rewindTapCount) / 1000).toInt() },
seekIcon = { controlsCustomization.rewindIconContent(it) }
)
Spacer(modifier = Modifier.weight(0.2f))
SeekClickableArea(
modifier = Modifier.weight(1f),
tapCount = forwardTapCount,
scaleAnimation = scale.value,
onSeekDoubleTap = ::onForwardDoubleTap,
onSeekSingleTap = ::onForwardSingleTap,
checkIfCanToggleIsControlsVisible = ::checkIfCanToggleIsControlsVisible,
getSeekTime = { (transformSeekIncrementRatio(forwardTapCount) / 1000).toInt() },
seekIcon = { controlsCustomization.forwardIconContent(it) }
)
}
}

0 comments on commit 277569c

Please sign in to comment.