mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-14 14:20:30 +05:30
Merge pull request #5923 from Bnyro/master
feat: pip controls for offline player
This commit is contained in:
commit
b9a761de68
@ -1,8 +1,7 @@
|
|||||||
package com.github.libretube.enums
|
package com.github.libretube.enums
|
||||||
|
|
||||||
enum class PlayerEvent {
|
enum class PlayerEvent {
|
||||||
Pause,
|
PlayPause,
|
||||||
Play,
|
|
||||||
Forward,
|
Forward,
|
||||||
Rewind,
|
Rewind,
|
||||||
Next,
|
Next,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.github.libretube.helpers
|
package com.github.libretube.helpers
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
@ -397,30 +396,25 @@ object PlayerHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIntentAction(context: Context): String {
|
fun getIntentActionName(context: Context): String {
|
||||||
return context.packageName + "." + ACTION_MEDIA_CONTROL
|
return context.packageName + "." + ACTION_MEDIA_CONTROL
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPendingIntent(activity: Activity, event: PlayerEvent): PendingIntent {
|
|
||||||
val intent = Intent(getIntentAction(activity))
|
|
||||||
.setPackage(activity.packageName)
|
|
||||||
.putExtra(CONTROL_TYPE, event)
|
|
||||||
return PendingIntentCompat.getBroadcast(activity, event.ordinal, intent, 0, false)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getRemoteAction(
|
private fun getRemoteAction(
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
id: Int,
|
id: Int,
|
||||||
@StringRes title: Int,
|
@StringRes title: Int,
|
||||||
event: PlayerEvent
|
event: PlayerEvent
|
||||||
): RemoteActionCompat {
|
): RemoteActionCompat {
|
||||||
|
val intent = Intent(getIntentActionName(activity))
|
||||||
|
.setPackage(activity.packageName)
|
||||||
|
.putExtra(CONTROL_TYPE, event)
|
||||||
|
val pendingIntent = PendingIntentCompat.getBroadcast(activity, event.ordinal, intent, 0, false)!!
|
||||||
|
|
||||||
val text = activity.getString(title)
|
val text = activity.getString(title)
|
||||||
return RemoteActionCompat(
|
val icon = IconCompat.createWithResource(activity, id)
|
||||||
IconCompat.createWithResource(activity, id),
|
|
||||||
text,
|
return RemoteActionCompat(icon, text, text, pendingIntent)
|
||||||
text,
|
|
||||||
getPendingIntent(activity, event)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -444,8 +438,8 @@ object PlayerHelper {
|
|||||||
val playPauseAction = getRemoteAction(
|
val playPauseAction = getRemoteAction(
|
||||||
activity,
|
activity,
|
||||||
if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play,
|
if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play,
|
||||||
R.string.pause,
|
if (isPlaying) R.string.resume else R.string.pause,
|
||||||
if (isPlaying) PlayerEvent.Pause else PlayerEvent.Play
|
PlayerEvent.PlayPause
|
||||||
)
|
)
|
||||||
|
|
||||||
val skipNextAction = getRemoteAction(
|
val skipNextAction = getRemoteAction(
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
package com.github.libretube.ui.activities
|
package com.github.libretube.ui.activities
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.media.session.PlaybackState
|
import android.content.res.Configuration
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isInvisible
|
import androidx.core.view.isInvisible
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
@ -30,7 +35,11 @@ import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
|||||||
import com.github.libretube.db.DatabaseHolder.Database
|
import com.github.libretube.db.DatabaseHolder.Database
|
||||||
import com.github.libretube.db.obj.DownloadChapter
|
import com.github.libretube.db.obj.DownloadChapter
|
||||||
import com.github.libretube.enums.FileType
|
import com.github.libretube.enums.FileType
|
||||||
|
import com.github.libretube.enums.PlayerEvent
|
||||||
|
import com.github.libretube.extensions.seekBy
|
||||||
|
import com.github.libretube.extensions.serializableExtra
|
||||||
import com.github.libretube.extensions.toAndroidUri
|
import com.github.libretube.extensions.toAndroidUri
|
||||||
|
import com.github.libretube.extensions.togglePlayPauseState
|
||||||
import com.github.libretube.extensions.updateParameters
|
import com.github.libretube.extensions.updateParameters
|
||||||
import com.github.libretube.helpers.PlayerHelper
|
import com.github.libretube.helpers.PlayerHelper
|
||||||
import com.github.libretube.helpers.WindowHelper
|
import com.github.libretube.helpers.WindowHelper
|
||||||
@ -74,6 +83,10 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||||
super.onIsPlayingChanged(isPlaying)
|
super.onIsPlayingChanged(isPlaying)
|
||||||
|
|
||||||
|
if (PlayerHelper.pipEnabled) {
|
||||||
|
PictureInPictureCompat.setPictureInPictureParams(this@OfflinePlayerActivity, pipParams)
|
||||||
|
}
|
||||||
|
|
||||||
// Start or pause watch position timer
|
// Start or pause watch position timer
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
watchPositionTimer.resume()
|
watchPositionTimer.resume()
|
||||||
@ -97,6 +110,32 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val playerActionReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
when (intent.serializableExtra<PlayerEvent>(PlayerHelper.CONTROL_TYPE) ?: return) {
|
||||||
|
PlayerEvent.PlayPause -> {
|
||||||
|
player.togglePlayPauseState()
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerEvent.Forward -> {
|
||||||
|
player.seekBy(PlayerHelper.seekIncrement)
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerEvent.Rewind -> {
|
||||||
|
player.seekBy(-PlayerHelper.seekIncrement)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val pipParams get() = PictureInPictureParamsCompat.Builder()
|
||||||
|
.setActions(PlayerHelper.getPiPModeActions(this, player.isPlaying))
|
||||||
|
.setAutoEnterEnabled(PlayerHelper.pipEnabled && player.isPlaying)
|
||||||
|
.setAspectRatio(player.videoSize)
|
||||||
|
.build()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
WindowHelper.toggleFullscreen(window, true)
|
WindowHelper.toggleFullscreen(window, true)
|
||||||
|
|
||||||
@ -116,6 +155,17 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
player.videoSize.width,
|
player.videoSize.width,
|
||||||
player.videoSize.height
|
player.videoSize.height
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ContextCompat.registerReceiver(
|
||||||
|
this,
|
||||||
|
playerActionReceiver,
|
||||||
|
IntentFilter(PlayerHelper.getIntentActionName(this)),
|
||||||
|
ContextCompat.RECEIVER_NOT_EXPORTED
|
||||||
|
)
|
||||||
|
|
||||||
|
if (PlayerHelper.pipEnabled) {
|
||||||
|
PictureInPictureCompat.setPictureInPictureParams(this, pipParams)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializePlayer() {
|
private fun initializePlayer() {
|
||||||
@ -248,6 +298,10 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
playerViewModel.isFullscreen.value = false
|
playerViewModel.isFullscreen.value = false
|
||||||
super.onPause()
|
super.onPause()
|
||||||
|
|
||||||
|
if (PlayerHelper.pauseOnQuit) {
|
||||||
|
player.pause()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
@ -257,22 +311,30 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
player.release()
|
player.release()
|
||||||
watchPositionTimer.destroy()
|
watchPositionTimer.destroy()
|
||||||
|
|
||||||
|
unregisterReceiver(playerActionReceiver)
|
||||||
|
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUserLeaveHint() {
|
override fun onUserLeaveHint() {
|
||||||
if (PlayerHelper.pipEnabled && player.playbackState != PlaybackState.STATE_PAUSED) {
|
if (PlayerHelper.pipEnabled && player.isPlaying) {
|
||||||
PictureInPictureCompat.enterPictureInPictureMode(
|
PictureInPictureCompat.enterPictureInPictureMode(this, pipParams)
|
||||||
this,
|
|
||||||
PictureInPictureParamsCompat.Builder()
|
|
||||||
.setAspectRatio(player.videoSize)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onUserLeaveHint()
|
super.onUserLeaveHint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, configuration: Configuration) {
|
||||||
|
super.onPictureInPictureModeChanged(isInPictureInPictureMode)
|
||||||
|
|
||||||
|
if (isInPictureInPictureMode) {
|
||||||
|
playerView.hideController()
|
||||||
|
playerView.useController = false
|
||||||
|
} else {
|
||||||
|
playerView.useController = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
||||||
if (binding.player.onKeyBoardAction(keyCode)) {
|
if (binding.player.onKeyBoardAction(keyCode)) {
|
||||||
return true
|
return true
|
||||||
|
@ -202,15 +202,11 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
|||||||
/**
|
/**
|
||||||
* Receiver for all actions in the PiP mode
|
* Receiver for all actions in the PiP mode
|
||||||
*/
|
*/
|
||||||
private val broadcastReceiver = object : BroadcastReceiver() {
|
private val playerActionReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
when (intent.serializableExtra<PlayerEvent>(PlayerHelper.CONTROL_TYPE) ?: return) {
|
when (intent.serializableExtra<PlayerEvent>(PlayerHelper.CONTROL_TYPE) ?: return) {
|
||||||
PlayerEvent.Play -> {
|
PlayerEvent.PlayPause -> {
|
||||||
exoPlayer.play()
|
exoPlayer.togglePlayPauseState()
|
||||||
}
|
|
||||||
|
|
||||||
PlayerEvent.Pause -> {
|
|
||||||
exoPlayer.pause()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerEvent.Forward -> {
|
PlayerEvent.Forward -> {
|
||||||
@ -372,8 +368,8 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
|||||||
// broadcast receiver for PiP actions
|
// broadcast receiver for PiP actions
|
||||||
ContextCompat.registerReceiver(
|
ContextCompat.registerReceiver(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
broadcastReceiver,
|
playerActionReceiver,
|
||||||
IntentFilter(PlayerHelper.getIntentAction(requireContext())),
|
IntentFilter(PlayerHelper.getIntentActionName(requireContext())),
|
||||||
ContextCompat.RECEIVER_NOT_EXPORTED
|
ContextCompat.RECEIVER_NOT_EXPORTED
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -835,7 +831,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
|||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
// unregister the receiver for player actions
|
// unregister the receiver for player actions
|
||||||
context?.unregisterReceiver(broadcastReceiver)
|
context?.unregisterReceiver(playerActionReceiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
_binding = null
|
_binding = null
|
||||||
|
Loading…
Reference in New Issue
Block a user