mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-28 07:50:31 +05:30
refactor advanced player options
This commit is contained in:
parent
47ed98ffc3
commit
3cde15c96f
@ -6,7 +6,6 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@ -45,6 +44,8 @@ import com.github.libretube.dialogs.AddToPlaylistDialog
|
|||||||
import com.github.libretube.dialogs.DownloadDialog
|
import com.github.libretube.dialogs.DownloadDialog
|
||||||
import com.github.libretube.dialogs.ShareDialog
|
import com.github.libretube.dialogs.ShareDialog
|
||||||
import com.github.libretube.extensions.BaseFragment
|
import com.github.libretube.extensions.BaseFragment
|
||||||
|
import com.github.libretube.interfaces.DoubleTapInterface
|
||||||
|
import com.github.libretube.interfaces.PlayerOptionsInterface
|
||||||
import com.github.libretube.obj.ChapterSegment
|
import com.github.libretube.obj.ChapterSegment
|
||||||
import com.github.libretube.obj.Segment
|
import com.github.libretube.obj.Segment
|
||||||
import com.github.libretube.obj.Segments
|
import com.github.libretube.obj.Segments
|
||||||
@ -57,13 +58,13 @@ import com.github.libretube.util.BackgroundHelper
|
|||||||
import com.github.libretube.util.ConnectionHelper
|
import com.github.libretube.util.ConnectionHelper
|
||||||
import com.github.libretube.util.CronetHelper
|
import com.github.libretube.util.CronetHelper
|
||||||
import com.github.libretube.util.NowPlayingNotification
|
import com.github.libretube.util.NowPlayingNotification
|
||||||
import com.github.libretube.util.OnDoubleTapEventListener
|
|
||||||
import com.github.libretube.util.PlayerHelper
|
import com.github.libretube.util.PlayerHelper
|
||||||
import com.github.libretube.util.RetrofitInstance
|
import com.github.libretube.util.RetrofitInstance
|
||||||
import com.github.libretube.util.SubscriptionHelper
|
import com.github.libretube.util.SubscriptionHelper
|
||||||
import com.github.libretube.util.formatShort
|
import com.github.libretube.util.formatShort
|
||||||
import com.github.libretube.util.hideKeyboard
|
import com.github.libretube.util.hideKeyboard
|
||||||
import com.github.libretube.util.toID
|
import com.github.libretube.util.toID
|
||||||
|
import com.github.libretube.views.BottomSheetFragment
|
||||||
import com.google.android.exoplayer2.C
|
import com.google.android.exoplayer2.C
|
||||||
import com.google.android.exoplayer2.DefaultLoadControl
|
import com.google.android.exoplayer2.DefaultLoadControl
|
||||||
import com.google.android.exoplayer2.ExoPlayer
|
import com.google.android.exoplayer2.ExoPlayer
|
||||||
@ -206,8 +207,6 @@ class PlayerFragment : BaseFragment() {
|
|||||||
|
|
||||||
setUserPrefs()
|
setUserPrefs()
|
||||||
|
|
||||||
if (autoplayEnabled) playerBinding.autoplayIV.setImageResource(R.drawable.ic_toggle_on)
|
|
||||||
|
|
||||||
val mainActivity = activity as MainActivity
|
val mainActivity = activity as MainActivity
|
||||||
if (autoRotationEnabled) {
|
if (autoRotationEnabled) {
|
||||||
// enable auto rotation
|
// enable auto rotation
|
||||||
@ -397,6 +396,127 @@ class PlayerFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val playerOptionsInterface = object : PlayerOptionsInterface {
|
||||||
|
override fun onAutoplayClicked() {
|
||||||
|
// toggle autoplay
|
||||||
|
autoplayEnabled = !autoplayEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCaptionClicked() {
|
||||||
|
if (streams.subtitles == null || streams.subtitles!!.isEmpty()) {
|
||||||
|
Toast.makeText(context, R.string.no_subtitles_available, Toast.LENGTH_SHORT).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val subtitlesNamesList = mutableListOf(context?.getString(R.string.none)!!)
|
||||||
|
val subtitleCodesList = mutableListOf("")
|
||||||
|
streams.subtitles!!.forEach {
|
||||||
|
subtitlesNamesList += it.name!!
|
||||||
|
subtitleCodesList += it.code!!
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(R.string.captions)
|
||||||
|
.setItems(subtitlesNamesList.toTypedArray()) { _, index ->
|
||||||
|
val newParams = if (index != 0) {
|
||||||
|
// caption selected
|
||||||
|
|
||||||
|
// get the caption name and language
|
||||||
|
val captionLanguage = subtitlesNamesList[index]
|
||||||
|
val captionLanguageCode = subtitleCodesList[index]
|
||||||
|
|
||||||
|
// select the new caption preference
|
||||||
|
trackSelector.buildUponParameters()
|
||||||
|
.setPreferredTextLanguages(
|
||||||
|
captionLanguage,
|
||||||
|
captionLanguageCode
|
||||||
|
)
|
||||||
|
.setPreferredTextRoleFlags(C.ROLE_FLAG_CAPTION)
|
||||||
|
} else {
|
||||||
|
// none selected
|
||||||
|
// disable captions
|
||||||
|
trackSelector.buildUponParameters()
|
||||||
|
.setPreferredTextLanguage("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the new caption language
|
||||||
|
trackSelector.setParameters(newParams)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQualityClicked() {
|
||||||
|
// get the available resolutions
|
||||||
|
val (videosNameArray, videosUrlArray) = getAvailableResolutions(streams)
|
||||||
|
|
||||||
|
// Dialog for quality selection
|
||||||
|
val lastPosition = exoPlayer.currentPosition
|
||||||
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(R.string.choose_quality_dialog)
|
||||||
|
.setItems(
|
||||||
|
videosNameArray
|
||||||
|
) { _, which ->
|
||||||
|
if (
|
||||||
|
videosNameArray[which] == getString(R.string.hls) ||
|
||||||
|
videosNameArray[which] == "LBRY HLS"
|
||||||
|
) {
|
||||||
|
// no need to merge sources if using hls
|
||||||
|
val mediaItem: MediaItem = MediaItem.Builder()
|
||||||
|
.setUri(videosUrlArray[which])
|
||||||
|
.setSubtitleConfigurations(subtitle)
|
||||||
|
.build()
|
||||||
|
exoPlayer.setMediaItem(mediaItem)
|
||||||
|
} else {
|
||||||
|
val videoUri = videosUrlArray[which]
|
||||||
|
val audioUrl = PlayerHelper.getAudioSource(streams.audioStreams!!)
|
||||||
|
setMediaSource(videoUri, audioUrl)
|
||||||
|
}
|
||||||
|
exoPlayer.seekTo(lastPosition)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlaybackSpeedClicked() {
|
||||||
|
val playbackSpeeds = context?.resources?.getStringArray(R.array.playbackSpeed)!!
|
||||||
|
val playbackSpeedValues =
|
||||||
|
context?.resources?.getStringArray(R.array.playbackSpeedValues)!!
|
||||||
|
|
||||||
|
// change playback speed dialog
|
||||||
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(R.string.change_playback_speed)
|
||||||
|
.setItems(playbackSpeeds) { _, index ->
|
||||||
|
// set the new playback speed
|
||||||
|
val newPlaybackSpeed = playbackSpeedValues[index].toFloat()
|
||||||
|
exoPlayer.setPlaybackSpeed(newPlaybackSpeed)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAspectRatioClicked() {
|
||||||
|
// switching between original aspect ratio (black bars) and zoomed to fill device screen
|
||||||
|
val aspectRatioModes = arrayOf(
|
||||||
|
AspectRatioFrameLayout.RESIZE_MODE_FIT,
|
||||||
|
AspectRatioFrameLayout.RESIZE_MODE_ZOOM,
|
||||||
|
AspectRatioFrameLayout.RESIZE_MODE_FILL
|
||||||
|
)
|
||||||
|
val index = aspectRatioModes.indexOf(exoPlayerView.resizeMode)
|
||||||
|
val newAspectRatioMode =
|
||||||
|
if (index + 1 < aspectRatioModes.size) aspectRatioModes[index + 1]
|
||||||
|
else aspectRatioModes[0]
|
||||||
|
exoPlayerView.resizeMode = newAspectRatioMode
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRepeatModeClicked() {
|
||||||
|
// repeat toggle button
|
||||||
|
if (exoPlayer.repeatMode == RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL) {
|
||||||
|
// turn off repeat mode
|
||||||
|
exoPlayer.repeatMode = RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE
|
||||||
|
} else {
|
||||||
|
exoPlayer.repeatMode = RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// actions that don't depend on video information
|
// actions that don't depend on video information
|
||||||
private fun initializeOnClickActions() {
|
private fun initializeOnClickActions() {
|
||||||
binding.closeImageView.setOnClickListener {
|
binding.closeImageView.setOnClickListener {
|
||||||
@ -417,24 +537,12 @@ class PlayerFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
// show the advanced player options
|
// show the advanced player options
|
||||||
playerBinding.toggleOptions.setOnClickListener {
|
playerBinding.toggleOptions.setOnClickListener {
|
||||||
if (playerBinding.advancedOptions.isVisible) {
|
val bottomSheetFragment = BottomSheetFragment().apply {
|
||||||
playerBinding.toggleOptions.animate().rotation(0F).setDuration(250).start()
|
setOnClickListeners(playerOptionsInterface)
|
||||||
playerBinding.advancedOptions.visibility = View.GONE
|
|
||||||
} else {
|
|
||||||
playerBinding.toggleOptions.animate().rotation(180F).setDuration(250).start()
|
|
||||||
playerBinding.advancedOptions.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// autoplay toggle button
|
|
||||||
playerBinding.autoplayLL.setOnClickListener {
|
|
||||||
autoplayEnabled = if (autoplayEnabled) {
|
|
||||||
playerBinding.autoplayIV.setImageResource(R.drawable.ic_toggle_off)
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
playerBinding.autoplayIV.setImageResource(R.drawable.ic_toggle_on)
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
bottomSheetFragment.show(childFragmentManager, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.playImageView.setOnClickListener {
|
binding.playImageView.setOnClickListener {
|
||||||
if (!exoPlayer.isPlaying) {
|
if (!exoPlayer.isPlaying) {
|
||||||
// start or go on playing
|
// start or go on playing
|
||||||
@ -471,20 +579,6 @@ class PlayerFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// switching between original aspect ratio (black bars) and zoomed to fill device screen
|
|
||||||
val aspectRatioModes = arrayOf(
|
|
||||||
AspectRatioFrameLayout.RESIZE_MODE_FIT,
|
|
||||||
AspectRatioFrameLayout.RESIZE_MODE_ZOOM,
|
|
||||||
AspectRatioFrameLayout.RESIZE_MODE_FILL
|
|
||||||
)
|
|
||||||
playerBinding.aspectRatioButton.setOnClickListener {
|
|
||||||
val index = aspectRatioModes.indexOf(exoPlayerView.resizeMode)
|
|
||||||
val newAspectRatioMode =
|
|
||||||
if (index + 1 < aspectRatioModes.size) aspectRatioModes[index + 1]
|
|
||||||
else aspectRatioModes[0]
|
|
||||||
exoPlayerView.resizeMode = newAspectRatioMode
|
|
||||||
}
|
|
||||||
|
|
||||||
// lock and unlock the player
|
// lock and unlock the player
|
||||||
playerBinding.lockPlayer.setOnClickListener {
|
playerBinding.lockPlayer.setOnClickListener {
|
||||||
// change the locked/unlocked icon
|
// change the locked/unlocked icon
|
||||||
@ -502,37 +596,7 @@ class PlayerFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set default playback speed
|
// set default playback speed
|
||||||
val playbackSpeeds = context?.resources?.getStringArray(R.array.playbackSpeed)!!
|
|
||||||
val playbackSpeedValues =
|
|
||||||
context?.resources?.getStringArray(R.array.playbackSpeedValues)!!
|
|
||||||
exoPlayer.setPlaybackSpeed(playbackSpeed.toFloat())
|
exoPlayer.setPlaybackSpeed(playbackSpeed.toFloat())
|
||||||
val speedIndex = playbackSpeedValues.indexOf(playbackSpeed)
|
|
||||||
playerBinding.speedText.text = playbackSpeeds[speedIndex]
|
|
||||||
|
|
||||||
// change playback speed button
|
|
||||||
playerBinding.speedText.setOnClickListener {
|
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
|
||||||
.setTitle(R.string.change_playback_speed)
|
|
||||||
.setItems(playbackSpeeds) { _, index ->
|
|
||||||
// set the new playback speed
|
|
||||||
val newPlaybackSpeed = playbackSpeedValues[index].toFloat()
|
|
||||||
exoPlayer.setPlaybackSpeed(newPlaybackSpeed)
|
|
||||||
playerBinding.speedText.text = playbackSpeeds[index]
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
// repeat toggle button
|
|
||||||
playerBinding.repeatToggle.setOnClickListener {
|
|
||||||
if (exoPlayer.repeatMode == RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL) {
|
|
||||||
// turn off repeat mode
|
|
||||||
exoPlayer.repeatMode = RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE
|
|
||||||
playerBinding.repeatToggle.setColorFilter(Color.GRAY)
|
|
||||||
} else {
|
|
||||||
exoPlayer.repeatMode = RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL
|
|
||||||
playerBinding.repeatToggle.setColorFilter(Color.WHITE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// share button
|
// share button
|
||||||
binding.relPlayerShare.setOnClickListener {
|
binding.relPlayerShare.setOnClickListener {
|
||||||
@ -785,7 +849,6 @@ class PlayerFragment : BaseFragment() {
|
|||||||
// switch back to normal speed when on the end of live stream
|
// switch back to normal speed when on the end of live stream
|
||||||
if (exoPlayer.duration - exoPlayer.currentPosition < 7000) {
|
if (exoPlayer.duration - exoPlayer.currentPosition < 7000) {
|
||||||
exoPlayer.setPlaybackSpeed(1F)
|
exoPlayer.setPlaybackSpeed(1F)
|
||||||
playerBinding.speedText.text = "1x"
|
|
||||||
playerBinding.liveSeparator.visibility = View.GONE
|
playerBinding.liveSeparator.visibility = View.GONE
|
||||||
playerBinding.liveDiff.text = ""
|
playerBinding.liveDiff.text = ""
|
||||||
} else {
|
} else {
|
||||||
@ -911,12 +974,6 @@ class PlayerFragment : BaseFragment() {
|
|||||||
override fun onVideoSizeChanged(
|
override fun onVideoSizeChanged(
|
||||||
videoSize: VideoSize
|
videoSize: VideoSize
|
||||||
) {
|
) {
|
||||||
// show the resolution in the video resolution text view
|
|
||||||
if (playerBinding.qualityText.text == context?.getString(R.string.hls)) {
|
|
||||||
playerBinding.qualityText.text =
|
|
||||||
"${context?.getString(R.string.hls)} (${videoSize.height}p)"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set new width/height of view
|
// Set new width/height of view
|
||||||
// height or width must be cast to float as int/int will give 0
|
// height or width must be cast to float as int/int will give 0
|
||||||
|
|
||||||
@ -1075,7 +1132,7 @@ class PlayerFragment : BaseFragment() {
|
|||||||
doubleTapOverlayBinding.rewindTV.text = seekIncrementText
|
doubleTapOverlayBinding.rewindTV.text = seekIncrementText
|
||||||
doubleTapOverlayBinding.forwardTV.text = seekIncrementText
|
doubleTapOverlayBinding.forwardTV.text = seekIncrementText
|
||||||
binding.player.setOnDoubleTapListener(
|
binding.player.setOnDoubleTapListener(
|
||||||
object : OnDoubleTapEventListener {
|
object : DoubleTapInterface {
|
||||||
override fun onEvent(x: Float) {
|
override fun onEvent(x: Float) {
|
||||||
val width = exoPlayerView.width
|
val width = exoPlayerView.width
|
||||||
when {
|
when {
|
||||||
@ -1234,17 +1291,17 @@ class PlayerFragment : BaseFragment() {
|
|||||||
exoPlayer.setMediaSource(mergeSource)
|
exoPlayer.setMediaSource(mergeSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setResolutionAndSubtitles(response: Streams) {
|
private fun getAvailableResolutions(streams: Streams): Pair<Array<String>, Array<Uri>> {
|
||||||
var videosNameArray: Array<CharSequence> = arrayOf()
|
var videosNameArray: Array<String> = arrayOf()
|
||||||
var videosUrlArray: Array<Uri> = arrayOf()
|
var videosUrlArray: Array<Uri> = arrayOf()
|
||||||
|
|
||||||
// append hls to list if available
|
// append hls to list if available
|
||||||
if (response.hls != null) {
|
if (streams.hls != null) {
|
||||||
videosNameArray += getString(R.string.hls)
|
videosNameArray += getString(R.string.hls)
|
||||||
videosUrlArray += response.hls.toUri()
|
videosUrlArray += streams.hls.toUri()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (vid in response.videoStreams!!) {
|
for (vid in streams.videoStreams!!) {
|
||||||
// append quality to list if it has the preferred format (e.g. MPEG)
|
// append quality to list if it has the preferred format (e.g. MPEG)
|
||||||
val preferredMimeType = "video/$videoFormatPreference"
|
val preferredMimeType = "video/$videoFormatPreference"
|
||||||
if (vid.url != null && vid.mimeType == preferredMimeType) { // preferred format
|
if (vid.url != null && vid.mimeType == preferredMimeType) { // preferred format
|
||||||
@ -1255,6 +1312,13 @@ class PlayerFragment : BaseFragment() {
|
|||||||
videosUrlArray += vid.url!!.toUri()
|
videosUrlArray += vid.url!!.toUri()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return Pair(videosNameArray, videosUrlArray)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setResolutionAndSubtitles(response: Streams) {
|
||||||
|
// get the available resolutions
|
||||||
|
val (videosNameArray, videosUrlArray) = getAvailableResolutions(response)
|
||||||
|
|
||||||
// create a list of subtitles
|
// create a list of subtitles
|
||||||
subtitle = mutableListOf()
|
subtitle = mutableListOf()
|
||||||
val subtitlesNamesList = mutableListOf(context?.getString(R.string.none)!!)
|
val subtitlesNamesList = mutableListOf(context?.getString(R.string.none)!!)
|
||||||
@ -1276,46 +1340,6 @@ class PlayerFragment : BaseFragment() {
|
|||||||
.setPreferredTextLanguage(defaultSubtitleCode)
|
.setPreferredTextLanguage(defaultSubtitleCode)
|
||||||
.setPreferredTextRoleFlags(C.ROLE_FLAG_CAPTION)
|
.setPreferredTextRoleFlags(C.ROLE_FLAG_CAPTION)
|
||||||
trackSelector.setParameters(newParams)
|
trackSelector.setParameters(newParams)
|
||||||
playerBinding.captions.setImageResource(R.drawable.ic_caption)
|
|
||||||
}
|
|
||||||
|
|
||||||
// captions selection dialog
|
|
||||||
// hide caption selection view if no subtitles available
|
|
||||||
if (response.subtitles.isEmpty()) playerBinding.captions.visibility = View.GONE
|
|
||||||
playerBinding.captions.setOnClickListener {
|
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
|
||||||
.setTitle(R.string.captions)
|
|
||||||
.setItems(subtitlesNamesList.toTypedArray()) { _, index ->
|
|
||||||
val newParams = if (index != 0) {
|
|
||||||
// caption selected
|
|
||||||
|
|
||||||
// get the caption name and language
|
|
||||||
val captionLanguage = subtitlesNamesList[index]
|
|
||||||
val captionLanguageCode = subtitleCodesList[index]
|
|
||||||
|
|
||||||
// update the icon of the captions button
|
|
||||||
playerBinding.captions.setImageResource(R.drawable.ic_caption)
|
|
||||||
|
|
||||||
// select the new caption preference
|
|
||||||
trackSelector.buildUponParameters()
|
|
||||||
.setPreferredTextLanguages(
|
|
||||||
captionLanguage,
|
|
||||||
captionLanguageCode
|
|
||||||
)
|
|
||||||
.setPreferredTextRoleFlags(C.ROLE_FLAG_CAPTION)
|
|
||||||
} else {
|
|
||||||
// none selected
|
|
||||||
playerBinding.captions.setImageResource(R.drawable.ic_caption_outlined)
|
|
||||||
|
|
||||||
// disable captions
|
|
||||||
trackSelector.buildUponParameters()
|
|
||||||
.setPreferredTextLanguage("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the new caption language
|
|
||||||
trackSelector.setParameters(newParams)
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set media source and resolution in the beginning
|
// set media source and resolution in the beginning
|
||||||
@ -1324,43 +1348,11 @@ class PlayerFragment : BaseFragment() {
|
|||||||
videosNameArray,
|
videosNameArray,
|
||||||
videosUrlArray
|
videosUrlArray
|
||||||
)
|
)
|
||||||
|
|
||||||
playerBinding.qualityText.setOnClickListener {
|
|
||||||
// Dialog for quality selection
|
|
||||||
val builder: MaterialAlertDialogBuilder? = activity?.let {
|
|
||||||
MaterialAlertDialogBuilder(it)
|
|
||||||
}
|
|
||||||
val lastPosition = exoPlayer.currentPosition
|
|
||||||
builder!!.setTitle(R.string.choose_quality_dialog)
|
|
||||||
.setItems(
|
|
||||||
videosNameArray
|
|
||||||
) { _, which ->
|
|
||||||
if (
|
|
||||||
videosNameArray[which] == getString(R.string.hls) ||
|
|
||||||
videosNameArray[which] == "LBRY HLS"
|
|
||||||
) {
|
|
||||||
// no need to merge sources if using hls
|
|
||||||
val mediaItem: MediaItem = MediaItem.Builder()
|
|
||||||
.setUri(videosUrlArray[which])
|
|
||||||
.setSubtitleConfigurations(subtitle)
|
|
||||||
.build()
|
|
||||||
exoPlayer.setMediaItem(mediaItem)
|
|
||||||
} else {
|
|
||||||
val videoUri = videosUrlArray[which]
|
|
||||||
val audioUrl = PlayerHelper.getAudioSource(response.audioStreams!!)
|
|
||||||
setMediaSource(videoUri, audioUrl)
|
|
||||||
}
|
|
||||||
exoPlayer.seekTo(lastPosition)
|
|
||||||
playerBinding.qualityText.text = videosNameArray[which]
|
|
||||||
}
|
|
||||||
val dialog = builder.create()
|
|
||||||
dialog.show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setStreamSource(
|
private fun setStreamSource(
|
||||||
streams: Streams,
|
streams: Streams,
|
||||||
videosNameArray: Array<CharSequence>,
|
videosNameArray: Array<String>,
|
||||||
videosUrlArray: Array<Uri>
|
videosUrlArray: Array<Uri>
|
||||||
) {
|
) {
|
||||||
if (defRes != "") {
|
if (defRes != "") {
|
||||||
@ -1370,7 +1362,6 @@ class PlayerFragment : BaseFragment() {
|
|||||||
val videoUri = videosUrlArray[index]
|
val videoUri = videosUrlArray[index]
|
||||||
val audioUrl = PlayerHelper.getAudioSource(streams.audioStreams!!)
|
val audioUrl = PlayerHelper.getAudioSource(streams.audioStreams!!)
|
||||||
setMediaSource(videoUri, audioUrl)
|
setMediaSource(videoUri, audioUrl)
|
||||||
playerBinding.qualityText.text = videosNameArray[index]
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1383,7 +1374,6 @@ class PlayerFragment : BaseFragment() {
|
|||||||
.setSubtitleConfigurations(subtitle)
|
.setSubtitleConfigurations(subtitle)
|
||||||
.build()
|
.build()
|
||||||
exoPlayer.setMediaItem(mediaItem)
|
exoPlayer.setMediaItem(mediaItem)
|
||||||
playerBinding.qualityText.text = context?.getString(R.string.hls)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1392,7 +1382,6 @@ class PlayerFragment : BaseFragment() {
|
|||||||
val videoUri = videosUrlArray[0]
|
val videoUri = videosUrlArray[0]
|
||||||
val audioUrl = PlayerHelper.getAudioSource(streams.audioStreams!!)
|
val audioUrl = PlayerHelper.getAudioSource(streams.audioStreams!!)
|
||||||
setMediaSource(videoUri, audioUrl)
|
setMediaSource(videoUri, audioUrl)
|
||||||
playerBinding.qualityText.text = videosNameArray[0]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.github.libretube.interfaces
|
||||||
|
|
||||||
|
interface DoubleTapInterface {
|
||||||
|
fun onEvent(x: Float)
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.github.libretube.interfaces
|
||||||
|
|
||||||
|
interface PlayerOptionsInterface {
|
||||||
|
|
||||||
|
fun onAutoplayClicked()
|
||||||
|
|
||||||
|
fun onCaptionClicked()
|
||||||
|
|
||||||
|
fun onQualityClicked()
|
||||||
|
|
||||||
|
fun onPlaybackSpeedClicked()
|
||||||
|
|
||||||
|
fun onAspectRatioClicked()
|
||||||
|
|
||||||
|
fun onRepeatModeClicked()
|
||||||
|
}
|
@ -1,5 +0,0 @@
|
|||||||
package com.github.libretube.util
|
|
||||||
|
|
||||||
interface OnDoubleTapEventListener {
|
|
||||||
fun onEvent(x: Float)
|
|
||||||
}
|
|
@ -0,0 +1,61 @@
|
|||||||
|
package com.github.libretube.views
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.github.libretube.databinding.BottomSheetBinding
|
||||||
|
import com.github.libretube.interfaces.PlayerOptionsInterface
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
class BottomSheetFragment : BottomSheetDialogFragment() {
|
||||||
|
private lateinit var binding: BottomSheetBinding
|
||||||
|
private lateinit var playerOptionsInterface: PlayerOptionsInterface
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
binding = BottomSheetBinding.inflate(layoutInflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnClickListeners(playerOptionsInterface: PlayerOptionsInterface) {
|
||||||
|
this.playerOptionsInterface = playerOptionsInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
binding.aspectRatio.setOnClickListener {
|
||||||
|
playerOptionsInterface.onAspectRatioClicked()
|
||||||
|
this.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.quality.setOnClickListener {
|
||||||
|
playerOptionsInterface.onQualityClicked()
|
||||||
|
this.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.playbackSpeed.setOnClickListener {
|
||||||
|
playerOptionsInterface.onPlaybackSpeedClicked()
|
||||||
|
this.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.captions.setOnClickListener {
|
||||||
|
playerOptionsInterface.onCaptionClicked()
|
||||||
|
this.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.autoplay.setOnClickListener {
|
||||||
|
playerOptionsInterface.onAutoplayClicked()
|
||||||
|
this.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.repeatMode.setOnClickListener {
|
||||||
|
playerOptionsInterface.onRepeatModeClicked()
|
||||||
|
this.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,10 +4,9 @@ import android.annotation.SuppressLint
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
|
||||||
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
||||||
|
import com.github.libretube.interfaces.DoubleTapInterface
|
||||||
import com.github.libretube.util.DoubleTapListener
|
import com.github.libretube.util.DoubleTapListener
|
||||||
import com.github.libretube.util.OnDoubleTapEventListener
|
|
||||||
import com.google.android.exoplayer2.ui.StyledPlayerView
|
import com.google.android.exoplayer2.ui.StyledPlayerView
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
@ -18,13 +17,13 @@ internal class CustomExoPlayerView(
|
|||||||
val TAG = "CustomExoPlayerView"
|
val TAG = "CustomExoPlayerView"
|
||||||
val binding: ExoStyledPlayerControlViewBinding = ExoStyledPlayerControlViewBinding.bind(this)
|
val binding: ExoStyledPlayerControlViewBinding = ExoStyledPlayerControlViewBinding.bind(this)
|
||||||
|
|
||||||
private var doubleTapListener: OnDoubleTapEventListener? = null
|
private var doubleTapListener: DoubleTapInterface? = null
|
||||||
|
|
||||||
// the x-position of where the user clicked
|
// the x-position of where the user clicked
|
||||||
private var xPos = 0F
|
private var xPos = 0F
|
||||||
|
|
||||||
fun setOnDoubleTapListener(
|
fun setOnDoubleTapListener(
|
||||||
eventListener: OnDoubleTapEventListener?
|
eventListener: DoubleTapInterface?
|
||||||
) {
|
) {
|
||||||
doubleTapListener = eventListener
|
doubleTapListener = eventListener
|
||||||
}
|
}
|
||||||
@ -44,11 +43,6 @@ internal class CustomExoPlayerView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setControllerVisibilityListener {
|
|
||||||
// hide the advanced options
|
|
||||||
binding.toggleOptions.animate().rotation(0F).setDuration(250).start()
|
|
||||||
binding.advancedOptions.visibility = View.GONE
|
|
||||||
}
|
|
||||||
// set the double click listener for rewind/forward
|
// set the double click listener for rewind/forward
|
||||||
setOnClickListener(doubleTouchListener)
|
setOnClickListener(doubleTouchListener)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:tint="@android:color/white"
|
android:tint="?attr/colorControlNormal"
|
||||||
android:viewportWidth="48"
|
android:viewportWidth="48"
|
||||||
android:viewportHeight="48">
|
android:viewportHeight="48">
|
||||||
<path
|
<path
|
||||||
|
44
app/src/main/res/layout/bottom_sheet.xml
Normal file
44
app/src/main/res/layout/bottom_sheet.xml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingVertical="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/quality"
|
||||||
|
style="@style/BottomSheetItem"
|
||||||
|
android:drawableStart="@drawable/ic_hd"
|
||||||
|
android:text="@string/quality" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/playbackSpeed"
|
||||||
|
style="@style/BottomSheetItem"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:drawableStart="@drawable/ic_speed"
|
||||||
|
android:text="@string/playback_speed" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/captions"
|
||||||
|
style="@style/BottomSheetItem"
|
||||||
|
android:drawableStart="@drawable/ic_caption"
|
||||||
|
android:text="@string/captions" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/autoplay"
|
||||||
|
style="@style/BottomSheetItem"
|
||||||
|
android:drawableStart="@drawable/ic_play"
|
||||||
|
android:text="@string/player_autoplay" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/repeatMode"
|
||||||
|
style="@style/BottomSheetItem"
|
||||||
|
android:drawableStart="@drawable/ic_repeat"
|
||||||
|
android:text="@string/repeat_mode" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/aspectRatio"
|
||||||
|
style="@style/BottomSheetItem"
|
||||||
|
android:drawableStart="@drawable/ic_aspect_ratio"
|
||||||
|
android:text="@string/aspect_ratio" />
|
||||||
|
</LinearLayout>
|
@ -67,17 +67,6 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layoutDirection="ltr">
|
android:layoutDirection="ltr">
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/speed_text"
|
|
||||||
style="@style/PlayerControlTop"
|
|
||||||
android:text="1x"
|
|
||||||
tools:ignore="HardcodedText" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/quality_text"
|
|
||||||
style="@style/PlayerControlTop"
|
|
||||||
android:text="@string/hls" />
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/toggle_options"
|
android:id="@+id/toggle_options"
|
||||||
style="@style/PlayerControlTop"
|
style="@style/PlayerControlTop"
|
||||||
@ -89,68 +78,6 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/advanced_options"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="end"
|
|
||||||
android:layout_marginHorizontal="3dp"
|
|
||||||
android:layout_marginTop="-12dp"
|
|
||||||
android:baselineAligned="false"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/autoplayLL"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginStart="5dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginBottom="1.5dp"
|
|
||||||
android:text="@string/player_autoplay"
|
|
||||||
android:textColor="@android:color/white" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/autoplayIV"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginStart="10dp"
|
|
||||||
android:src="@drawable/ic_toggle_off" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/captions"
|
|
||||||
style="@style/PlayerControlTop"
|
|
||||||
android:src="@drawable/ic_caption_outlined"
|
|
||||||
app:tint="@android:color/white" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/repeat_toggle"
|
|
||||||
style="@style/PlayerControlTop"
|
|
||||||
android:src="@drawable/ic_repeat"
|
|
||||||
app:tint="@android:color/darker_gray" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/aspect_ratio_button"
|
|
||||||
style="@style/PlayerControlTop"
|
|
||||||
android:src="@drawable/ic_aspect_ratio" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -303,4 +303,6 @@
|
|||||||
<string name="take_a_break">Time to take a break</string>
|
<string name="take_a_break">Time to take a break</string>
|
||||||
<string name="already_spent_time">You already spent %1$s minutes in the app, time to take a break.</string>
|
<string name="already_spent_time">You already spent %1$s minutes in the app, time to take a break.</string>
|
||||||
<string name="yt_shorts">Shorts</string>
|
<string name="yt_shorts">Shorts</string>
|
||||||
|
<string name="no_subtitles_available">No subtitles available</string>
|
||||||
|
<string name="repeat_mode">Repeat Mode</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -157,4 +157,14 @@
|
|||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="BottomSheetItem">
|
||||||
|
|
||||||
|
<item name="android:textSize">16sp</item>
|
||||||
|
<item name="android:drawablePadding">20dp</item>
|
||||||
|
<item name="android:layout_width">match_parent</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:padding">10dp</item>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
Loading…
x
Reference in New Issue
Block a user