fix content ratio using compose
This commit is contained in:
parent
356744814c
commit
4089de7272
|
@ -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>
|
|
@ -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,
|
||||||
|
@ -102,8 +113,33 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
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")
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue