| /* | |
| * Copyright (C) 2011 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| package android.media; | |
| import android.app.PendingIntent; | |
| import android.compat.annotation.UnsupportedAppUsage; | |
| import android.content.ComponentName; | |
| import android.content.Intent; | |
| import android.graphics.Bitmap; | |
| import android.media.session.MediaSession; | |
| import android.media.session.MediaSessionLegacyHelper; | |
| import android.media.session.PlaybackState; | |
| import android.os.Build; | |
| import android.os.Bundle; | |
| import android.os.Looper; | |
| import android.os.SystemClock; | |
| import android.util.Log; | |
| /** | |
| * RemoteControlClient enables exposing information meant to be consumed by remote controls | |
| * capable of displaying metadata, artwork and media transport control buttons. | |
| * | |
| * <p>A remote control client object is associated with a media button event receiver. This | |
| * event receiver must have been previously registered with | |
| * {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} before the | |
| * RemoteControlClient can be registered through | |
| * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}. | |
| * | |
| * <p>Here is an example of creating a RemoteControlClient instance after registering a media | |
| * button event receiver: | |
| * <pre>ComponentName myEventReceiver = new ComponentName(getPackageName(), MyRemoteControlEventReceiver.class.getName()); | |
| * AudioManager myAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); | |
| * myAudioManager.registerMediaButtonEventReceiver(myEventReceiver); | |
| * // build the PendingIntent for the remote control client | |
| * Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); | |
| * mediaButtonIntent.setComponent(myEventReceiver); | |
| * PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED); | |
| * // create and register the remote control client | |
| * RemoteControlClient myRemoteControlClient = new RemoteControlClient(mediaPendingIntent); | |
| * myAudioManager.registerRemoteControlClient(myRemoteControlClient);</pre> | |
| * | |
| * @deprecated Use {@link MediaSession} instead. | |
| */ | |
| @Deprecated public class RemoteControlClient | |
| { | |
| private final static String TAG = "RemoteControlClient"; | |
| private final static boolean DEBUG = false; | |
| /** | |
| * Playback state of a RemoteControlClient which is stopped. | |
| * | |
| * @see #setPlaybackState(int) | |
| */ | |
| public final static int PLAYSTATE_STOPPED = 1; | |
| /** | |
| * Playback state of a RemoteControlClient which is paused. | |
| * | |
| * @see #setPlaybackState(int) | |
| */ | |
| public final static int PLAYSTATE_PAUSED = 2; | |
| /** | |
| * Playback state of a RemoteControlClient which is playing media. | |
| * | |
| * @see #setPlaybackState(int) | |
| */ | |
| public final static int PLAYSTATE_PLAYING = 3; | |
| /** | |
| * Playback state of a RemoteControlClient which is fast forwarding in the media | |
| * it is currently playing. | |
| * | |
| * @see #setPlaybackState(int) | |
| */ | |
| public final static int PLAYSTATE_FAST_FORWARDING = 4; | |
| /** | |
| * Playback state of a RemoteControlClient which is fast rewinding in the media | |
| * it is currently playing. | |
| * | |
| * @see #setPlaybackState(int) | |
| */ | |
| public final static int PLAYSTATE_REWINDING = 5; | |
| /** | |
| * Playback state of a RemoteControlClient which is skipping to the next | |
| * logical chapter (such as a song in a playlist) in the media it is currently playing. | |
| * | |
| * @see #setPlaybackState(int) | |
| */ | |
| public final static int PLAYSTATE_SKIPPING_FORWARDS = 6; | |
| /** | |
| * Playback state of a RemoteControlClient which is skipping back to the previous | |
| * logical chapter (such as a song in a playlist) in the media it is currently playing. | |
| * | |
| * @see #setPlaybackState(int) | |
| */ | |
| public final static int PLAYSTATE_SKIPPING_BACKWARDS = 7; | |
| /** | |
| * Playback state of a RemoteControlClient which is buffering data to play before it can | |
| * start or resume playback. | |
| * | |
| * @see #setPlaybackState(int) | |
| */ | |
| public final static int PLAYSTATE_BUFFERING = 8; | |
| /** | |
| * Playback state of a RemoteControlClient which cannot perform any playback related | |
| * operation because of an internal error. Examples of such situations are no network | |
| * connectivity when attempting to stream data from a server, or expired user credentials | |
| * when trying to play subscription-based content. | |
| * | |
| * @see #setPlaybackState(int) | |
| */ | |
| public final static int PLAYSTATE_ERROR = 9; | |
| /** | |
| * @hide | |
| * The value of a playback state when none has been declared. | |
| * Intentionally hidden as an application shouldn't set such a playback state value. | |
| */ | |
| public final static int PLAYSTATE_NONE = 0; | |
| /** | |
| * @hide | |
| * The default playback type, "local", indicating the presentation of the media is happening on | |
| * the same device (e.g. a phone, a tablet) as where it is controlled from. | |
| */ | |
| public final static int PLAYBACK_TYPE_LOCAL = 0; | |
| /** | |
| * @hide | |
| * A playback type indicating the presentation of the media is happening on | |
| * a different device (i.e. the remote device) than where it is controlled from. | |
| */ | |
| public final static int PLAYBACK_TYPE_REMOTE = 1; | |
| private final static int PLAYBACK_TYPE_MIN = PLAYBACK_TYPE_LOCAL; | |
| private final static int PLAYBACK_TYPE_MAX = PLAYBACK_TYPE_REMOTE; | |
| /** | |
| * @hide | |
| * Playback information indicating the playback volume is fixed, i.e. it cannot be controlled | |
| * from this object. An example of fixed playback volume is a remote player, playing over HDMI | |
| * where the user prefer to control the volume on the HDMI sink, rather than attenuate at the | |
| * source. | |
| * @see #PLAYBACKINFO_VOLUME_HANDLING. | |
| */ | |
| public final static int PLAYBACK_VOLUME_FIXED = 0; | |
| /** | |
| * @hide | |
| * Playback information indicating the playback volume is variable and can be controlled from | |
| * this object. | |
| * @see #PLAYBACKINFO_VOLUME_HANDLING. | |
| */ | |
| public final static int PLAYBACK_VOLUME_VARIABLE = 1; | |
| /** | |
| * @hide (to be un-hidden) | |
| * The playback information value indicating the value of a given information type is invalid. | |
| * @see #PLAYBACKINFO_VOLUME_HANDLING. | |
| */ | |
| public final static int PLAYBACKINFO_INVALID_VALUE = Integer.MIN_VALUE; | |
| /** | |
| * @hide | |
| * An unknown or invalid playback position value. | |
| */ | |
| public final static long PLAYBACK_POSITION_INVALID = -1; | |
| /** | |
| * @hide | |
| * An invalid playback position value associated with the use of {@link #setPlaybackState(int)} | |
| * used to indicate that playback position will remain unknown. | |
| */ | |
| public final static long PLAYBACK_POSITION_ALWAYS_UNKNOWN = 0x8019771980198300L; | |
| /** | |
| * @hide | |
| * The default playback speed, 1x. | |
| */ | |
| public final static float PLAYBACK_SPEED_1X = 1.0f; | |
| //========================================== | |
| // Public keys for playback information | |
| /** | |
| * @hide | |
| * Playback information that defines the type of playback associated with this | |
| * RemoteControlClient. See {@link #PLAYBACK_TYPE_LOCAL} and {@link #PLAYBACK_TYPE_REMOTE}. | |
| */ | |
| public final static int PLAYBACKINFO_PLAYBACK_TYPE = 1; | |
| /** | |
| * @hide | |
| * Playback information that defines at what volume the playback associated with this | |
| * RemoteControlClient is performed. This information is only used when the playback type is not | |
| * local (see {@link #PLAYBACKINFO_PLAYBACK_TYPE}). | |
| */ | |
| public final static int PLAYBACKINFO_VOLUME = 2; | |
| /** | |
| * @hide | |
| * Playback information that defines the maximum volume volume value that is supported | |
| * by the playback associated with this RemoteControlClient. This information is only used | |
| * when the playback type is not local (see {@link #PLAYBACKINFO_PLAYBACK_TYPE}). | |
| */ | |
| public final static int PLAYBACKINFO_VOLUME_MAX = 3; | |
| /** | |
| * @hide | |
| * Playback information that defines how volume is handled for the presentation of the media. | |
| * @see #PLAYBACK_VOLUME_FIXED | |
| * @see #PLAYBACK_VOLUME_VARIABLE | |
| */ | |
| public final static int PLAYBACKINFO_VOLUME_HANDLING = 4; | |
| /** | |
| * @hide | |
| * Playback information that defines over what stream type the media is presented. | |
| */ | |
| public final static int PLAYBACKINFO_USES_STREAM = 5; | |
| //========================================== | |
| // Public flags for the supported transport control capabilities | |
| /** | |
| * Flag indicating a RemoteControlClient makes use of the "previous" media key. | |
| * | |
| * @see #setTransportControlFlags(int) | |
| * @see android.view.KeyEvent#KEYCODE_MEDIA_PREVIOUS | |
| */ | |
| public final static int FLAG_KEY_MEDIA_PREVIOUS = 1 << 0; | |
| /** | |
| * Flag indicating a RemoteControlClient makes use of the "rewind" media key. | |
| * | |
| * @see #setTransportControlFlags(int) | |
| * @see android.view.KeyEvent#KEYCODE_MEDIA_REWIND | |
| */ | |
| public final static int FLAG_KEY_MEDIA_REWIND = 1 << 1; | |
| /** | |
| * Flag indicating a RemoteControlClient makes use of the "play" media key. | |
| * | |
| * @see #setTransportControlFlags(int) | |
| * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY | |
| */ | |
| public final static int FLAG_KEY_MEDIA_PLAY = 1 << 2; | |
| /** | |
| * Flag indicating a RemoteControlClient makes use of the "play/pause" media key. | |
| * | |
| * @see #setTransportControlFlags(int) | |
| * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE | |
| */ | |
| public final static int FLAG_KEY_MEDIA_PLAY_PAUSE = 1 << 3; | |
| /** | |
| * Flag indicating a RemoteControlClient makes use of the "pause" media key. | |
| * | |
| * @see #setTransportControlFlags(int) | |
| * @see android.view.KeyEvent#KEYCODE_MEDIA_PAUSE | |
| */ | |
| public final static int FLAG_KEY_MEDIA_PAUSE = 1 << 4; | |
| /** | |
| * Flag indicating a RemoteControlClient makes use of the "stop" media key. | |
| * | |
| * @see #setTransportControlFlags(int) | |
| * @see android.view.KeyEvent#KEYCODE_MEDIA_STOP | |
| */ | |
| public final static int FLAG_KEY_MEDIA_STOP = 1 << 5; | |
| /** | |
| * Flag indicating a RemoteControlClient makes use of the "fast forward" media key. | |
| * | |
| * @see #setTransportControlFlags(int) | |
| * @see android.view.KeyEvent#KEYCODE_MEDIA_FAST_FORWARD | |
| */ | |
| public final static int FLAG_KEY_MEDIA_FAST_FORWARD = 1 << 6; | |
| /** | |
| * Flag indicating a RemoteControlClient makes use of the "next" media key. | |
| * | |
| * @see #setTransportControlFlags(int) | |
| * @see android.view.KeyEvent#KEYCODE_MEDIA_NEXT | |
| */ | |
| public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7; | |
| /** | |
| * Flag indicating a RemoteControlClient can receive changes in the media playback position | |
| * through the {@link OnPlaybackPositionUpdateListener} interface. This flag must be set | |
| * in order for components that display the RemoteControlClient information, to display and | |
| * let the user control media playback position. | |
| * @see #setTransportControlFlags(int) | |
| * @see #setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener) | |
| * @see #setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener) | |
| */ | |
| public final static int FLAG_KEY_MEDIA_POSITION_UPDATE = 1 << 8; | |
| /** | |
| * Flag indicating a RemoteControlClient supports ratings. | |
| * This flag must be set in order for components that display the RemoteControlClient | |
| * information, to display ratings information, and, if ratings are declared editable | |
| * (by calling {@link MediaMetadataEditor#addEditableKey(int)} with the | |
| * {@link MediaMetadataEditor#RATING_KEY_BY_USER} key), it will enable the user to rate | |
| * the media, with values being received through the interface set with | |
| * {@link #setMetadataUpdateListener(OnMetadataUpdateListener)}. | |
| * @see #setTransportControlFlags(int) | |
| */ | |
| public final static int FLAG_KEY_MEDIA_RATING = 1 << 9; | |
| /** | |
| * @hide | |
| * The flags for when no media keys are declared supported. | |
| * Intentionally hidden as an application shouldn't set the transport control flags | |
| * to this value. | |
| */ | |
| public final static int FLAGS_KEY_MEDIA_NONE = 0; | |
| /** | |
| * @hide | |
| * Flag used to signal some type of metadata exposed by the RemoteControlClient is requested. | |
| */ | |
| public final static int FLAG_INFORMATION_REQUEST_METADATA = 1 << 0; | |
| /** | |
| * @hide | |
| * Flag used to signal that the transport control buttons supported by the | |
| * RemoteControlClient are requested. | |
| * This can for instance happen when playback is at the end of a playlist, and the "next" | |
| * operation is not supported anymore. | |
| */ | |
| public final static int FLAG_INFORMATION_REQUEST_KEY_MEDIA = 1 << 1; | |
| /** | |
| * @hide | |
| * Flag used to signal that the playback state of the RemoteControlClient is requested. | |
| */ | |
| public final static int FLAG_INFORMATION_REQUEST_PLAYSTATE = 1 << 2; | |
| /** | |
| * @hide | |
| * Flag used to signal that the album art for the RemoteControlClient is requested. | |
| */ | |
| public final static int FLAG_INFORMATION_REQUEST_ALBUM_ART = 1 << 3; | |
| private MediaSession mSession; | |
| /** | |
| * Class constructor. | |
| * @param mediaButtonIntent The intent that will be sent for the media button events sent | |
| * by remote controls. | |
| * This intent needs to have been constructed with the {@link Intent#ACTION_MEDIA_BUTTON} | |
| * action, and have a component that will handle the intent (set with | |
| * {@link Intent#setComponent(ComponentName)}) registered with | |
| * {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} | |
| * before this new RemoteControlClient can itself be registered with | |
| * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}. | |
| * @see AudioManager#registerMediaButtonEventReceiver(ComponentName) | |
| * @see AudioManager#registerRemoteControlClient(RemoteControlClient) | |
| */ | |
| public RemoteControlClient(PendingIntent mediaButtonIntent) { | |
| mRcMediaIntent = mediaButtonIntent; | |
| } | |
| /** | |
| * Class constructor for a remote control client whose internal event handling | |
| * happens on a user-provided Looper. | |
| * @param mediaButtonIntent The intent that will be sent for the media button events sent | |
| * by remote controls. | |
| * This intent needs to have been constructed with the {@link Intent#ACTION_MEDIA_BUTTON} | |
| * action, and have a component that will handle the intent (set with | |
| * {@link Intent#setComponent(ComponentName)}) registered with | |
| * {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} | |
| * before this new RemoteControlClient can itself be registered with | |
| * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}. | |
| * @param looper The Looper running the event loop. | |
| * @see AudioManager#registerMediaButtonEventReceiver(ComponentName) | |
| * @see AudioManager#registerRemoteControlClient(RemoteControlClient) | |
| */ | |
| public RemoteControlClient(PendingIntent mediaButtonIntent, Looper looper) { | |
| mRcMediaIntent = mediaButtonIntent; | |
| } | |
| /** | |
| * @hide | |
| */ | |
| public void registerWithSession(MediaSessionLegacyHelper helper) { | |
| helper.addRccListener(mRcMediaIntent, mTransportListener); | |
| mSession = helper.getSession(mRcMediaIntent); | |
| setTransportControlFlags(mTransportControlFlags); | |
| } | |
| /** | |
| * @hide | |
| */ | |
| public void unregisterWithSession(MediaSessionLegacyHelper helper) { | |
| helper.removeRccListener(mRcMediaIntent); | |
| mSession = null; | |
| } | |
| /** | |
| * Get a {@link MediaSession} associated with this RCC. It will only have a | |
| * session while it is registered with | |
| * {@link AudioManager#registerRemoteControlClient}. The session returned | |
| * should not be modified directly by the application but may be used with | |
| * other APIs that require a session. | |
| * | |
| * @return A media session object or null. | |
| */ | |
| public MediaSession getMediaSession() { | |
| return mSession; | |
| } | |
| /** | |
| * Class used to modify metadata in a {@link RemoteControlClient} object. | |
| * Use {@link RemoteControlClient#editMetadata(boolean)} to create an instance of an editor, | |
| * on which you set the metadata for the RemoteControlClient instance. Once all the information | |
| * has been set, use {@link #apply()} to make it the new metadata that should be displayed | |
| * for the associated client. Once the metadata has been "applied", you cannot reuse this | |
| * instance of the MetadataEditor. | |
| * | |
| * @deprecated Use {@link MediaMetadata} and {@link MediaSession} instead. | |
| */ | |
| @Deprecated public class MetadataEditor extends MediaMetadataEditor { | |
| // only use RemoteControlClient.editMetadata() to get a MetadataEditor instance | |
| private MetadataEditor() { } | |
| /** | |
| * @hide | |
| */ | |
| public Object clone() throws CloneNotSupportedException { | |
| throw new CloneNotSupportedException(); | |
| } | |
| /** | |
| * The metadata key for the content artwork / album art. | |
| */ | |
| public final static int BITMAP_KEY_ARTWORK = 100; | |
| /** | |
| * @hide | |
| * TODO(jmtrivi) have lockscreen move to the new key name and remove | |
| */ | |
| public final static int METADATA_KEY_ARTWORK = BITMAP_KEY_ARTWORK; | |
| /** | |
| * Adds textual information to be displayed. | |
| * Note that none of the information added after {@link #apply()} has been called, | |
| * will be displayed. | |
| * @param key The identifier of a the metadata field to set. Valid values are | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM}, | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST}, | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST}, | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR}, | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION}, | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER}, | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE}, | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE}, | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER}. | |
| * @param value The text for the given key, or {@code null} to signify there is no valid | |
| * information for the field. | |
| * @return Returns a reference to the same MetadataEditor object, so you can chain put | |
| * calls together. | |
| */ | |
| public synchronized MetadataEditor putString(int key, String value) | |
| throws IllegalArgumentException { | |
| super.putString(key, value); | |
| if (mMetadataBuilder != null) { | |
| // MediaMetadata supports all the same fields as MetadataEditor | |
| String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key); | |
| // But just in case, don't add things we don't understand | |
| if (metadataKey != null) { | |
| mMetadataBuilder.putText(metadataKey, value); | |
| } | |
| } | |
| return this; | |
| } | |
| /** | |
| * Adds numerical information to be displayed. | |
| * Note that none of the information added after {@link #apply()} has been called, | |
| * will be displayed. | |
| * @param key the identifier of a the metadata field to set. Valid values are | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER}, | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER}, | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} (with a value | |
| * expressed in milliseconds), | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}. | |
| * @param value The long value for the given key | |
| * @return Returns a reference to the same MetadataEditor object, so you can chain put | |
| * calls together. | |
| * @throws IllegalArgumentException | |
| */ | |
| public synchronized MetadataEditor putLong(int key, long value) | |
| throws IllegalArgumentException { | |
| super.putLong(key, value); | |
| if (mMetadataBuilder != null) { | |
| // MediaMetadata supports all the same fields as MetadataEditor | |
| String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key); | |
| // But just in case, don't add things we don't understand | |
| if (metadataKey != null) { | |
| mMetadataBuilder.putLong(metadataKey, value); | |
| } | |
| } | |
| return this; | |
| } | |
| /** | |
| * Sets the album / artwork picture to be displayed on the remote control. | |
| * @param key the identifier of the bitmap to set. The only valid value is | |
| * {@link #BITMAP_KEY_ARTWORK} | |
| * @param bitmap The bitmap for the artwork, or null if there isn't any. | |
| * @return Returns a reference to the same MetadataEditor object, so you can chain put | |
| * calls together. | |
| * @throws IllegalArgumentException | |
| * @see android.graphics.Bitmap | |
| */ | |
| @Override | |
| public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap) | |
| throws IllegalArgumentException { | |
| super.putBitmap(key, bitmap); | |
| if (mMetadataBuilder != null) { | |
| // MediaMetadata supports all the same fields as MetadataEditor | |
| String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key); | |
| // But just in case, don't add things we don't understand | |
| if (metadataKey != null) { | |
| mMetadataBuilder.putBitmap(metadataKey, bitmap); | |
| } | |
| } | |
| return this; | |
| } | |
| @Override | |
| public synchronized MetadataEditor putObject(int key, Object object) | |
| throws IllegalArgumentException { | |
| super.putObject(key, object); | |
| if (mMetadataBuilder != null && | |
| (key == MediaMetadataEditor.RATING_KEY_BY_USER || | |
| key == MediaMetadataEditor.RATING_KEY_BY_OTHERS)) { | |
| String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key); | |
| if (metadataKey != null) { | |
| mMetadataBuilder.putRating(metadataKey, (Rating) object); | |
| } | |
| } | |
| return this; | |
| } | |
| /** | |
| * Clears all the metadata that has been set since the MetadataEditor instance was created | |
| * (with {@link RemoteControlClient#editMetadata(boolean)}). | |
| * Note that clearing the metadata doesn't reset the editable keys | |
| * (use {@link MediaMetadataEditor#removeEditableKeys()} instead). | |
| */ | |
| @Override | |
| public synchronized void clear() { | |
| super.clear(); | |
| } | |
| /** | |
| * Associates all the metadata that has been set since the MetadataEditor instance was | |
| * created with {@link RemoteControlClient#editMetadata(boolean)}, or since | |
| * {@link #clear()} was called, with the RemoteControlClient. Once "applied", | |
| * this MetadataEditor cannot be reused to edit the RemoteControlClient's metadata. | |
| */ | |
| public synchronized void apply() { | |
| if (mApplied) { | |
| Log.e(TAG, "Can't apply a previously applied MetadataEditor"); | |
| return; | |
| } | |
| synchronized (mCacheLock) { | |
| // Still build the old metadata so when creating a new editor | |
| // you get the expected values. | |
| // assign the edited data | |
| mMetadata = new Bundle(mEditorMetadata); | |
| // add the information about editable keys | |
| mMetadata.putLong(String.valueOf(KEY_EDITABLE_MASK), mEditableKeys); | |
| if ((mOriginalArtwork != null) && (!mOriginalArtwork.equals(mEditorArtwork))) { | |
| mOriginalArtwork.recycle(); | |
| } | |
| mOriginalArtwork = mEditorArtwork; | |
| mEditorArtwork = null; | |
| // USE_SESSIONS | |
| if (mSession != null && mMetadataBuilder != null) { | |
| mMediaMetadata = mMetadataBuilder.build(); | |
| mSession.setMetadata(mMediaMetadata); | |
| } | |
| mApplied = true; | |
| } | |
| } | |
| } | |
| /** | |
| * Creates a {@link MetadataEditor}. | |
| * @param startEmpty Set to false if you want the MetadataEditor to contain the metadata that | |
| * was previously applied to the RemoteControlClient, or true if it is to be created empty. | |
| * @return a new MetadataEditor instance. | |
| */ | |
| public MetadataEditor editMetadata(boolean startEmpty) { | |
| MetadataEditor editor = new MetadataEditor(); | |
| if (startEmpty) { | |
| editor.mEditorMetadata = new Bundle(); | |
| editor.mEditorArtwork = null; | |
| editor.mMetadataChanged = true; | |
| editor.mArtworkChanged = true; | |
| editor.mEditableKeys = 0; | |
| } else { | |
| editor.mEditorMetadata = new Bundle(mMetadata); | |
| editor.mEditorArtwork = mOriginalArtwork; | |
| editor.mMetadataChanged = false; | |
| editor.mArtworkChanged = false; | |
| } | |
| // USE_SESSIONS | |
| if (startEmpty || mMediaMetadata == null) { | |
| editor.mMetadataBuilder = new MediaMetadata.Builder(); | |
| } else { | |
| editor.mMetadataBuilder = new MediaMetadata.Builder(mMediaMetadata); | |
| } | |
| return editor; | |
| } | |
| /** | |
| * Sets the current playback state. | |
| * @param state The current playback state, one of the following values: | |
| * {@link #PLAYSTATE_STOPPED}, | |
| * {@link #PLAYSTATE_PAUSED}, | |
| * {@link #PLAYSTATE_PLAYING}, | |
| * {@link #PLAYSTATE_FAST_FORWARDING}, | |
| * {@link #PLAYSTATE_REWINDING}, | |
| * {@link #PLAYSTATE_SKIPPING_FORWARDS}, | |
| * {@link #PLAYSTATE_SKIPPING_BACKWARDS}, | |
| * {@link #PLAYSTATE_BUFFERING}, | |
| * {@link #PLAYSTATE_ERROR}. | |
| */ | |
| public void setPlaybackState(int state) { | |
| setPlaybackStateInt(state, PLAYBACK_POSITION_ALWAYS_UNKNOWN, PLAYBACK_SPEED_1X, | |
| false /* legacy API, converting to method with position and speed */); | |
| } | |
| /** | |
| * Sets the current playback state and the matching media position for the current playback | |
| * speed. | |
| * @param state The current playback state, one of the following values: | |
| * {@link #PLAYSTATE_STOPPED}, | |
| * {@link #PLAYSTATE_PAUSED}, | |
| * {@link #PLAYSTATE_PLAYING}, | |
| * {@link #PLAYSTATE_FAST_FORWARDING}, | |
| * {@link #PLAYSTATE_REWINDING}, | |
| * {@link #PLAYSTATE_SKIPPING_FORWARDS}, | |
| * {@link #PLAYSTATE_SKIPPING_BACKWARDS}, | |
| * {@link #PLAYSTATE_BUFFERING}, | |
| * {@link #PLAYSTATE_ERROR}. | |
| * @param timeInMs a 0 or positive value for the current media position expressed in ms | |
| * (same unit as for when sending the media duration, if applicable, with | |
| * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} in the | |
| * {@link RemoteControlClient.MetadataEditor}). Negative values imply that position is not | |
| * known (e.g. listening to a live stream of a radio) or not applicable (e.g. when state | |
| * is {@link #PLAYSTATE_BUFFERING} and nothing had played yet). | |
| * @param playbackSpeed a value expressed as a ratio of 1x playback: 1.0f is normal playback, | |
| * 2.0f is 2x, 0.5f is half-speed, -2.0f is rewind at 2x speed. 0.0f means nothing is | |
| * playing (e.g. when state is {@link #PLAYSTATE_ERROR}). | |
| */ | |
| public void setPlaybackState(int state, long timeInMs, float playbackSpeed) { | |
| setPlaybackStateInt(state, timeInMs, playbackSpeed, true); | |
| } | |
| private void setPlaybackStateInt(int state, long timeInMs, float playbackSpeed, | |
| boolean hasPosition) { | |
| synchronized(mCacheLock) { | |
| if ((mPlaybackState != state) || (mPlaybackPositionMs != timeInMs) | |
| || (mPlaybackSpeed != playbackSpeed)) { | |
| // store locally | |
| mPlaybackState = state; | |
| // distinguish between an application not knowing the current playback position | |
| // at the moment and an application using the API where only the playback state | |
| // is passed, not the playback position. | |
| if (hasPosition) { | |
| if (timeInMs < 0) { | |
| mPlaybackPositionMs = PLAYBACK_POSITION_INVALID; | |
| } else { | |
| mPlaybackPositionMs = timeInMs; | |
| } | |
| } else { | |
| mPlaybackPositionMs = PLAYBACK_POSITION_ALWAYS_UNKNOWN; | |
| } | |
| mPlaybackSpeed = playbackSpeed; | |
| // keep track of when the state change occurred | |
| mPlaybackStateChangeTimeMs = SystemClock.elapsedRealtime(); | |
| // USE_SESSIONS | |
| if (mSession != null) { | |
| int pbState = getStateFromRccState(state); | |
| long position = hasPosition ? mPlaybackPositionMs | |
| : PlaybackState.PLAYBACK_POSITION_UNKNOWN; | |
| PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState); | |
| bob.setState(pbState, position, playbackSpeed, SystemClock.elapsedRealtime()); | |
| bob.setErrorMessage(null); | |
| mSessionPlaybackState = bob.build(); | |
| mSession.setPlaybackState(mSessionPlaybackState); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Sets the flags for the media transport control buttons that this client supports. | |
| * @param transportControlFlags A combination of the following flags: | |
| * {@link #FLAG_KEY_MEDIA_PREVIOUS}, | |
| * {@link #FLAG_KEY_MEDIA_REWIND}, | |
| * {@link #FLAG_KEY_MEDIA_PLAY}, | |
| * {@link #FLAG_KEY_MEDIA_PLAY_PAUSE}, | |
| * {@link #FLAG_KEY_MEDIA_PAUSE}, | |
| * {@link #FLAG_KEY_MEDIA_STOP}, | |
| * {@link #FLAG_KEY_MEDIA_FAST_FORWARD}, | |
| * {@link #FLAG_KEY_MEDIA_NEXT}, | |
| * {@link #FLAG_KEY_MEDIA_POSITION_UPDATE}, | |
| * {@link #FLAG_KEY_MEDIA_RATING}. | |
| */ | |
| public void setTransportControlFlags(int transportControlFlags) { | |
| synchronized(mCacheLock) { | |
| // store locally | |
| mTransportControlFlags = transportControlFlags; | |
| // USE_SESSIONS | |
| if (mSession != null) { | |
| PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState); | |
| bob.setActions(getActionsFromRccControlFlags(transportControlFlags)); | |
| mSessionPlaybackState = bob.build(); | |
| mSession.setPlaybackState(mSessionPlaybackState); | |
| } | |
| } | |
| } | |
| /** | |
| * Interface definition for a callback to be invoked when one of the metadata values has | |
| * been updated. | |
| * Implement this interface to receive metadata updates after registering your listener | |
| * through {@link RemoteControlClient#setMetadataUpdateListener(OnMetadataUpdateListener)}. | |
| */ | |
| public interface OnMetadataUpdateListener { | |
| /** | |
| * Called on the implementer to notify that the metadata field for the given key has | |
| * been updated to the new value. | |
| * @param key the identifier of the updated metadata field. | |
| * @param newValue the Object storing the new value for the key. | |
| */ | |
| public abstract void onMetadataUpdate(int key, Object newValue); | |
| } | |
| /** | |
| * Sets the listener to be called whenever the metadata is updated. | |
| * New metadata values will be received in the same thread as the one in which | |
| * RemoteControlClient was created. | |
| * @param l the metadata update listener | |
| */ | |
| public void setMetadataUpdateListener(OnMetadataUpdateListener l) { | |
| synchronized(mCacheLock) { | |
| mMetadataUpdateListener = l; | |
| } | |
| } | |
| /** | |
| * Interface definition for a callback to be invoked when the media playback position is | |
| * requested to be updated. | |
| * @see RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE | |
| */ | |
| public interface OnPlaybackPositionUpdateListener { | |
| /** | |
| * Called on the implementer to notify it that the playback head should be set at the given | |
| * position. If the position can be changed from its current value, the implementor of | |
| * the interface must also update the playback position using | |
| * {@link #setPlaybackState(int, long, float)} to reflect the actual new | |
| * position being used, regardless of whether it differs from the requested position. | |
| * Failure to do so would cause the system to not know the new actual playback position, | |
| * and user interface components would fail to show the user where playback resumed after | |
| * the position was updated. | |
| * @param newPositionMs the new requested position in the current media, expressed in ms. | |
| */ | |
| void onPlaybackPositionUpdate(long newPositionMs); | |
| } | |
| /** | |
| * Interface definition for a callback to be invoked when the media playback position is | |
| * queried. | |
| * @see RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE | |
| */ | |
| public interface OnGetPlaybackPositionListener { | |
| /** | |
| * Called on the implementer of the interface to query the current playback position. | |
| * @return a negative value if the current playback position (or the last valid playback | |
| * position) is not known, or a zero or positive value expressed in ms indicating the | |
| * current position, or the last valid known position. | |
| */ | |
| long onGetPlaybackPosition(); | |
| } | |
| /** | |
| * Sets the listener to be called whenever the media playback position is requested | |
| * to be updated. | |
| * Notifications will be received in the same thread as the one in which RemoteControlClient | |
| * was created. | |
| * @param l the position update listener to be called | |
| */ | |
| public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l) { | |
| synchronized(mCacheLock) { | |
| mPositionUpdateListener = l; | |
| } | |
| } | |
| /** | |
| * Sets the listener to be called whenever the media current playback position is needed. | |
| * Queries will be received in the same thread as the one in which RemoteControlClient | |
| * was created. | |
| * @param l the listener to be called to retrieve the playback position | |
| */ | |
| public void setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener l) { | |
| synchronized(mCacheLock) { | |
| mPositionProvider = l; | |
| } | |
| } | |
| /** | |
| * @hide | |
| * Flag to reflect that the application controlling this RemoteControlClient sends playback | |
| * position updates. The playback position being "readable" is considered from the application's | |
| * point of view. | |
| */ | |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | |
| public static int MEDIA_POSITION_READABLE = 1 << 0; | |
| /** | |
| * @hide | |
| * Flag to reflect that the application controlling this RemoteControlClient can receive | |
| * playback position updates. The playback position being "writable" | |
| * is considered from the application's point of view. | |
| */ | |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | |
| public static int MEDIA_POSITION_WRITABLE = 1 << 1; | |
| /** @hide */ | |
| public final static int DEFAULT_PLAYBACK_VOLUME_HANDLING = PLAYBACK_VOLUME_VARIABLE; | |
| /** @hide */ | |
| // hard-coded to the same number of steps as AudioService.MAX_STREAM_VOLUME[STREAM_MUSIC] | |
| public final static int DEFAULT_PLAYBACK_VOLUME = 15; | |
| /** | |
| * Lock for all cached data | |
| */ | |
| private final Object mCacheLock = new Object(); | |
| /** | |
| * Cache for the playback state. | |
| * Access synchronized on mCacheLock | |
| */ | |
| private int mPlaybackState = PLAYSTATE_NONE; | |
| /** | |
| * Time of last play state change | |
| * Access synchronized on mCacheLock | |
| */ | |
| private long mPlaybackStateChangeTimeMs = 0; | |
| /** | |
| * Last playback position in ms reported by the user | |
| */ | |
| private long mPlaybackPositionMs = PLAYBACK_POSITION_INVALID; | |
| /** | |
| * Last playback speed reported by the user | |
| */ | |
| private float mPlaybackSpeed = PLAYBACK_SPEED_1X; | |
| /** | |
| * Cache for the artwork bitmap. | |
| * Access synchronized on mCacheLock | |
| * Artwork and metadata are not kept in one Bundle because the bitmap sometimes needs to be | |
| * accessed to be resized, in which case a copy will be made. This would add overhead in | |
| * Bundle operations. | |
| */ | |
| private Bitmap mOriginalArtwork; | |
| /** | |
| * Cache for the transport control mask. | |
| * Access synchronized on mCacheLock | |
| */ | |
| private int mTransportControlFlags = FLAGS_KEY_MEDIA_NONE; | |
| /** | |
| * Cache for the metadata strings. | |
| * Access synchronized on mCacheLock | |
| * This is re-initialized in apply() and so cannot be final. | |
| */ | |
| private Bundle mMetadata = new Bundle(); | |
| /** | |
| * Listener registered by user of RemoteControlClient to receive requests for playback position | |
| * update requests. | |
| */ | |
| private OnPlaybackPositionUpdateListener mPositionUpdateListener; | |
| /** | |
| * Provider registered by user of RemoteControlClient to provide the current playback position. | |
| */ | |
| private OnGetPlaybackPositionListener mPositionProvider; | |
| /** | |
| * Listener registered by user of RemoteControlClient to receive edit changes to metadata | |
| * it exposes. | |
| */ | |
| private OnMetadataUpdateListener mMetadataUpdateListener; | |
| /** | |
| * The current remote control client generation ID across the system, as known by this object | |
| */ | |
| private int mCurrentClientGenId = -1; | |
| /** | |
| * The media button intent description associated with this remote control client | |
| * (can / should include target component for intent handling, used when persisting media | |
| * button event receiver across reboots). | |
| */ | |
| private final PendingIntent mRcMediaIntent; | |
| /** | |
| * Reflects whether any "plugged in" IRemoteControlDisplay has mWantsPositonSync set to true. | |
| */ | |
| // TODO consider using a ref count for IRemoteControlDisplay requiring sync instead | |
| private boolean mNeedsPositionSync = false; | |
| /** | |
| * Cache for the current playback state using Session APIs. | |
| */ | |
| private PlaybackState mSessionPlaybackState = null; | |
| /** | |
| * Cache for metadata using Session APIs. This is re-initialized in apply(). | |
| */ | |
| private MediaMetadata mMediaMetadata; | |
| /** | |
| * @hide | |
| * Accessor to media button intent description (includes target component) | |
| */ | |
| public PendingIntent getRcMediaIntent() { | |
| return mRcMediaIntent; | |
| } | |
| /** | |
| * @hide | |
| * Default value for the unique identifier | |
| */ | |
| public final static int RCSE_ID_UNREGISTERED = -1; | |
| // USE_SESSIONS | |
| private MediaSession.Callback mTransportListener = new MediaSession.Callback() { | |
| @Override | |
| public void onSeekTo(long pos) { | |
| RemoteControlClient.this.onSeekTo(mCurrentClientGenId, pos); | |
| } | |
| @Override | |
| public void onSetRating(Rating rating) { | |
| if ((mTransportControlFlags & FLAG_KEY_MEDIA_RATING) != 0) { | |
| onUpdateMetadata(mCurrentClientGenId, MetadataEditor.RATING_KEY_BY_USER, rating); | |
| } | |
| } | |
| }; | |
| //=========================================================== | |
| // Message handlers | |
| private void onSeekTo(int generationId, long timeMs) { | |
| synchronized (mCacheLock) { | |
| if ((mCurrentClientGenId == generationId) && (mPositionUpdateListener != null)) { | |
| mPositionUpdateListener.onPlaybackPositionUpdate(timeMs); | |
| } | |
| } | |
| } | |
| private void onUpdateMetadata(int generationId, int key, Object value) { | |
| synchronized (mCacheLock) { | |
| if ((mCurrentClientGenId == generationId) && (mMetadataUpdateListener != null)) { | |
| mMetadataUpdateListener.onMetadataUpdate(key, value); | |
| } | |
| } | |
| } | |
| //=========================================================== | |
| // Internal utilities | |
| /** | |
| * Returns whether, for the given playback state, the playback position is expected to | |
| * be changing. | |
| * @param playstate the playback state to evaluate | |
| * @return true during any form of playback, false if it's not playing anything while in this | |
| * playback state | |
| */ | |
| static boolean playbackPositionShouldMove(int playstate) { | |
| switch(playstate) { | |
| case PLAYSTATE_STOPPED: | |
| case PLAYSTATE_PAUSED: | |
| case PLAYSTATE_BUFFERING: | |
| case PLAYSTATE_ERROR: | |
| case PLAYSTATE_SKIPPING_FORWARDS: | |
| case PLAYSTATE_SKIPPING_BACKWARDS: | |
| return false; | |
| case PLAYSTATE_PLAYING: | |
| case PLAYSTATE_FAST_FORWARDING: | |
| case PLAYSTATE_REWINDING: | |
| default: | |
| return true; | |
| } | |
| } | |
| /** | |
| * Period for playback position drift checks, 15s when playing at 1x or slower. | |
| */ | |
| private final static long POSITION_REFRESH_PERIOD_PLAYING_MS = 15000; | |
| /** | |
| * Minimum period for playback position drift checks, never more often when every 2s, when | |
| * fast forwarding or rewinding. | |
| */ | |
| private final static long POSITION_REFRESH_PERIOD_MIN_MS = 2000; | |
| /** | |
| * The value above which the difference between client-reported playback position and | |
| * estimated position is considered a drift. | |
| */ | |
| private final static long POSITION_DRIFT_MAX_MS = 500; | |
| /** | |
| * Compute the period at which the estimated playback position should be compared against the | |
| * actual playback position. Is a funciton of playback speed. | |
| * @param speed 1.0f is normal playback speed | |
| * @return the period in ms | |
| */ | |
| private static long getCheckPeriodFromSpeed(float speed) { | |
| if (Math.abs(speed) <= 1.0f) { | |
| return POSITION_REFRESH_PERIOD_PLAYING_MS; | |
| } else { | |
| return Math.max((long)(POSITION_REFRESH_PERIOD_PLAYING_MS / Math.abs(speed)), | |
| POSITION_REFRESH_PERIOD_MIN_MS); | |
| } | |
| } | |
| /** | |
| * Get the {@link PlaybackState} state for the given | |
| * {@link RemoteControlClient} state. | |
| * | |
| * @param rccState The state used by {@link RemoteControlClient}. | |
| * @return The equivalent state used by {@link PlaybackState}. | |
| */ | |
| private static int getStateFromRccState(int rccState) { | |
| switch (rccState) { | |
| case PLAYSTATE_BUFFERING: | |
| return PlaybackState.STATE_BUFFERING; | |
| case PLAYSTATE_ERROR: | |
| return PlaybackState.STATE_ERROR; | |
| case PLAYSTATE_FAST_FORWARDING: | |
| return PlaybackState.STATE_FAST_FORWARDING; | |
| case PLAYSTATE_NONE: | |
| return PlaybackState.STATE_NONE; | |
| case PLAYSTATE_PAUSED: | |
| return PlaybackState.STATE_PAUSED; | |
| case PLAYSTATE_PLAYING: | |
| return PlaybackState.STATE_PLAYING; | |
| case PLAYSTATE_REWINDING: | |
| return PlaybackState.STATE_REWINDING; | |
| case PLAYSTATE_SKIPPING_BACKWARDS: | |
| return PlaybackState.STATE_SKIPPING_TO_PREVIOUS; | |
| case PLAYSTATE_SKIPPING_FORWARDS: | |
| return PlaybackState.STATE_SKIPPING_TO_NEXT; | |
| case PLAYSTATE_STOPPED: | |
| return PlaybackState.STATE_STOPPED; | |
| default: | |
| return -1; | |
| } | |
| } | |
| /** | |
| * Get the {@link RemoteControlClient} state for the given | |
| * {@link PlaybackState} state. | |
| * | |
| * @param state The state used by {@link PlaybackState}. | |
| * @return The equivalent state used by {@link RemoteControlClient}. | |
| */ | |
| static int getRccStateFromState(int state) { | |
| switch (state) { | |
| case PlaybackState.STATE_BUFFERING: | |
| return PLAYSTATE_BUFFERING; | |
| case PlaybackState.STATE_ERROR: | |
| return PLAYSTATE_ERROR; | |
| case PlaybackState.STATE_FAST_FORWARDING: | |
| return PLAYSTATE_FAST_FORWARDING; | |
| case PlaybackState.STATE_NONE: | |
| return PLAYSTATE_NONE; | |
| case PlaybackState.STATE_PAUSED: | |
| return PLAYSTATE_PAUSED; | |
| case PlaybackState.STATE_PLAYING: | |
| return PLAYSTATE_PLAYING; | |
| case PlaybackState.STATE_REWINDING: | |
| return PLAYSTATE_REWINDING; | |
| case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: | |
| return PLAYSTATE_SKIPPING_BACKWARDS; | |
| case PlaybackState.STATE_SKIPPING_TO_NEXT: | |
| return PLAYSTATE_SKIPPING_FORWARDS; | |
| case PlaybackState.STATE_STOPPED: | |
| return PLAYSTATE_STOPPED; | |
| default: | |
| return -1; | |
| } | |
| } | |
| private static long getActionsFromRccControlFlags(int rccFlags) { | |
| long actions = 0; | |
| long flag = 1; | |
| while (flag <= rccFlags) { | |
| if ((flag & rccFlags) != 0) { | |
| actions |= getActionForRccFlag((int) flag); | |
| } | |
| flag = flag << 1; | |
| } | |
| return actions; | |
| } | |
| static int getRccControlFlagsFromActions(long actions) { | |
| int rccFlags = 0; | |
| long action = 1; | |
| while (action <= actions && action < Integer.MAX_VALUE) { | |
| if ((action & actions) != 0) { | |
| rccFlags |= getRccFlagForAction(action); | |
| } | |
| action = action << 1; | |
| } | |
| return rccFlags; | |
| } | |
| private static long getActionForRccFlag(int flag) { | |
| switch (flag) { | |
| case FLAG_KEY_MEDIA_PREVIOUS: | |
| return PlaybackState.ACTION_SKIP_TO_PREVIOUS; | |
| case FLAG_KEY_MEDIA_REWIND: | |
| return PlaybackState.ACTION_REWIND; | |
| case FLAG_KEY_MEDIA_PLAY: | |
| return PlaybackState.ACTION_PLAY; | |
| case FLAG_KEY_MEDIA_PLAY_PAUSE: | |
| return PlaybackState.ACTION_PLAY_PAUSE; | |
| case FLAG_KEY_MEDIA_PAUSE: | |
| return PlaybackState.ACTION_PAUSE; | |
| case FLAG_KEY_MEDIA_STOP: | |
| return PlaybackState.ACTION_STOP; | |
| case FLAG_KEY_MEDIA_FAST_FORWARD: | |
| return PlaybackState.ACTION_FAST_FORWARD; | |
| case FLAG_KEY_MEDIA_NEXT: | |
| return PlaybackState.ACTION_SKIP_TO_NEXT; | |
| case FLAG_KEY_MEDIA_POSITION_UPDATE: | |
| return PlaybackState.ACTION_SEEK_TO; | |
| case FLAG_KEY_MEDIA_RATING: | |
| return PlaybackState.ACTION_SET_RATING; | |
| } | |
| return 0; | |
| } | |
| private static int getRccFlagForAction(long action) { | |
| // We only care about the lower set of actions that can map to rcc | |
| // flags. | |
| int testAction = action < Integer.MAX_VALUE ? (int) action : 0; | |
| switch (testAction) { | |
| case (int) PlaybackState.ACTION_SKIP_TO_PREVIOUS: | |
| return FLAG_KEY_MEDIA_PREVIOUS; | |
| case (int) PlaybackState.ACTION_REWIND: | |
| return FLAG_KEY_MEDIA_REWIND; | |
| case (int) PlaybackState.ACTION_PLAY: | |
| return FLAG_KEY_MEDIA_PLAY; | |
| case (int) PlaybackState.ACTION_PLAY_PAUSE: | |
| return FLAG_KEY_MEDIA_PLAY_PAUSE; | |
| case (int) PlaybackState.ACTION_PAUSE: | |
| return FLAG_KEY_MEDIA_PAUSE; | |
| case (int) PlaybackState.ACTION_STOP: | |
| return FLAG_KEY_MEDIA_STOP; | |
| case (int) PlaybackState.ACTION_FAST_FORWARD: | |
| return FLAG_KEY_MEDIA_FAST_FORWARD; | |
| case (int) PlaybackState.ACTION_SKIP_TO_NEXT: | |
| return FLAG_KEY_MEDIA_NEXT; | |
| case (int) PlaybackState.ACTION_SEEK_TO: | |
| return FLAG_KEY_MEDIA_POSITION_UPDATE; | |
| case (int) PlaybackState.ACTION_SET_RATING: | |
| return FLAG_KEY_MEDIA_RATING; | |
| } | |
| return 0; | |
| } | |
| } |