From c64dacf3df9097dc2c4535674a31dac4f568d344 Mon Sep 17 00:00:00 2001 From: ibaker Date: Wed, 10 Jul 2024 07:49:17 -0700 Subject: [PATCH] Transform double-tap of `HEADSETHOOK` to skip-to-next As reported in Issue: androidx/media#1493 we already use `HEADSETHOOK` to start waiting for a double-tap, but we don't consider it for the second tap. Similarly, we previously considered `PLAY` for the second tap, but not the first. Only `HEADSETHOOK` and `PLAY_PAUSE` are [documented](https://developer.android.com/reference/androidx/media3/session/MediaSession#media-key-events-mapping) to transform double-tap to `seekToNext`. So this change removes the `PLAY` detection from the second tap. #cherrypick PiperOrigin-RevId: 651017522 --- RELEASENOTES.md | 4 ++++ .../androidx/media3/session/MediaSessionImpl.java | 5 +++-- .../media3/session/MediaSessionKeyEventTest.java | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d42f1c9205..7d60c59e54 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -64,6 +64,10 @@ playback can't be suppressed without the system crashing the service with a `ForegroundServiceDidNotStartInTimeException` ([#1528](https://github.com/google/ExoPlayer/issues/1528)). + * Transform a double-tap of `KEYCODE_HEADSETHOOK` into a 'seek to next' + action, as + [documented](https://developer.android.com/reference/androidx/media3/session/MediaSession#media-key-events-mapping) + ([#1493](https://github.com/androidx/media/issues/1493)). * UI: * Downloads: * OkHttp Extension: diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java index e67512598a..b14554b65f 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -1242,7 +1242,8 @@ private void handleAvailablePlayerCommandsChanged(Player.Commands availableComma } if (!isMediaNotificationControllerConnected()) { - if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE && doubleTapCompleted) { + if ((keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_HEADSETHOOK) + && doubleTapCompleted) { // Double tap completion for legacy when media notification controller is disabled. sessionLegacyStub.onSkipToNext(); return true; @@ -1262,7 +1263,7 @@ private boolean applyMediaButtonKeyEvent(KeyEvent keyEvent, boolean doubleTapCom ControllerInfo controllerInfo = checkNotNull(instance.getMediaNotificationControllerInfo()); Runnable command; int keyCode = keyEvent.getKeyCode(); - if ((keyCode == KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KEYCODE_MEDIA_PLAY) + if ((keyCode == KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_HEADSETHOOK) && doubleTapCompleted) { keyCode = KEYCODE_MEDIA_NEXT; } diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionKeyEventTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionKeyEventTest.java index 641b2c1213..9f20f6aeb7 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionKeyEventTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionKeyEventTest.java @@ -346,6 +346,20 @@ public void playPauseKeyEvent_doubleTapOnPlayPause_seekNext() throws Exception { player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_NEXT, TIMEOUT_MS); } + @Test + public void playPauseKeyEvent_doubleTapOnHeadsetHook_seekNext() throws Exception { + Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19. + handler.postAndSync( + () -> { + player.playWhenReady = true; + player.playbackState = Player.STATE_READY; + }); + + dispatchMediaKeyEvent(KeyEvent.KEYCODE_HEADSETHOOK, /* doubleTap= */ true); + + player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_NEXT, TIMEOUT_MS); + } + private MediaController connectMediaNotificationController() throws Exception { return threadTestRule .getHandler()