mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-29 08:20:32 +05:30
Code refactor: Separate online and offline player
This commit is contained in:
parent
2a4aad88ee
commit
44d54d37c1
@ -5,13 +5,9 @@ import android.media.session.PlaybackState
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup.MarginLayoutParams
|
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.core.view.isInvisible
|
import androidx.core.view.isInvisible
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.marginTop
|
|
||||||
import androidx.core.view.updateLayoutParams
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.media3.common.C
|
import androidx.media3.common.C
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
@ -30,9 +26,7 @@ import com.github.libretube.constants.IntentData
|
|||||||
import com.github.libretube.databinding.ActivityOfflinePlayerBinding
|
import com.github.libretube.databinding.ActivityOfflinePlayerBinding
|
||||||
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
||||||
import com.github.libretube.db.DatabaseHolder.Database
|
import com.github.libretube.db.DatabaseHolder.Database
|
||||||
import com.github.libretube.db.obj.DownloadItem
|
|
||||||
import com.github.libretube.enums.FileType
|
import com.github.libretube.enums.FileType
|
||||||
import com.github.libretube.extensions.dpToPx
|
|
||||||
import com.github.libretube.extensions.toAndroidUri
|
import com.github.libretube.extensions.toAndroidUri
|
||||||
import com.github.libretube.extensions.updateParameters
|
import com.github.libretube.extensions.updateParameters
|
||||||
import com.github.libretube.helpers.PlayerHelper
|
import com.github.libretube.helpers.PlayerHelper
|
||||||
@ -105,24 +99,14 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
playerView.player = player
|
playerView.player = player
|
||||||
playerBinding = binding.player.binding
|
playerBinding = binding.player.binding
|
||||||
|
|
||||||
// increase the margin to the status bar
|
|
||||||
playerBinding.topBar.setPadding(
|
|
||||||
playerBinding.topBar.paddingLeft,
|
|
||||||
playerBinding.topBar.paddingTop * 2,
|
|
||||||
playerBinding.topBar.paddingRight,
|
|
||||||
playerBinding.topBar.paddingBottom
|
|
||||||
)
|
|
||||||
|
|
||||||
playerBinding.fullscreen.isInvisible = true
|
playerBinding.fullscreen.isInvisible = true
|
||||||
playerBinding.closeImageButton.setOnClickListener {
|
playerBinding.closeImageButton.setOnClickListener {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.player.initialize(
|
binding.player.initialize(
|
||||||
null,
|
|
||||||
binding.doubleTapOverlay.binding,
|
binding.doubleTapOverlay.binding,
|
||||||
binding.playerGestureControlsView.binding,
|
binding.playerGestureControlsView.binding,
|
||||||
trackSelector,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,6 +165,7 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
|
|
||||||
player.setMediaSource(mediaSource)
|
player.setMediaSource(mediaSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
videoUri != null -> player.setMediaItem(
|
videoUri != null -> player.setMediaItem(
|
||||||
MediaItem.Builder()
|
MediaItem.Builder()
|
||||||
.setUri(videoUri)
|
.setUri(videoUri)
|
||||||
@ -189,6 +174,7 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
|
|
||||||
audioUri != null -> player.setMediaItem(
|
audioUri != null -> player.setMediaItem(
|
||||||
MediaItem.Builder()
|
MediaItem.Builder()
|
||||||
.setUri(audioUri)
|
.setUri(audioUri)
|
||||||
|
@ -16,7 +16,6 @@ import android.os.PowerManager
|
|||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
import android.text.util.Linkify
|
import android.text.util.Linkify
|
||||||
import android.util.Base64
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -76,8 +75,6 @@ import com.github.libretube.extensions.toID
|
|||||||
import com.github.libretube.extensions.toastFromMainDispatcher
|
import com.github.libretube.extensions.toastFromMainDispatcher
|
||||||
import com.github.libretube.extensions.updateParameters
|
import com.github.libretube.extensions.updateParameters
|
||||||
import com.github.libretube.helpers.BackgroundHelper
|
import com.github.libretube.helpers.BackgroundHelper
|
||||||
import com.github.libretube.helpers.DashHelper
|
|
||||||
import com.github.libretube.helpers.DisplayHelper
|
|
||||||
import com.github.libretube.helpers.ImageHelper
|
import com.github.libretube.helpers.ImageHelper
|
||||||
import com.github.libretube.helpers.LocaleHelper
|
import com.github.libretube.helpers.LocaleHelper
|
||||||
import com.github.libretube.helpers.NavigationHelper
|
import com.github.libretube.helpers.NavigationHelper
|
||||||
@ -892,14 +889,8 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
|||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
private fun initializePlayerView() {
|
private fun initializePlayerView() {
|
||||||
// initialize the player view actions
|
// initialize the player view actions
|
||||||
binding.player.initialize(
|
binding.player.initialize(doubleTapOverlayBinding, playerGestureControlsViewBinding)
|
||||||
this,
|
binding.player.initPlayerOptions(viewModel, viewLifecycleOwner, trackSelector, this)
|
||||||
doubleTapOverlayBinding,
|
|
||||||
playerGestureControlsViewBinding,
|
|
||||||
trackSelector,
|
|
||||||
viewModel,
|
|
||||||
viewLifecycleOwner,
|
|
||||||
)
|
|
||||||
|
|
||||||
binding.apply {
|
binding.apply {
|
||||||
val views = streams.views.formatShort()
|
val views = streams.views.formatShort()
|
||||||
|
@ -17,16 +17,13 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.os.postDelayed
|
import androidx.core.os.postDelayed
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.marginStart
|
import androidx.core.view.marginStart
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.lifecycle.LifecycleOwner
|
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import androidx.media3.common.text.Cue
|
import androidx.media3.common.text.Cue
|
||||||
import androidx.media3.common.util.RepeatModeUtil
|
import androidx.media3.common.util.RepeatModeUtil
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
import androidx.media3.exoplayer.trackselection.TrackSelector
|
|
||||||
import androidx.media3.ui.AspectRatioFrameLayout
|
import androidx.media3.ui.AspectRatioFrameLayout
|
||||||
import androidx.media3.ui.CaptionStyleCompat
|
import androidx.media3.ui.CaptionStyleCompat
|
||||||
import androidx.media3.ui.PlayerView
|
import androidx.media3.ui.PlayerView
|
||||||
@ -42,26 +39,23 @@ import com.github.libretube.extensions.round
|
|||||||
import com.github.libretube.helpers.AudioHelper
|
import com.github.libretube.helpers.AudioHelper
|
||||||
import com.github.libretube.helpers.BrightnessHelper
|
import com.github.libretube.helpers.BrightnessHelper
|
||||||
import com.github.libretube.helpers.PlayerHelper
|
import com.github.libretube.helpers.PlayerHelper
|
||||||
import com.github.libretube.helpers.WindowHelper
|
|
||||||
import com.github.libretube.obj.BottomSheetItem
|
import com.github.libretube.obj.BottomSheetItem
|
||||||
import com.github.libretube.ui.base.BaseActivity
|
import com.github.libretube.ui.base.BaseActivity
|
||||||
import com.github.libretube.ui.extensions.toggleSystemBars
|
|
||||||
import com.github.libretube.ui.interfaces.OnlinePlayerOptions
|
|
||||||
import com.github.libretube.ui.interfaces.PlayerGestureOptions
|
import com.github.libretube.ui.interfaces.PlayerGestureOptions
|
||||||
import com.github.libretube.ui.interfaces.PlayerOptions
|
import com.github.libretube.ui.interfaces.PlayerOptions
|
||||||
import com.github.libretube.ui.listeners.PlayerGestureController
|
import com.github.libretube.ui.listeners.PlayerGestureController
|
||||||
import com.github.libretube.ui.models.PlayerViewModel
|
|
||||||
import com.github.libretube.ui.sheets.BaseBottomSheet
|
import com.github.libretube.ui.sheets.BaseBottomSheet
|
||||||
import com.github.libretube.ui.sheets.PlaybackOptionsSheet
|
import com.github.libretube.ui.sheets.PlaybackOptionsSheet
|
||||||
import com.github.libretube.util.PlayingQueue
|
import com.github.libretube.util.PlayingQueue
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
||||||
internal class CustomExoPlayerView(
|
open class CustomExoPlayerView(
|
||||||
context: Context,
|
context: Context,
|
||||||
attributeSet: AttributeSet? = null,
|
attributeSet: AttributeSet? = null,
|
||||||
) : PlayerView(context, attributeSet), PlayerOptions, PlayerGestureOptions {
|
) : PlayerView(context, attributeSet), PlayerOptions, PlayerGestureOptions {
|
||||||
val binding: ExoStyledPlayerControlViewBinding = ExoStyledPlayerControlViewBinding.bind(this)
|
@Suppress("LeakingThis")
|
||||||
|
val binding = ExoStyledPlayerControlViewBinding.bind(this)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Objects for player tap and swipe gesture
|
* Objects for player tap and swipe gesture
|
||||||
@ -71,27 +65,20 @@ internal class CustomExoPlayerView(
|
|||||||
private lateinit var brightnessHelper: BrightnessHelper
|
private lateinit var brightnessHelper: BrightnessHelper
|
||||||
private lateinit var audioHelper: AudioHelper
|
private lateinit var audioHelper: AudioHelper
|
||||||
private var doubleTapOverlayBinding: DoubleTapOverlayBinding? = null
|
private var doubleTapOverlayBinding: DoubleTapOverlayBinding? = null
|
||||||
private var playerViewModel: PlayerViewModel? = null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Objects from the parent fragment
|
* Objects from the parent fragment
|
||||||
*/
|
*/
|
||||||
private var playerOptionsInterface: OnlinePlayerOptions? = null
|
|
||||||
private var trackSelector: TrackSelector? = null
|
|
||||||
|
|
||||||
private val runnableHandler = Handler(Looper.getMainLooper())
|
private val runnableHandler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
var isPlayerLocked: Boolean = false
|
var isPlayerLocked: Boolean = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preferences
|
* Preferences
|
||||||
*/
|
*/
|
||||||
var autoplayEnabled = PlayerHelper.autoPlayEnabled
|
|
||||||
|
|
||||||
private var resizeModePref = PlayerHelper.resizeModePref
|
private var resizeModePref = PlayerHelper.resizeModePref
|
||||||
|
|
||||||
private val activity
|
val activity get() = context as BaseActivity
|
||||||
get() = context as BaseActivity
|
|
||||||
|
|
||||||
private val supportFragmentManager
|
private val supportFragmentManager
|
||||||
get() = activity.supportFragmentManager
|
get() = activity.supportFragmentManager
|
||||||
@ -101,18 +88,11 @@ internal class CustomExoPlayerView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun initialize(
|
fun initialize(
|
||||||
playerViewInterface: OnlinePlayerOptions?,
|
|
||||||
doubleTapOverlayBinding: DoubleTapOverlayBinding,
|
doubleTapOverlayBinding: DoubleTapOverlayBinding,
|
||||||
playerGestureControlsViewBinding: PlayerGestureControlsViewBinding,
|
playerGestureControlsViewBinding: PlayerGestureControlsViewBinding,
|
||||||
trackSelector: TrackSelector?,
|
|
||||||
playerViewModel: PlayerViewModel? = null,
|
|
||||||
viewLifecycleOwner: LifecycleOwner? = null,
|
|
||||||
) {
|
) {
|
||||||
this.playerOptionsInterface = playerViewInterface
|
|
||||||
this.doubleTapOverlayBinding = doubleTapOverlayBinding
|
this.doubleTapOverlayBinding = doubleTapOverlayBinding
|
||||||
this.trackSelector = trackSelector
|
|
||||||
this.gestureViewBinding = playerGestureControlsViewBinding
|
this.gestureViewBinding = playerGestureControlsViewBinding
|
||||||
this.playerViewModel = playerViewModel
|
|
||||||
this.playerGestureController = PlayerGestureController(context as BaseActivity, this)
|
this.playerGestureController = PlayerGestureController(context as BaseActivity, this)
|
||||||
this.brightnessHelper = BrightnessHelper(context as Activity)
|
this.brightnessHelper = BrightnessHelper(context as Activity)
|
||||||
this.audioHelper = AudioHelper(context)
|
this.audioHelper = AudioHelper(context)
|
||||||
@ -123,7 +103,7 @@ internal class CustomExoPlayerView(
|
|||||||
|
|
||||||
initRewindAndForward()
|
initRewindAndForward()
|
||||||
applyCaptionsStyle()
|
applyCaptionsStyle()
|
||||||
initializeAdvancedOptions(context)
|
initializeAdvancedOptions()
|
||||||
|
|
||||||
// don't let the player view hide its controls automatically
|
// don't let the player view hide its controls automatically
|
||||||
controllerShowTimeoutMs = -1
|
controllerShowTimeoutMs = -1
|
||||||
@ -148,12 +128,6 @@ internal class CustomExoPlayerView(
|
|||||||
isPlayerLocked = !isPlayerLocked
|
isPlayerLocked = !isPlayerLocked
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.autoPlay.isChecked = autoplayEnabled
|
|
||||||
|
|
||||||
binding.autoPlay.setOnCheckedChangeListener { _, isChecked ->
|
|
||||||
autoplayEnabled = isChecked
|
|
||||||
}
|
|
||||||
|
|
||||||
resizeMode = when (resizeModePref) {
|
resizeMode = when (resizeModePref) {
|
||||||
"fill" -> AspectRatioFrameLayout.RESIZE_MODE_FILL
|
"fill" -> AspectRatioFrameLayout.RESIZE_MODE_FILL
|
||||||
"zoom" -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
"zoom" -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
||||||
@ -183,11 +157,7 @@ internal class CustomExoPlayerView(
|
|||||||
|
|
||||||
// keep screen on if the video is playing
|
// keep screen on if the video is playing
|
||||||
keepScreenOn = player.isPlaying == true
|
keepScreenOn = player.isPlaying == true
|
||||||
|
onPlayerEvent(player, events)
|
||||||
if (player.playbackState == Player.STATE_ENDED && !autoplayEnabled) {
|
|
||||||
showController()
|
|
||||||
cancelHideControllerTask()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -206,25 +176,9 @@ internal class CustomExoPlayerView(
|
|||||||
enqueueHideControllerTask()
|
enqueueHideControllerTask()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
setControllerVisibilityListener(
|
open fun onPlayerEvent(player: Player, playerEvents: Player.Events) = Unit
|
||||||
ControllerVisibilityListener { visibility ->
|
|
||||||
playerViewModel?.isFullscreen?.value?.let { isFullscreen ->
|
|
||||||
if (!isFullscreen) return@let
|
|
||||||
// Show status bar only not navigation bar if the player controls are visible and hide it otherwise
|
|
||||||
activity.toggleSystemBars(
|
|
||||||
types = WindowInsetsCompat.Type.statusBars(),
|
|
||||||
showBars = visibility == View.VISIBLE,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
playerViewModel?.isFullscreen?.observe(viewLifecycleOwner!!) { isFullscreen ->
|
|
||||||
WindowHelper.toggleFullscreen(activity, isFullscreen)
|
|
||||||
updateTopBarMargin()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updatePlayPauseButton() {
|
private fun updatePlayPauseButton() {
|
||||||
binding.playPauseBTN.setImageResource(
|
binding.playPauseBTN.setImageResource(
|
||||||
@ -250,16 +204,6 @@ internal class CustomExoPlayerView(
|
|||||||
// remove the callback to hide the controller
|
// remove the callback to hide the controller
|
||||||
cancelHideControllerTask()
|
cancelHideControllerTask()
|
||||||
super.hideController()
|
super.hideController()
|
||||||
|
|
||||||
// hide system bars if in fullscreen or offline player
|
|
||||||
if (playerViewModel != null) {
|
|
||||||
if (playerViewModel!!.isFullscreen.value == true) {
|
|
||||||
WindowHelper.toggleFullscreen(activity, true)
|
|
||||||
}
|
|
||||||
updateTopBarMargin()
|
|
||||||
} else {
|
|
||||||
activity.toggleSystemBars(WindowInsetsCompat.Type.systemBars(), false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showController() {
|
override fun showController() {
|
||||||
@ -268,11 +212,6 @@ internal class CustomExoPlayerView(
|
|||||||
// automatically hide the controller after 2 seconds
|
// automatically hide the controller after 2 seconds
|
||||||
enqueueHideControllerTask()
|
enqueueHideControllerTask()
|
||||||
super.showController()
|
super.showController()
|
||||||
|
|
||||||
// show the system bars when in offline player
|
|
||||||
if (playerViewModel == null) {
|
|
||||||
activity.toggleSystemBars(WindowInsetsCompat.Type.statusBars(), true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTouchEvent(event: MotionEvent) = false
|
override fun onTouchEvent(event: MotionEvent) = false
|
||||||
@ -300,9 +239,15 @@ internal class CustomExoPlayerView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializeAdvancedOptions(context: Context) {
|
private fun initializeAdvancedOptions() {
|
||||||
binding.toggleOptions.setOnClickListener {
|
binding.toggleOptions.setOnClickListener {
|
||||||
val items = mutableListOf(
|
val items = getOptionsMenuItems()
|
||||||
|
val bottomSheetFragment = BaseBottomSheet().setItems(items, null)
|
||||||
|
bottomSheetFragment.show(supportFragmentManager, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun getOptionsMenuItems(): List<BottomSheetItem> = listOf(
|
||||||
BottomSheetItem(
|
BottomSheetItem(
|
||||||
context.getString(R.string.repeat_mode),
|
context.getString(R.string.repeat_mode),
|
||||||
R.drawable.ic_repeat,
|
R.drawable.ic_repeat,
|
||||||
@ -344,53 +289,6 @@ internal class CustomExoPlayerView(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
playerOptionsInterface?.let {
|
|
||||||
items.addAll(
|
|
||||||
listOf(
|
|
||||||
BottomSheetItem(
|
|
||||||
context.getString(R.string.quality),
|
|
||||||
R.drawable.ic_hd,
|
|
||||||
{ "${player?.videoSize?.height}p" },
|
|
||||||
) {
|
|
||||||
it.onQualityClicked()
|
|
||||||
},
|
|
||||||
BottomSheetItem(
|
|
||||||
context.getString(R.string.audio_track),
|
|
||||||
R.drawable.ic_audio,
|
|
||||||
{
|
|
||||||
trackSelector?.parameters?.preferredAudioLanguages?.firstOrNull()
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
it.onAudioStreamClicked()
|
|
||||||
},
|
|
||||||
BottomSheetItem(
|
|
||||||
context.getString(R.string.captions),
|
|
||||||
R.drawable.ic_caption,
|
|
||||||
{
|
|
||||||
if (trackSelector != null && trackSelector!!.parameters.preferredTextLanguages.isNotEmpty()) {
|
|
||||||
trackSelector!!.parameters.preferredTextLanguages[0]
|
|
||||||
} else {
|
|
||||||
context.getString(R.string.none)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
it.onCaptionsClicked()
|
|
||||||
},
|
|
||||||
BottomSheetItem(
|
|
||||||
context.getString(R.string.stats_for_nerds),
|
|
||||||
R.drawable.ic_info,
|
|
||||||
) {
|
|
||||||
it.onStatsClicked()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val bottomSheetFragment = BaseBottomSheet().setItems(items, null)
|
|
||||||
bottomSheetFragment.show(supportFragmentManager, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lock the player
|
// lock the player
|
||||||
private fun lockPlayer(isLocked: Boolean) {
|
private fun lockPlayer(isLocked: Boolean) {
|
||||||
// isLocked is the current (old) state of the player lock
|
// isLocked is the current (old) state of the player lock
|
||||||
@ -606,12 +504,14 @@ internal class CustomExoPlayerView(
|
|||||||
.show(supportFragmentManager)
|
.show(supportFragmentManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun isFullscreen() = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||||
|
|
||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
super.onConfigurationChanged(newConfig)
|
super.onConfigurationChanged(newConfig)
|
||||||
|
|
||||||
// add a larger bottom margin to the time bar in landscape mode
|
// add a larger bottom margin to the time bar in landscape mode
|
||||||
val offset = when {
|
val offset = when {
|
||||||
playerViewModel?.isFullscreen?.value ?: (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) -> 20.dpToPx()
|
isFullscreen() -> 20.dpToPx()
|
||||||
else -> 10.dpToPx()
|
else -> 10.dpToPx()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,17 +556,16 @@ internal class CustomExoPlayerView(
|
|||||||
/**
|
/**
|
||||||
* Add extra margin to the top bar to not overlap the status bar
|
* Add extra margin to the top bar to not overlap the status bar
|
||||||
*/
|
*/
|
||||||
private fun updateTopBarMargin() {
|
fun updateTopBarMargin() {
|
||||||
val margin = when {
|
|
||||||
resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE -> 10
|
|
||||||
playerViewModel?.isFullscreen?.value == true -> 20
|
|
||||||
else -> 0
|
|
||||||
}
|
|
||||||
binding.topBar.updateLayoutParams<MarginLayoutParams> {
|
binding.topBar.updateLayoutParams<MarginLayoutParams> {
|
||||||
topMargin = margin.dpToPx().toInt()
|
topMargin = getTopBarMarginDp().dpToPx().toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun getTopBarMarginDp(): Int {
|
||||||
|
return if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) 10 else 0
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSingleTap() {
|
override fun onSingleTap() {
|
||||||
toggleController()
|
toggleController()
|
||||||
}
|
}
|
||||||
@ -719,7 +618,6 @@ internal class CustomExoPlayerView(
|
|||||||
|
|
||||||
playerGestureController.isMoving = false
|
playerGestureController.isMoving = false
|
||||||
(context as? AppCompatActivity)?.onBackPressedDispatcher?.onBackPressed()
|
(context as? AppCompatActivity)?.onBackPressedDispatcher?.onBackPressed()
|
||||||
playerViewModel?.isFullscreen?.value = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSwipeEnd() {
|
override fun onSwipeEnd() {
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.github.libretube.ui.views
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import com.github.libretube.ui.extensions.toggleSystemBars
|
||||||
|
|
||||||
|
class OfflinePlayerView(
|
||||||
|
context: Context,
|
||||||
|
attributeSet: AttributeSet? = null,
|
||||||
|
): CustomExoPlayerView(context, attributeSet) {
|
||||||
|
override fun hideController() {
|
||||||
|
super.hideController()
|
||||||
|
// hide the status bars when continuing to watch video
|
||||||
|
activity.toggleSystemBars(WindowInsetsCompat.Type.systemBars(), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showController() {
|
||||||
|
super.showController()
|
||||||
|
// show status bar when showing player options
|
||||||
|
activity.toggleSystemBars(WindowInsetsCompat.Type.statusBars(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTopBarMarginDp(): Int {
|
||||||
|
// the offline player requires a bigger top bar margin
|
||||||
|
return if (isFullscreen()) 18 else super.getTopBarMarginDp()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,132 @@
|
|||||||
|
package com.github.libretube.ui.views
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.media3.exoplayer.trackselection.TrackSelector
|
||||||
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.helpers.PlayerHelper
|
||||||
|
import com.github.libretube.helpers.WindowHelper
|
||||||
|
import com.github.libretube.obj.BottomSheetItem
|
||||||
|
import com.github.libretube.ui.extensions.toggleSystemBars
|
||||||
|
import com.github.libretube.ui.interfaces.OnlinePlayerOptions
|
||||||
|
import com.github.libretube.ui.models.PlayerViewModel
|
||||||
|
|
||||||
|
class OnlinePlayerView(
|
||||||
|
context: Context,
|
||||||
|
attributeSet: AttributeSet? = null,
|
||||||
|
) : CustomExoPlayerView(context, attributeSet) {
|
||||||
|
private var playerOptions: OnlinePlayerOptions? = null
|
||||||
|
private var playerViewModel: PlayerViewModel? = null
|
||||||
|
private var trackSelector: TrackSelector? = null
|
||||||
|
private var viewLifecycleOwner: LifecycleOwner? = null
|
||||||
|
var autoplayEnabled = PlayerHelper.autoPlayEnabled
|
||||||
|
|
||||||
|
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
||||||
|
override fun getOptionsMenuItems(): List<BottomSheetItem> {
|
||||||
|
return super.getOptionsMenuItems() +
|
||||||
|
listOf(
|
||||||
|
BottomSheetItem(
|
||||||
|
context.getString(R.string.quality),
|
||||||
|
R.drawable.ic_hd,
|
||||||
|
{ "${player?.videoSize?.height}p" },
|
||||||
|
) {
|
||||||
|
playerOptions?.onQualityClicked()
|
||||||
|
},
|
||||||
|
BottomSheetItem(
|
||||||
|
context.getString(R.string.audio_track),
|
||||||
|
R.drawable.ic_audio,
|
||||||
|
{
|
||||||
|
trackSelector?.parameters?.preferredAudioLanguages?.firstOrNull()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
playerOptions?.onAudioStreamClicked()
|
||||||
|
},
|
||||||
|
BottomSheetItem(
|
||||||
|
context.getString(R.string.captions),
|
||||||
|
R.drawable.ic_caption,
|
||||||
|
{
|
||||||
|
if (trackSelector != null && trackSelector!!.parameters.preferredTextLanguages.isNotEmpty()) {
|
||||||
|
trackSelector!!.parameters.preferredTextLanguages[0]
|
||||||
|
} else {
|
||||||
|
context.getString(R.string.none)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
playerOptions?.onCaptionsClicked()
|
||||||
|
},
|
||||||
|
BottomSheetItem(
|
||||||
|
context.getString(R.string.stats_for_nerds),
|
||||||
|
R.drawable.ic_info,
|
||||||
|
) {
|
||||||
|
playerOptions?.onStatsClicked()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initPlayerOptions(
|
||||||
|
playerViewModel: PlayerViewModel,
|
||||||
|
viewLifecycleOwner: LifecycleOwner,
|
||||||
|
trackSelector: TrackSelector,
|
||||||
|
playerOptions: OnlinePlayerOptions
|
||||||
|
) {
|
||||||
|
this.playerViewModel = playerViewModel
|
||||||
|
this.viewLifecycleOwner = viewLifecycleOwner
|
||||||
|
this.trackSelector = trackSelector
|
||||||
|
this.playerOptions = playerOptions
|
||||||
|
|
||||||
|
playerViewModel.isFullscreen.observe(viewLifecycleOwner) { isFullscreen ->
|
||||||
|
WindowHelper.toggleFullscreen(activity, isFullscreen)
|
||||||
|
updateTopBarMargin()
|
||||||
|
}
|
||||||
|
|
||||||
|
setControllerVisibilityListener(
|
||||||
|
ControllerVisibilityListener { visibility ->
|
||||||
|
playerViewModel.isFullscreen.value?.let { isFullscreen ->
|
||||||
|
if (!isFullscreen) return@let
|
||||||
|
// Show status bar only not navigation bar if the player controls are visible and hide it otherwise
|
||||||
|
activity.toggleSystemBars(
|
||||||
|
types = WindowInsetsCompat.Type.statusBars(),
|
||||||
|
showBars = visibility == View.VISIBLE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
binding.autoPlay.isChecked = autoplayEnabled
|
||||||
|
|
||||||
|
binding.autoPlay.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
autoplayEnabled = isChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hideController() {
|
||||||
|
super.hideController()
|
||||||
|
|
||||||
|
if (playerViewModel?.isFullscreen?.value == true) {
|
||||||
|
WindowHelper.toggleFullscreen(activity, true)
|
||||||
|
}
|
||||||
|
updateTopBarMargin()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSwipeCenterScreen(distanceY: Float) {
|
||||||
|
super.onSwipeCenterScreen(distanceY)
|
||||||
|
playerViewModel?.isFullscreen?.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTopBarMarginDp(): Int {
|
||||||
|
return when {
|
||||||
|
resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE -> 15
|
||||||
|
playerViewModel?.isFullscreen?.value == true -> 20
|
||||||
|
else -> super.getTopBarMarginDp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isFullscreen(): Boolean {
|
||||||
|
return playerViewModel?.isFullscreen?.value ?: super.isFullscreen()
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<com.github.libretube.ui.views.CustomExoPlayerView
|
<com.github.libretube.ui.views.OfflinePlayerView
|
||||||
android:id="@+id/player"
|
android:id="@+id/player"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@ -27,6 +27,6 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:gravity="center" />
|
android:gravity="center" />
|
||||||
|
|
||||||
</com.github.libretube.ui.views.CustomExoPlayerView>
|
</com.github.libretube.ui.views.OfflinePlayerView>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -299,7 +299,7 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<com.github.libretube.ui.views.CustomExoPlayerView
|
<com.github.libretube.ui.views.OnlinePlayerView
|
||||||
android:id="@+id/player"
|
android:id="@+id/player"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@ -357,7 +357,7 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
</com.github.libretube.ui.views.CustomExoPlayerView>
|
</com.github.libretube.ui.views.OnlinePlayerView>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/close_imageView"
|
android:id="@+id/close_imageView"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user