make progress update more smooth

This commit is contained in:
Christian Schabesberger 2024-09-23 19:11:41 +02:00
parent 642e5e78db
commit f750fa8b4b
7 changed files with 76 additions and 13 deletions

View File

@ -38,6 +38,7 @@ interface NewPlayerViewModel {
var contentFitMode: ContentScale var contentFitMode: ContentScale
val embeddedPlayerDraggedDownBy: SharedFlow<Float> val embeddedPlayerDraggedDownBy: SharedFlow<Float>
val onBackPressed: SharedFlow<Unit> val onBackPressed: SharedFlow<Unit>
var deviceInPowerSaveMode: Boolean
fun initUIState(instanceState: Bundle) fun initUIState(instanceState: Bundle)
fun play() fun play()

View File

@ -18,6 +18,7 @@ open class NewPlayerViewModelDummy : NewPlayerViewModel {
override var contentFitMode = ContentScale.FIT_INSIDE override var contentFitMode = ContentScale.FIT_INSIDE
override val embeddedPlayerDraggedDownBy = MutableSharedFlow<Float>().asSharedFlow() override val embeddedPlayerDraggedDownBy = MutableSharedFlow<Float>().asSharedFlow()
override val onBackPressed: SharedFlow<Unit> = MutableSharedFlow<Unit>().asSharedFlow() override val onBackPressed: SharedFlow<Unit> = MutableSharedFlow<Unit>().asSharedFlow()
override var deviceInPowerSaveMode: Boolean = false
override fun initUIState(instanceState: Bundle) { override fun initUIState(instanceState: Bundle) {
println("dummy impl") println("dummy impl")

View File

@ -49,6 +49,7 @@ import net.newpipe.newplayer.NewPlayer
import net.newpipe.newplayer.NewPlayerException import net.newpipe.newplayer.NewPlayerException
import net.newpipe.newplayer.RepeatMode import net.newpipe.newplayer.RepeatMode
import net.newpipe.newplayer.ui.ContentScale import net.newpipe.newplayer.ui.ContentScale
import net.newpipe.newplayer.utils.isInPowerSaveMode
import java.util.LinkedList import java.util.LinkedList
val VIDEOPLAYER_UI_STATE = "video_player_ui_state" val VIDEOPLAYER_UI_STATE = "video_player_ui_state"
@ -136,6 +137,15 @@ class NewPlayerViewModelImpl @Inject constructor(
private var mutableOnBackPressed = MutableSharedFlow<Unit>() private var mutableOnBackPressed = MutableSharedFlow<Unit>()
override val onBackPressed: SharedFlow<Unit> = mutableOnBackPressed.asSharedFlow() override val onBackPressed: SharedFlow<Unit> = mutableOnBackPressed.asSharedFlow()
override var deviceInPowerSaveMode: Boolean = false
get() = field
set(value) {
field = value
if (progressUpdaterJob?.isActive == true) {
resetProgressUpdatePeriodicallyJob()
}
}
private fun installNewPlayer() { private fun installNewPlayer() {
newPlayer?.let { newPlayer -> newPlayer?.let { newPlayer ->
viewModelScope.launch { viewModelScope.launch {
@ -335,7 +345,8 @@ class NewPlayerViewModelImpl @Inject constructor(
} }
if (!(newUiModeState == UIModeState.EMBEDDED_VIDEO_CONTROLLER_UI || if (!(newUiModeState == UIModeState.EMBEDDED_VIDEO_CONTROLLER_UI ||
newUiModeState == UIModeState.FULLSCREEN_VIDEO_CONTROLLER_UI)) { newUiModeState == UIModeState.FULLSCREEN_VIDEO_CONTROLLER_UI)
) {
uiVisibilityJob?.cancel() uiVisibilityJob?.cancel()
} else { } else {
resetHideUiDelayedJob() resetHideUiDelayedJob()
@ -387,7 +398,7 @@ class NewPlayerViewModelImpl @Inject constructor(
progressUpdaterJob = viewModelScope.launch { progressUpdaterJob = viewModelScope.launch {
while (true) { while (true) {
updateProgressOnce() updateProgressOnce()
delay(1000) delay(if (deviceInPowerSaveMode) 1000 else 1000 / 30/*fps*/)
} }
} }
} }

View File

@ -22,6 +22,7 @@ package net.newpipe.newplayer.ui
import android.app.Activity import android.app.Activity
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.os.Build
import android.util.Log import android.util.Log
import android.view.SurfaceView import android.view.SurfaceView
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
@ -54,6 +55,7 @@ import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.ui.videoplayer.VideoPlayerUi import net.newpipe.newplayer.ui.videoplayer.VideoPlayerUi
import net.newpipe.newplayer.utils.LockScreenOrientation import net.newpipe.newplayer.utils.LockScreenOrientation
import net.newpipe.newplayer.utils.getDefaultBrightness import net.newpipe.newplayer.utils.getDefaultBrightness
import net.newpipe.newplayer.utils.isInPowerSaveMode
import net.newpipe.newplayer.utils.setScreenBrightness import net.newpipe.newplayer.utils.setScreenBrightness
private const val TAG = "VideoPlayerUI" private const val TAG = "VideoPlayerUI"
@ -112,6 +114,13 @@ fun NewPlayerUI(
} }
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val isInPowerSaveMode = isInPowerSaveMode()
LaunchedEffect(key1 = isInPowerSaveMode) {
viewModel.deviceInPowerSaveMode = isInPowerSaveMode
}
}
if (uiState.uiMode.fitScreenRotation) { if (uiState.uiMode.fitScreenRotation) {
if (uiState.contentRatio < 1) { if (uiState.contentRatio < 1) {
LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)

View File

@ -29,6 +29,7 @@ import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -59,6 +60,8 @@ import net.newpipe.newplayer.ui.selection_ui.StreamSelectUI
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.utils.Thumbnail import net.newpipe.newplayer.utils.Thumbnail
import net.newpipe.newplayer.utils.getInsets import net.newpipe.newplayer.utils.getInsets
import net.newpipe.newplayer.utils.getLocale
import net.newpipe.newplayer.utils.getTimeStringFromMs
private val UI_ENTER_ANIMATION = fadeIn(tween(200)) private val UI_ENTER_ANIMATION = fadeIn(tween(200))
@ -75,6 +78,9 @@ fun lightAudioControlButtonColorScheme() = ButtonDefaults.buttonColors().copy(
@Composable @Composable
fun AudioPlayerUI(viewModel: NewPlayerViewModel, uiState: NewPlayerUIState) { fun AudioPlayerUI(viewModel: NewPlayerViewModel, uiState: NewPlayerUIState) {
val insets = getInsets() val insets = getInsets()
val locale = getLocale()!!
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -164,7 +170,25 @@ fun AudioPlayerUI(viewModel: NewPlayerViewModel, uiState: NewPlayerUIState) {
.fillMaxSize() .fillMaxSize()
.weight(0.2f) .weight(0.2f)
) )
NewPlayerSeeker(viewModel = viewModel, uiState = uiState) NewPlayerSeeker(viewModel = viewModel, uiState = uiState)
Row() {
Text(
getTimeStringFromMs(
uiState.playbackPositionInMs,
getLocale() ?: locale
)
)
Box(modifier = Modifier.fillMaxWidth().weight(1f))
Text(
getTimeStringFromMs(
uiState.durationInMs,
getLocale() ?: locale
)
)
}
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -195,6 +219,9 @@ fun AudioPlayerUI(viewModel: NewPlayerViewModel, uiState: NewPlayerUIState) {
@Composable @Composable
fun AudioPlayerUIPreview() { fun AudioPlayerUIPreview() {
VideoPlayerTheme { VideoPlayerTheme {
AudioPlayerUI(viewModel = NewPlayerViewModelDummy(), uiState = NewPlayerUIState.DUMMY) AudioPlayerUI(
viewModel = NewPlayerViewModelDummy(),
uiState = NewPlayerUIState.DUMMY.copy(uiMode = UIModeState.FULLSCREEN_AUDIO)
)
} }
} }

View File

@ -23,7 +23,9 @@ package net.newpipe.newplayer.ui.videoplayer.controller
import android.app.Activity import android.app.Activity
import androidx.annotation.OptIn import androidx.annotation.OptIn
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Fullscreen import androidx.compose.material.icons.filled.Fullscreen
import androidx.compose.material.icons.filled.FullscreenExit import androidx.compose.material.icons.filled.FullscreenExit
@ -38,6 +40,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import net.newpipe.newplayer.R import net.newpipe.newplayer.R
import net.newpipe.newplayer.model.EmbeddedUiConfig import net.newpipe.newplayer.model.EmbeddedUiConfig
@ -67,7 +70,13 @@ fun BottomUI(
val locale = getLocale()!! val locale = getLocale()!!
Text(getTimeStringFromMs(uiState.playbackPositionInMs, getLocale() ?: locale)) Text(getTimeStringFromMs(uiState.playbackPositionInMs, getLocale() ?: locale))
NewPlayerSeeker(modifier = Modifier.weight(1F), viewModel = viewModel, uiState = uiState) NewPlayerSeeker(
modifier = Modifier
.weight(1F)
.padding(start = 4.dp, end = 4.dp),
viewModel = viewModel,
uiState = uiState
)
Text(getTimeStringFromMs(uiState.durationInMs, getLocale() ?: locale)) Text(getTimeStringFromMs(uiState.durationInMs, getLocale() ?: locale))
@ -112,7 +121,7 @@ fun VideoPlayerControllerBottomUIPreview() {
viewModel = NewPlayerViewModelDummy(), viewModel = NewPlayerViewModelDummy(),
uiState = NewPlayerUIState.DUMMY.copy( uiState = NewPlayerUIState.DUMMY.copy(
uiMode = UIModeState.FULLSCREEN_VIDEO_CONTROLLER_UI, uiMode = UIModeState.FULLSCREEN_VIDEO_CONTROLLER_UI,
seekerPosition = 0.2f, seekerPosition = 0.0f,
playbackPositionInMs = 3 * 60 * 1000, playbackPositionInMs = 3 * 60 * 1000,
bufferedPercentage = 0.4f bufferedPercentage = 0.4f
), ),

View File

@ -24,25 +24,23 @@ import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.graphics.drawable.shapes.Shape
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.PowerManager
import android.view.WindowManager import android.view.WindowManager
import androidx.annotation.OptIn import androidx.annotation.OptIn
import androidx.compose.animation.core.withInfiniteAnimationFrameMillis import androidx.annotation.RequiresApi
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.displayCutout import androidx.compose.foundation.layout.displayCutout
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.union import androidx.compose.foundation.layout.union
import androidx.compose.foundation.layout.waterfall import androidx.compose.foundation.layout.waterfall
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
@ -184,6 +182,13 @@ fun Thumbnail(
} }
} }
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
@Composable
fun isInPowerSaveMode() =
(LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager)
.isPowerSaveMode
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
fun getPlaylistDurationInMS(playlist: List<MediaItem>) : Long { fun getPlaylistDurationInMS(playlist: List<MediaItem>) : Long {
var duration = 0L var duration = 0L