make embedded ui config foo have a more streamlinet implementation
This commit is contained in:
parent
888d518304
commit
8ad95be57a
|
@ -0,0 +1,4 @@
|
|||
kotlin version: 2.0.20-Beta2
|
||||
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
|
||||
1. Kotlin compile daemon is ready
|
||||
|
|
@ -55,13 +55,39 @@ enum class UIModeState {
|
|||
else -> false
|
||||
}
|
||||
|
||||
val systemUiVisible: Boolean
|
||||
val isStreamSelect: Boolean
|
||||
get() =
|
||||
when (this) {
|
||||
when(this) {
|
||||
EMBEDDED_VIDEO_STREAM_SELECT -> true
|
||||
FULLSCREEN_VIDEO_STREAM_SELECT -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
val isChapterSelect: Boolean
|
||||
get() =
|
||||
when(this) {
|
||||
EMBEDDED_VIDEO_CHAPTER_SELECT -> true
|
||||
FULLSCREEN_VIDEO_CHAPTER_SELECT -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
val systemInsetsVisible: Boolean
|
||||
get() =
|
||||
when(this) {
|
||||
FULLSCREEN_VIDEO -> false
|
||||
else -> true
|
||||
}
|
||||
|
||||
val fitScreenRotation: Boolean
|
||||
get() =
|
||||
when(this) {
|
||||
FULLSCREEN_VIDEO -> true
|
||||
FULLSCREEN_VIDEO_CONTROLLER_UI -> true
|
||||
FULLSCREEN_VIDEO_CHAPTER_SELECT -> true
|
||||
FULLSCREEN_VIDEO_STREAM_SELECT -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
// STATE TRANSITIONS
|
||||
|
||||
fun getControllerUiVisibleState() =
|
||||
|
@ -72,7 +98,6 @@ enum class UIModeState {
|
|||
else -> this
|
||||
}
|
||||
|
||||
|
||||
fun getUiHiddenState() =
|
||||
when (this) {
|
||||
FULLSCREEN_VIDEO -> FULLSCREEN_VIDEO
|
||||
|
|
|
@ -43,7 +43,7 @@ interface VideoPlayerViewModel {
|
|||
fun pause()
|
||||
fun prevStream()
|
||||
fun nextStream()
|
||||
fun switchToFullscreen()
|
||||
fun switchToFullscreen(embeddedUiConfig: EmbeddedUiConfig)
|
||||
fun switchToEmbeddedView()
|
||||
fun showUi()
|
||||
fun hideUi()
|
||||
|
@ -54,5 +54,6 @@ interface VideoPlayerViewModel {
|
|||
fun finishFastSeek()
|
||||
fun brightnessChange(changeRate: Float, systemBrightness: Float)
|
||||
fun volumeChange(changeRate: Float)
|
||||
fun onReportEmbeddedConfig(embeddedUiConfig: EmbeddedUiConfig?)
|
||||
fun openStreamSelection(selectChapter: Boolean)
|
||||
fun closeStreamSelection()
|
||||
}
|
|
@ -65,12 +65,15 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
private var uiVisibilityJob: Job? = null
|
||||
private var progressUpdaterJob: Job? = null
|
||||
|
||||
// this is necesary to restore the embedded view UI configuration when returning from fullscreen
|
||||
private var embeddedUiConfig: EmbeddedUiConfig? = null
|
||||
|
||||
private val audioManager =
|
||||
getSystemService(application.applicationContext, AudioManager::class.java)!!
|
||||
|
||||
init {
|
||||
val soundVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC).toFloat() /
|
||||
audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC).toFloat()
|
||||
val soundVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
.toFloat() / audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC).toFloat()
|
||||
mutableUiState.update {
|
||||
it.copy(soundVolume = soundVolume)
|
||||
}
|
||||
|
@ -154,7 +157,7 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
override fun onPlaylistMetadataChanged(mediaMetadata: MediaMetadata) {
|
||||
super.onPlaylistMetadataChanged(mediaMetadata)
|
||||
updatePlaylist()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
newPlayer?.let { newPlayer ->
|
||||
|
@ -163,7 +166,8 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
val currentMode = mutableUiState.value.uiMode.toPlayMode()
|
||||
if (currentMode != newMode) {
|
||||
mutableUiState.update {
|
||||
it.copy(uiMode = UIModeState.fromPlayMode(newMode))
|
||||
it.copy(uiMode = UIModeState.fromPlayMode(newMode),
|
||||
embeddedUiConfig = embeddedUiConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -353,18 +357,20 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onReportEmbeddedConfig(embeddedUiConfig: EmbeddedUiConfig?) {
|
||||
if (embeddedUiConfig == null) {
|
||||
mutableUiState.update {
|
||||
it.copy(embeddedUiConfig = null)
|
||||
}
|
||||
} else {
|
||||
if (uiState.value.embeddedUiConfig == null) {
|
||||
println("gurken: ${embeddedUiConfig}")
|
||||
mutableUiState.update {
|
||||
it.copy(embeddedUiConfig = embeddedUiConfig)
|
||||
}
|
||||
}
|
||||
override fun openStreamSelection(selectChapter: Boolean) {
|
||||
mutableUiState.update {
|
||||
it.copy(
|
||||
uiMode = if (selectChapter) it.uiMode.getChapterSelectUiState()
|
||||
else it.uiMode.getStreamSelectUiState()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun closeStreamSelection() {
|
||||
mutableUiState.update {
|
||||
it.copy(
|
||||
uiMode = it.uiMode.getUiHiddenState()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,10 +380,11 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
updateUiMode(UIModeState.EMBEDDED_VIDEO)
|
||||
}
|
||||
|
||||
override fun switchToFullscreen() {
|
||||
override fun switchToFullscreen(embeddedUiConfig: EmbeddedUiConfig) {
|
||||
uiVisibilityJob?.cancel()
|
||||
finishFastSeek()
|
||||
|
||||
this.embeddedUiConfig = embeddedUiConfig
|
||||
updateUiMode(UIModeState.FULLSCREEN_VIDEO)
|
||||
}
|
||||
|
||||
|
@ -405,8 +412,7 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
newPlayer?.let { newPlayer ->
|
||||
viewModelScope.launch {
|
||||
val playlist = getPlaylistItemsFromItemList(
|
||||
newPlayer.playlist,
|
||||
newPlayer.repository
|
||||
newPlayer.playlist, newPlayer.repository
|
||||
)
|
||||
mutableUiState.update {
|
||||
it.copy(playList = playlist)
|
||||
|
|
|
@ -29,7 +29,7 @@ open class VideoPlayerViewModelDummy : VideoPlayerViewModel {
|
|||
println("dummy impl")
|
||||
}
|
||||
|
||||
override fun switchToFullscreen() {
|
||||
override fun switchToFullscreen(embeddedUiConfig: EmbeddedUiConfig) {
|
||||
println("dummy impl")
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,11 @@ open class VideoPlayerViewModelDummy : VideoPlayerViewModel {
|
|||
println("dummy impl")
|
||||
}
|
||||
|
||||
override fun onReportEmbeddedConfig(embeddedUiConfig: EmbeddedUiConfig?) {
|
||||
override fun openStreamSelection(selectChapter: Boolean) {
|
||||
println("dummy impl")
|
||||
}
|
||||
|
||||
override fun closeStreamSelection() {
|
||||
println("dummy impl")
|
||||
}
|
||||
|
||||
|
|
|
@ -119,7 +119,9 @@ fun VideoPlayerControllerUI(
|
|||
.align(Alignment.TopStart)
|
||||
.fillMaxWidth()
|
||||
.defaultMinSize(minHeight = 45.dp)
|
||||
.padding(top = 4.dp, start = 16.dp, end = 16.dp)
|
||||
.padding(top = 4.dp, start = 16.dp, end = 16.dp),
|
||||
viewModel = viewModel,
|
||||
uiState = uiState
|
||||
)
|
||||
|
||||
BottomUI(
|
||||
|
|
|
@ -24,6 +24,7 @@ import android.app.Activity
|
|||
import android.content.pm.ActivityInfo
|
||||
import android.util.Log
|
||||
import android.view.SurfaceView
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
|
@ -37,7 +38,6 @@ import androidx.compose.runtime.collectAsState
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
@ -57,6 +57,7 @@ import net.newpipe.newplayer.model.EmbeddedUiConfig
|
|||
import net.newpipe.newplayer.model.VideoPlayerViewModel
|
||||
import net.newpipe.newplayer.model.VideoPlayerViewModelDummy
|
||||
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
|
||||
import net.newpipe.newplayer.ui.videoplayer.StreamSelectUI
|
||||
import net.newpipe.newplayer.utils.LockScreenOrientation
|
||||
import net.newpipe.newplayer.utils.getDefaultBrightness
|
||||
import net.newpipe.newplayer.utils.setScreenBrightness
|
||||
|
@ -89,37 +90,17 @@ fun VideoPlayerUI(
|
|||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
|
||||
val defaultBrightness = getDefaultBrightness(activity)
|
||||
val screenOrientation = activity.requestedOrientation
|
||||
|
||||
// Setup fullscreen
|
||||
|
||||
var embeddedUiConfig: EmbeddedUiConfig? by rememberSaveable {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
|
||||
LaunchedEffect(uiState.uiMode.fullscreen) {
|
||||
if (uiState.uiMode.fullscreen) {
|
||||
viewModel.onReportEmbeddedConfig(
|
||||
EmbeddedUiConfig(
|
||||
WindowCompat.getInsetsController(
|
||||
window,
|
||||
view
|
||||
).isAppearanceLightStatusBars,
|
||||
defaultBrightness,
|
||||
screenOrientation,
|
||||
)
|
||||
)
|
||||
} else {
|
||||
viewModel.onReportEmbeddedConfig(null)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(uiState.uiMode.fullscreen) {
|
||||
if (uiState.uiMode.fullscreen) {
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars =
|
||||
false
|
||||
} else {
|
||||
println("gurken dings")
|
||||
uiState.embeddedUiConfig?.let {
|
||||
println("gurken bumbs")
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars =
|
||||
it.systemBarInLightMode
|
||||
}
|
||||
|
@ -128,17 +109,16 @@ fun VideoPlayerUI(
|
|||
|
||||
// setup immersive mode
|
||||
LaunchedEffect(
|
||||
key1 = uiState.uiMode.controllerUiVisible,
|
||||
key2 = uiState.uiMode.fullscreen
|
||||
key1 = uiState.uiMode.systemInsetsVisible,
|
||||
) {
|
||||
if (uiState.uiMode.fullscreen && !uiState.uiMode.systemUiVisible) {
|
||||
if (uiState.uiMode.fullscreen && !uiState.uiMode.systemInsetsVisible) {
|
||||
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
|
||||
} else {
|
||||
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
|
||||
}
|
||||
}
|
||||
|
||||
if (uiState.uiMode.fullscreen) {
|
||||
if (uiState.uiMode.fitScreenRotation) {
|
||||
if (uiState.contentRatio < 1) {
|
||||
LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
|
||||
} else {
|
||||
|
@ -195,9 +175,19 @@ fun VideoPlayerUI(
|
|||
)
|
||||
}
|
||||
|
||||
// the checks if VideoPlayerControllerUI should be visible or not are done by
|
||||
// The VideoPlayerControllerUI composable itself. This is because Visibility of
|
||||
// the controller is more complicated than just using a simple if statement.
|
||||
VideoPlayerControllerUI(
|
||||
viewModel, uiState = uiState
|
||||
)
|
||||
|
||||
AnimatedVisibility(visible = uiState.uiMode.isStreamSelect) {
|
||||
StreamSelectUI(viewModel = viewModel, uiState = uiState, isChapterSelect = false)
|
||||
}
|
||||
AnimatedVisibility(visible = uiState.uiMode.isChapterSelect) {
|
||||
StreamSelectUI(viewModel = viewModel, uiState = uiState, isChapterSelect = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
package net.newpipe.newplayer.ui.videoplayer
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.LocaleConfig
|
||||
import android.icu.text.DecimalFormat
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
|
@ -38,6 +39,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.core.os.ConfigurationCompat
|
||||
|
@ -51,6 +53,7 @@ import net.newpipe.newplayer.ui.seeker.Seeker
|
|||
import net.newpipe.newplayer.ui.seeker.SeekerColors
|
||||
import net.newpipe.newplayer.ui.seeker.SeekerDefaults
|
||||
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
|
||||
import net.newpipe.newplayer.utils.getEmbeddedUiConfig
|
||||
import net.newpipe.newplayer.utils.getLocale
|
||||
import net.newpipe.newplayer.utils.getTimeStringFromMs
|
||||
import java.util.Locale
|
||||
|
@ -79,9 +82,14 @@ fun BottomUI(
|
|||
|
||||
Text(getTimeStringFromMs(uiState.durationInMs, getLocale() ?: Locale.US))
|
||||
|
||||
val embeddedUiConfig = getEmbeddedUiConfig(LocalContext.current as Activity)
|
||||
IconButton(
|
||||
onClick = if (uiState.uiMode.fullscreen) viewModel::switchToEmbeddedView
|
||||
else viewModel::switchToFullscreen
|
||||
else {
|
||||
{ // <- head of lambda ... yea kotlin is weird
|
||||
viewModel.switchToFullscreen(embeddedUiConfig)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (uiState.uiMode.fullscreen) Icons.Filled.FullscreenExit
|
||||
|
|
|
@ -80,7 +80,7 @@ fun StreamSelectUI(
|
|||
topBar = {
|
||||
if (isChapterSelect) {
|
||||
ChapterSelectTopBar(onClose = {
|
||||
TODO("implement me")
|
||||
viewModel.closeStreamSelection()
|
||||
})
|
||||
} else {
|
||||
StreamSelectTopBar()
|
||||
|
|
|
@ -44,11 +44,18 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import net.newpipe.newplayer.R
|
||||
import net.newpipe.newplayer.model.VideoPlayerUIState
|
||||
import net.newpipe.newplayer.model.VideoPlayerViewModel
|
||||
import net.newpipe.newplayer.model.VideoPlayerViewModelDummy
|
||||
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
|
||||
import net.newpipe.newplayer.ui.theme.video_player_onSurface
|
||||
|
||||
@Composable
|
||||
fun TopUI(modifier: Modifier) {
|
||||
fun TopUI(
|
||||
modifier: Modifier,
|
||||
viewModel: VideoPlayerViewModel,
|
||||
uiState: VideoPlayerUIState
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
@ -81,7 +88,7 @@ fun TopUI(modifier: Modifier) {
|
|||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = { /*TODO*/ },
|
||||
onClick = { viewModel.openStreamSelection(selectChapter = true) },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.MenuBook,
|
||||
|
@ -89,7 +96,7 @@ fun TopUI(modifier: Modifier) {
|
|||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = { /*TODO*/ },
|
||||
onClick = { viewModel.openStreamSelection(selectChapter = false) },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.List,
|
||||
|
@ -109,7 +116,7 @@ fun TopUI(modifier: Modifier) {
|
|||
fun VideoPlayerControllerTopUIPreview() {
|
||||
VideoPlayerTheme {
|
||||
Surface(color = Color.Black) {
|
||||
TopUI(modifier = Modifier)
|
||||
TopUI(modifier = Modifier, VideoPlayerViewModelDummy(), VideoPlayerUIState.DEFAULT)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
package net.newpipe.newplayer.ui.videoplayer.gesture_ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
@ -35,11 +36,13 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import net.newpipe.newplayer.model.VideoPlayerUIState
|
||||
import net.newpipe.newplayer.model.VideoPlayerViewModel
|
||||
import net.newpipe.newplayer.model.VideoPlayerViewModelDummy
|
||||
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
|
||||
import net.newpipe.newplayer.utils.getEmbeddedUiConfig
|
||||
|
||||
private const val TAG = "EmbeddedGestureUI"
|
||||
|
||||
|
@ -52,6 +55,8 @@ fun EmbeddedGestureUI(
|
|||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
val embeddedUiConfig = getEmbeddedUiConfig(LocalContext.current as Activity)
|
||||
|
||||
val handleMovement = { movement: TouchedPosition ->
|
||||
Log.d(TAG, "${movement.x}:${movement.y}")
|
||||
if (0 < movement.y) {
|
||||
|
@ -61,7 +66,7 @@ fun EmbeddedGestureUI(
|
|||
|
||||
// this check is there to allow a temporary move up in the downward gesture
|
||||
if (downwardMovementMode == false) {
|
||||
viewModel.switchToFullscreen()
|
||||
viewModel.switchToFullscreen(embeddedUiConfig)
|
||||
} else {
|
||||
viewModel.embeddedDraggedDown(movement.y)
|
||||
}
|
||||
|
|
|
@ -31,7 +31,10 @@ import androidx.compose.runtime.LaunchedEffect
|
|||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.os.ConfigurationCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import net.newpipe.newplayer.model.EmbeddedUiConfig
|
||||
import java.util.Locale
|
||||
|
||||
@Composable
|
||||
|
@ -44,14 +47,14 @@ fun LockScreenOrientation(orientation: Int) {
|
|||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
fun getDefaultBrightness(activity: Activity) : Float {
|
||||
fun getDefaultBrightness(activity: Activity): Float {
|
||||
val window = activity.window
|
||||
val layout = window.attributes as WindowManager.LayoutParams
|
||||
return if(layout.screenBrightness < 0) 0.5f else layout.screenBrightness
|
||||
return if (layout.screenBrightness < 0) 0.5f else layout.screenBrightness
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
fun setScreenBrightness(value:Float, activity: Activity) {
|
||||
fun setScreenBrightness(value: Float, activity: Activity) {
|
||||
val window = activity.window
|
||||
val layout = window.attributes as WindowManager.LayoutParams
|
||||
layout.screenBrightness = value
|
||||
|
@ -72,6 +75,24 @@ fun getLocale(): Locale? {
|
|||
return ConfigurationCompat.getLocales(configuration).get(0)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
fun getEmbeddedUiConfig(activity: Activity): EmbeddedUiConfig {
|
||||
val window = activity.window
|
||||
val view = LocalView.current
|
||||
|
||||
val isLightStatusBar = WindowCompat.getInsetsController(
|
||||
window,
|
||||
view
|
||||
).isAppearanceLightStatusBars
|
||||
val screenOrientation = activity.requestedOrientation
|
||||
val defaultBrightness = getDefaultBrightness(activity)
|
||||
return EmbeddedUiConfig(
|
||||
systemBarInLightMode = isLightStatusBar,
|
||||
brightness = defaultBrightness,
|
||||
screenOrientation = screenOrientation
|
||||
)
|
||||
}
|
||||
|
||||
private const val HOURS_PER_DAY = 24
|
||||
private const val MINUTES_PER_HOUR = 60
|
||||
|
|
Loading…
Reference in New Issue