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 486a858d7..7a1e06197 100644 --- a/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt +++ b/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt @@ -93,7 +93,6 @@ object PreferenceKeys { /** * Background mode */ - const val BACKGROUND_PLAYBACK_SPEED = "background_playback_speed" const val AUDIO_ONLY_MODE = "audio_only_mode" /** diff --git a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt index 1f32551c3..d1413db69 100644 --- a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt +++ b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt @@ -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) diff --git a/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt index 17dd44643..2f9cd70b2 100644 --- a/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt @@ -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) } } 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 5072bb5d6..d56276ea0 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 @@ -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) diff --git a/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt index 20ff28c2c..d0875c073 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt @@ -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() { ) 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) } } } diff --git a/app/src/main/java/com/github/libretube/ui/adapters/PlaylistAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/PlaylistAdapter.kt index a39275984..5fba1f429 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/PlaylistAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/PlaylistAdapter.kt @@ -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) diff --git a/app/src/main/java/com/github/libretube/ui/adapters/SearchAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/SearchAdapter.kt index 67802600e..c99229616 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/SearchAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/SearchAdapter.kt @@ -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 "" diff --git a/app/src/main/java/com/github/libretube/ui/adapters/VideosAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/VideosAdapter.kt index 0e44dfd25..fccbcaa0a 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/VideosAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/VideosAdapter.kt @@ -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) } diff --git a/app/src/main/java/com/github/libretube/ui/adapters/WatchHistoryAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/WatchHistoryAdapter.kt index c329996a2..1a8930306 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/WatchHistoryAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/WatchHistoryAdapter.kt @@ -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) diff --git a/app/src/main/java/com/github/libretube/ui/extensions/SetFormattedDuration.kt b/app/src/main/java/com/github/libretube/ui/extensions/SetFormattedDuration.kt index a57e70b68..6817f4a5c 100644 --- a/app/src/main/java/com/github/libretube/ui/extensions/SetFormattedDuration.kt +++ b/app/src/main/java/com/github/libretube/ui/extensions/SetFormattedDuration.kt @@ -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 } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt index b2b599952..9190e8402 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt @@ -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) 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 a53352c30..fdd295f8a 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 @@ -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() } /** diff --git a/app/src/main/java/com/github/libretube/ui/sheets/PlaybackSpeedSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/PlaybackOptionsSheet.kt similarity index 98% rename from app/src/main/java/com/github/libretube/ui/sheets/PlaybackSpeedSheet.kt rename to app/src/main/java/com/github/libretube/ui/sheets/PlaybackOptionsSheet.kt index 4bf141c9e..caaa79898 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/PlaybackSpeedSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/PlaybackOptionsSheet.kt @@ -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 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 9f35f5c5e..c740b5d78 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 @@ -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() } } diff --git a/app/src/main/java/com/github/libretube/util/NavigationHelper.kt b/app/src/main/java/com/github/libretube/util/NavigationHelper.kt index ef8f30cd3..44e659d42 100644 --- a/app/src/main/java/com/github/libretube/util/NavigationHelper.kt +++ b/app/src/main/java/com/github/libretube/util/NavigationHelper.kt @@ -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, diff --git a/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt b/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt index 286c3d084..ae00b2bfc 100644 --- a/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt +++ b/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt @@ -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() diff --git a/app/src/main/java/com/github/libretube/util/PlayerHelper.kt b/app/src/main/java/com/github/libretube/util/PlayerHelper.kt index 916ea78b2..06a8395ac 100644 --- a/app/src/main/java/com/github/libretube/util/PlayerHelper.kt +++ b/app/src/main/java/com/github/libretube/util/PlayerHelper.kt @@ -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 + } } diff --git a/app/src/main/java/com/github/libretube/util/WindowHelper.kt b/app/src/main/java/com/github/libretube/util/WindowHelper.kt index cb8259c80..3a36279d2 100644 --- a/app/src/main/java/com/github/libretube/util/WindowHelper.kt +++ b/app/src/main/java/com/github/libretube/util/WindowHelper.kt @@ -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 } } diff --git a/app/src/main/res/drawable/ic_video.xml b/app/src/main/res/drawable/ic_video.xml new file mode 100644 index 000000000..d8424bad1 --- /dev/null +++ b/app/src/main/res/drawable/ic_video.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/fragment_audio_player.xml b/app/src/main/res/layout/fragment_audio_player.xml index 67ec3bd23..e88be5878 100644 --- a/app/src/main/res/layout/fragment_audio_player.xml +++ b/app/src/main/res/layout/fragment_audio_player.xml @@ -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" /> @@ -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" /> + tools:text="00:00" /> + tools:text="10:15" /> @@ -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"> + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/style.xml b/app/src/main/res/values/style.xml index c12c90ca0..488986625 100644 --- a/app/src/main/res/values/style.xml +++ b/app/src/main/res/values/style.xml @@ -238,4 +238,12 @@ + + \ No newline at end of file diff --git a/app/src/main/res/xml/audio_video_settings.xml b/app/src/main/res/xml/audio_video_settings.xml index 9ea6e0d77..217e00cb3 100644 --- a/app/src/main/res/xml/audio_video_settings.xml +++ b/app/src/main/res/xml/audio_video_settings.xml @@ -78,17 +78,4 @@ - - - - - - \ No newline at end of file