diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index cdaeb62..6293d9d 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -18,7 +18,7 @@
# along with NewPlayer. If not, see .
[versions]
-agp = "8.5.0"
+agp = "8.5.1"
kotlin = "2.0.20-Beta2"
coreKtx = "1.13.1"
junit = "4.13.2"
@@ -31,7 +31,7 @@ constraintlayout = "2.1.4"
material3 = "1.2.1"
uiTooling = "1.6.8"
materialIconsExtendedAndroid = "1.7.0-beta05"
-media3Ui = "1.4.0-beta01"
+media3 = "1.3.1"
hiltAndroid = "2.51.1"
hiltCompiler = "1.2.0"
hiltNavigationCompose = "1.2.0"
@@ -39,7 +39,6 @@ lifecycleViewmodelCompose = "2.8.3"
kspVersion = "2.0.20-Beta2-1.0.23"
fragmentKtx = "1.8.1"
lifecycleRuntimeKtx = "2.8.3"
-composeBom = "2024.06.01"
kotlinParcelize = "2.0.20-Beta2"
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-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-media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3Ui" }
-androidx-media3-ui = { group = "androidx.media3", name = "media3-ui", 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 = "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" }
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" }
@@ -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" }
+
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
diff --git a/new-player/build.gradle.kts b/new-player/build.gradle.kts
index 1aa941e..263fa04 100644
--- a/new-player/build.gradle.kts
+++ b/new-player/build.gradle.kts
@@ -65,6 +65,7 @@ dependencies {
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.hilt.navigation.compose)
+ implementation(libs.androidx.media3.common)
ksp(libs.hilt.android.compiler)
ksp(libs.androidx.hilt.compiler)
diff --git a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt
index eb828c5..453c824 100644
--- a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt
+++ b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt
@@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.update
import net.newpipe.newplayer.utils.VideoSize
import kotlinx.parcelize.Parcelize
import net.newpipe.newplayer.NewPlayer
+import net.newpipe.newplayer.ui.ContentFitMode
val VIDEOPLAYER_UI_STATE = "video_player_ui_state"
@@ -47,19 +48,19 @@ private const val TAG = "VideoPlayerViewModel"
data class VideoPlayerUIState(
val playing: Boolean,
var fullscreen: Boolean,
- var uiVissible: Boolean,
- var contentRatio: Float,
- var minContentRatio: Float,
- var maxContentRatio: Float
+ var uiVisible: Boolean,
+ val contentRatio: Float,
+ val uiRatio: Float,
+ val contentFitMode: ContentFitMode
) : Parcelable {
companion object {
val DEFAULT = VideoPlayerUIState(
playing = false,
fullscreen = false,
- uiVissible = false,
+ uiVisible = false,
contentRatio = 0F,
- minContentRatio = 4F / 3F,
- maxContentRatio = 16F / 9F
+ uiRatio = 16F / 9F,
+ contentFitMode = ContentFitMode.FIT_INSIDE
)
}
}
@@ -68,7 +69,6 @@ interface VideoPlayerViewModel {
var newPlayer: NewPlayer?
val player: Player?
val uiState: StateFlow
- var listener: Listener?
var minContentRatio: Float
var maxContentRatio: Float
@@ -79,15 +79,6 @@ interface VideoPlayerViewModel {
fun nextStream()
fun switchToFullscreen()
fun switchToEmbeddedView()
-
- interface Listener {
- fun requestUpdateLayoutRatio(ratio: Float)
- }
-
- sealed class Events {
- object SwitchToFullscreen : Events()
- object SwitchToEmbeddedView : Events()
- }
}
@HiltViewModel
@@ -98,7 +89,6 @@ class VideoPlayerViewModelImpl @Inject constructor(
// private
private val mutableUiState = MutableStateFlow(VideoPlayerUIState.DEFAULT)
- private var current_video_size = VideoSize.DEFAULT
//interface
override var newPlayer: NewPlayer? = null
@@ -108,75 +98,71 @@ class VideoPlayerViewModelImpl @Inject constructor(
}
override val uiState = mutableUiState.asStateFlow()
- override var listener: VideoPlayerViewModel.Listener? = null
override val player: Player?
get() = newPlayer?.player
- override var minContentRatio: Float
+ override var minContentRatio: Float = 4F / 3F
set(value) {
- if (value <= 0 || mutableUiState.value.maxContentRatio < value)
+ if (value <= 0 || 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) }
+ else {
+ 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) {
- if (value <= 0 || value < mutableUiState.value.minContentRatio)
+ if (value <= 0 || 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) }
+ else {
+ field = value
+ mutableUiState.update { it.copy(uiRatio = getUiRatio()) }
+ }
}
private fun installExoPlayer() {
player?.let { player ->
- Log.i(TAG, "Install player")
+ Log.d(TAG, "Install player: ${player.videoSize.width}")
+
player.addListener(object : Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) {
super.onIsPlayingChanged(isPlaying)
-
+ Log.d(TAG, "Playing state changed. Is Playing: $isPlaying")
+ Log.d(TAG, "Gurken: ${VideoSize.fromMedia3VideoSize(player.videoSize)}")
mutableUiState.update {
it.copy(playing = isPlaying)
}
}
- // We need to updated the layout of our player view if the video ratio changes
- // However, this should be done differently depending on weather we are in
- // embedded or fullscreen view.
- // If we are in embedded view, we tell the mother layout (only ConstraintLayout supported!)
- // 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
- }
+ override fun onVideoSizeChanged(videoSize: androidx.media3.common.VideoSize) {
+ super.onVideoSizeChanged(videoSize)
+ println("gurken aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+ updateContentRatio(VideoSize.fromMedia3VideoSize(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() {
super.onCleared()
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 {
val dummy = object : VideoPlayerViewModel {
override var newPlayer: NewPlayer? = null
override val player: Player? = null
override val uiState = MutableStateFlow(VideoPlayerUIState.DEFAULT)
- override var listener: VideoPlayerViewModel.Listener? = null
override var minContentRatio = 4F / 3F
override var maxContentRatio = 16F / 9F
diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/ContentFitMode.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/ContentFitMode.kt
new file mode 100644
index 0000000..585bb92
--- /dev/null
+++ b/new-player/src/main/java/net/newpipe/newplayer/ui/ContentFitMode.kt
@@ -0,0 +1,8 @@
+package net.newpipe.newplayer.ui
+
+
+enum class ContentFitMode {
+ FILL,
+ FIT_INSIDE,
+ ZOOM
+}
diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerLoadingPlaceholder.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerLoadingPlaceholder.kt
index bd60582..1af153b 100644
--- a/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerLoadingPlaceholder.kt
+++ b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerLoadingPlaceholder.kt
@@ -17,13 +17,17 @@ import androidx.compose.ui.unit.dp
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
@Composable
-fun VideoPlayerLoadingPlaceholder(aspectRatio:Float = 3F/1F) {
+fun VideoPlayerLoadingPlaceholder(aspectRatio: Float = 3F / 1F) {
Surface(
- modifier = Modifier.fillMaxWidth().aspectRatio(aspectRatio),
+ modifier = Modifier
+ .fillMaxWidth()
+ .aspectRatio(aspectRatio),
color = Color.Black
) {
- Box {
- CircularProgressIndicator(modifier = Modifier.width(64.dp).align((Alignment.Center)))
+ Box(contentAlignment = Alignment.Center) {
+ CircularProgressIndicator(modifier = Modifier
+ .width(64.dp)
+ .align((Alignment.Center)))
}
}
}
diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt
index aa29e91..881eb21 100644
--- a/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt
+++ b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt
@@ -23,9 +23,9 @@ package net.newpipe.newplayer.ui
import android.content.pm.ActivityInfo
import android.view.SurfaceView
import androidx.activity.compose.BackHandler
-import androidx.activity.compose.rememberLauncherForActivityResult
-import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Surface
@@ -37,9 +37,9 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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.platform.LocalLifecycleOwner
import androidx.compose.ui.tooling.preview.Preview
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.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.utils.LockScreenOrientation
-import net.newpipe.newplayer.utils.findActivity
@Composable
fun VideoPlayerUI(
@@ -57,8 +56,8 @@ fun VideoPlayerUI(
) {
if (viewModel == null) {
VideoPlayerLoadingPlaceholder()
- } else if (viewModel.player == null) {
- VideoPlayerLoadingPlaceholder(viewModel.uiState.collectAsState().value.maxContentRatio)
+ } else if (viewModel.player == null || viewModel.uiState.collectAsState().value.contentRatio == 0.0F) {
+ VideoPlayerLoadingPlaceholder(viewModel.uiState.collectAsState().value.uiRatio)
} else {
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) {
println("gurken launch fullscreen: ${uiState.fullscreen}")
}
@@ -110,41 +101,54 @@ fun VideoPlayerUI(
}
// Set UI
- val aspectRatio =
- uiState.contentRatio.coerceIn(uiState.minContentRatio, uiState.maxContentRatio)
-
Surface(
modifier = Modifier
.fillMaxWidth()
- .aspectRatio(aspectRatio),
+ .aspectRatio(uiState.uiRatio),
color = Color.Black
) {
- AndroidView(
- modifier = Modifier.fillMaxSize(),
- factory = { context ->
- SurfaceView(context).also { view ->
- viewModel.player?.setVideoSurfaceView(view)
- }
- }, update = { view ->
- when (lifecycle) {
- Lifecycle.Event.ON_RESUME -> {
+ Box(contentAlignment = Alignment.Center){
+ AndroidView(
+ 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 ->
+ SurfaceView(context).also { view ->
viewModel.player?.setVideoSurfaceView(view)
}
+ }, update = { view ->
+ when (lifecycle) {
+ Lifecycle.Event.ON_RESUME -> {
+ viewModel.player?.setVideoSurfaceView(view)
+ }
- else -> Unit
- }
- })
+ else -> Unit
+ }
+ })
- VideoPlayerControllerUI(
- isPlaying = uiState.playing,
- fullscreen = uiState.fullscreen,
- play = viewModel::play,
- pause = viewModel::pause,
- prevStream = viewModel::prevStream,
- nextStream = viewModel::nextStream,
- switchToFullscreen = viewModel::switchToFullscreen,
- switchToEmbeddedView = viewModel::switchToEmbeddedView
- )
+ VideoPlayerControllerUI(
+ isPlaying = uiState.playing,
+ fullscreen = uiState.fullscreen,
+ play = viewModel::play,
+ pause = viewModel::pause,
+ prevStream = viewModel::prevStream,
+ nextStream = viewModel::nextStream,
+ switchToFullscreen = viewModel::switchToFullscreen,
+ switchToEmbeddedView = viewModel::switchToEmbeddedView
+ )
+ }
}
}
}
diff --git a/new-player/src/main/java/net/newpipe/newplayer/utils/VideoSize.kt b/new-player/src/main/java/net/newpipe/newplayer/utils/VideoSize.kt
index 8f4c3f8..3a7ddd2 100644
--- a/new-player/src/main/java/net/newpipe/newplayer/utils/VideoSize.kt
+++ b/new-player/src/main/java/net/newpipe/newplayer/utils/VideoSize.kt
@@ -20,6 +20,9 @@ data class VideoSize(
fun getRatio() =
(width * pixelWidthHeightRatio) / height
+ override fun toString() =
+ "VideoSize(width = $width, height = $height, pixelRatio = $pixelWidthHeightRatio, ratio = ${getRatio()})"
+
companion object {
val DEFAULT = VideoSize(0, 0, 1F)
diff --git a/test-app/src/main/java/net/newpipe/newplayer/testapp/MainActivity.kt b/test-app/src/main/java/net/newpipe/newplayer/testapp/MainActivity.kt
index a1487ff..4e907f9 100644
--- a/test-app/src/main/java/net/newpipe/newplayer/testapp/MainActivity.kt
+++ b/test-app/src/main/java/net/newpipe/newplayer/testapp/MainActivity.kt
@@ -98,6 +98,5 @@ class MainActivity : AppCompatActivity() {
)
insets
}
-
}
}
\ No newline at end of file