diff options
| -rw-r--r-- | media/java/android/media/session/MediaSession.java | 78 |
1 files changed, 59 insertions, 19 deletions
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index dfd2bb35d6ea..44bd252a349e 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -43,6 +43,7 @@ import android.service.media.MediaBrowserService; import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; +import android.view.ViewConfiguration; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -200,8 +201,7 @@ public final class MediaSession { return; } if (mCallback != null) { - // We're updating the callback, clear the session from the old - // one. + // We're updating the callback, clear the session from the old one. mCallback.mCallback.mSession = null; } if (handler == null) { @@ -735,6 +735,8 @@ public final class MediaSession { */ public abstract static class Callback { private MediaSession mSession; + private CallbackMessageHandler mHandler; + private boolean mMediaPlayPauseKeyPending; public Callback() { } @@ -766,13 +768,41 @@ public final class MediaSession { * @return True if the event was handled, false otherwise. */ public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) { - if (mSession != null + if (mSession != null && mHandler != null && Intent.ACTION_MEDIA_BUTTON.equals(mediaButtonIntent.getAction())) { KeyEvent ke = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); if (ke != null && ke.getAction() == KeyEvent.ACTION_DOWN) { PlaybackState state = mSession.mPlaybackState; long validActions = state == null ? 0 : state.getActions(); switch (ke.getKeyCode()) { + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_HEADSETHOOK: + if (ke.getRepeatCount() > 0) { + // Consider long-press as a single tap. + handleMediaPlayPauseKeySingleTapIfPending(); + } else if (mMediaPlayPauseKeyPending) { + // Consider double tap as the next. + mHandler.removeMessages(CallbackMessageHandler + .MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT); + mMediaPlayPauseKeyPending = false; + if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) { + onSkipToNext(); + } + } else { + mMediaPlayPauseKeyPending = true; + mHandler.sendEmptyMessageDelayed(CallbackMessageHandler + .MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT, + ViewConfiguration.getDoubleTapTimeout()); + } + return true; + default: + // If another key is pressed within double tap timeout, consider the + // pending play/pause as a single tap to handle media keys in order. + handleMediaPlayPauseKeySingleTapIfPending(); + break; + } + + switch (ke.getKeyCode()) { case KeyEvent.KEYCODE_MEDIA_PLAY: if ((validActions & PlaybackState.ACTION_PLAY) != 0) { onPlay(); @@ -815,28 +845,33 @@ public final class MediaSession { return true; } break; - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - case KeyEvent.KEYCODE_HEADSETHOOK: - boolean isPlaying = state == null ? false - : state.getState() == PlaybackState.STATE_PLAYING; - boolean canPlay = (validActions & (PlaybackState.ACTION_PLAY_PAUSE - | PlaybackState.ACTION_PLAY)) != 0; - boolean canPause = (validActions & (PlaybackState.ACTION_PLAY_PAUSE - | PlaybackState.ACTION_PAUSE)) != 0; - if (isPlaying && canPause) { - onPause(); - return true; - } else if (!isPlaying && canPlay) { - onPlay(); - return true; - } - break; } } } return false; } + private void handleMediaPlayPauseKeySingleTapIfPending() { + if (!mMediaPlayPauseKeyPending) { + return; + } + mMediaPlayPauseKeyPending = false; + mHandler.removeMessages(CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT); + PlaybackState state = mSession.mPlaybackState; + long validActions = state == null ? 0 : state.getActions(); + boolean isPlaying = state != null + && state.getState() == PlaybackState.STATE_PLAYING; + boolean canPlay = (validActions & (PlaybackState.ACTION_PLAY_PAUSE + | PlaybackState.ACTION_PLAY)) != 0; + boolean canPause = (validActions & (PlaybackState.ACTION_PLAY_PAUSE + | PlaybackState.ACTION_PAUSE)) != 0; + if (isPlaying && canPause) { + onPause(); + } else if (!isPlaying && canPlay) { + onPlay(); + } + } + /** * Override to handle requests to prepare playback. During the preparation, a session should * not hold audio focus in order to allow other sessions play seamlessly. The state of @@ -1294,12 +1329,14 @@ public final class MediaSession { private static final int MSG_CUSTOM_ACTION = 20; private static final int MSG_ADJUST_VOLUME = 21; private static final int MSG_SET_VOLUME = 22; + private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23; private MediaSession.Callback mCallback; public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) { super(looper, null, true); mCallback = callback; + mCallback.mHandler = this; } public void post(int what, Object obj, Bundle bundle) { @@ -1401,6 +1438,9 @@ public final class MediaSession { vp.onSetVolumeTo((int) msg.obj); } break; + case MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT: + mCallback.handleMediaPlayPauseKeySingleTapIfPending(); + break; } } } |