@@ -49,8 +49,6 @@ import io.reactivex.rxkotlin.Observables
49
49
import io.reactivex.rxkotlin.addTo
50
50
import io.reactivex.rxkotlin.subscribeBy
51
51
import io.reactivex.schedulers.Schedulers
52
- import java.util.Timer
53
- import java.util.TimerTask
54
52
import kotlin.coroutines.CoroutineContext
55
53
import kotlinx.coroutines.CoroutineScope
56
54
import kotlinx.coroutines.Dispatchers
@@ -524,43 +522,37 @@ class MediaSessionManager(
524
522
val enqueueCommand : (String , suspend () -> Unit ) -> Unit ,
525
523
) : MediaSessionCompat.Callback() {
526
524
527
- private var playPauseTimer: Timer ? = null
528
525
private var playFromSearchDisposable: Disposable ? = null
529
- private var buttonPressSuccessions : Int = 0
526
+ private val mediaEventQueue = MediaEventQueue (scope = this @MediaSessionManager)
530
527
531
528
override fun onMediaButtonEvent (mediaButtonEvent : Intent ): Boolean {
532
529
if (Intent .ACTION_MEDIA_BUTTON == mediaButtonEvent.action) {
533
- val keyEvent = IntentCompat .getParcelableExtra(mediaButtonEvent, Intent .EXTRA_KEY_EVENT , KeyEvent ::class .java)
534
- ? : return false
530
+ val keyEvent = IntentCompat .getParcelableExtra(mediaButtonEvent, Intent .EXTRA_KEY_EVENT , KeyEvent ::class .java) ? : return false
535
531
logEvent(keyEvent.toString())
536
532
if (keyEvent.action == KeyEvent .ACTION_DOWN ) {
537
- when (keyEvent.keyCode) {
538
- // When the phone is in sleep mode, and the audio player doesn't have focus, KEYCODE_MEDIA_PLAY is
539
- // called instead of KEYCODE_MEDIA_PLAY_PAUSE or KEYCODE_HEADSETHOOK
540
- KeyEvent .KEYCODE_MEDIA_PLAY -> {
541
- handleMediaButtonSingleTap()
542
- return true
543
- }
544
- // Some wired headphones, such as the Apple USB-C headphones do not invoke KeyEvent.KEYCODE_HEADSETHOOK.
545
- KeyEvent .KEYCODE_MEDIA_PLAY_PAUSE -> {
546
- handleMediaButtonSingleTap()
547
- return true
548
- }
549
- // This should be called on most wired headsets.
550
- KeyEvent .KEYCODE_HEADSETHOOK -> {
551
- handleMediaButtonSingleTap()
552
- return true
553
- }
554
- KeyEvent .KEYCODE_MEDIA_NEXT -> {
555
- // Not sent on some devices. Use KEYCODE_MEDIA_PLAY_PAUSE or KEYCODE_HEADSETHOOK timeout workarounds
556
- handleMediaButtonDoubleTap()
557
- return true
558
- }
559
- KeyEvent .KEYCODE_MEDIA_PREVIOUS -> {
560
- // Not sent on some devices. Use KEYCODE_MEDIA_PLAY_PAUSE or KEYCODE_HEADSETHOOK timeout workarounds
561
- handleMediaButtonTripleTap()
562
- return true
533
+ val inputEvent = when (keyEvent.keyCode) {
534
+ /* *
535
+ * KEYCODE_MEDIA_PLAY_PAUSE - called when the player audio has focus
536
+ * KEYCODE_MEDIA_PLAY - can be called when the player doesn't have focus such when sleep mode
537
+ * KEYCODE_HEADSETHOOK - called on most wired headsets
538
+ */
539
+ KeyEvent .KEYCODE_MEDIA_PLAY , KeyEvent .KEYCODE_MEDIA_PLAY_PAUSE , KeyEvent .KEYCODE_HEADSETHOOK -> MediaEvent .SingleTap
540
+ KeyEvent .KEYCODE_MEDIA_NEXT -> MediaEvent .DoubleTap
541
+ KeyEvent .KEYCODE_MEDIA_PREVIOUS -> MediaEvent .TripleTap
542
+ else -> null
543
+ }
544
+
545
+ if (inputEvent != null ) {
546
+ launch {
547
+ val outputEvent = mediaEventQueue.consumeEvent(inputEvent)
548
+ when (outputEvent) {
549
+ MediaEvent .SingleTap -> handleMediaButtonSingleTap()
550
+ MediaEvent .DoubleTap -> handleMediaButtonDoubleTap()
551
+ MediaEvent .TripleTap -> handleMediaButtonTripleTap()
552
+ null -> Unit
553
+ }
563
554
}
555
+ return true
564
556
}
565
557
}
566
558
} else {
@@ -582,49 +574,15 @@ class MediaSessionManager(
582
574
}
583
575
}
584
576
585
- private fun getCurrentControllerInfo (): String {
586
- val info = mediaSession.currentControllerInfo
587
- return " Controller: ${info.packageName} pid: ${info.pid} uid: ${info.uid} "
577
+ private fun logEvent (action : String ) {
578
+ val userInfo = runCatching {
579
+ val info = mediaSession.currentControllerInfo
580
+ " Controller: ${info.packageName} pid: ${info.pid} uid: ${info.uid} "
581
+ }.getOrNull()
582
+ LogBuffer .i(LogBuffer .TAG_PLAYBACK , " Event from Media Session to $action . ${userInfo.orEmpty()} " )
588
583
}
589
-
590
- // The parameter inSessionCallback can only be set to true if being called from the MediaSession.Callback thread. The method getCurrentControllerInfo() can only be called from this thread.
591
- private fun logEvent (action : String , inSessionCallback : Boolean = true) {
592
- LogBuffer .i(LogBuffer .TAG_PLAYBACK , " Event from Media Session to $action . ${if (inSessionCallback) getCurrentControllerInfo() else " " } " )
593
- }
594
-
595
584
private fun handleMediaButtonSingleTap () {
596
- // this code allows the user to double tap or triple tap their play pause button to skip ahead.
597
- // Basically it allows them 600ms to press it again (or a third time) to cause a skip forward/backward instead of a play/pause
598
- buttonPressSuccessions++
599
-
600
- if (buttonPressSuccessions == 1 ) {
601
- playPauseTimer = Timer ().apply {
602
- schedule(
603
- object : TimerTask () {
604
- override fun run () {
605
- logEvent(" play from headset hook" , inSessionCallback = false )
606
-
607
- when {
608
- buttonPressSuccessions == 2 && playbackManager.isPlaying() ->
609
- playbackManager.skipForward(sourceView = source)
610
-
611
- buttonPressSuccessions == 3 && playbackManager.isPlaying() ->
612
- playbackManager.skipBackward(sourceView = source)
613
-
614
- else ->
615
- playbackManager.playPause(sourceView = source)
616
- }
617
-
618
- // Invalidate timer and reset the number of button press quick successions
619
- playPauseTimer?.cancel()
620
- playPauseTimer = null
621
- buttonPressSuccessions = 0
622
- }
623
- },
624
- 600 ,
625
- )
626
- }
627
- }
585
+ playbackManager.playPause(sourceView = source)
628
586
}
629
587
630
588
private fun handleMediaButtonDoubleTap () {
@@ -722,7 +680,7 @@ class MediaSessionManager(
722
680
override fun onPlayFromMediaId (mediaId : String? , extras : Bundle ? ) {
723
681
mediaId ? : return
724
682
launch {
725
- logEvent(" play from media id" , inSessionCallback = false )
683
+ logEvent(" play from media id" )
726
684
727
685
val autoMediaId = AutoMediaId .fromMediaId(mediaId)
728
686
val episodeId = autoMediaId.episodeId
0 commit comments