PiP: Forward and Rewind actions

This commit is contained in:
Bnyro 2023-01-09 17:08:05 +01:00
parent 99182b34af
commit 07dc1a80b8
4 changed files with 147 additions and 10 deletions

View File

@ -0,0 +1,14 @@
package com.github.libretube.enums
enum class PlayerEvent(val value: Int) {
Pause(0),
Play(1),
Forward(2),
Rewind(3),
Next(5),
Prev(6);
companion object {
fun fromInt(value: Int) = PlayerEvent.values().first { it.value == value }
}
}

View File

@ -2,8 +2,10 @@ package com.github.libretube.ui.fragments
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.PictureInPictureParams import android.app.PictureInPictureParams
import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.res.Configuration import android.content.res.Configuration
import android.media.session.PlaybackState import android.media.session.PlaybackState
@ -50,6 +52,7 @@ import com.github.libretube.databinding.PlayerGestureControlsViewBinding
import com.github.libretube.db.DatabaseHelper import com.github.libretube.db.DatabaseHelper
import com.github.libretube.db.DatabaseHolder.Companion.Database import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.db.obj.WatchPosition import com.github.libretube.db.obj.WatchPosition
import com.github.libretube.enums.PlayerEvent
import com.github.libretube.enums.ShareObjectType import com.github.libretube.enums.ShareObjectType
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.awaitQuery
@ -107,15 +110,15 @@ import com.google.android.exoplayer2.ui.StyledPlayerView
import com.google.android.exoplayer2.upstream.DefaultDataSource import com.google.android.exoplayer2.upstream.DefaultDataSource
import com.google.android.exoplayer2.util.MimeTypes import com.google.android.exoplayer2.util.MimeTypes
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.IOException
import java.util.*
import java.util.concurrent.Executors
import kotlin.math.abs
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.chromium.net.CronetEngine import org.chromium.net.CronetEngine
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException
import java.util.*
import java.util.concurrent.Executors
import kotlin.math.abs
class PlayerFragment : BaseFragment(), OnlinePlayerOptions { class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
@ -178,6 +181,28 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
val handler = Handler(Looper.getMainLooper()) val handler = Handler(Looper.getMainLooper())
private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val action = intent?.getIntExtra(PlayerHelper.CONTROL_TYPE, 0) ?: return
when (PlayerEvent.fromInt(action)) {
PlayerEvent.Play -> {
exoPlayer.play()
}
PlayerEvent.Pause -> {
exoPlayer.pause()
}
PlayerEvent.Forward -> {
exoPlayer.seekTo(exoPlayer.currentPosition + PlayerHelper.seekIncrement)
}
PlayerEvent.Rewind -> {
exoPlayer.seekTo(exoPlayer.currentPosition - PlayerHelper.seekIncrement)
}
else -> {
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
arguments?.let { arguments?.let {
@ -186,6 +211,12 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
channelId = it.getString(IntentData.channelId) channelId = it.getString(IntentData.channelId)
keepQueue = it.getBoolean(IntentData.keepQueue, false) keepQueue = it.getBoolean(IntentData.keepQueue, false)
} }
// broadcast receiver for PiP actions
context?.registerReceiver(
broadcastReceiver,
IntentFilter(PlayerHelper.getIntentActon(requireContext()))
)
} }
override fun onCreateView( override fun onCreateView(
@ -529,6 +560,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
// disable the auto PiP mode for SDK >= 32 // disable the auto PiP mode for SDK >= 32
disableAutoPiP() disableAutoPiP()
// unregister the receiver for player actions
context?.unregisterReceiver(broadcastReceiver)
saveWatchPosition() saveWatchPosition()
// clear the playing queue and release the player // clear the playing queue and release the player
@ -794,7 +828,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
private fun localizedDate(date: String?): String? { private fun localizedDate(date: String?): String? {
return if (SDK_INT >= Build.VERSION_CODES.N) { return if (SDK_INT >= Build.VERSION_CODES.N) {
TextUtils.localizeDate(date, resources.configuration.locales[0]) TextUtils.localizeDate(date, resources.configuration.locales[0])
} else TextUtils.localizeDate(date) } else {
TextUtils.localizeDate(date)
}
} }
private fun handleLiveVideo() { private fun handleLiveVideo() {
@ -855,10 +891,11 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
// Listener for play and pause icon change // Listener for play and pause icon change
exoPlayer.addListener(object : Player.Listener { exoPlayer.addListener(object : Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) { override fun onIsPlayingChanged(isPlaying: Boolean) {
if (usePiP()) activity?.setPictureInPictureParams(getPipParams())
if (isPlaying) { if (isPlaying) {
// Stop [BackgroundMode] service if it is running. // Stop [BackgroundMode] service if it is running.
BackgroundHelper.stopBackgroundPlay(requireContext()) BackgroundHelper.stopBackgroundPlay(requireContext())
if (usePiP()) activity?.setPictureInPictureParams(getPipParams())
} else { } else {
disableAutoPiP() disableAutoPiP()
} }
@ -1460,7 +1497,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
fun getPipParams(): PictureInPictureParams = PictureInPictureParams.Builder() fun getPipParams(): PictureInPictureParams = PictureInPictureParams.Builder()
.setActions(emptyList()) .setActions(PlayerHelper.getPIPModeActions(requireActivity(), exoPlayer.isPlaying))
.apply { .apply {
if (SDK_INT >= Build.VERSION_CODES.S) { if (SDK_INT >= Build.VERSION_CODES.S) {
setAutoEnterEnabled(true) setAutoEnterEnabled(true)

View File

@ -1,16 +1,29 @@
package com.github.libretube.util package com.github.libretube.util
import android.app.Activity
import android.app.PendingIntent
import android.app.RemoteAction
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.graphics.drawable.Icon
import android.os.Build
import android.view.accessibility.CaptioningManager import android.view.accessibility.CaptioningManager
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import com.github.libretube.R
import com.github.libretube.api.obj.PipedStream import com.github.libretube.api.obj.PipedStream
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.enums.AudioQuality import com.github.libretube.enums.AudioQuality
import com.github.libretube.enums.PlayerEvent
import com.google.android.exoplayer2.ui.CaptionStyleCompat import com.google.android.exoplayer2.ui.CaptionStyleCompat
import com.google.android.exoplayer2.video.VideoSize import com.google.android.exoplayer2.video.VideoSize
import kotlin.math.roundToInt import kotlin.math.roundToInt
object PlayerHelper { object PlayerHelper {
private const val ACTION_MEDIA_CONTROL = "media_control"
const val CONTROL_TYPE = "control_type"
/** /**
* Get the audio source following the users preferences * Get the audio source following the users preferences
*/ */
@ -43,7 +56,7 @@ object PlayerHelper {
/** /**
* Get the best or worst bitrate from a list of audio streams * Get the best or worst bitrate from a list of audio streams
* @param audios list of the audio streams * @param audios list of the audio streams
* @param getLeast whether the least bitrate should be returned * @param quality Whether to use the best or worst quality available
* @return Url of the audio source * @return Url of the audio source
*/ */
private fun getBitRate(audios: List<PipedStream>, quality: AudioQuality): String { private fun getBitRate(audios: List<PipedStream>, quality: AudioQuality): String {
@ -332,4 +345,74 @@ object PlayerHelper {
) )
} }
} }
fun getIntentActon(context: Context): String {
return context.packageName + "." + ACTION_MEDIA_CONTROL
}
private fun getPendingIntent(activity: Activity, code: Int): PendingIntent {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.getBroadcast(
activity,
code,
Intent(getIntentActon(activity)).putExtra(CONTROL_TYPE, code),
PendingIntent.FLAG_IMMUTABLE
)
} else {
PendingIntent.getBroadcast(
activity,
code,
Intent(getIntentActon(activity)).putExtra(CONTROL_TYPE, code),
0
)
}
}
@RequiresApi(Build.VERSION_CODES.O)
private fun getRemoteAction(
activity: Activity,
id: Int,
@StringRes title: Int,
event: PlayerEvent
): RemoteAction {
val text = activity.getString(title)
return RemoteAction(
Icon.createWithResource(activity, id),
text,
text,
getPendingIntent(activity, event.value)
)
}
@RequiresApi(Build.VERSION_CODES.O)
fun getPIPModeActions(activity: Activity, isPlaying: Boolean): ArrayList<RemoteAction> {
val actions: ArrayList<RemoteAction> = ArrayList()
actions.add(
getRemoteAction(
activity,
R.drawable.ic_rewind,
R.string.rewind,
PlayerEvent.Rewind
)
)
actions.add(
getRemoteAction(
activity,
if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play,
R.string.pause,
if (isPlaying) PlayerEvent.Pause else PlayerEvent.Play
)
)
actions.add(
getRemoteAction(
activity,
R.drawable.ic_forward,
R.string.forward,
PlayerEvent.Forward
)
)
return actions
}
} }

View File

@ -425,6 +425,9 @@
<string name="shuffle">Shuffle</string> <string name="shuffle">Shuffle</string>
<string name="add_to_bookmarks">Add to bookmarks</string> <string name="add_to_bookmarks">Add to bookmarks</string>
<string name="remove_bookmark">Remove bookmark</string> <string name="remove_bookmark">Remove bookmark</string>
<string name="rewind">Rewind</string>
<string name="forward">Forward</string>
<string name="pause">Pause</string>
<!-- Notification channel strings --> <!-- Notification channel strings -->
<string name="download_channel_name">Download Service</string> <string name="download_channel_name">Download Service</string>