mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-27 23:40:33 +05:30
Merge branch 'master' into KotlinX_Serialization
# Conflicts: # app/src/main/java/com/github/libretube/ui/adapters/SearchAdapter.kt
This commit is contained in:
commit
56763ec1a9
@ -93,7 +93,6 @@ object PreferenceKeys {
|
||||
/**
|
||||
* Background mode
|
||||
*/
|
||||
const val BACKGROUND_PLAYBACK_SPEED = "background_playback_speed"
|
||||
const val AUDIO_ONLY_MODE = "audio_only_mode"
|
||||
|
||||
/**
|
||||
|
@ -22,7 +22,6 @@ import com.github.libretube.api.obj.Streams
|
||||
import com.github.libretube.constants.BACKGROUND_CHANNEL_ID
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.constants.PLAYER_NOTIFICATION_ID
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.db.DatabaseHolder.Companion.Database
|
||||
import com.github.libretube.db.obj.WatchPosition
|
||||
import com.github.libretube.extensions.TAG
|
||||
@ -32,8 +31,8 @@ import com.github.libretube.extensions.toID
|
||||
import com.github.libretube.extensions.toStreamItem
|
||||
import com.github.libretube.util.NowPlayingNotification
|
||||
import com.github.libretube.util.PlayerHelper
|
||||
import com.github.libretube.util.PlayerHelper.loadPlaybackParams
|
||||
import com.github.libretube.util.PlayingQueue
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
import com.google.android.exoplayer2.MediaItem
|
||||
import com.google.android.exoplayer2.PlaybackException
|
||||
@ -66,7 +65,7 @@ class BackgroundMode : Service() {
|
||||
/**
|
||||
* The [ExoPlayer] player. Followed tutorial [here](https://developer.android.com/codelabs/exoplayer-intro)
|
||||
*/
|
||||
private var player: ExoPlayer? = null
|
||||
var player: ExoPlayer? = null
|
||||
private var playWhenReadyPlayer = true
|
||||
|
||||
/**
|
||||
@ -224,13 +223,6 @@ class BackgroundMode : Service() {
|
||||
}
|
||||
}
|
||||
|
||||
// set the playback speed
|
||||
val playbackSpeed = PreferenceHelper.getString(
|
||||
PreferenceKeys.BACKGROUND_PLAYBACK_SPEED,
|
||||
"1"
|
||||
).toFloat()
|
||||
player?.setPlaybackSpeed(playbackSpeed)
|
||||
|
||||
fetchSponsorBlockSegments()
|
||||
}
|
||||
|
||||
@ -245,6 +237,7 @@ class BackgroundMode : Service() {
|
||||
.setAudioAttributes(PlayerHelper.getAudioAttributes(), true)
|
||||
.setLoadControl(PlayerHelper.getLoadControl())
|
||||
.build()
|
||||
.loadPlaybackParams()
|
||||
|
||||
/**
|
||||
* Listens for changed playbackStates (e.g. pause, end)
|
||||
|
@ -97,7 +97,7 @@ class MainActivity : BaseActivity() {
|
||||
|
||||
// sets the navigation bar color to the previously calculated color
|
||||
window.navigationBarColor = if (binding.bottomNav.menu.size() > 0) {
|
||||
SurfaceColors.getColorForElevation(this, 10F)
|
||||
SurfaceColors.getColorForElevation(this, binding.bottomNav.elevation)
|
||||
} else {
|
||||
ThemeHelper.getThemeColor(this, android.R.attr.colorBackground)
|
||||
}
|
||||
@ -120,7 +120,7 @@ class MainActivity : BaseActivity() {
|
||||
val navHostFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.fragment) as NavHostFragment?
|
||||
// get the current fragment
|
||||
val fragment = navHostFragment?.childFragmentManager?.fragments?.get(0)
|
||||
val fragment = navHostFragment?.childFragmentManager?.fragments?.getOrNull(0)
|
||||
tryScrollToTop(fragment?.requireView() as? ViewGroup)
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import com.github.libretube.ui.base.BaseActivity
|
||||
import com.github.libretube.ui.extensions.setAspectRatio
|
||||
import com.github.libretube.ui.models.PlayerViewModel
|
||||
import com.github.libretube.util.PlayerHelper
|
||||
import com.github.libretube.util.PlayerHelper.loadPlaybackParams
|
||||
import com.github.libretube.util.WindowHelper
|
||||
import com.google.android.exoplayer2.C
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
@ -81,6 +82,7 @@ class OfflinePlayerActivity : BaseActivity() {
|
||||
}
|
||||
})
|
||||
}
|
||||
.loadPlaybackParams()
|
||||
|
||||
playerView = binding.player
|
||||
playerView.setShowSubtitleButton(true)
|
||||
|
@ -7,6 +7,7 @@ import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.databinding.QueueRowBinding
|
||||
import com.github.libretube.extensions.toID
|
||||
import com.github.libretube.ui.viewholders.PlayingQueueViewHolder
|
||||
import com.github.libretube.util.ImageHelper
|
||||
import com.github.libretube.util.PlayingQueue
|
||||
@ -46,10 +47,16 @@ class PlayingQueueAdapter : RecyclerView.Adapter<PlayingQueueViewHolder>() {
|
||||
)
|
||||
|
||||
root.setOnClickListener {
|
||||
val oldIndex = PlayingQueue.currentIndex()
|
||||
PlayingQueue.onQueueItemSelected(position)
|
||||
notifyItemChanged(oldIndex)
|
||||
notifyItemChanged(position)
|
||||
val oldPosition = PlayingQueue.currentIndex()
|
||||
// get the new position from the queue to work properly after reordering the queue
|
||||
val newPosition = PlayingQueue.getStreams().indexOfFirst {
|
||||
it.url?.toID() == streamItem.url?.toID()
|
||||
}.takeIf { it >= 0 } ?: return@setOnClickListener
|
||||
|
||||
// select the new item in the queue and update the selected item in the UI
|
||||
PlayingQueue.onQueueItemSelected(newPosition)
|
||||
notifyItemChanged(oldPosition)
|
||||
notifyItemChanged(newPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class PlaylistAdapter(
|
||||
videoInfo.text = streamItem.uploaderName
|
||||
channelImage.visibility = View.GONE
|
||||
|
||||
thumbnailDuration.setFormattedDuration(streamItem.duration!!)
|
||||
thumbnailDuration.setFormattedDuration(streamItem.duration!!, streamItem.isShort)
|
||||
ImageHelper.loadImage(streamItem.thumbnail, thumbnail)
|
||||
root.setOnClickListener {
|
||||
NavigationHelper.navigateVideo(root.context, streamItem.url, playlistId)
|
||||
|
@ -83,7 +83,7 @@ class SearchAdapter(
|
||||
private fun bindWatch(item: ContentItem, binding: VideoRowBinding) {
|
||||
binding.apply {
|
||||
ImageHelper.loadImage(item.thumbnail, thumbnail)
|
||||
thumbnailDuration.setFormattedDuration(item.duration)
|
||||
thumbnailDuration.setFormattedDuration(item.duration, item.isShort)
|
||||
ImageHelper.loadImage(item.uploaderAvatar, channelImage)
|
||||
videoTitle.text = item.title
|
||||
val viewsString = if (item.views != -1L) item.views.formatShort() else ""
|
||||
|
@ -133,7 +133,7 @@ class VideosAdapter(
|
||||
TextUtils.SEPARATOR + video.uploaded?.let {
|
||||
DateUtils.getRelativeTimeSpanString(it)
|
||||
}
|
||||
video.duration?.let { thumbnailDuration.setFormattedDuration(it) }
|
||||
video.duration?.let { thumbnailDuration.setFormattedDuration(it, video.isShort) }
|
||||
channelImage.setOnClickListener {
|
||||
NavigationHelper.navigateChannel(root.context, video.uploaderUrl)
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class WatchHistoryAdapter(
|
||||
videoTitle.text = video.title
|
||||
channelName.text = video.uploader
|
||||
videoInfo.text = video.uploadDate
|
||||
thumbnailDuration.setFormattedDuration(video.duration!!)
|
||||
thumbnailDuration.setFormattedDuration(video.duration!!, null)
|
||||
ImageHelper.loadImage(video.thumbnailUrl, thumbnail)
|
||||
ImageHelper.loadImage(video.uploaderAvatar, channelImage)
|
||||
|
||||
|
@ -4,13 +4,10 @@ import android.text.format.DateUtils
|
||||
import android.widget.TextView
|
||||
import com.github.libretube.R
|
||||
|
||||
fun TextView.setFormattedDuration(duration: Long) {
|
||||
val text = if (duration < 0L) {
|
||||
this.context.getString(R.string.live)
|
||||
} else if (duration in 0L..60L) {
|
||||
this.context.getString(R.string.yt_shorts)
|
||||
} else {
|
||||
DateUtils.formatElapsedTime(duration)
|
||||
fun TextView.setFormattedDuration(duration: Long, isShort: Boolean?) {
|
||||
this.text = when {
|
||||
isShort == true -> context.getString(R.string.yt_shorts)
|
||||
duration < 0L -> context.getString(R.string.live)
|
||||
else -> DateUtils.formatElapsedTime(duration)
|
||||
}
|
||||
this.text = text
|
||||
}
|
||||
|
@ -12,14 +12,20 @@ import android.text.format.DateUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.obj.StreamItem
|
||||
import com.github.libretube.databinding.FragmentAudioPlayerBinding
|
||||
import com.github.libretube.enums.ShareObjectType
|
||||
import com.github.libretube.extensions.toID
|
||||
import com.github.libretube.obj.ShareData
|
||||
import com.github.libretube.services.BackgroundMode
|
||||
import com.github.libretube.ui.activities.MainActivity
|
||||
import com.github.libretube.ui.base.BaseFragment
|
||||
import com.github.libretube.ui.dialogs.ShareDialog
|
||||
import com.github.libretube.ui.sheets.PlaybackOptionsSheet
|
||||
import com.github.libretube.ui.sheets.PlayingQueueSheet
|
||||
import com.github.libretube.util.BackgroundHelper
|
||||
import com.github.libretube.util.ImageHelper
|
||||
import com.github.libretube.util.NavigationHelper
|
||||
import com.github.libretube.util.PlayingQueue
|
||||
@ -32,7 +38,7 @@ class AudioPlayerFragment : BaseFragment() {
|
||||
private var handler = Handler(Looper.getMainLooper())
|
||||
private var isPaused: Boolean = false
|
||||
|
||||
private lateinit var playerService: BackgroundMode
|
||||
private var playerService: BackgroundMode? = null
|
||||
|
||||
/** Defines callbacks for service binding, passed to bindService() */
|
||||
private val connection = object : ServiceConnection {
|
||||
@ -86,16 +92,42 @@ class AudioPlayerFragment : BaseFragment() {
|
||||
PlayingQueue.onQueueItemSelected(currentIndex + 1)
|
||||
}
|
||||
|
||||
binding.thumbnail.setOnClickListener {
|
||||
binding.openQueue.setOnClickListener {
|
||||
PlayingQueueSheet().show(childFragmentManager)
|
||||
}
|
||||
|
||||
binding.playbackOptions.setOnClickListener {
|
||||
playerService?.player?.let {
|
||||
PlaybackOptionsSheet(it)
|
||||
.show(childFragmentManager)
|
||||
}
|
||||
}
|
||||
|
||||
binding.openVideo.setOnClickListener {
|
||||
NavigationHelper.navigateVideo(
|
||||
context = requireContext(),
|
||||
videoId = PlayingQueue.getCurrent()?.url?.toID(),
|
||||
keepQueue = true,
|
||||
forceVideo = true
|
||||
)
|
||||
BackgroundHelper.stopBackgroundPlay(requireContext())
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
|
||||
binding.share.setOnClickListener {
|
||||
val currentVideo = PlayingQueue.getCurrent() ?: return@setOnClickListener
|
||||
ShareDialog(
|
||||
id = currentVideo.url!!.toID(),
|
||||
shareObjectType = ShareObjectType.VIDEO,
|
||||
shareData = ShareData(currentVideo = currentVideo.title)
|
||||
).show(childFragmentManager, null)
|
||||
}
|
||||
|
||||
// Listen for track changes due to autoplay or the notification
|
||||
PlayingQueue.addOnTrackChangedListener(onTrackChangeListener)
|
||||
|
||||
binding.playPause.setOnClickListener {
|
||||
if (!this::playerService.isInitialized) return@setOnClickListener
|
||||
if (isPaused) playerService.play() else playerService.pause()
|
||||
if (isPaused) playerService?.play() else playerService?.pause()
|
||||
}
|
||||
|
||||
// load the stream info into the UI
|
||||
@ -121,10 +153,8 @@ class AudioPlayerFragment : BaseFragment() {
|
||||
}
|
||||
|
||||
private fun initializeSeekBar() {
|
||||
if (!this::playerService.isInitialized) return
|
||||
|
||||
binding.timeBar.addOnChangeListener { _, value, fromUser ->
|
||||
if (fromUser) playerService.seekToPosition(value.toLong() * 1000)
|
||||
if (fromUser) playerService?.seekToPosition(value.toLong() * 1000)
|
||||
}
|
||||
updateSeekBar()
|
||||
}
|
||||
@ -133,7 +163,7 @@ class AudioPlayerFragment : BaseFragment() {
|
||||
* Update the position, duration and text views belonging to the seek bar
|
||||
*/
|
||||
private fun updateSeekBar() {
|
||||
val duration = playerService.getDuration()?.toFloat() ?: return
|
||||
val duration = playerService?.getDuration()?.toFloat() ?: return
|
||||
|
||||
// when the video is not loaded yet, retry in 100 ms
|
||||
if (duration <= 0) {
|
||||
@ -142,7 +172,7 @@ class AudioPlayerFragment : BaseFragment() {
|
||||
}
|
||||
|
||||
// get the current position from the player service
|
||||
val currentPosition = playerService.getCurrentPosition()?.toFloat() ?: 0f
|
||||
val currentPosition = playerService?.getCurrentPosition()?.toFloat() ?: 0f
|
||||
|
||||
// set the text for the indicators
|
||||
binding.duration.text = DateUtils.formatElapsedTime((duration / 1000).toLong())
|
||||
@ -161,7 +191,7 @@ class AudioPlayerFragment : BaseFragment() {
|
||||
}
|
||||
|
||||
private fun handleServiceConnection() {
|
||||
playerService.onIsPlayingChanged = { isPlaying ->
|
||||
playerService?.onIsPlayingChanged = { isPlaying ->
|
||||
binding.playPause.setIconResource(
|
||||
if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play
|
||||
)
|
||||
@ -172,7 +202,7 @@ class AudioPlayerFragment : BaseFragment() {
|
||||
|
||||
override fun onDestroy() {
|
||||
// unregister all listeners and the connected [playerService]
|
||||
playerService.onIsPlayingChanged = null
|
||||
playerService?.onIsPlayingChanged = null
|
||||
activity?.unbindService(connection)
|
||||
PlayingQueue.removeOnTrackChangedListener(onTrackChangeListener)
|
||||
|
||||
|
@ -94,6 +94,7 @@ import com.github.libretube.util.LinkHandler
|
||||
import com.github.libretube.util.NavigationHelper
|
||||
import com.github.libretube.util.NowPlayingNotification
|
||||
import com.github.libretube.util.PlayerHelper
|
||||
import com.github.libretube.util.PlayerHelper.loadPlaybackParams
|
||||
import com.github.libretube.util.PlayingQueue
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.github.libretube.util.SeekbarPreviewListener
|
||||
@ -1343,6 +1344,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
||||
.setHandleAudioBecomingNoisy(true)
|
||||
.setAudioAttributes(PlayerHelper.getAudioAttributes(), true)
|
||||
.build()
|
||||
.loadPlaybackParams()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,7 +11,7 @@ import com.github.libretube.util.PreferenceHelper
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
import com.google.android.exoplayer2.PlaybackParameters
|
||||
|
||||
class PlaybackSpeedSheet(
|
||||
class PlaybackOptionsSheet(
|
||||
private val player: ExoPlayer
|
||||
) : ExpandedBottomSheet() {
|
||||
private lateinit var binding: PlaybackBottomSheetBinding
|
@ -16,7 +16,6 @@ import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
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
|
||||
@ -30,15 +29,13 @@ import com.github.libretube.ui.interfaces.PlayerGestureOptions
|
||||
import com.github.libretube.ui.interfaces.PlayerOptions
|
||||
import com.github.libretube.ui.models.PlayerViewModel
|
||||
import com.github.libretube.ui.sheets.BaseBottomSheet
|
||||
import com.github.libretube.ui.sheets.PlaybackSpeedSheet
|
||||
import com.github.libretube.ui.sheets.PlaybackOptionsSheet
|
||||
import com.github.libretube.util.AudioHelper
|
||||
import com.github.libretube.util.BrightnessHelper
|
||||
import com.github.libretube.util.PlayerGestureController
|
||||
import com.github.libretube.util.PlayerHelper
|
||||
import com.github.libretube.util.PlayingQueue
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
import com.google.android.exoplayer2.PlaybackParameters
|
||||
import com.google.android.exoplayer2.Player
|
||||
import com.google.android.exoplayer2.text.Cue
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector
|
||||
@ -92,9 +89,6 @@ internal class CustomExoPlayerView(
|
||||
if (isControllerFullyVisible) hideController() else showController()
|
||||
}
|
||||
|
||||
// saved to only load the playback speed once (for the first video)
|
||||
private var playbackPrefSet = false
|
||||
|
||||
private val hideControllerRunnable = Runnable {
|
||||
hideController()
|
||||
}
|
||||
@ -127,17 +121,6 @@ internal class CustomExoPlayerView(
|
||||
// don't let the player view hide its controls automatically
|
||||
controllerShowTimeoutMs = -1
|
||||
|
||||
if (!playbackPrefSet) {
|
||||
player?.playbackParameters = PlaybackParameters(
|
||||
PlayerHelper.playbackSpeed.toFloat(),
|
||||
1.0f
|
||||
)
|
||||
PreferenceHelper.getBoolean(PreferenceKeys.SKIP_SILENCE, false).let {
|
||||
(player as ExoPlayer).skipSilenceEnabled = it
|
||||
}
|
||||
playbackPrefSet = true
|
||||
}
|
||||
|
||||
// locking the player
|
||||
binding.lockPlayer.setOnClickListener {
|
||||
// change the locked/unlocked icon
|
||||
@ -218,9 +201,15 @@ internal class CustomExoPlayerView(
|
||||
}
|
||||
}
|
||||
|
||||
private fun cancelHideControllerTask() {
|
||||
runCatching {
|
||||
handler.removeCallbacks(hideControllerRunnable)
|
||||
}
|
||||
}
|
||||
|
||||
override fun hideController() {
|
||||
// remove the callback to hide the controller
|
||||
handler.removeCallbacks(hideControllerRunnable)
|
||||
cancelHideControllerTask()
|
||||
super.hideController()
|
||||
|
||||
// hide system bars if in fullscreen
|
||||
@ -228,14 +217,15 @@ internal class CustomExoPlayerView(
|
||||
if (it.isFullscreen.value == true) {
|
||||
windowHelper?.setFullscreen()
|
||||
}
|
||||
updateTopBarMargin()
|
||||
}
|
||||
}
|
||||
|
||||
override fun showController() {
|
||||
// remove the previous callback from the queue to prevent a flashing behavior
|
||||
handler.removeCallbacks(hideControllerRunnable)
|
||||
cancelHideControllerTask()
|
||||
// automatically hide the controller after 2 seconds
|
||||
handler.postDelayed(hideControllerRunnable, 2000)
|
||||
handler.postDelayed(hideControllerRunnable, AUTO_HIDE_CONTROLLER_DELAY)
|
||||
super.showController()
|
||||
}
|
||||
|
||||
@ -392,8 +382,8 @@ internal class CustomExoPlayerView(
|
||||
doubleTapOverlayBinding?.apply {
|
||||
animateSeeking(rewindBTN, rewindIV, rewindTV, true)
|
||||
|
||||
runnableHandler.removeCallbacks(hideRewindButtonRunnable)
|
||||
// start callback to hide the button
|
||||
runnableHandler.removeCallbacks(hideRewindButtonRunnable)
|
||||
runnableHandler.postDelayed(hideRewindButtonRunnable, 700)
|
||||
}
|
||||
}
|
||||
@ -530,7 +520,7 @@ internal class CustomExoPlayerView(
|
||||
|
||||
override fun onPlaybackSpeedClicked() {
|
||||
player?.let {
|
||||
PlaybackSpeedSheet(it as ExoPlayer).show(supportFragmentManager)
|
||||
PlaybackOptionsSheet(it as ExoPlayer).show(supportFragmentManager)
|
||||
}
|
||||
}
|
||||
|
||||
@ -591,15 +581,7 @@ internal class CustomExoPlayerView(
|
||||
it.layoutParams = params
|
||||
}
|
||||
|
||||
// add padding to the top bar to not overlap the status bar
|
||||
binding.topBar.let {
|
||||
setPadding(
|
||||
it.paddingLeft,
|
||||
(if (newConfig?.orientation == Configuration.ORIENTATION_LANDSCAPE) 25 else 5).toPixel().toInt(),
|
||||
it.paddingRight,
|
||||
it.paddingBottom
|
||||
)
|
||||
}
|
||||
updateTopBarMargin()
|
||||
|
||||
// don't add extra padding if there's no cutout
|
||||
if ((context as? MainActivity)?.windowHelper?.hasCutout() == false) return
|
||||
@ -632,6 +614,19 @@ internal class CustomExoPlayerView(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add extra margin to the top bar to not overlap the status bar
|
||||
*/
|
||||
private fun updateTopBarMargin() {
|
||||
val isFullscreen = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE ||
|
||||
playerViewModel?.isFullscreen?.value == true
|
||||
binding.topBar.let {
|
||||
it.layoutParams = (it.layoutParams as MarginLayoutParams).apply {
|
||||
topMargin = (if (isFullscreen) 25 else 5).toPixel().toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSingleTap() {
|
||||
toggleController()
|
||||
}
|
||||
@ -706,9 +701,22 @@ internal class CustomExoPlayerView(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for all child touch events
|
||||
*/
|
||||
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
|
||||
// when a control is clicked, restart the countdown to hide the controller
|
||||
if (isControllerFullyVisible) {
|
||||
cancelHideControllerTask()
|
||||
handler.postDelayed(hideControllerRunnable, AUTO_HIDE_CONTROLLER_DELAY)
|
||||
}
|
||||
return super.onInterceptTouchEvent(ev)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SUBTITLE_BOTTOM_PADDING_FRACTION = 0.158f
|
||||
private const val ANIMATION_DURATION = 100L
|
||||
private const val AUTO_HIDE_CONTROLLER_DELAY = 2000L
|
||||
private val LANDSCAPE_MARGIN_HORIZONTAL = (20).toPixel().toInt()
|
||||
}
|
||||
}
|
||||
|
@ -60,11 +60,12 @@ object NavigationHelper {
|
||||
playlistId: String? = null,
|
||||
channelId: String? = null,
|
||||
keepQueue: Boolean = false,
|
||||
timeStamp: Long? = null
|
||||
timeStamp: Long? = null,
|
||||
forceVideo: Boolean = false
|
||||
) {
|
||||
if (videoId == null) return
|
||||
|
||||
if (PreferenceHelper.getBoolean(PreferenceKeys.AUDIO_ONLY_MODE, false)) {
|
||||
if (PreferenceHelper.getBoolean(PreferenceKeys.AUDIO_ONLY_MODE, false) && !forceVideo) {
|
||||
BackgroundHelper.stopBackgroundPlay(context)
|
||||
BackgroundHelper.playOnBackground(
|
||||
context,
|
||||
|
@ -38,6 +38,7 @@ class NowPlayingNotification(
|
||||
) {
|
||||
private var videoId: String? = null
|
||||
private var streams: Streams? = null
|
||||
private var bitmap: Bitmap? = null
|
||||
|
||||
/**
|
||||
* The [MediaSessionCompat] for the [streams].
|
||||
@ -105,24 +106,7 @@ class NowPlayingNotification(
|
||||
): Bitmap? {
|
||||
if (DataSaverMode.isEnabled(context)) return null
|
||||
|
||||
var bitmap: Bitmap? = null
|
||||
|
||||
val request = ImageRequest.Builder(context)
|
||||
.data(streams?.thumbnailUrl)
|
||||
.target { result ->
|
||||
val bm = (result as BitmapDrawable).bitmap
|
||||
// returns the bitmap on Android 13+, for everything below scaled down to a square
|
||||
bitmap = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
ImageHelper.getSquareBitmap(bm)
|
||||
} else {
|
||||
bm
|
||||
}
|
||||
callback.onBitmap(bitmap!!)
|
||||
}
|
||||
.build()
|
||||
|
||||
// enqueue the thumbnail loading request
|
||||
ImageHelper.imageLoader.enqueue(request)
|
||||
if (bitmap == null) enqueueThumbnailRequest(callback)
|
||||
|
||||
return bitmap
|
||||
}
|
||||
@ -132,6 +116,25 @@ class NowPlayingNotification(
|
||||
}
|
||||
}
|
||||
|
||||
private fun enqueueThumbnailRequest(callback: PlayerNotificationManager.BitmapCallback) {
|
||||
val request = ImageRequest.Builder(context)
|
||||
.data(streams?.thumbnailUrl)
|
||||
.target { result ->
|
||||
val bm = (result as BitmapDrawable).bitmap
|
||||
// returns the bitmap on Android 13+, for everything below scaled down to a square
|
||||
bitmap = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
ImageHelper.getSquareBitmap(bm)
|
||||
} else {
|
||||
bm
|
||||
}
|
||||
callback.onBitmap(bitmap!!)
|
||||
}
|
||||
.build()
|
||||
|
||||
// enqueue the thumbnail loading request
|
||||
ImageHelper.imageLoader.enqueue(request)
|
||||
}
|
||||
|
||||
private val customActionReceiver = object : CustomActionReceiver {
|
||||
override fun createCustomActions(
|
||||
context: Context,
|
||||
@ -257,6 +260,8 @@ class NowPlayingNotification(
|
||||
) {
|
||||
this.videoId = videoId
|
||||
this.streams = streams
|
||||
// reset the thumbnail bitmap in order to become reloaded for the new video
|
||||
this.bitmap = null
|
||||
|
||||
if (playerNotification == null) {
|
||||
createMediaSession()
|
||||
|
@ -18,7 +18,9 @@ import com.github.libretube.enums.AudioQuality
|
||||
import com.github.libretube.enums.PlayerEvent
|
||||
import com.google.android.exoplayer2.C
|
||||
import com.google.android.exoplayer2.DefaultLoadControl
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
import com.google.android.exoplayer2.LoadControl
|
||||
import com.google.android.exoplayer2.PlaybackParameters
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||
import com.google.android.exoplayer2.ui.CaptionStyleCompat
|
||||
import com.google.android.exoplayer2.video.VideoSize
|
||||
@ -342,6 +344,12 @@ object PlayerHelper {
|
||||
false
|
||||
)
|
||||
|
||||
private val skipSilence: Boolean
|
||||
get() = PreferenceHelper.getBoolean(
|
||||
PreferenceKeys.SKIP_SILENCE,
|
||||
false
|
||||
)
|
||||
|
||||
fun getDefaultResolution(context: Context): String {
|
||||
return if (NetworkHelper.isNetworkMobile(context)) {
|
||||
PreferenceHelper.getString(
|
||||
@ -459,4 +467,16 @@ object PlayerHelper {
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Load playback parameters such as speed and skip silence
|
||||
*/
|
||||
fun ExoPlayer.loadPlaybackParams(): ExoPlayer {
|
||||
skipSilenceEnabled = skipSilence
|
||||
playbackParameters = PlaybackParameters(
|
||||
playbackSpeed.toFloat(),
|
||||
1.0f
|
||||
)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.github.libretube.util
|
||||
|
||||
import android.os.Build
|
||||
import android.view.WindowManager
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
@ -48,10 +49,6 @@ class WindowHelper(private val activity: BaseActivity) {
|
||||
}
|
||||
|
||||
fun hasCutout(): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
activity.window.decorView.rootWindowInsets.displayCutout != null
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return ViewCompat.getRootWindowInsets(activity.window.decorView)?.displayCutout != null
|
||||
}
|
||||
}
|
||||
|
10
app/src/main/res/drawable/ic_video.xml
Normal file
10
app/src/main/res/drawable/ic_video.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M21,3L3,3c-1.11,0 -2,0.89 -2,2v12c0,1.1 0.89,2 2,2h5v2h8v-2h5c1.1,0 1.99,-0.9 1.99,-2L23,5c0,-1.11 -0.9,-2 -2,-2zM21,17L3,17L3,5h18v12zM16,11l-7,4L9,7z" />
|
||||
</vector>
|
@ -47,8 +47,8 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textSize="18sp"
|
||||
android:layout_marginTop="10dp" />
|
||||
android:layout_marginTop="10dp"
|
||||
android:textSize="18sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -56,8 +56,8 @@
|
||||
android:id="@+id/time_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:labelBehavior="gone"
|
||||
android:layout_marginHorizontal="20dp" />
|
||||
android:layout_marginHorizontal="20dp"
|
||||
app:labelBehavior="gone" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -69,14 +69,14 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center"
|
||||
tools:text="00:00"/>
|
||||
tools:text="00:00" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center"
|
||||
tools:text="10:15"/>
|
||||
tools:text="10:15" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
@ -84,7 +84,8 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginVertical="50dp">
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginBottom="36dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/prev"
|
||||
@ -118,4 +119,44 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/Widget.Material3.CardView.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
app:cardCornerRadius="18dp"
|
||||
android:layout_marginBottom="30dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:padding="15dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/open_queue"
|
||||
style="@style/AudioPlayerButton"
|
||||
android:src="@drawable/ic_queue" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/playback_options"
|
||||
style="@style/AudioPlayerButton"
|
||||
android:layout_width="27dp"
|
||||
android:layout_height="27dp"
|
||||
android:src="@drawable/ic_speed" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/open_video"
|
||||
style="@style/AudioPlayerButton"
|
||||
android:src="@drawable/ic_video" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/share"
|
||||
style="@style/AudioPlayerButton"
|
||||
android:src="@drawable/ic_share" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
@ -238,4 +238,12 @@
|
||||
|
||||
</style>
|
||||
|
||||
<style name="AudioPlayerButton">
|
||||
<item name="android:layout_width">30dp</item>
|
||||
<item name="android:layout_height">30dp</item>
|
||||
<item name="android:background">?attr/selectableItemBackgroundBorderless</item>
|
||||
<item name="android:layout_marginStart">10dp</item>
|
||||
<item name="android:layout_marginEnd">10dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
@ -78,17 +78,4 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/background_mode">
|
||||
|
||||
<com.github.libretube.ui.views.SliderPreference
|
||||
android:icon="@drawable/ic_speed"
|
||||
app:defValue="1.0"
|
||||
app:key="background_playback_speed"
|
||||
app:stepSize="0.1"
|
||||
app:title="@string/playback_speed"
|
||||
app:valueFrom="0.2"
|
||||
app:valueTo="4.0" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
Loading…
x
Reference in New Issue
Block a user