mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-29 16:30:31 +05:30
Merge pull request #4128 from Bnyro/master
Show time left when clicking player duration
This commit is contained in:
commit
b771d89067
@ -100,6 +100,7 @@ object PreferenceKeys {
|
|||||||
const val FULLSCREEN_GESTURES = "fullscreen_gestures"
|
const val FULLSCREEN_GESTURES = "fullscreen_gestures"
|
||||||
const val UNLIMITED_SEARCH_HISTORY = "unlimited_search_history"
|
const val UNLIMITED_SEARCH_HISTORY = "unlimited_search_history"
|
||||||
const val SB_HIGHLIGHTS = "sb_highlights"
|
const val SB_HIGHLIGHTS = "sb_highlights"
|
||||||
|
const val SHOW_TIME_LEFT = "show_time_left"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Background mode
|
* Background mode
|
||||||
|
@ -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 */
|
* Seek to saved watch position if available */
|
||||||
private fun seekToWatchPosition() {
|
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")
|
@SuppressLint("SetTextI18n")
|
||||||
private fun initializePlayerView() {
|
private fun initializePlayerView() {
|
||||||
// initialize the player view actions
|
// initialize the player view actions
|
||||||
@ -916,11 +887,9 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
|||||||
R.string.subscribers,
|
R.string.subscribers,
|
||||||
streams.uploaderSubscriberCount.formatShort()
|
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
|
playerBinding.exoTitle.text = streams.title
|
||||||
|
|
||||||
// init the chapters recyclerview
|
// init the chapters recyclerview
|
||||||
@ -962,6 +931,13 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
|||||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||||
saveWatchPosition()
|
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.
|
// check if video has ended, next video is available and autoplay is enabled.
|
||||||
if (
|
if (
|
||||||
playbackState == Player.STATE_ENDED &&
|
playbackState == Player.STATE_ENDED &&
|
||||||
|
@ -7,6 +7,7 @@ import android.content.res.Configuration
|
|||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.text.format.DateUtils
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -17,9 +18,11 @@ 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.isGone
|
||||||
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.media3.common.C
|
||||||
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
|
||||||
@ -30,6 +33,7 @@ import androidx.media3.ui.PlayerView
|
|||||||
import androidx.media3.ui.SubtitleView
|
import androidx.media3.ui.SubtitleView
|
||||||
import androidx.media3.ui.TimeBar
|
import androidx.media3.ui.TimeBar
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
import com.github.libretube.databinding.DoubleTapOverlayBinding
|
import com.github.libretube.databinding.DoubleTapOverlayBinding
|
||||||
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
||||||
import com.github.libretube.databinding.PlayerGestureControlsViewBinding
|
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.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.PreferenceHelper
|
||||||
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.interfaces.PlayerGestureOptions
|
import com.github.libretube.ui.interfaces.PlayerGestureOptions
|
||||||
@ -72,6 +77,13 @@ open class CustomExoPlayerView(
|
|||||||
|
|
||||||
private val runnableHandler = Handler(Looper.getMainLooper())
|
private val runnableHandler = Handler(Looper.getMainLooper())
|
||||||
var isPlayerLocked: Boolean = false
|
var isPlayerLocked: Boolean = false
|
||||||
|
var isLive: Boolean = false
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
updateDisplayedDurationType()
|
||||||
|
updateCurrentPosition()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preferences
|
* Preferences
|
||||||
@ -177,6 +189,21 @@ open class CustomExoPlayerView(
|
|||||||
enqueueHideControllerTask()
|
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
|
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() {
|
private fun enqueueHideControllerTask() {
|
||||||
handler.postDelayed(AUTO_HIDE_CONTROLLER_DELAY, HIDE_CONTROLLER_TOKEN) {
|
handler.postDelayed(AUTO_HIDE_CONTROLLER_DELAY, HIDE_CONTROLLER_TOKEN) {
|
||||||
hideController()
|
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 {
|
open fun getTopBarMarginDp(): Int {
|
||||||
return if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) 10 else 0
|
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_CONTROLLER_TOKEN = "hideController"
|
||||||
private const val HIDE_FORWARD_BUTTON_TOKEN = "hideForwardButton"
|
private const val HIDE_FORWARD_BUTTON_TOKEN = "hideForwardButton"
|
||||||
private const val HIDE_REWIND_BUTTON_TOKEN = "hideRewindButton"
|
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 SUBTITLE_BOTTOM_PADDING_FRACTION = 0.158f
|
||||||
private const val ANIMATION_DURATION = 100L
|
private const val ANIMATION_DURATION = 100L
|
||||||
|
@ -251,19 +251,12 @@
|
|||||||
android:layout_marginStart="10dp">
|
android:layout_marginStart="10dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@id/exo_position"
|
android:id="@+id/position"
|
||||||
style="@style/TimeString"
|
style="@style/TimeString"
|
||||||
android:text="00:00"
|
android:text="00:00"
|
||||||
tools:ignore="HardcodedText" />
|
tools:ignore="HardcodedText" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/liveDiff"
|
|
||||||
style="@style/TimeString"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:text="05:10" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/time_separator"
|
|
||||||
style="@style/TimeString"
|
style="@style/TimeString"
|
||||||
android:text=" • "
|
android:text=" • "
|
||||||
tools:ignore="HardcodedText" />
|
tools:ignore="HardcodedText" />
|
||||||
@ -274,6 +267,13 @@
|
|||||||
android:text="00:00"
|
android:text="00:00"
|
||||||
tools:ignore="HardcodedText" />
|
tools:ignore="HardcodedText" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/timeLeft"
|
||||||
|
style="@style/TimeString"
|
||||||
|
android:text="00:00"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:ignore="HardcodedText" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
Loading…
x
Reference in New Issue
Block a user