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
val embeddedPlayerDraggedDownBy: SharedFlow<Float>
val onBackPressed: SharedFlow<Unit>
var deviceInPowerSaveMode: Boolean
fun initUIState(instanceState: Bundle)
fun play()

View File

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

View File

@ -49,6 +49,7 @@ import net.newpipe.newplayer.NewPlayer
import net.newpipe.newplayer.NewPlayerException
import net.newpipe.newplayer.RepeatMode
import net.newpipe.newplayer.ui.ContentScale
import net.newpipe.newplayer.utils.isInPowerSaveMode
import java.util.LinkedList
val VIDEOPLAYER_UI_STATE = "video_player_ui_state"
@ -136,6 +137,15 @@ class NewPlayerViewModelImpl @Inject constructor(
private var mutableOnBackPressed = MutableSharedFlow<Unit>()
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() {
newPlayer?.let { newPlayer ->
viewModelScope.launch {
@ -335,7 +345,8 @@ class NewPlayerViewModelImpl @Inject constructor(
}
if (!(newUiModeState == UIModeState.EMBEDDED_VIDEO_CONTROLLER_UI ||
newUiModeState == UIModeState.FULLSCREEN_VIDEO_CONTROLLER_UI)) {
newUiModeState == UIModeState.FULLSCREEN_VIDEO_CONTROLLER_UI)
) {
uiVisibilityJob?.cancel()
} else {
resetHideUiDelayedJob()
@ -387,7 +398,7 @@ class NewPlayerViewModelImpl @Inject constructor(
progressUpdaterJob = viewModelScope.launch {
while (true) {
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.content.pm.ActivityInfo
import android.os.Build
import android.util.Log
import android.view.SurfaceView
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.utils.LockScreenOrientation
import net.newpipe.newplayer.utils.getDefaultBrightness
import net.newpipe.newplayer.utils.isInPowerSaveMode
import net.newpipe.newplayer.utils.setScreenBrightness
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.contentRatio < 1) {
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.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
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.utils.Thumbnail
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))
@ -75,6 +78,9 @@ fun lightAudioControlButtonColorScheme() = ButtonDefaults.buttonColors().copy(
@Composable
fun AudioPlayerUI(viewModel: NewPlayerViewModel, uiState: NewPlayerUIState) {
val insets = getInsets()
val locale = getLocale()!!
Box(
modifier = Modifier
.fillMaxSize()
@ -164,7 +170,25 @@ fun AudioPlayerUI(viewModel: NewPlayerViewModel, uiState: NewPlayerUIState) {
.fillMaxSize()
.weight(0.2f)
)
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(
modifier = Modifier
.fillMaxSize()
@ -195,6 +219,9 @@ fun AudioPlayerUI(viewModel: NewPlayerViewModel, uiState: NewPlayerUIState) {
@Composable
fun AudioPlayerUIPreview() {
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 androidx.annotation.OptIn
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Fullscreen
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.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.media3.common.util.UnstableApi
import net.newpipe.newplayer.R
import net.newpipe.newplayer.model.EmbeddedUiConfig
@ -67,7 +70,13 @@ fun BottomUI(
val locale = getLocale()!!
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))
@ -112,7 +121,7 @@ fun VideoPlayerControllerBottomUIPreview() {
viewModel = NewPlayerViewModelDummy(),
uiState = NewPlayerUIState.DUMMY.copy(
uiMode = UIModeState.FULLSCREEN_VIDEO_CONTROLLER_UI,
seekerPosition = 0.2f,
seekerPosition = 0.0f,
playbackPositionInMs = 3 * 60 * 1000,
bufferedPercentage = 0.4f
),

View File

@ -24,25 +24,23 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.graphics.drawable.shapes.Shape
import android.net.Uri
import android.os.Build
import android.os.PowerManager
import android.view.WindowManager
import androidx.annotation.OptIn
import androidx.compose.animation.core.withInfiniteAnimationFrameMillis
import androidx.annotation.RequiresApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.displayCutout
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.union
import androidx.compose.foundation.layout.waterfall
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
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)
fun getPlaylistDurationInMS(playlist: List<MediaItem>) : Long {
var duration = 0L