Skip to content

Commit

Permalink
feat: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Thalys Matias Carrara committed Jun 6, 2023
1 parent 06b49d9 commit 3022035
Show file tree
Hide file tree
Showing 2 changed files with 228 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,63 @@ package com.profusion.androidenhancedvideoplayer.components

import android.content.res.Configuration.ORIENTATION_LANDSCAPE
import android.net.Uri
import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.*
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.*
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.*
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.util.Log
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView
import com.profusion.androidenhancedvideoplayer.components.playerOverlay.ControlsCustomization
import com.profusion.androidenhancedvideoplayer.components.playerOverlay.PlayerControls
import com.profusion.androidenhancedvideoplayer.utils.setLandscape
import com.profusion.androidenhancedvideoplayer.utils.setPortrait
import com.profusion.androidenhancedvideoplayer.utils.executeAfterTimeout
import kotlinx.coroutines.Job

data class SeekState(
val isSeekEnabled: Boolean,
val tapCount: Int
)

private const val MAIN_PACKAGE_PATH_PREFIX = "android.resource://"

Expand All @@ -41,6 +74,7 @@ fun EnhancedVideoPlayer(
) {
val context = LocalContext.current
val configuration = LocalConfiguration.current
val scope = rememberCoroutineScope()

val mainPackagePath = "$MAIN_PACKAGE_PATH_PREFIX${context.packageName}/"
val exoPlayer = remember {
Expand All @@ -62,6 +96,39 @@ fun EnhancedVideoPlayer(
var hasEnded by remember { mutableStateOf(exoPlayer.playbackState == ExoPlayer.STATE_ENDED) }
var isControlsVisible by remember { mutableStateOf(false) }
val isFullScreen = configuration.orientation == ORIENTATION_LANDSCAPE
var backwardState by remember {
mutableStateOf(
SeekState(
isSeekEnabled = false,
tapCount = 0
)
)
}

var backwardJob: Job? = null

fun setBackwardState(isSeekEnabled: Boolean, shouldResetTapCount: Boolean = false) {
backwardState = backwardState.copy(
isSeekEnabled = isSeekEnabled,
tapCount = if (shouldResetTapCount) 0 else backwardState.tapCount + 1
)
}

fun onBackwardSingleTap() {
Log.d("BUTTON", "backward tap")
Log.d("BUTTON", "${backwardJob}")
setBackwardState(isSeekEnabled = true)
backwardJob = executeAfterTimeout(scope, backwardJob) { setBackwardState(false) }
}

fun onBackwardDoubletap() {
setBackwardState(isSeekEnabled = true)
Log.d("BUTTON", "backward double tap")
backwardJob = executeAfterTimeout(scope, backwardJob) {
Log.d("BUTTON", "disable state after timeout called")
setBackwardState(false, shouldResetTapCount = true)
}
}

DisposableEffect(context) {
val listener = object : Player.Listener {
Expand Down Expand Up @@ -103,26 +170,138 @@ fun EnhancedVideoPlayer(
}
}
)
PlayerControls(
isVisible = isControlsVisible,
isPlaying = isPlaying,
isFullScreen = isFullScreen,
hasEnded = hasEnded,
onPreviousClick = exoPlayer::seekToPrevious,
onNextClick = exoPlayer::seekToNext,
onPauseToggle = when {
hasEnded -> exoPlayer::seekToDefaultPosition
isPlaying -> exoPlayer::pause
else -> exoPlayer::play
},
onFullScreenToggle = {
when (isFullScreen) {
true -> context.setPortrait()
false -> context.setLandscape()
Row(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.5f))
) {
Box(
modifier = Modifier
.fillMaxHeight()
.weight(1f)
.pointerInput(backwardState) {
Log.d("BUTTON", "executed pointerInput After state change")
detectTapGestures(
onTap = when (backwardState.isSeekEnabled) {
true -> { _ -> onBackwardSingleTap() }
false -> null },
onDoubleTap = when (backwardState.isSeekEnabled) {
true -> null
false -> { _ -> onBackwardDoubletap() }
}
)
}
.background(Color.Red.copy(alpha = 0.4f))
)
Box(
modifier = Modifier
.fillMaxHeight()
.weight(1f)
.clickable { Log.d("BUTTON", "rightside clicked") }
.background(Color.Green.copy(alpha = 0.4f))
)
}
Button(
onClick = { Log.d("BUTTON", "botão clicado") },
modifier = Modifier
.align(Alignment.Center)
.padding(16.dp)
) {
Text("Clique aqui")
}

// PlayerControls(
// isVisible = isControlsVisible,
// isPlaying = isPlaying,
// isFullScreen = isFullScreen,
// hasEnded = hasEnded,
// onPreviousClick = exoPlayer::seekToPrevious,
// onNextClick = exoPlayer::seekToNext,
// onPauseToggle = when {
// hasEnded -> exoPlayer::seekToDefaultPosition
// isPlaying -> exoPlayer::pause
// else -> exoPlayer::play
// },
// onFullScreenToggle = {
// when (isFullScreen) {
// true -> context.setPortrait()
// false -> context.setLandscape()
// }
// },
// customization = controlsCustomization,
// modifier = Modifier.matchParentSize().testTag("PlayerControlsParent")
// )
}
}

@ExperimentalAnimationApi
@Composable
fun AnimatedIcon() {
var isVisible by remember { mutableStateOf(false) }
val transition = updateTransition(isVisible, label = "")

val scale by transition.animateFloat(
transitionSpec = {
keyframes {
durationMillis = 300
0f at 0
1f at 150
0f at 300
}
}
) { state ->
if (state) 1f else 0f
}

val alpha by transition.animateFloat(
transitionSpec = {
keyframes {
durationMillis = 300
0f at 0
1f at 150
0f at 300
}
}
) { state ->
if (state) 1f else 0f
}

Column(
modifier = Modifier
.fillMaxWidth()
.height(90.dp)
.wrapContentHeight()
.clickable {
if (transition.currentState == false) {
isVisible = true
} else {
isVisible = false
}
},
customization = controlsCustomization,
modifier = Modifier.matchParentSize().testTag("PlayerControlsParent")
)
}
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
AnimatedVisibility(
visible = isVisible,
enter = fadeIn() + expandIn(),
exit = fadeOut() + shrinkOut()
) {
Box(
modifier = Modifier
.size(48.dp)
.graphicsLayer {
scaleX = scale
scaleY = scale
this.alpha = alpha
}
) {
Icon(
imageVector = Icons.Default.Favorite,
contentDescription = null,
tint = Color.Red
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.profusion.androidenhancedvideoplayer.utils

import android.util.Log
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

fun executeAfterTimeout(
scope: CoroutineScope,
job: Job?,
timeInMillis: Long = 2000L,
onJobComplete: () -> Unit
): Job {
job?.cancel()
return scope.launch() {
try {
delay(timeInMillis)
onJobComplete()
} catch (e: CancellationException) {
Log.d("BUTTON", "timeOut reseted")
}
}
}

0 comments on commit 3022035

Please sign in to comment.