diff --git a/app/src/main/java/com/github/libretube/ui/activities/OfflinePlayerActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/OfflinePlayerActivity.kt
index 793a823dfc..ec1f8e9b02 100644
--- a/app/src/main/java/com/github/libretube/ui/activities/OfflinePlayerActivity.kt
+++ b/app/src/main/java/com/github/libretube/ui/activities/OfflinePlayerActivity.kt
@@ -199,11 +199,7 @@ class OfflinePlayerActivity : BaseActivity() {
playNextVideo(PlayingQueue.getNext() ?: return@setOnClickListener)
}
- binding.player.initialize(
- binding.doubleTapOverlay.binding,
- binding.playerGestureControlsView.binding,
- chaptersViewModel
- )
+ binding.player.initialize(chaptersViewModel)
}
private suspend fun loadPlayerData() {
diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt
index 308290d21a..d8ecfb2c91 100644
--- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt
+++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt
@@ -125,9 +125,8 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
private var _binding: FragmentPlayerBinding? = null
val binding get() = _binding!!
- private val playerBinding get() = binding.player.binding
- private val doubleTapOverlayBinding get() = binding.doubleTapOverlay.binding
- private val playerGestureControlsViewBinding get() = binding.playerGestureControlsView.binding
+ private val playerControlsBinding get() = binding.player.binding
+ private val playerBackgroundBinding get() = binding.player.backgroundBinding
private val commonPlayerViewModel: CommonPlayerViewModel by activityViewModels()
private val viewModel: PlayerViewModel by viewModels()
@@ -275,7 +274,7 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
// check if video has ended, next video is available and autoplay is enabled/the video is part of a played playlist.
if (playbackState == Player.STATE_ENDED) {
- binding.sbSkipBtn.isGone = true
+ playerBackgroundBinding.sbSkipBtn.isGone = true
if (PlayerHelper.isAutoPlayEnabled(playlistId != null) && autoPlayCountdownEnabled) {
showAutoPlayCountdown()
} else {
@@ -326,7 +325,7 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
mediaMetadata.extras?.getString(IntentData.videoId)?.let {
videoId = it
- _binding?.autoplayCountdown?.cancelAndHideCountdown()
+ if (_binding != null) playerBackgroundBinding.autoplayCountdown.cancelAndHideCountdown()
// fix: if the fragment is recreated, play the current video, and not the initial one
arguments?.run {
@@ -432,8 +431,8 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
viewModel.segments.observe(viewLifecycleOwner) { segments ->
binding.descriptionLayout.setSegments(segments)
- playerBinding.exoProgress.setSegments(segments)
- playerBinding.sbToggle.isVisible = segments.isNotEmpty()
+ playerControlsBinding.exoProgress.setSegments(segments)
+ playerControlsBinding.sbToggle.isVisible = segments.isNotEmpty()
segments.firstOrNull { it.category == PlayerHelper.SPONSOR_HIGHLIGHT_CATEGORY }
?.let {
lifecycleScope.launch(Dispatchers.IO) { initializeHighlight(it) }
@@ -508,6 +507,8 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
// if the player is minimized, the fragment behind the player should handle the event
onBackPressedCallback.isEnabled = isMiniPlayerVisible != true
}
+
+ toggleVideoInfoVisibility(false)
}
private fun attachToPlayerService(playerData: PlayerData, startNewSession: Boolean) {
@@ -596,7 +597,7 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
updateCurrentSubtitle(null)
disableController()
commonPlayerViewModel.setSheetExpand(null)
- binding.sbSkipBtn.isGone = true
+ playerBackgroundBinding.sbSkipBtn.isGone = true
if (NavBarHelper.hasTabs()) {
mainMotionLayout.progress = 1F
}
@@ -644,7 +645,7 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
binding.closeImageView.setOnClickListener {
killPlayerFragment()
}
- playerBinding.closeImageButton.setOnClickListener {
+ playerControlsBinding.closeImageButton.setOnClickListener {
killPlayerFragment()
}
@@ -672,7 +673,7 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
// FullScreen button trigger
// hide fullscreen button if autorotation enabled
- playerBinding.fullscreen.setOnClickListener {
+ playerControlsBinding.fullscreen.setOnClickListener {
toggleFullscreen()
}
@@ -739,11 +740,11 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
}.show(childFragmentManager, AddToPlaylistDialog::class.java.name)
}
- playerBinding.skipPrev.setOnClickListener {
+ playerControlsBinding.skipPrev.setOnClickListener {
PlayingQueue.getPrev()?.let { prev -> playNextVideo(prev) }
}
- playerBinding.skipNext.setOnClickListener {
+ playerControlsBinding.skipNext.setOnClickListener {
PlayingQueue.getNext()?.let { next -> playNextVideo(next) }
}
@@ -1040,27 +1041,27 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
SbSkipOptions.MANUAL
) || autoSkipTemporarilyDisabled
) {
- binding.sbSkipBtn.isVisible = true
- binding.sbSkipBtn.setOnClickListener {
+ playerBackgroundBinding.sbSkipBtn.isVisible = true
+ playerBackgroundBinding.sbSkipBtn.setOnClickListener {
playerController.seekTo((segment.segmentStartAndEnd.second * 1000f).toLong())
segment.skipped = true
}
}
} else {
- binding.sbSkipBtn.isGone = true
+ playerBackgroundBinding.sbSkipBtn.isGone = true
}
}
private fun setPlayerDefaults() {
// reset the player view
- playerBinding.exoProgress.clearSegments()
- playerBinding.sbToggle.isGone = true
+ playerControlsBinding.exoProgress.clearSegments()
+ playerControlsBinding.sbToggle.isGone = true
// reset the comments to become reloaded later
commentsViewModel.reset()
// hide the button to skip SponsorBlock segments manually
- binding.sbSkipBtn.isGone = true
+ playerBackgroundBinding.sbSkipBtn.isGone = true
// use the video's default audio track when starting playback
playerController.sendCustomCommand(
@@ -1104,7 +1105,7 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
binding.descriptionLayout.isInvisible = !show
binding.relatedRecView.isInvisible = !show
binding.playerChannel.isInvisible = !show
- binding.videoTransitionProgress.isVisible = !show
+ playerBackgroundBinding.videoTransitionProgress.isVisible = !show
}
@SuppressLint("SetTextI18n")
@@ -1125,11 +1126,7 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
}
// initialize the player view actions
- binding.player.initialize(
- doubleTapOverlayBinding,
- playerGestureControlsViewBinding,
- chaptersViewModel
- )
+ binding.player.initialize(chaptersViewModel)
binding.player.initPlayerOptions(
viewModel,
commonPlayerViewModel,
@@ -1163,7 +1160,7 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
player.isLive = streams.isLive
relPlayerDownload.isVisible = !streams.isLive
}
- playerBinding.exoTitle.text = streams.title
+ playerControlsBinding.exoTitle.text = streams.title
// init the chapters recyclerview
chaptersViewModel.chaptersLiveData.postValue(streams.chapters)
@@ -1186,10 +1183,10 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
)
// seekbar preview setup
- playerBinding.seekbarPreview.isGone = true
- seekBarPreviewListener?.let { playerBinding.exoProgress.removeListener(it) }
+ playerControlsBinding.seekbarPreview.isGone = true
+ seekBarPreviewListener?.let { playerControlsBinding.exoProgress.removeListener(it) }
seekBarPreviewListener = createSeekbarPreviewListener().also {
- playerBinding.exoProgress.addSeekBarListener(it)
+ playerControlsBinding.exoProgress.addSeekBarListener(it)
}
}
@@ -1197,14 +1194,14 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
if (!PlayingQueue.hasNext()) return
disableController()
- binding.autoplayCountdown.setHideSelfListener {
+ playerBackgroundBinding.autoplayCountdown.setHideSelfListener {
// could fail if the video already got closed before
runCatching {
- binding.autoplayCountdown.isGone = true
+ playerBackgroundBinding.autoplayCountdown.isGone = true
binding.player.useController = true
}
}
- binding.autoplayCountdown.startCountdown {
+ playerBackgroundBinding.autoplayCountdown.startCountdown {
PlayingQueue.getNext()?.let { playNextVideo(it) }
}
}
@@ -1507,7 +1504,7 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
private fun createSeekbarPreviewListener(): SeekbarPreviewListener {
return SeekbarPreviewListener(
OnlineTimeFrameReceiver(requireContext(), streams.previewFrames),
- playerBinding,
+ playerControlsBinding,
streams.duration * 1000
)
}
diff --git a/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt b/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt
index 7b019e4796..16a321eada 100644
--- a/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt
+++ b/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt
@@ -12,7 +12,6 @@ import android.text.format.DateUtils
import android.util.AttributeSet
import android.view.KeyEvent
import android.view.MotionEvent
-import android.view.View
import android.view.Window
import android.widget.FrameLayout
import android.widget.ImageView
@@ -40,6 +39,7 @@ import androidx.media3.ui.TimeBar
import com.github.libretube.R
import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys
+import com.github.libretube.databinding.CustomExoPlayerViewTemplateBinding
import com.github.libretube.databinding.DoubleTapOverlayBinding
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
import com.github.libretube.databinding.PlayerGestureControlsViewBinding
@@ -80,16 +80,18 @@ abstract class CustomExoPlayerView(
) : PlayerView(context, attributeSet), PlayerOptions, PlayerGestureOptions {
@Suppress("LeakingThis")
val binding = ExoStyledPlayerControlViewBinding.bind(this)
+ val backgroundBinding = CustomExoPlayerViewTemplateBinding.bind(this)
/**
* Objects for player tap and swipe gesture
*/
- private lateinit var gestureViewBinding: PlayerGestureControlsViewBinding
+ private val gestureViewBinding: PlayerGestureControlsViewBinding get() = backgroundBinding.playerGestureControlsView.binding
+ private val doubleTapOverlayBinding: DoubleTapOverlayBinding get() = backgroundBinding.doubleTapOverlay.binding
+
private lateinit var playerGestureController: PlayerGestureController
private lateinit var brightnessHelper: BrightnessHelper
private lateinit var audioHelper: AudioHelper
private lateinit var chaptersViewModel: ChaptersViewModel
- private var doubleTapOverlayBinding: DoubleTapOverlayBinding? = null
private var chaptersBottomSheet: ChaptersBottomSheet? = null
private var scrubbingTimeBar = false
@@ -132,13 +134,7 @@ abstract class CustomExoPlayerView(
if (isControllerFullyVisible) hideController() else showController()
}
- fun initialize(
- doubleTapOverlayBinding: DoubleTapOverlayBinding,
- playerGestureControlsViewBinding: PlayerGestureControlsViewBinding,
- chaptersViewModel: ChaptersViewModel
- ) {
- this.doubleTapOverlayBinding = doubleTapOverlayBinding
- this.gestureViewBinding = playerGestureControlsViewBinding
+ fun initialize(chaptersViewModel: ChaptersViewModel) {
this.chaptersViewModel = chaptersViewModel
this.playerGestureController = PlayerGestureController(context as BaseActivity, this)
this.brightnessHelper = BrightnessHelper(context as Activity)
@@ -367,6 +363,10 @@ abstract class CustomExoPlayerView(
// remove the callback to hide the controller
cancelHideControllerTask()
super.hideController()
+ backgroundBinding.exoControlsBackground.animate()
+ .alpha(0f)
+ .setDuration(500)
+ .start()
}
override fun showController() {
@@ -375,6 +375,10 @@ abstract class CustomExoPlayerView(
// automatically hide the controller after 2 seconds
enqueueHideControllerTask()
super.showController()
+ backgroundBinding.exoControlsBackground.animate()
+ .alpha(1f)
+ .setDuration(200)
+ .start()
}
fun showControllerPermanently() {
@@ -388,12 +392,12 @@ abstract class CustomExoPlayerView(
private fun initRewindAndForward() {
val seekIncrementText = (PlayerHelper.seekIncrement / 1000).toString()
listOf(
- doubleTapOverlayBinding?.rewindTV,
- doubleTapOverlayBinding?.forwardTV,
+ doubleTapOverlayBinding.rewindTV,
+ doubleTapOverlayBinding.forwardTV,
binding.forwardTV,
binding.rewindTV
).forEach {
- it?.text = seekIncrementText
+ it.text = seekIncrementText
}
binding.forwardBTN.setOnClickListener {
player?.seekBy(PlayerHelper.seekIncrement)
@@ -475,7 +479,7 @@ abstract class CustomExoPlayerView(
}
// hide the dimming background overlay if locked
- binding.exoControlsBackground.setBackgroundColor(
+ backgroundBinding.exoControlsBackground.setBackgroundColor(
if (isLocked) {
ContextCompat.getColor(
context,
@@ -494,7 +498,7 @@ abstract class CustomExoPlayerView(
player?.seekBy(-PlayerHelper.seekIncrement)
// show the rewind button
- doubleTapOverlayBinding?.apply {
+ doubleTapOverlayBinding.apply {
animateSeeking(rewindBTN, rewindIV, rewindTV, true)
// start callback to hide the button
@@ -509,7 +513,7 @@ abstract class CustomExoPlayerView(
player?.seekBy(PlayerHelper.seekIncrement)
// show the forward button
- doubleTapOverlayBinding?.apply {
+ doubleTapOverlayBinding.apply {
animateSeeking(forwardBTN, forwardIV, forwardTV, false)
// start callback to hide the button
@@ -604,7 +608,7 @@ abstract class CustomExoPlayerView(
private fun updateVolume(distance: Float) {
val bar = gestureViewBinding.volumeProgressBar
gestureViewBinding.volumeControlView.apply {
- if (visibility == View.GONE) {
+ if (isGone) {
isVisible = true
// Volume could be changed using other mediums, sync progress
// bar with new value.
diff --git a/app/src/main/res/layout-land/fragment_player.xml b/app/src/main/res/layout-land/fragment_player.xml
index 1c14d50e7d..addf2c3031 100644
--- a/app/src/main/res/layout-land/fragment_player.xml
+++ b/app/src/main/res/layout-land/fragment_player.xml
@@ -207,70 +207,14 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/black"
+ app:player_layout_id="@layout/custom_exo_player_view"
app:controller_layout_id="@layout/exo_styled_player_control_view"
app:layout_constraintBottom_toBottomOf="@id/main_container"
app:layout_constraintEnd_toEndOf="@id/main_container"
app:layout_constraintStart_toStartOf="@id/main_container"
app:layout_constraintTop_toTopOf="@id/main_container"
app:show_buffering="when_playing"
- app:surface_type="surface_view">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ app:surface_type="surface_view" />
-
-
-
-
-
-
+ app:show_buffering="when_playing" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/custom_exo_player_view.xml b/app/src/main/res/layout/custom_exo_player_view.xml
new file mode 100644
index 0000000000..ef226383e3
--- /dev/null
+++ b/app/src/main/res/layout/custom_exo_player_view.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/custom_exo_player_view_template.xml b/app/src/main/res/layout/custom_exo_player_view_template.xml
new file mode 100644
index 0000000000..64cbd0bdba
--- /dev/null
+++ b/app/src/main/res/layout/custom_exo_player_view_template.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/exo_styled_player_control_view.xml b/app/src/main/res/layout/exo_styled_player_control_view.xml
index 50282750f5..74c16484b5 100644
--- a/app/src/main/res/layout/exo_styled_player_control_view.xml
+++ b/app/src/main/res/layout/exo_styled_player_control_view.xml
@@ -3,12 +3,6 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ app:surface_type="surface_view" />