diff --git a/app/src/main/java/org/shirabox/app/ui/activity/player/PlayerActivity.kt b/app/src/main/java/org/shirabox/app/ui/activity/player/PlayerActivity.kt index cb6aeae..c9f539a 100644 --- a/app/src/main/java/org/shirabox/app/ui/activity/player/PlayerActivity.kt +++ b/app/src/main/java/org/shirabox/app/ui/activity/player/PlayerActivity.kt @@ -66,6 +66,8 @@ class PlayerActivity : ComponentActivity() { rememberSystemUiController().apply { Util.hideSystemUi(this) } + val exoPlayer = remember { ExoPlayer.Builder(context).build().apply(ExoPlayer::prepare) } + val model = hiltViewModel { it.create( contentUid = arguments!!.getLong("content_uid"), @@ -74,11 +76,10 @@ class PlayerActivity : ComponentActivity() { team = arguments.getString("acting_team").toString(), repository = arguments.getString("repository").toString(), initialEpisode = arguments.getInt("episode"), + exoPlayer = exoPlayer ) } - val exoPlayer = remember { ExoPlayer.Builder(context).build().apply(ExoPlayer::prepare) } - player = exoPlayer ShiraPlayer(exoPlayer = exoPlayer, model = model) diff --git a/app/src/main/java/org/shirabox/app/ui/activity/player/PlayerViewModel.kt b/app/src/main/java/org/shirabox/app/ui/activity/player/PlayerViewModel.kt index 76e1b09..095281b 100644 --- a/app/src/main/java/org/shirabox/app/ui/activity/player/PlayerViewModel.kt +++ b/app/src/main/java/org/shirabox/app/ui/activity/player/PlayerViewModel.kt @@ -8,12 +8,15 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.media3.exoplayer.ExoPlayer import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.catch @@ -27,6 +30,7 @@ import org.shirabox.core.entity.EpisodeEntity import org.shirabox.core.model.ContentType import org.shirabox.core.model.Quality import org.shirabox.core.util.Util +import org.shirabox.core.util.Values import org.shirabox.data.EpisodesHelper import org.shirabox.data.animeskip.AnimeSkipRepository import org.shirabox.data.content.ContentRepositoryRegistry @@ -39,6 +43,7 @@ class PlayerViewModel @AssistedInject constructor( @Assisted("team") val team: String, @Assisted("repository") val repository: String, @Assisted val initialEpisode: Int, + @Assisted val exoPlayer: ExoPlayer, @ApplicationContext context: Context ) : ViewModel() { @@ -57,6 +62,8 @@ class PlayerViewModel @AssistedInject constructor( val currentRepository = ContentRepositoryRegistry.getRepositoryByName(repository) val isCurrentItemOffline = mutableStateOf(false) + var controlsVisibilityJob: Job? = null + @AssistedFactory interface PlayerViewModelFactory { fun create( @@ -65,6 +72,7 @@ class PlayerViewModel @AssistedInject constructor( @Assisted("contentEnName") contentEnName: String, @Assisted("team") team: String, @Assisted("repository") repository: String, + exoPlayer: ExoPlayer, initialEpisode: Int ): PlayerViewModel } @@ -151,6 +159,16 @@ class PlayerViewModel @AssistedInject constructor( } } + fun hideUi() { + controlsVisibilityJob?.cancel() + controlsVisibilityJob = viewModelScope.launch { + val delayMs = Values.CONTROLS_HIDE_DELAY + delay(delayMs).let { + if (exoPlayer.isPlaying) controlsVisibilityState = false + } + } + } + fun defaultQualityPreferenceFlow(context: Context): Flow = AppDataStore.read(context, DataStoreScheme.FIELD_DEFAULT_QUALITY.key) diff --git a/app/src/main/java/org/shirabox/app/ui/activity/player/ShiraPlayer.kt b/app/src/main/java/org/shirabox/app/ui/activity/player/ShiraPlayer.kt index 0b3f238..6476ce0 100644 --- a/app/src/main/java/org/shirabox/app/ui/activity/player/ShiraPlayer.kt +++ b/app/src/main/java/org/shirabox/app/ui/activity/player/ShiraPlayer.kt @@ -33,10 +33,8 @@ import androidx.media3.exoplayer.hls.HlsMediaSource import androidx.media3.exoplayer.source.ProgressiveMediaSource import androidx.media3.ui.PlayerView import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.launch import org.shirabox.app.ui.activity.player.presentation.PlayerScaffold import org.shirabox.app.ui.activity.player.presentation.SettingsBottomSheet -import org.shirabox.app.ui.activity.player.presentation.hideControls import org.shirabox.core.datastore.DataStoreScheme import org.shirabox.core.entity.EpisodeEntity import org.shirabox.core.model.Quality @@ -47,7 +45,6 @@ import org.shirabox.data.content.AbstractContentRepository @OptIn(UnstableApi::class) @Composable fun ShiraPlayer(exoPlayer: ExoPlayer, model: PlayerViewModel) { - val coroutineScope = rememberCoroutineScope() val interactionSource = remember(::MutableInteractionSource) val playlist by model.playlistFlow().collectAsStateWithLifecycle(initialValue = emptyList()) @@ -60,10 +57,8 @@ fun ShiraPlayer(exoPlayer: ExoPlayer, model: PlayerViewModel) { .clickable( interactionSource = interactionSource, indication = null ) { - coroutineScope.launch { - model.controlsVisibilityState = !model.controlsVisibilityState - hideControls(exoPlayer, model) - } + model.controlsVisibilityState = !model.controlsVisibilityState + model.hideUi() } ) { androidx.compose.animation.AnimatedVisibility( @@ -83,7 +78,11 @@ fun ShiraPlayer(exoPlayer: ExoPlayer, model: PlayerViewModel) { @OptIn(UnstableApi::class) @Composable -private fun PlayerSurface(exoPlayer: ExoPlayer, model: PlayerViewModel, playlist: List) { +private fun PlayerSurface( + exoPlayer: ExoPlayer, + model: PlayerViewModel, + playlist: List +) { val context = LocalContext.current val playerView = PlayerView(context) val coroutineScope = rememberCoroutineScope() diff --git a/app/src/main/java/org/shirabox/app/ui/activity/player/presentation/PlayerScaffold.kt b/app/src/main/java/org/shirabox/app/ui/activity/player/presentation/PlayerScaffold.kt index d7070b6..ce34bcb 100644 --- a/app/src/main/java/org/shirabox/app/ui/activity/player/presentation/PlayerScaffold.kt +++ b/app/src/main/java/org/shirabox/app/ui/activity/player/presentation/PlayerScaffold.kt @@ -15,7 +15,6 @@ import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableLongStateOf 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 @@ -24,7 +23,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.media3.common.Player import androidx.media3.exoplayer.ExoPlayer import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import org.shirabox.app.ui.activity.player.PlayerViewModel import org.shirabox.app.ui.activity.player.presentation.controls.InstantSeekArea import org.shirabox.app.ui.activity.player.presentation.controls.PlaybackControls @@ -33,13 +31,15 @@ import org.shirabox.app.ui.activity.player.presentation.controls.PlayerSkipButto import org.shirabox.app.ui.activity.player.presentation.controls.PlayerTopBar import org.shirabox.core.datastore.DataStoreScheme import org.shirabox.core.entity.EpisodeEntity -import org.shirabox.core.util.Values @Composable -fun PlayerScaffold(exoPlayer: ExoPlayer, playlist: List, model: PlayerViewModel) { +fun PlayerScaffold( + exoPlayer: ExoPlayer, + playlist: List, + model: PlayerViewModel +) { val context = LocalContext.current val activity = context as Activity - val coroutineScope = rememberCoroutineScope() var isPlaying by remember { mutableStateOf(exoPlayer.isPlaying) } var currentPosition by remember { mutableLongStateOf(exoPlayer.currentPosition) } @@ -148,10 +148,8 @@ fun PlayerScaffold(exoPlayer: ExoPlayer, playlist: List, model: P ) }, onClick = { - coroutineScope.launch { - model.controlsVisibilityState = !model.controlsVisibilityState - hideControls(exoPlayer, model) - } + model.controlsVisibilityState = !model.controlsVisibilityState + model.hideUi() } ) @@ -202,7 +200,7 @@ fun PlayerScaffold(exoPlayer: ExoPlayer, playlist: List, model: P }, onPlayToggle = { exoPlayer.playWhenReady = !exoPlayer.isPlaying - coroutineScope.launch { hideControls(exoPlayer, model) } + model.hideUi() }, onSkipNext = { model.saveEpisodePosition(currentEpisodeInt, exoPlayer.currentPosition, exoPlayer.duration) @@ -238,15 +236,4 @@ fun PlayerScaffold(exoPlayer: ExoPlayer, playlist: List, model: P } }) } -} - -suspend fun hideControls( - exoPlayer: ExoPlayer, - model: PlayerViewModel -) { - val delayMs = Values.CONTROLS_HIDE_DELAY - - delay(delayMs).let { - if (exoPlayer.isPlaying) model.controlsVisibilityState = false - } } \ No newline at end of file