fix content ratio using compose

This commit is contained in:
Christian Schabesberger 2024-07-22 16:59:18 +02:00
parent 356744814c
commit 4089de7272
5 changed files with 106 additions and 16 deletions

View File

@ -0,0 +1,41 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
</profile>
</component>

View File

@ -24,6 +24,7 @@ import android.app.Application
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.util.Log
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
@ -40,16 +41,25 @@ import net.newpipe.newplayer.NewPlayer
val VIDEOPLAYER_UI_STATE = "video_player_ui_state" val VIDEOPLAYER_UI_STATE = "video_player_ui_state"
private const val TAG = "VideoPlayerViewModel"
@Parcelize @Parcelize
data class VideoPlayerUIState( data class VideoPlayerUIState(
val playing: Boolean, val playing: Boolean,
var fullscreen: Boolean, var fullscreen: Boolean,
var uiVissible: Boolean, var uiVissible: Boolean,
var contentRatio: Float var contentRatio: Float,
var minContentRatio: Float,
var maxContentRatio: Float
) : Parcelable { ) : Parcelable {
companion object { companion object {
val DEFAULT = VideoPlayerUIState( val DEFAULT = VideoPlayerUIState(
playing = false, fullscreen = false, uiVissible = false, 0F playing = false,
fullscreen = false,
uiVissible = false,
contentRatio = 0F,
minContentRatio = 4F / 3F,
maxContentRatio = 16F / 9F
) )
} }
} }
@ -59,6 +69,8 @@ interface VideoPlayerViewModel {
val player: Player? val player: Player?
val uiState: StateFlow<VideoPlayerUIState> val uiState: StateFlow<VideoPlayerUIState>
var listener: Listener? var listener: Listener?
var minContentRatio: Float
var maxContentRatio: Float
fun initUIState(instanceState: Bundle) fun initUIState(instanceState: Bundle)
fun play() fun play()
@ -78,7 +90,6 @@ interface VideoPlayerViewModel {
} }
} }
@HiltViewModel @HiltViewModel
class VideoPlayerViewModelImpl @Inject constructor( class VideoPlayerViewModelImpl @Inject constructor(
private val savedStateHandle: SavedStateHandle, private val savedStateHandle: SavedStateHandle,
@ -99,11 +110,36 @@ class VideoPlayerViewModelImpl @Inject constructor(
override val uiState = mutableUiState.asStateFlow() override val uiState = mutableUiState.asStateFlow()
override var listener: VideoPlayerViewModel.Listener? = null override var listener: VideoPlayerViewModel.Listener? = null
override val player:Player? override val player: Player?
get() = newPlayer?.player get() = newPlayer?.player
override var minContentRatio: Float
set(value) {
if (value <= 0 || mutableUiState.value.maxContentRatio < value)
Log.e(
TAG,
"Ignoring maxContentRatio: It must not be 0 or less and it may not be bigger then mmaxContentRatio. It was Set to: $value"
)
else
mutableUiState.update { it.copy(minContentRatio = value) }
}
get() = mutableUiState.value.minContentRatio
override var maxContentRatio: Float
get() = mutableUiState.value.maxContentRatio
set(value) {
if (value <= 0 || value < mutableUiState.value.minContentRatio)
Log.e(
TAG,
"Ignoring maxContentRatio: It must not be 0 or less and it may not be smaller then minContentRatio. It was Set to: $value"
)
else
mutableUiState.update { it.copy(maxContentRatio = value) }
}
private fun installExoPlayer() { private fun installExoPlayer() {
player?.let { player -> player?.let { player ->
Log.i(TAG, "Install player")
player.addListener(object : Player.Listener { player.addListener(object : Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) { override fun onIsPlayingChanged(isPlaying: Boolean) {
super.onIsPlayingChanged(isPlaying) super.onIsPlayingChanged(isPlaying)
@ -143,17 +179,17 @@ class VideoPlayerViewModelImpl @Inject constructor(
override fun onCleared() { override fun onCleared() {
super.onCleared() super.onCleared()
println("gurken viewmodel cleared") Log.d(TAG, "viewmodel cleared")
} }
@RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun initUIState(instanceState: Bundle) { override fun initUIState(instanceState: Bundle) {
val uiState = val uiState =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) instanceState.getParcelable(
instanceState.getParcelable(VIDEOPLAYER_UI_STATE, VideoPlayerUIState::class.java) VIDEOPLAYER_UI_STATE, VideoPlayerUIState::class.java
else )
instanceState.getParcelable(VIDEOPLAYER_UI_STATE) else instanceState.getParcelable(VIDEOPLAYER_UI_STATE)
uiState?.let { uiState -> uiState?.let { uiState ->
mutableUiState.update { mutableUiState.update {
@ -171,11 +207,11 @@ class VideoPlayerViewModelImpl @Inject constructor(
} }
override fun prevStream() { override fun prevStream() {
println("imeplement prev stream") Log.e(TAG, "imeplement prev stream")
} }
override fun nextStream() { override fun nextStream() {
println("implement next stream") Log.e(TAG, "implement next stream")
} }
override fun switchToEmbeddedView() { override fun switchToEmbeddedView() {
@ -196,7 +232,8 @@ class VideoPlayerViewModelImpl @Inject constructor(
override val player: Player? = null override val player: Player? = null
override val uiState = MutableStateFlow(VideoPlayerUIState.DEFAULT) override val uiState = MutableStateFlow(VideoPlayerUIState.DEFAULT)
override var listener: VideoPlayerViewModel.Listener? = null override var listener: VideoPlayerViewModel.Listener? = null
override var minContentRatio = 4F / 3F
override var maxContentRatio = 16F / 9F
override fun initUIState(instanceState: Bundle) { override fun initUIState(instanceState: Bundle) {
println("dummy impl") println("dummy impl")

View File

@ -1,6 +1,7 @@
package net.newpipe.newplayer.ui package net.newpipe.newplayer.ui
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
@ -16,9 +17,9 @@ import androidx.compose.ui.unit.dp
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
@Composable @Composable
fun VideoPlayerLoadingPlaceholder() { fun VideoPlayerLoadingPlaceholder(aspectRatio:Float = 3F/1F) {
Surface( Surface(
modifier = Modifier.fillMaxWidth().height(200.dp), modifier = Modifier.fillMaxWidth().aspectRatio(aspectRatio),
color = Color.Black color = Color.Black
) { ) {
Box { Box {

View File

@ -25,7 +25,9 @@ import android.view.SurfaceView
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
@ -53,8 +55,10 @@ import net.newpipe.newplayer.utils.findActivity
fun VideoPlayerUI( fun VideoPlayerUI(
viewModel: VideoPlayerViewModel?, viewModel: VideoPlayerViewModel?,
) { ) {
if (viewModel?.player == null) { if (viewModel == null) {
VideoPlayerLoadingPlaceholder() VideoPlayerLoadingPlaceholder()
} else if (viewModel.player == null) {
VideoPlayerLoadingPlaceholder(viewModel.uiState.collectAsState().value.maxContentRatio)
} else { } else {
val uiState by viewModel.uiState.collectAsState() val uiState by viewModel.uiState.collectAsState()
@ -106,8 +110,13 @@ fun VideoPlayerUI(
} }
// Set UI // Set UI
val aspectRatio =
uiState.contentRatio.coerceIn(uiState.minContentRatio, uiState.maxContentRatio)
Surface( Surface(
modifier = Modifier.fillMaxSize(), modifier = Modifier
.fillMaxWidth()
.aspectRatio(aspectRatio),
color = Color.Black color = Color.Black
) { ) {
AndroidView( AndroidView(

View File

@ -51,6 +51,8 @@ class MainActivity : AppCompatActivity() {
video_view.viewModel = videoPlayerViewModel video_view.viewModel = videoPlayerViewModel
videoPlayerViewModel.newPlayer = newPlayer videoPlayerViewModel.newPlayer = newPlayer
videoPlayerViewModel.maxContentRatio = 4F/3F
/* /*
video_view.fullScreenToggleListener = object : VideoPlayerView.FullScreenToggleListener { video_view.fullScreenToggleListener = object : VideoPlayerView.FullScreenToggleListener {
override fun fullscreenToggle(turnOn: Boolean) { override fun fullscreenToggle(turnOn: Boolean) {