diff --git a/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt b/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt
index b3e98243b..22d4fbb6d 100644
--- a/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt
+++ b/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt
@@ -100,6 +100,7 @@ object PreferenceKeys {
const val FULLSCREEN_GESTURES = "fullscreen_gestures"
const val UNLIMITED_SEARCH_HISTORY = "unlimited_search_history"
const val SB_HIGHLIGHTS = "sb_highlights"
+ const val SHOW_TIME_LEFT = "show_time_left"
/**
* Background mode
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 a5eb94d06..14a8e06f3 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
@@ -799,25 +799,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
}
}
- @SuppressLint("SetTextI18n")
- private fun refreshLiveStatus() {
- // switch back to normal speed when on the end of live stream
- if (exoPlayer.duration - exoPlayer.currentPosition < 7000) {
- exoPlayer.setPlaybackSpeed(1F)
- playerBinding.timeSeparator.visibility = View.GONE
- playerBinding.liveDiff.text = ""
- } else {
- // live stream but not watching at the end/live position
- playerBinding.timeSeparator.visibility = View.VISIBLE
- val diffText = DateUtils.formatElapsedTime(
- (exoPlayer.duration - exoPlayer.currentPosition) / 1000
- )
- playerBinding.liveDiff.text = "-$diffText"
- }
- // call the function again after 100ms
- handler.postDelayed(this@PlayerFragment::refreshLiveStatus, 100)
- }
-
/**
* Seek to saved watch position if available */
private fun seekToWatchPosition() {
@@ -882,16 +863,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
}
}
- private fun handleLiveVideo() {
- playerBinding.exoPosition.visibility = View.GONE
- playerBinding.liveDiff.visibility = View.VISIBLE
- playerBinding.duration.text = getString(R.string.live)
- playerBinding.exoTime.setOnClickListener {
- exoPlayer.seekTo(exoPlayer.duration)
- }
- refreshLiveStatus()
- }
-
@SuppressLint("SetTextI18n")
private fun initializePlayerView() {
// initialize the player view actions
@@ -916,11 +887,9 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
R.string.subscribers,
streams.uploaderSubscriberCount.formatShort()
)
+
+ player.isLive = streams.livestream
}
-
- // duration that's not greater than 0 indicates that the video is live
- if (streams.livestream) handleLiveVideo()
-
playerBinding.exoTitle.text = streams.title
// init the chapters recyclerview
@@ -962,6 +931,13 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
override fun onPlaybackStateChanged(playbackState: Int) {
saveWatchPosition()
+ // set the playback speed to one if having reached the end of a livestream
+ if (playbackState == Player.STATE_BUFFERING && binding.player.isLive &&
+ exoPlayer.duration - exoPlayer.currentPosition < 700
+ ) {
+ exoPlayer.setPlaybackSpeed(1f)
+ }
+
// check if video has ended, next video is available and autoplay is enabled.
if (
playbackState == Player.STATE_ENDED &&
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 0576daa00..b201f3c62 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
@@ -7,6 +7,7 @@ import android.content.res.Configuration
import android.graphics.Color
import android.os.Handler
import android.os.Looper
+import android.text.format.DateUtils
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
@@ -17,9 +18,11 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.os.postDelayed
import androidx.core.view.ViewCompat
+import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.view.marginStart
import androidx.core.view.updateLayoutParams
+import androidx.media3.common.C
import androidx.media3.common.Player
import androidx.media3.common.text.Cue
import androidx.media3.common.util.RepeatModeUtil
@@ -30,6 +33,7 @@ import androidx.media3.ui.PlayerView
import androidx.media3.ui.SubtitleView
import androidx.media3.ui.TimeBar
import com.github.libretube.R
+import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.DoubleTapOverlayBinding
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
import com.github.libretube.databinding.PlayerGestureControlsViewBinding
@@ -39,6 +43,7 @@ import com.github.libretube.extensions.round
import com.github.libretube.helpers.AudioHelper
import com.github.libretube.helpers.BrightnessHelper
import com.github.libretube.helpers.PlayerHelper
+import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.obj.BottomSheetItem
import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.interfaces.PlayerGestureOptions
@@ -72,6 +77,13 @@ open class CustomExoPlayerView(
private val runnableHandler = Handler(Looper.getMainLooper())
var isPlayerLocked: Boolean = false
+ var isLive: Boolean = false
+ set(value) {
+ field = value
+ updateDisplayedDurationType()
+ updateCurrentPosition()
+ }
+
/**
* Preferences
@@ -177,6 +189,21 @@ open class CustomExoPlayerView(
enqueueHideControllerTask()
}
})
+
+ // restore the duration type from the previous session
+ updateDisplayedDurationType()
+
+ binding.duration.setOnClickListener {
+ updateDisplayedDurationType(true)
+ }
+ binding.timeLeft.setOnClickListener {
+ updateDisplayedDurationType(false)
+ }
+ binding.position.setOnClickListener {
+ if (isLive) player?.let { it.seekTo(it.duration) }
+ }
+
+ updateCurrentPosition()
}
open fun onPlayerEvent(player: Player, playerEvents: Player.Events) = Unit
@@ -191,6 +218,19 @@ open class CustomExoPlayerView(
)
}
+ private fun updateDisplayedDurationType(showTimeLeft: Boolean? = null) {
+ var shouldShowTimeLeft = showTimeLeft ?: PreferenceHelper
+ .getBoolean(PreferenceKeys.SHOW_TIME_LEFT, false)
+ // always show the time left only if it's a livestream
+ if (isLive) shouldShowTimeLeft = true
+ if (showTimeLeft != null) {
+ // save whether to show time left or duration for next session
+ PreferenceHelper.putBoolean(PreferenceKeys.SHOW_TIME_LEFT, shouldShowTimeLeft)
+ }
+ binding.timeLeft.isVisible = shouldShowTimeLeft
+ binding.duration.isGone = shouldShowTimeLeft
+ }
+
private fun enqueueHideControllerTask() {
handler.postDelayed(AUTO_HIDE_CONTROLLER_DELAY, HIDE_CONTROLLER_TOKEN) {
hideController()
@@ -568,6 +608,19 @@ open class CustomExoPlayerView(
}
}
+ @SuppressLint("SetTextI18n")
+ private fun updateCurrentPosition() {
+ val position = player?.currentPosition?.div(1000) ?: 0
+ val duration = player?.duration?.takeIf { it != C.TIME_UNSET }?.div(1000) ?: 0
+ val timeLeft = duration - position
+
+ binding.position.text =
+ if (isLive) context.getString(R.string.live) else DateUtils.formatElapsedTime(position)
+ binding.timeLeft.text = "-${DateUtils.formatElapsedTime(timeLeft)}"
+
+ runnableHandler.postDelayed(100, UPDATE_POSITION_TOKEN, this::updateCurrentPosition)
+ }
+
open fun getTopBarMarginDp(): Int {
return if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) 10 else 0
}
@@ -676,6 +729,7 @@ open class CustomExoPlayerView(
private const val HIDE_CONTROLLER_TOKEN = "hideController"
private const val HIDE_FORWARD_BUTTON_TOKEN = "hideForwardButton"
private const val HIDE_REWIND_BUTTON_TOKEN = "hideRewindButton"
+ private const val UPDATE_POSITION_TOKEN = "updatePosition"
private const val SUBTITLE_BOTTOM_PADDING_FRACTION = 0.158f
private const val ANIMATION_DURATION = 100L
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 f7cbcbce3..11876126a 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
@@ -251,19 +251,12 @@
android:layout_marginStart="10dp">
-
-
@@ -274,6 +267,13 @@
android:text="00:00"
tools:ignore="HardcodedText" />
+
+