onVideoSizeChanged is not cought anymore

This commit is contained in:
Christian Schabesberger 2024-07-22 20:13:27 +02:00
parent 4089de7272
commit 8bed5b701d
8 changed files with 120 additions and 105 deletions

View File

@ -18,7 +18,7 @@
# along with NewPlayer. If not, see <http://www.gnu.org/licenses/>. # along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
[versions] [versions]
agp = "8.5.0" agp = "8.5.1"
kotlin = "2.0.20-Beta2" kotlin = "2.0.20-Beta2"
coreKtx = "1.13.1" coreKtx = "1.13.1"
junit = "4.13.2" junit = "4.13.2"
@ -31,7 +31,7 @@ constraintlayout = "2.1.4"
material3 = "1.2.1" material3 = "1.2.1"
uiTooling = "1.6.8" uiTooling = "1.6.8"
materialIconsExtendedAndroid = "1.7.0-beta05" materialIconsExtendedAndroid = "1.7.0-beta05"
media3Ui = "1.4.0-beta01" media3 = "1.3.1"
hiltAndroid = "2.51.1" hiltAndroid = "2.51.1"
hiltCompiler = "1.2.0" hiltCompiler = "1.2.0"
hiltNavigationCompose = "1.2.0" hiltNavigationCompose = "1.2.0"
@ -39,7 +39,6 @@ lifecycleViewmodelCompose = "2.8.3"
kspVersion = "2.0.20-Beta2-1.0.23" kspVersion = "2.0.20-Beta2-1.0.23"
fragmentKtx = "1.8.1" fragmentKtx = "1.8.1"
lifecycleRuntimeKtx = "2.8.3" lifecycleRuntimeKtx = "2.8.3"
composeBom = "2024.06.01"
kotlinParcelize = "2.0.20-Beta2" kotlinParcelize = "2.0.20-Beta2"
newplayer = "master-SNAPSHOT" newplayer = "master-SNAPSHOT"
@ -56,8 +55,9 @@ androidx-activity-compose = { group = "androidx.activity", name = "activity-comp
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "uiTooling" } androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "uiTooling" }
androidx-material-icons-extended-android = { group = "androidx.compose.material", name = "material-icons-extended-android", version.ref = "materialIconsExtendedAndroid" } androidx-material-icons-extended-android = { group = "androidx.compose.material", name = "material-icons-extended-android", version.ref = "materialIconsExtendedAndroid" }
androidx-media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3Ui" } androidx-media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" }
androidx-media3-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "media3Ui" } androidx-media3-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "media3" }
androidx-media3-common = { group = "androidx.media3", name = "media3-common", version.ref = "media3" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hiltAndroid" } hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hiltAndroid" }
androidx-hilt-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hiltCompiler" } androidx-hilt-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hiltCompiler" }
androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigationCompose" } androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
@ -75,6 +75,7 @@ androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit
newplayer = { group = "com.github.theScrabi.NewPlayer", name = "new-player", version.ref = "newplayer" } newplayer = { group = "com.github.theScrabi.NewPlayer", name = "new-player", version.ref = "newplayer" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

View File

@ -65,6 +65,7 @@ dependencies {
implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.hilt.navigation.compose)
implementation(libs.androidx.media3.common)
ksp(libs.hilt.android.compiler) ksp(libs.hilt.android.compiler)
ksp(libs.androidx.hilt.compiler) ksp(libs.androidx.hilt.compiler)

View File

@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.update
import net.newpipe.newplayer.utils.VideoSize import net.newpipe.newplayer.utils.VideoSize
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import net.newpipe.newplayer.NewPlayer import net.newpipe.newplayer.NewPlayer
import net.newpipe.newplayer.ui.ContentFitMode
val VIDEOPLAYER_UI_STATE = "video_player_ui_state" val VIDEOPLAYER_UI_STATE = "video_player_ui_state"
@ -47,19 +48,19 @@ private const val TAG = "VideoPlayerViewModel"
data class VideoPlayerUIState( data class VideoPlayerUIState(
val playing: Boolean, val playing: Boolean,
var fullscreen: Boolean, var fullscreen: Boolean,
var uiVissible: Boolean, var uiVisible: Boolean,
var contentRatio: Float, val contentRatio: Float,
var minContentRatio: Float, val uiRatio: Float,
var maxContentRatio: Float val contentFitMode: ContentFitMode
) : Parcelable { ) : Parcelable {
companion object { companion object {
val DEFAULT = VideoPlayerUIState( val DEFAULT = VideoPlayerUIState(
playing = false, playing = false,
fullscreen = false, fullscreen = false,
uiVissible = false, uiVisible = false,
contentRatio = 0F, contentRatio = 0F,
minContentRatio = 4F / 3F, uiRatio = 16F / 9F,
maxContentRatio = 16F / 9F contentFitMode = ContentFitMode.FIT_INSIDE
) )
} }
} }
@ -68,7 +69,6 @@ interface VideoPlayerViewModel {
var newPlayer: NewPlayer? var newPlayer: NewPlayer?
val player: Player? val player: Player?
val uiState: StateFlow<VideoPlayerUIState> val uiState: StateFlow<VideoPlayerUIState>
var listener: Listener?
var minContentRatio: Float var minContentRatio: Float
var maxContentRatio: Float var maxContentRatio: Float
@ -79,15 +79,6 @@ interface VideoPlayerViewModel {
fun nextStream() fun nextStream()
fun switchToFullscreen() fun switchToFullscreen()
fun switchToEmbeddedView() fun switchToEmbeddedView()
interface Listener {
fun requestUpdateLayoutRatio(ratio: Float)
}
sealed class Events {
object SwitchToFullscreen : Events()
object SwitchToEmbeddedView : Events()
}
} }
@HiltViewModel @HiltViewModel
@ -98,7 +89,6 @@ class VideoPlayerViewModelImpl @Inject constructor(
// private // private
private val mutableUiState = MutableStateFlow(VideoPlayerUIState.DEFAULT) private val mutableUiState = MutableStateFlow(VideoPlayerUIState.DEFAULT)
private var current_video_size = VideoSize.DEFAULT
//interface //interface
override var newPlayer: NewPlayer? = null override var newPlayer: NewPlayer? = null
@ -108,75 +98,71 @@ class VideoPlayerViewModelImpl @Inject constructor(
} }
override val uiState = mutableUiState.asStateFlow() override val uiState = mutableUiState.asStateFlow()
override var listener: VideoPlayerViewModel.Listener? = null
override val player: Player? override val player: Player?
get() = newPlayer?.player get() = newPlayer?.player
override var minContentRatio: Float override var minContentRatio: Float = 4F / 3F
set(value) { set(value) {
if (value <= 0 || mutableUiState.value.maxContentRatio < value) if (value <= 0 || maxContentRatio < value)
Log.e( Log.e(
TAG, TAG,
"Ignoring maxContentRatio: It must not be 0 or less and it may not be bigger then mmaxContentRatio. It was Set to: $value" "Ignoring maxContentRatio: It must not be 0 or less and it may not be bigger then mmaxContentRatio. It was Set to: $value"
) )
else else {
mutableUiState.update { it.copy(minContentRatio = value) } field = value
mutableUiState.update { it.copy(uiRatio = getUiRatio()) }
}
} }
get() = mutableUiState.value.minContentRatio
override var maxContentRatio: Float
get() = mutableUiState.value.maxContentRatio override var maxContentRatio: Float = 16F / 9F
set(value) { set(value) {
if (value <= 0 || value < mutableUiState.value.minContentRatio) if (value <= 0 || value < minContentRatio)
Log.e( Log.e(
TAG, TAG,
"Ignoring maxContentRatio: It must not be 0 or less and it may not be smaller then minContentRatio. It was Set to: $value" "Ignoring maxContentRatio: It must not be 0 or less and it may not be smaller then minContentRatio. It was Set to: $value"
) )
else else {
mutableUiState.update { it.copy(maxContentRatio = value) } field = value
mutableUiState.update { it.copy(uiRatio = getUiRatio()) }
}
} }
private fun installExoPlayer() { private fun installExoPlayer() {
player?.let { player -> player?.let { player ->
Log.i(TAG, "Install player") Log.d(TAG, "Install player: ${player.videoSize.width}")
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)
Log.d(TAG, "Playing state changed. Is Playing: $isPlaying")
Log.d(TAG, "Gurken: ${VideoSize.fromMedia3VideoSize(player.videoSize)}")
mutableUiState.update { mutableUiState.update {
it.copy(playing = isPlaying) it.copy(playing = isPlaying)
} }
} }
// We need to updated the layout of our player view if the video ratio changes override fun onVideoSizeChanged(videoSize: androidx.media3.common.VideoSize) {
// However, this should be done differently depending on weather we are in super.onVideoSizeChanged(videoSize)
// embedded or fullscreen view. println("gurken aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
// If we are in embedded view, we tell the mother layout (only ConstraintLayout supported!) updateContentRatio(VideoSize.fromMedia3VideoSize(videoSize))
// to change the ratio of the whole player view.
// If we are in fullscreen we only want to change the ratio of the SurfaceView
override fun onVideoSizeChanged(media3VideoSize: androidx.media3.common.VideoSize) {
super.onVideoSizeChanged(media3VideoSize)
val videoSize = VideoSize.fromMedia3VideoSize(media3VideoSize)
if (current_video_size != videoSize) {
val newRatio = videoSize.getRatio()
if (current_video_size.getRatio() != newRatio) {
mutableUiState.update {
it.copy(contentRatio = newRatio)
}
if (!mutableUiState.value.fullscreen) {
listener?.requestUpdateLayoutRatio(newRatio)
}
}
current_video_size = videoSize
}
} }
}) })
} }
} }
fun updateContentRatio(videoSize: VideoSize) {
val newRatio = videoSize.getRatio()
Log.d(TAG, "Update Content ratio: $newRatio")
mutableUiState.update {
it.copy(
contentRatio = newRatio,
uiRatio = getUiRatio()
)
}
}
override fun onCleared() { override fun onCleared() {
super.onCleared() super.onCleared()
Log.d(TAG, "viewmodel cleared") Log.d(TAG, "viewmodel cleared")
@ -226,12 +212,21 @@ class VideoPlayerViewModelImpl @Inject constructor(
} }
} }
private fun getUiRatio() =
player?.let { player ->
val videoRatio = VideoSize.fromMedia3VideoSize(player.videoSize).getRatio()
return if (videoRatio.isNaN())
minContentRatio
else
videoRatio.coerceIn(minContentRatio, maxContentRatio)
} ?: minContentRatio
companion object { companion object {
val dummy = object : VideoPlayerViewModel { val dummy = object : VideoPlayerViewModel {
override var newPlayer: NewPlayer? = null override var newPlayer: NewPlayer? = null
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 minContentRatio = 4F / 3F override var minContentRatio = 4F / 3F
override var maxContentRatio = 16F / 9F override var maxContentRatio = 16F / 9F

View File

@ -0,0 +1,8 @@
package net.newpipe.newplayer.ui
enum class ContentFitMode {
FILL,
FIT_INSIDE,
ZOOM
}

View File

@ -17,13 +17,17 @@ import androidx.compose.ui.unit.dp
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
@Composable @Composable
fun VideoPlayerLoadingPlaceholder(aspectRatio:Float = 3F/1F) { fun VideoPlayerLoadingPlaceholder(aspectRatio: Float = 3F / 1F) {
Surface( Surface(
modifier = Modifier.fillMaxWidth().aspectRatio(aspectRatio), modifier = Modifier
.fillMaxWidth()
.aspectRatio(aspectRatio),
color = Color.Black color = Color.Black
) { ) {
Box { Box(contentAlignment = Alignment.Center) {
CircularProgressIndicator(modifier = Modifier.width(64.dp).align((Alignment.Center))) CircularProgressIndicator(modifier = Modifier
.width(64.dp)
.align((Alignment.Center)))
} }
} }
} }

View File

@ -23,9 +23,9 @@ package net.newpipe.newplayer.ui
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.view.SurfaceView import android.view.SurfaceView
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.layout.Box
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
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.material3.Surface import androidx.compose.material3.Surface
@ -37,9 +37,9 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
@ -49,7 +49,6 @@ import net.newpipe.newplayer.model.VideoPlayerViewModel
import net.newpipe.newplayer.model.VideoPlayerViewModelImpl import net.newpipe.newplayer.model.VideoPlayerViewModelImpl
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.utils.LockScreenOrientation import net.newpipe.newplayer.utils.LockScreenOrientation
import net.newpipe.newplayer.utils.findActivity
@Composable @Composable
fun VideoPlayerUI( fun VideoPlayerUI(
@ -57,8 +56,8 @@ fun VideoPlayerUI(
) { ) {
if (viewModel == null) { if (viewModel == null) {
VideoPlayerLoadingPlaceholder() VideoPlayerLoadingPlaceholder()
} else if (viewModel.player == null) { } else if (viewModel.player == null || viewModel.uiState.collectAsState().value.contentRatio == 0.0F) {
VideoPlayerLoadingPlaceholder(viewModel.uiState.collectAsState().value.maxContentRatio) VideoPlayerLoadingPlaceholder(viewModel.uiState.collectAsState().value.uiRatio)
} else { } else {
val uiState by viewModel.uiState.collectAsState() val uiState by viewModel.uiState.collectAsState()
@ -88,14 +87,6 @@ fun VideoPlayerUI(
} }
} }
val fullscreenLauncher =
rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
println("gurken returned for result")
viewModel.initUIState(result.data?.extras!!)
}
LaunchedEffect(key1 = uiState.fullscreen) { LaunchedEffect(key1 = uiState.fullscreen) {
println("gurken launch fullscreen: ${uiState.fullscreen}") println("gurken launch fullscreen: ${uiState.fullscreen}")
} }
@ -110,17 +101,29 @@ fun VideoPlayerUI(
} }
// Set UI // Set UI
val aspectRatio =
uiState.contentRatio.coerceIn(uiState.minContentRatio, uiState.maxContentRatio)
Surface( Surface(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.aspectRatio(aspectRatio), .aspectRatio(uiState.uiRatio),
color = Color.Black color = Color.Black
) { ) {
Box(contentAlignment = Alignment.Center){
AndroidView( AndroidView(
modifier = Modifier.fillMaxSize(), modifier = Modifier.also { modifier ->
when (uiState.contentFitMode) {
ContentFitMode.FILL -> modifier.fillMaxSize()
ContentFitMode.FIT_INSIDE -> if (uiState.contentRatio < uiState.uiRatio) {
modifier.fillMaxHeight().aspectRatio(uiState.contentRatio)
} else if (uiState.uiRatio < uiState.contentRatio) {
modifier.fillMaxWidth().aspectRatio(uiState.contentRatio)
}
ContentFitMode.ZOOM -> if(uiState.uiRatio < uiState.contentRatio) {
modifier.fillMaxHeight().aspectRatio(uiState.contentRatio)
} else if (uiState.contentRatio < uiState.uiRatio) {
modifier.fillMaxWidth().aspectRatio(uiState.contentRatio)
}
}
},
factory = { context -> factory = { context ->
SurfaceView(context).also { view -> SurfaceView(context).also { view ->
viewModel.player?.setVideoSurfaceView(view) viewModel.player?.setVideoSurfaceView(view)
@ -147,6 +150,7 @@ fun VideoPlayerUI(
) )
} }
} }
}
} }
@Preview(device = "spec:width=1080px,height=700px,dpi=440,orientation=landscape") @Preview(device = "spec:width=1080px,height=700px,dpi=440,orientation=landscape")

View File

@ -20,6 +20,9 @@ data class VideoSize(
fun getRatio() = fun getRatio() =
(width * pixelWidthHeightRatio) / height (width * pixelWidthHeightRatio) / height
override fun toString() =
"VideoSize(width = $width, height = $height, pixelRatio = $pixelWidthHeightRatio, ratio = ${getRatio()})"
companion object { companion object {
val DEFAULT = VideoSize(0, 0, 1F) val DEFAULT = VideoSize(0, 0, 1F)

View File

@ -98,6 +98,5 @@ class MainActivity : AppCompatActivity() {
) )
insets insets
} }
} }
} }