Skip to content

Commit

Permalink
feat(player): handle media buttons from earphones, bluetooth devices …
Browse files Browse the repository at this point in the history
…and possibly android tv remotes
  • Loading branch information
Samfun75 committed Aug 4, 2023
1 parent 04aad93 commit ab2b6da
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 2 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ dependencies {
implementation(androidx.recyclerview)
implementation(androidx.viewpager)
implementation(androidx.profileinstaller)
implementation(androidx.mediasession)

implementation(androidx.bundles.lifecycle)

Expand Down
107 changes: 105 additions & 2 deletions app/src/main/java/eu/kanade/tachiyomi/ui/player/PlayerActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.ParcelFileDescriptor
import android.support.v4.media.session.MediaControllerCompat
import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
import android.util.DisplayMetrics
import android.view.KeyEvent
import android.view.MotionEvent
Expand Down Expand Up @@ -146,6 +149,10 @@ class PlayerActivity : BaseActivity() {

lateinit var binding: PlayerActivityBinding

private lateinit var mediaSession: MediaSessionCompat

private val playbackStateBuilder = PlaybackStateCompat.Builder()

internal val player get() = binding.player

internal val playerControls get() = binding.playerControls
Expand Down Expand Up @@ -276,6 +283,7 @@ class PlayerActivity : BaseActivity() {
super.onCreate(savedInstanceState)

setupPlayerControls()
setupMediaSession()
setupPlayerMPV()
setupPlayerAudio()
setupPlayerBrightness()
Expand Down Expand Up @@ -433,6 +441,89 @@ class PlayerActivity : BaseActivity() {
verticalScrollLeft(0F)
}

@Suppress("DEPRECATION")
private fun setupMediaSession() {
mediaSession = MediaSessionCompat(this, "Aniyomi_Player_Session").apply {
// Enable callbacks from MediaButtons and TransportControls
setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS,
)

// Do not let MediaButtons restart the player when the app is not visible
setMediaButtonReceiver(null)

setPlaybackState(
with(playbackStateBuilder) {
setState(
PlaybackStateCompat.STATE_NONE,
0L,
0.0F,
)
build()
},
)

// Implement methods that handle callbacks from a media controller
setCallback(
object : MediaSessionCompat.Callback() {
override fun onPlay() {
player.paused = false
playerControls.toggleControls(isTapped = true)
updatePlaybackState()
}

override fun onPause() {
player.paused = true
playerControls.toggleControls()
updatePlaybackState(pause = true)
}

override fun onSkipToPrevious() {
changeEpisode(viewModel.getAdjacentEpisodeId(previous = true))
}

override fun onSkipToNext() {
changeEpisode(viewModel.getAdjacentEpisodeId(previous = false))
}
},
)
}

MediaControllerCompat(this, mediaSession).also { mediaController ->
MediaControllerCompat.setMediaController(this, mediaController)
}
}

private fun updatePlaybackState(cachePause: Boolean = false, pause: Boolean = false) {
val state = when {
player.timePos?.let { it < 0 } ?: true ||
player.duration?.let { it <= 0 } ?: true -> PlaybackStateCompat.STATE_CONNECTING
cachePause -> PlaybackStateCompat.STATE_BUFFERING
pause or (player.paused == true) -> PlaybackStateCompat.STATE_PAUSED
else -> PlaybackStateCompat.STATE_PLAYING
}
var actions = PlaybackStateCompat.ACTION_PLAY or
PlaybackStateCompat.ACTION_PLAY_PAUSE or
PlaybackStateCompat.ACTION_PAUSE
if (viewModel.currentPlaylist.size > 1) {
actions = actions or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or
PlaybackStateCompat.ACTION_SKIP_TO_NEXT
}

mediaSession.setPlaybackState(
with(playbackStateBuilder) {
setState(
state,
player.timePos?.toLong() ?: 0L,
player.playbackSpeed?.toFloat() ?: 1.0f,
)
setActions(actions)
build()
},
)
}

@Suppress("DEPRECATION")
private fun loadDeviceDimensions() {
this@PlayerActivity.requestedOrientation = playerPreferences.defaultPlayerOrientationType().get()
Expand Down Expand Up @@ -497,6 +588,9 @@ class PlayerActivity : BaseActivity() {
}

override fun onDestroy() {
mediaSession.isActive = false
mediaSession.release()

playerPreferences.playerVolumeValue().set(fineVolume)
playerPreferences.playerBrightnessValue().set(brightness)
MPVLib.removeLogObserver(playerObserver)
Expand Down Expand Up @@ -1560,19 +1654,28 @@ class PlayerActivity : BaseActivity() {
"time-pos" -> {
playerControls.updatePlaybackPos(value.toInt())
viewModel.viewModelScope.launchUI { aniSkipStuff(value) }
updatePlaybackState()
}
"duration" -> {
playerControls.updatePlaybackDuration(value.toInt())
mediaSession.isActive = true
updatePlaybackState()
}
"duration" -> playerControls.updatePlaybackDuration(value.toInt())
}
}

internal fun eventPropertyUi(property: String, value: Boolean) {
when (property) {
"seeking" -> isSeeking(value)
"paused-for-cache" -> showLoadingIndicator(value)
"paused-for-cache" -> {
showLoadingIndicator(value)
updatePlaybackState(cachePause = true)
}
"pause" -> {
if (!isFinishing) {
setAudioFocus(value)
updatePlaybackStatus(value)
updatePlaybackState(pause = true)
}
}
"eof-reached" -> endFile(value)
Expand Down
1 change: 1 addition & 0 deletions gradle/androidx.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ recyclerview = "androidx.recyclerview:recyclerview:1.3.0"
viewpager = "androidx.viewpager:viewpager:1.1.0-alpha01"
glance = "androidx.glance:glance-appwidget:1.0.0-alpha03"
profileinstaller = "androidx.profileinstaller:profileinstaller:1.3.0"
mediasession = "androidx.media:media:1.6.0"

lifecycle-common = { module = "androidx.lifecycle:lifecycle-common", version.ref = "lifecycle_version" }
lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle_version" }
Expand Down

0 comments on commit ab2b6da

Please sign in to comment.