start implementing NewPlayer interface
This commit is contained in:
parent
f11d35818f
commit
d526527e94
|
@ -21,12 +21,21 @@
|
||||||
package net.newpipe.newplayer
|
package net.newpipe.newplayer
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
|
|
||||||
|
|
||||||
interface NewPlayer {
|
interface NewPlayer {
|
||||||
val player: Player
|
val player: Player
|
||||||
|
var playWhenReady: Boolean
|
||||||
|
|
||||||
|
fun prepare()
|
||||||
|
fun play()
|
||||||
|
fun pause()
|
||||||
|
|
||||||
|
//TODO: This is only temporary
|
||||||
|
fun setStream(uri: String)
|
||||||
|
|
||||||
data class Builder(val app: Application) {
|
data class Builder(val app: Application) {
|
||||||
fun build(): NewPlayer {
|
fun build(): NewPlayer {
|
||||||
|
@ -37,4 +46,31 @@ interface NewPlayer {
|
||||||
|
|
||||||
class NewPlayerImpl(internal_player: Player) : NewPlayer {
|
class NewPlayerImpl(internal_player: Player) : NewPlayer {
|
||||||
override val player = internal_player
|
override val player = internal_player
|
||||||
|
|
||||||
|
override var playWhenReady: Boolean
|
||||||
|
set(value) {
|
||||||
|
player.playWhenReady = value
|
||||||
|
}
|
||||||
|
get() = player.playWhenReady
|
||||||
|
|
||||||
|
override fun prepare() {
|
||||||
|
player.prepare()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun play() {
|
||||||
|
player.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pause() {
|
||||||
|
player.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun setStream(uri: String) {
|
||||||
|
if (player.playbackState == Player.STATE_IDLE) {
|
||||||
|
player.prepare()
|
||||||
|
}
|
||||||
|
|
||||||
|
player.setMediaItem(MediaItem.fromUri(uri))
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -43,7 +43,11 @@ class VideoPlayerView : FrameLayout {
|
||||||
|
|
||||||
var minLayoutRatio: Float
|
var minLayoutRatio: Float
|
||||||
get() = videoPlayerFragment.minLayoutRatio
|
get() = videoPlayerFragment.minLayoutRatio
|
||||||
set(value) {videoPlayerFragment.maxLayoutRatio = value}
|
set(value) {videoPlayerFragment.minLayoutRatio = value}
|
||||||
|
|
||||||
|
var newPlayer:NewPlayer?
|
||||||
|
set(value) {videoPlayerFragment.newPlayer = value}
|
||||||
|
get() = videoPlayerFragment.newPlayer
|
||||||
|
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
@ -34,6 +34,7 @@ import androidx.core.view.updateLayoutParams
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import net.newpipe.newplayer.NewPlayer
|
||||||
import net.newpipe.newplayer.R
|
import net.newpipe.newplayer.R
|
||||||
import net.newpipe.newplayer.internal.model.VideoPlayerViewModel
|
import net.newpipe.newplayer.internal.model.VideoPlayerViewModel
|
||||||
import net.newpipe.newplayer.internal.model.VideoPlayerViewModelImpl
|
import net.newpipe.newplayer.internal.model.VideoPlayerViewModelImpl
|
||||||
|
@ -48,6 +49,16 @@ class VideoPlayerFragment() : Fragment() {
|
||||||
private var currentVideoRatio = 0F
|
private var currentVideoRatio = 0F
|
||||||
private lateinit var composeView: ComposeView
|
private lateinit var composeView: ComposeView
|
||||||
|
|
||||||
|
var newPlayer: NewPlayer? = null
|
||||||
|
set(value) {
|
||||||
|
if(context != null) {
|
||||||
|
viewModel.newPlayer = value
|
||||||
|
} else {
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get() = viewModel.newPlayer ?: field
|
||||||
|
|
||||||
var minLayoutRatio = 4F / 3F
|
var minLayoutRatio = 4F / 3F
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value <= 0 && maxLayoutRatio < minLayoutRatio)
|
if (value <= 0 && maxLayoutRatio < minLayoutRatio)
|
||||||
|
@ -87,6 +98,11 @@ class VideoPlayerFragment() : Fragment() {
|
||||||
val view = inflater.inflate(R.layout.video_player_framgent, container, false)
|
val view = inflater.inflate(R.layout.video_player_framgent, container, false)
|
||||||
composeView = view.findViewById(R.id.player_copose_view)
|
composeView = view.findViewById(R.id.player_copose_view)
|
||||||
|
|
||||||
|
// late init player in case player was set before fragment was attached to a context
|
||||||
|
if (viewModel.newPlayer == null) {
|
||||||
|
viewModel.newPlayer = newPlayer
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.listener = object : VideoPlayerViewModel.Listener {
|
viewModel.listener = object : VideoPlayerViewModel.Listener {
|
||||||
override fun requestUpdateLayoutRatio(videoRatio: Float) {
|
override fun requestUpdateLayoutRatio(videoRatio: Float) {
|
||||||
currentVideoRatio = videoRatio
|
currentVideoRatio = videoRatio
|
||||||
|
@ -103,15 +119,15 @@ class VideoPlayerFragment() : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.preparePlayer()
|
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateViewRatio() {
|
private fun updateViewRatio() {
|
||||||
composeView.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
if(this::composeView.isInitialized) {
|
||||||
val ratio = currentVideoRatio.coerceIn(minLayoutRatio, maxLayoutRatio)
|
composeView.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||||
dimensionRatio = "$ratio:1"
|
val ratio = currentVideoRatio.coerceIn(minLayoutRatio, maxLayoutRatio)
|
||||||
|
dimensionRatio = "$ratio:1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -28,13 +28,11 @@ import androidx.annotation.RequiresApi
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.media3.common.MediaItem
|
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import net.newpipe.newplayer.R
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
@ -61,14 +59,13 @@ data class VideoPlayerUIState(
|
||||||
}
|
}
|
||||||
|
|
||||||
interface VideoPlayerViewModel {
|
interface VideoPlayerViewModel {
|
||||||
val new_player: NewPlayer?
|
var newPlayer: NewPlayer?
|
||||||
val player: Player?
|
val player: Player?
|
||||||
val uiState: StateFlow<VideoPlayerUIState>
|
val uiState: StateFlow<VideoPlayerUIState>
|
||||||
var listener: Listener?
|
var listener: Listener?
|
||||||
val events: SharedFlow<Events>?
|
val events: SharedFlow<Events>?
|
||||||
|
|
||||||
fun initUIState(instanceState: Bundle)
|
fun initUIState(instanceState: Bundle)
|
||||||
fun preparePlayer()
|
|
||||||
fun play()
|
fun play()
|
||||||
fun pause()
|
fun pause()
|
||||||
fun prevStream()
|
fun prevStream()
|
||||||
|
@ -90,12 +87,10 @@ interface VideoPlayerViewModel {
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class VideoPlayerViewModelImpl @Inject constructor(
|
class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
private val savedStateHandle: SavedStateHandle,
|
private val savedStateHandle: SavedStateHandle,
|
||||||
override val new_player: NewPlayer,
|
|
||||||
application: Application
|
application: Application
|
||||||
) : AndroidViewModel(application), VideoPlayerViewModel {
|
) : AndroidViewModel(application), VideoPlayerViewModel {
|
||||||
|
|
||||||
// private
|
// private
|
||||||
private val app = getApplication<Application>()
|
|
||||||
private val mutableUiState = MutableStateFlow(VideoPlayerUIState.DEFAULT)
|
private val mutableUiState = MutableStateFlow(VideoPlayerUIState.DEFAULT)
|
||||||
private val mutableEvent = MutableSharedFlow<VideoPlayerViewModel.Events>()
|
private val mutableEvent = MutableSharedFlow<VideoPlayerViewModel.Events>()
|
||||||
private var current_video_size = VideoSize.DEFAULT
|
private var current_video_size = VideoSize.DEFAULT
|
||||||
|
@ -104,45 +99,51 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
override val uiState = mutableUiState.asStateFlow()
|
override val uiState = mutableUiState.asStateFlow()
|
||||||
override val events: SharedFlow<VideoPlayerViewModel.Events> = mutableEvent
|
override val events: SharedFlow<VideoPlayerViewModel.Events> = mutableEvent
|
||||||
override var listener: VideoPlayerViewModel.Listener? = null
|
override var listener: VideoPlayerViewModel.Listener? = null
|
||||||
override val player = new_player.player
|
override var newPlayer: NewPlayer? = null
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
installExoPlayer()
|
||||||
|
}
|
||||||
|
override val player:Player?
|
||||||
|
get() = newPlayer?.player
|
||||||
|
|
||||||
|
private fun installExoPlayer() {
|
||||||
init {
|
player?.let { 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)
|
println("gurken playerstate: $isPlaying")
|
||||||
println("gurken playerstate: $isPlaying")
|
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
|
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||||
|
@ -161,21 +162,13 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun preparePlayer() {
|
|
||||||
if (player.playbackState == Player.STATE_IDLE) {
|
|
||||||
player.prepare()
|
|
||||||
}
|
|
||||||
|
|
||||||
player.setMediaItem(MediaItem.fromUri(app.getString(R.string.portrait_video_example)))
|
|
||||||
player.playWhenReady = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun play() {
|
override fun play() {
|
||||||
player.play()
|
println("gurken player: $newPlayer")
|
||||||
|
newPlayer?.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pause() {
|
override fun pause() {
|
||||||
player.pause()
|
newPlayer?.pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun prevStream() {
|
override fun prevStream() {
|
||||||
|
@ -201,8 +194,8 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val dummy = object : VideoPlayerViewModel {
|
val dummy = object : VideoPlayerViewModel {
|
||||||
override val new_player = null
|
override var newPlayer: NewPlayer? = null
|
||||||
override val 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 val events: SharedFlow<VideoPlayerViewModel.Events>? = null
|
override val events: SharedFlow<VideoPlayerViewModel.Events>? = null
|
||||||
|
@ -211,10 +204,6 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
println("dummy impl")
|
println("dummy impl")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun preparePlayer() {
|
|
||||||
println("dummy impl")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun play() {
|
override fun play() {
|
||||||
println("dummy impl")
|
println("dummy impl")
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,16 +112,13 @@ fun VideoPlayerUI(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
factory = { context ->
|
factory = { context ->
|
||||||
SurfaceView(context).also { view ->
|
SurfaceView(context).also { view ->
|
||||||
|
println("gurken attach player: ${viewModel.player}")
|
||||||
viewModel.player?.setVideoSurfaceView(view)
|
viewModel.player?.setVideoSurfaceView(view)
|
||||||
}
|
}
|
||||||
}, update = { view ->
|
}, update = { view ->
|
||||||
when (lifecycle) {
|
when (lifecycle) {
|
||||||
Lifecycle.Event.ON_PAUSE -> {
|
|
||||||
println("gurken state on pause")
|
|
||||||
}
|
|
||||||
|
|
||||||
Lifecycle.Event.ON_RESUME -> {
|
Lifecycle.Event.ON_RESUME -> {
|
||||||
println("gurken resume")
|
println("gurken reattach player: ${viewModel.player}")
|
||||||
viewModel.player?.setVideoSurfaceView(view)
|
viewModel.player?.setVideoSurfaceView(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +126,7 @@ fun VideoPlayerUI(
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
val isPlaying = viewModel.player!!.isPlaying
|
val isPlaying = viewModel.player?.isPlaying ?: false
|
||||||
|
|
||||||
VideoPlayerControllerUI(
|
VideoPlayerControllerUI(
|
||||||
isPlaying = uiState.playing,
|
isPlaying = uiState.playing,
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
<?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/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<resources>
|
|
||||||
<string name="ccc_6502_video">https://ftp.fau.de/cdn.media.ccc.de/congress/2010/mp4-h264-HQ/27c3-4159-en-reverse_engineering_mos_6502.mp4</string>
|
|
||||||
<string name="ccc_6502_audio">https://ftp.fau.de/cdn.media.ccc.de/congress/2010/ogg-audio-only/27c3-4159-en-reverse_engineering_mos_6502.ogg</string>
|
|
||||||
<string name="ccc_chromebooks_video">https://ftp.fau.de/cdn.media.ccc.de/congress/2023/h264-hd/37c3-11929-eng-deu-swe-Turning_Chromebooks_into_regular_laptops_hd.mp4</string>
|
|
||||||
<string name="portrait_video_example">https://videos.pexels.com/video-files/5512609/5512609-hd_1080_1920_25fps.mp4</string>
|
|
||||||
</resources>
|
|
|
@ -27,18 +27,25 @@ import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import net.newpipe.newplayer.NewPlayer
|
||||||
import net.newpipe.newplayer.VideoPlayerView
|
import net.newpipe.newplayer.VideoPlayerView
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var newPlayer: NewPlayer
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
|
||||||
val video_view = findViewById<VideoPlayerView>(R.id.new_player_video_view)
|
val video_view = findViewById<VideoPlayerView>(R.id.new_player_video_view)
|
||||||
|
video_view.newPlayer = newPlayer
|
||||||
video_view.minLayoutRatio
|
newPlayer.playWhenReady = true
|
||||||
|
newPlayer.setStream(getString(R.string.ccc_chromebooks_video))
|
||||||
|
|
||||||
//TODO: This is a dirty hack. Fix this later on
|
//TODO: This is a dirty hack. Fix this later on
|
||||||
if (getResources().configuration.orientation != Configuration.ORIENTATION_LANDSCAPE) {
|
if (getResources().configuration.orientation != Configuration.ORIENTATION_LANDSCAPE) {
|
||||||
|
|
|
@ -18,11 +18,9 @@
|
||||||
* along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.newpipe.newplayer.internal
|
package net.newpipe.newplayer.testapp
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.media3.common.Player
|
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
@ -32,7 +30,7 @@ import javax.inject.Singleton
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
object VideoPlayerComponent {
|
object NewPlayerComponent {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideNewPlayer(app: Application) : NewPlayer {
|
fun provideNewPlayer(app: Application) : NewPlayer {
|
|
@ -1,6 +1,27 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<string name="ccc_6502_video">https://ftp.fau.de/cdn.media.ccc.de/congress/2010/mp4-h264-HQ/27c3-4159-en-reverse_engineering_mos_6502.mp4</string>
|
<string name="ccc_6502_video" translatable="false">https://ftp.fau.de/cdn.media.ccc.de/congress/2010/mp4-h264-HQ/27c3-4159-en-reverse_engineering_mos_6502.mp4</string>
|
||||||
<string name="ccc_6502_audio">https://ftp.fau.de/cdn.media.ccc.de/congress/2010/ogg-audio-only/27c3-4159-en-reverse_engineering_mos_6502.ogg</string>
|
<string name="ccc_6502_audio" translatable="false">https://ftp.fau.de/cdn.media.ccc.de/congress/2010/ogg-audio-only/27c3-4159-en-reverse_engineering_mos_6502.ogg</string>
|
||||||
<string name="ccc_chromebooks_video">https://ftp.fau.de/cdn.media.ccc.de/congress/2023/h264-hd/37c3-11929-eng-deu-swe-Turning_Chromebooks_into_regular_laptops_hd.mp4</string>
|
<string name="ccc_chromebooks_video" translatable="false">https://ftp.fau.de/cdn.media.ccc.de/congress/2023/h264-hd/37c3-11929-eng-deu-swe-Turning_Chromebooks_into_regular_laptops_hd.mp4</string>
|
||||||
|
<string name="portrait_video_example" translatable="false">https://videos.pexels.com/video-files/5512609/5512609-hd_1080_1920_25fps.mp4</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue