experimental fullscreen videoplayeractivity
This commit is contained in:
parent
66b49e225a
commit
0d85401cdd
|
@ -4,6 +4,14 @@
|
|||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2024-07-17T11:02:47.259317096Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="LocalEmulator" identifier="path=/home/schabi/.android/avd/Medium_Phone_API_33.avd" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
|
|
|
@ -46,6 +46,9 @@ android {
|
|||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
@ -64,6 +67,11 @@ android {
|
|||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -83,6 +91,15 @@ dependencies {
|
|||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
implementation(libs.androidx.foundation)
|
||||
implementation(libs.androidx.fragment.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
ksp(libs.hilt.android.compiler)
|
||||
ksp(libs.androidx.hilt.compiler)
|
||||
implementation(libs.androidx.hilt.navigation.compose)
|
||||
|
|
|
@ -1,27 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- NewPlayer
|
||||
|
||||
@author Christian Schabesberger
|
||||
|
||||
Copyright (C) NewPipe e.V. 2024 <code(at)newpipe-ev.de>
|
||||
|
||||
NewPlayer is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
NewPlayer is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".NewPlayerApp"
|
||||
android:allowBackup="true"
|
||||
|
@ -33,6 +15,11 @@
|
|||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.NewPlayer"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".VideoPlayerActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/title_activity_video_player"
|
||||
android:theme="@style/noAnimTheme" />
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package net.newpipe.newplayer
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import net.newpipe.newplayer.model.VideoPlayerViewModel
|
||||
import net.newpipe.newplayer.model.VideoPlayerViewModelImpl
|
||||
import net.newpipe.newplayer.ui.VideoPlayerUI
|
||||
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
|
||||
|
||||
@AndroidEntryPoint
|
||||
class VideoPlayerActivity : ComponentActivity() {
|
||||
|
||||
private val viewModel: VideoPlayerViewModel by viewModels<VideoPlayerViewModelImpl>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
VideoPlayerTheme {
|
||||
VideoPlayerUI(viewModel = viewModel, isFullscreen = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,12 +28,14 @@ import dagger.Provides
|
|||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ViewModelComponent
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(ViewModelComponent::class)
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object VideoPlayerComponent {
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideVideoPlayer(app: Application) : Player {
|
||||
return ExoPlayer.Builder(app).build()
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ class VideoPlayerFragment() : Fragment() {
|
|||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
VideoPlayerTheme {
|
||||
VideoPlayerUI(viewModel)
|
||||
VideoPlayerUI(viewModel = viewModel, isFullscreen = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
package net.newpipe.newplayer.model
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.media3.common.MediaItem
|
||||
|
@ -32,20 +33,15 @@ import javax.inject.Inject
|
|||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import net.newpipe.newplayer.VideoPlayerActivity
|
||||
import net.newpipe.newplayer.utils.VideoSize
|
||||
|
||||
data class VideoPlayerUIState(
|
||||
val playing: Boolean,
|
||||
var fullscreen: Boolean,
|
||||
var uiVissible: Boolean,
|
||||
var contentRatio: Float
|
||||
val playing: Boolean, var fullscreen: Boolean, var uiVissible: Boolean, var contentRatio: Float
|
||||
) {
|
||||
companion object {
|
||||
val DEFAULT = VideoPlayerUIState(
|
||||
playing = false,
|
||||
fullscreen = false,
|
||||
uiVissible = false,
|
||||
0F
|
||||
playing = false, fullscreen = false, uiVissible = false, 0F
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +84,12 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
var current_video_size = VideoSize.DEFAULT
|
||||
|
||||
init {
|
||||
player.prepare()
|
||||
|
||||
println("gurken $this")
|
||||
|
||||
if (player.playbackState == Player.STATE_IDLE) {
|
||||
player.prepare()
|
||||
}
|
||||
player.addListener(object : Player.Listener {
|
||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||
super.onIsPlayingChanged(isPlaying)
|
||||
|
@ -109,13 +110,13 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
|
||||
val videoSize = VideoSize.fromMedia3VideoSize(media3VideoSize)
|
||||
|
||||
if(current_video_size != videoSize) {
|
||||
if (current_video_size != videoSize) {
|
||||
val newRatio = videoSize.getRatio()
|
||||
if(current_video_size.getRatio() != newRatio) {
|
||||
if (current_video_size.getRatio() != newRatio) {
|
||||
mutableUiState.update {
|
||||
it.copy(contentRatio = newRatio)
|
||||
}
|
||||
if(!mutableUiState.value.fullscreen) {
|
||||
if (!mutableUiState.value.fullscreen) {
|
||||
listener?.requestUpdateLayoutRatio(newRatio)
|
||||
}
|
||||
}
|
||||
|
@ -125,17 +126,15 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
})
|
||||
|
||||
player.setMediaItem(MediaItem.fromUri(app.getString(R.string.ccc_6502_video)))
|
||||
player.playWhenReady = true
|
||||
//player.playWhenReady = true
|
||||
}
|
||||
|
||||
override fun play() {
|
||||
println("gurken Play")
|
||||
player.play()
|
||||
//player.play()
|
||||
}
|
||||
|
||||
override fun pause() {
|
||||
println("gurken pause")
|
||||
player.pause()
|
||||
//player.pause()
|
||||
}
|
||||
|
||||
override fun prevStream() {
|
||||
|
@ -156,7 +155,7 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
mutableUiState.update {
|
||||
it.copy(fullscreen = true)
|
||||
}
|
||||
listener?.switchToFullscreen()
|
||||
//listener?.switchToFullscreen()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
|
|
|
@ -82,11 +82,12 @@ import androidx.compose.ui.unit.sp
|
|||
import net.newpipe.newplayer.R
|
||||
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
|
||||
import net.newpipe.newplayer.ui.theme.video_player_onSurface
|
||||
import net.newpipe.newplayer.utils.LockScreenOrientation
|
||||
|
||||
@Composable
|
||||
fun VideoPlayerControllerUI(
|
||||
isPlaying: Boolean,
|
||||
isFullscreen: Boolean,
|
||||
fullscreen: Boolean,
|
||||
play: () -> Unit,
|
||||
pause: () -> Unit,
|
||||
prevStream: () -> Unit,
|
||||
|
@ -98,7 +99,7 @@ fun VideoPlayerControllerUI(
|
|||
modifier = Modifier.fillMaxSize(), color = Color(0x75000000)
|
||||
) {
|
||||
Box(
|
||||
modifier = if (isFullscreen) {
|
||||
modifier = if (fullscreen) {
|
||||
Modifier
|
||||
.background(Color.Transparent)
|
||||
.windowInsetsPadding(WindowInsets.systemBars)
|
||||
|
@ -128,13 +129,13 @@ fun VideoPlayerControllerUI(
|
|||
.padding(start = 16.dp, end = 16.dp)
|
||||
.defaultMinSize(minHeight = 40.dp)
|
||||
.fillMaxWidth(),
|
||||
isFullscreen = isFullscreen,
|
||||
isFullscreen = fullscreen,
|
||||
switchToFullscreen,
|
||||
switchToEmbeddedView
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isFullscreen) {
|
||||
if (fullscreen) {
|
||||
BackHandler {
|
||||
switchToEmbeddedView()
|
||||
}
|
||||
|
@ -404,7 +405,7 @@ fun VideoPlayerControllerUIPreviewEmbeded() {
|
|||
VideoPlayerTheme {
|
||||
PreviewBackgroundSurface {
|
||||
VideoPlayerControllerUI(isPlaying = false,
|
||||
isFullscreen = false,
|
||||
fullscreen = false,
|
||||
play = {},
|
||||
pause = {},
|
||||
prevStream = {},
|
||||
|
@ -421,7 +422,7 @@ fun VideoPlayerControllerUIPreviewLandscape() {
|
|||
VideoPlayerTheme {
|
||||
PreviewBackgroundSurface {
|
||||
VideoPlayerControllerUI(isPlaying = true,
|
||||
isFullscreen = true,
|
||||
fullscreen = true,
|
||||
play = {},
|
||||
pause = {},
|
||||
prevStream = {},
|
||||
|
@ -439,7 +440,7 @@ fun VideoPlayerControllerUIPreviewPortrait() {
|
|||
PreviewBackgroundSurface {
|
||||
VideoPlayerControllerUI(
|
||||
isPlaying = false,
|
||||
isFullscreen = true,
|
||||
fullscreen = true,
|
||||
play = {},
|
||||
pause = {},
|
||||
prevStream = {},
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
package net.newpipe.newplayer.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.view.SurfaceView
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Surface
|
||||
|
@ -32,21 +33,23 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.setValue
|
||||
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
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import net.newpipe.newplayer.VideoPlayerActivity
|
||||
import net.newpipe.newplayer.model.VideoPlayerViewModel
|
||||
import net.newpipe.newplayer.model.VideoPlayerViewModelImpl
|
||||
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
|
||||
import net.newpipe.newplayer.utils.findActivity
|
||||
|
||||
@Composable
|
||||
fun VideoPlayerUI(
|
||||
viewModel: VideoPlayerViewModel
|
||||
viewModel: VideoPlayerViewModel,
|
||||
isFullscreen: Boolean
|
||||
) {
|
||||
|
||||
val uiState by viewModel.uiState.collectAsState()
|
||||
|
||||
var lifecycle by remember {
|
||||
|
@ -65,6 +68,22 @@ fun VideoPlayerUI(
|
|||
}
|
||||
}
|
||||
|
||||
var fullscreen_requested by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
if(isFullscreen != uiState.fullscreen && !fullscreen_requested) {
|
||||
fullscreen_requested = true
|
||||
val current_acitivity = LocalContext.current.findActivity()
|
||||
if(uiState.fullscreen) {
|
||||
val fullscreen_acitivity_intent = Intent(current_acitivity, VideoPlayerActivity::class.java)
|
||||
current_acitivity!!.startActivity(fullscreen_acitivity_intent)
|
||||
} else {
|
||||
current_acitivity!!.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = Color.Black
|
||||
|
@ -73,7 +92,7 @@ fun VideoPlayerUI(
|
|||
modifier = Modifier.fillMaxSize(),
|
||||
factory = { context ->
|
||||
SurfaceView(context).also {
|
||||
viewModel.player?.setVideoSurfaceView(it)
|
||||
//viewModel.player?.setVideoSurfaceView(it)
|
||||
}
|
||||
}, update = {
|
||||
when (lifecycle) {
|
||||
|
@ -90,7 +109,7 @@ fun VideoPlayerUI(
|
|||
println("is Player playing: $isPlaying")
|
||||
VideoPlayerControllerUI(
|
||||
isPlaying = uiState.playing,
|
||||
isFullscreen = uiState.fullscreen,
|
||||
fullscreen = uiState.fullscreen,
|
||||
play = viewModel::play,
|
||||
pause = viewModel::pause,
|
||||
prevStream = viewModel::prevStream,
|
||||
|
@ -105,6 +124,6 @@ fun VideoPlayerUI(
|
|||
@Composable
|
||||
fun PlayerUIPreviewEmbeded() {
|
||||
VideoPlayerTheme {
|
||||
VideoPlayerUI(viewModel = VideoPlayerViewModelImpl.dummy)
|
||||
VideoPlayerUI(viewModel = VideoPlayerViewModelImpl.dummy, isFullscreen = false)
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
* along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package net.newpipe.newplayer.ui
|
||||
package net.newpipe.newplayer.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
|
@ -26,7 +26,6 @@ import android.content.ContextWrapper
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.view.WindowCompat
|
||||
|
||||
@Composable
|
||||
fun LockScreenOrientation(orientation: Int) {
|
|
@ -36,7 +36,7 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:name="net.newpipe.newplayer.PlayerFragment"
|
||||
android:name="net.newpipe.newplayer.VideoPlayerFragment"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
|
|
|
@ -35,4 +35,5 @@
|
|||
<string name="widget_description_toggle_fullscreen">Toggle fullscreen</string>
|
||||
<string name="widget_description_chapter_selection">Chapter selection</string>
|
||||
<string name="widget_descriptoin_playlist_item_selection">Playlist item selection</string>
|
||||
<string name="title_activity_video_player">VideoPlayerActivity</string>
|
||||
</resources>
|
|
@ -26,4 +26,8 @@
|
|||
</style>
|
||||
|
||||
<style name="Theme.NewPlayer" parent="Base.Theme.NewPlayer" />
|
||||
|
||||
<style name="noAnimTheme" parent="Base.Theme.NewPlayer">
|
||||
<item name="android:windowAnimationStyle">@null</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -39,6 +39,8 @@ lifecycleViewmodelCompose = "2.8.3"
|
|||
kspVersion = "1.9.0-1.0.13"
|
||||
runtimeLivedata = "1.7.0-beta04"
|
||||
fragmentKtx = "1.8.1"
|
||||
lifecycleRuntimeKtx = "2.8.3"
|
||||
composeBom = "2024.04.01"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
|
@ -62,6 +64,13 @@ hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-comp
|
|||
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" }
|
||||
androidx-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "uiTooling" }
|
||||
androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" }
|
||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
|
Loading…
Reference in New Issue