1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media; 18 19 import android.app.PendingIntent; 20 import android.compat.annotation.UnsupportedAppUsage; 21 import android.content.ComponentName; 22 import android.content.Intent; 23 import android.graphics.Bitmap; 24 import android.media.session.MediaSession; 25 import android.media.session.MediaSessionLegacyHelper; 26 import android.media.session.PlaybackState; 27 import android.os.Bundle; 28 import android.os.Looper; 29 import android.os.SystemClock; 30 import android.util.Log; 31 32 /** 33 * RemoteControlClient enables exposing information meant to be consumed by remote controls 34 * capable of displaying metadata, artwork and media transport control buttons. 35 * 36 * <p>A remote control client object is associated with a media button event receiver. This 37 * event receiver must have been previously registered with 38 * {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} before the 39 * RemoteControlClient can be registered through 40 * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}. 41 * 42 * <p>Here is an example of creating a RemoteControlClient instance after registering a media 43 * button event receiver: 44 * <pre>ComponentName myEventReceiver = new ComponentName(getPackageName(), MyRemoteControlEventReceiver.class.getName()); 45 * AudioManager myAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 46 * myAudioManager.registerMediaButtonEventReceiver(myEventReceiver); 47 * // build the PendingIntent for the remote control client 48 * Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 49 * mediaButtonIntent.setComponent(myEventReceiver); 50 * PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0); 51 * // create and register the remote control client 52 * RemoteControlClient myRemoteControlClient = new RemoteControlClient(mediaPendingIntent); 53 * myAudioManager.registerRemoteControlClient(myRemoteControlClient);</pre> 54 * 55 * @deprecated Use {@link MediaSession} instead. 56 */ 57 @Deprecated public class RemoteControlClient 58 { 59 private final static String TAG = "RemoteControlClient"; 60 private final static boolean DEBUG = false; 61 62 /** 63 * Playback state of a RemoteControlClient which is stopped. 64 * 65 * @see #setPlaybackState(int) 66 */ 67 public final static int PLAYSTATE_STOPPED = 1; 68 /** 69 * Playback state of a RemoteControlClient which is paused. 70 * 71 * @see #setPlaybackState(int) 72 */ 73 public final static int PLAYSTATE_PAUSED = 2; 74 /** 75 * Playback state of a RemoteControlClient which is playing media. 76 * 77 * @see #setPlaybackState(int) 78 */ 79 public final static int PLAYSTATE_PLAYING = 3; 80 /** 81 * Playback state of a RemoteControlClient which is fast forwarding in the media 82 * it is currently playing. 83 * 84 * @see #setPlaybackState(int) 85 */ 86 public final static int PLAYSTATE_FAST_FORWARDING = 4; 87 /** 88 * Playback state of a RemoteControlClient which is fast rewinding in the media 89 * it is currently playing. 90 * 91 * @see #setPlaybackState(int) 92 */ 93 public final static int PLAYSTATE_REWINDING = 5; 94 /** 95 * Playback state of a RemoteControlClient which is skipping to the next 96 * logical chapter (such as a song in a playlist) in the media it is currently playing. 97 * 98 * @see #setPlaybackState(int) 99 */ 100 public final static int PLAYSTATE_SKIPPING_FORWARDS = 6; 101 /** 102 * Playback state of a RemoteControlClient which is skipping back to the previous 103 * logical chapter (such as a song in a playlist) in the media it is currently playing. 104 * 105 * @see #setPlaybackState(int) 106 */ 107 public final static int PLAYSTATE_SKIPPING_BACKWARDS = 7; 108 /** 109 * Playback state of a RemoteControlClient which is buffering data to play before it can 110 * start or resume playback. 111 * 112 * @see #setPlaybackState(int) 113 */ 114 public final static int PLAYSTATE_BUFFERING = 8; 115 /** 116 * Playback state of a RemoteControlClient which cannot perform any playback related 117 * operation because of an internal error. Examples of such situations are no network 118 * connectivity when attempting to stream data from a server, or expired user credentials 119 * when trying to play subscription-based content. 120 * 121 * @see #setPlaybackState(int) 122 */ 123 public final static int PLAYSTATE_ERROR = 9; 124 /** 125 * @hide 126 * The value of a playback state when none has been declared. 127 * Intentionally hidden as an application shouldn't set such a playback state value. 128 */ 129 public final static int PLAYSTATE_NONE = 0; 130 131 /** 132 * @hide 133 * The default playback type, "local", indicating the presentation of the media is happening on 134 * the same device (e.g. a phone, a tablet) as where it is controlled from. 135 */ 136 public final static int PLAYBACK_TYPE_LOCAL = 0; 137 /** 138 * @hide 139 * A playback type indicating the presentation of the media is happening on 140 * a different device (i.e. the remote device) than where it is controlled from. 141 */ 142 public final static int PLAYBACK_TYPE_REMOTE = 1; 143 private final static int PLAYBACK_TYPE_MIN = PLAYBACK_TYPE_LOCAL; 144 private final static int PLAYBACK_TYPE_MAX = PLAYBACK_TYPE_REMOTE; 145 /** 146 * @hide 147 * Playback information indicating the playback volume is fixed, i.e. it cannot be controlled 148 * from this object. An example of fixed playback volume is a remote player, playing over HDMI 149 * where the user prefer to control the volume on the HDMI sink, rather than attenuate at the 150 * source. 151 * @see #PLAYBACKINFO_VOLUME_HANDLING. 152 */ 153 public final static int PLAYBACK_VOLUME_FIXED = 0; 154 /** 155 * @hide 156 * Playback information indicating the playback volume is variable and can be controlled from 157 * this object. 158 * @see #PLAYBACKINFO_VOLUME_HANDLING. 159 */ 160 public final static int PLAYBACK_VOLUME_VARIABLE = 1; 161 /** 162 * @hide (to be un-hidden) 163 * The playback information value indicating the value of a given information type is invalid. 164 * @see #PLAYBACKINFO_VOLUME_HANDLING. 165 */ 166 public final static int PLAYBACKINFO_INVALID_VALUE = Integer.MIN_VALUE; 167 168 /** 169 * @hide 170 * An unknown or invalid playback position value. 171 */ 172 public final static long PLAYBACK_POSITION_INVALID = -1; 173 /** 174 * @hide 175 * An invalid playback position value associated with the use of {@link #setPlaybackState(int)} 176 * used to indicate that playback position will remain unknown. 177 */ 178 public final static long PLAYBACK_POSITION_ALWAYS_UNKNOWN = 0x8019771980198300L; 179 /** 180 * @hide 181 * The default playback speed, 1x. 182 */ 183 public final static float PLAYBACK_SPEED_1X = 1.0f; 184 185 //========================================== 186 // Public keys for playback information 187 /** 188 * @hide 189 * Playback information that defines the type of playback associated with this 190 * RemoteControlClient. See {@link #PLAYBACK_TYPE_LOCAL} and {@link #PLAYBACK_TYPE_REMOTE}. 191 */ 192 public final static int PLAYBACKINFO_PLAYBACK_TYPE = 1; 193 /** 194 * @hide 195 * Playback information that defines at what volume the playback associated with this 196 * RemoteControlClient is performed. This information is only used when the playback type is not 197 * local (see {@link #PLAYBACKINFO_PLAYBACK_TYPE}). 198 */ 199 public final static int PLAYBACKINFO_VOLUME = 2; 200 /** 201 * @hide 202 * Playback information that defines the maximum volume volume value that is supported 203 * by the playback associated with this RemoteControlClient. This information is only used 204 * when the playback type is not local (see {@link #PLAYBACKINFO_PLAYBACK_TYPE}). 205 */ 206 public final static int PLAYBACKINFO_VOLUME_MAX = 3; 207 /** 208 * @hide 209 * Playback information that defines how volume is handled for the presentation of the media. 210 * @see #PLAYBACK_VOLUME_FIXED 211 * @see #PLAYBACK_VOLUME_VARIABLE 212 */ 213 public final static int PLAYBACKINFO_VOLUME_HANDLING = 4; 214 /** 215 * @hide 216 * Playback information that defines over what stream type the media is presented. 217 */ 218 public final static int PLAYBACKINFO_USES_STREAM = 5; 219 220 //========================================== 221 // Public flags for the supported transport control capabilities 222 /** 223 * Flag indicating a RemoteControlClient makes use of the "previous" media key. 224 * 225 * @see #setTransportControlFlags(int) 226 * @see android.view.KeyEvent#KEYCODE_MEDIA_PREVIOUS 227 */ 228 public final static int FLAG_KEY_MEDIA_PREVIOUS = 1 << 0; 229 /** 230 * Flag indicating a RemoteControlClient makes use of the "rewind" media key. 231 * 232 * @see #setTransportControlFlags(int) 233 * @see android.view.KeyEvent#KEYCODE_MEDIA_REWIND 234 */ 235 public final static int FLAG_KEY_MEDIA_REWIND = 1 << 1; 236 /** 237 * Flag indicating a RemoteControlClient makes use of the "play" media key. 238 * 239 * @see #setTransportControlFlags(int) 240 * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY 241 */ 242 public final static int FLAG_KEY_MEDIA_PLAY = 1 << 2; 243 /** 244 * Flag indicating a RemoteControlClient makes use of the "play/pause" media key. 245 * 246 * @see #setTransportControlFlags(int) 247 * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE 248 */ 249 public final static int FLAG_KEY_MEDIA_PLAY_PAUSE = 1 << 3; 250 /** 251 * Flag indicating a RemoteControlClient makes use of the "pause" media key. 252 * 253 * @see #setTransportControlFlags(int) 254 * @see android.view.KeyEvent#KEYCODE_MEDIA_PAUSE 255 */ 256 public final static int FLAG_KEY_MEDIA_PAUSE = 1 << 4; 257 /** 258 * Flag indicating a RemoteControlClient makes use of the "stop" media key. 259 * 260 * @see #setTransportControlFlags(int) 261 * @see android.view.KeyEvent#KEYCODE_MEDIA_STOP 262 */ 263 public final static int FLAG_KEY_MEDIA_STOP = 1 << 5; 264 /** 265 * Flag indicating a RemoteControlClient makes use of the "fast forward" media key. 266 * 267 * @see #setTransportControlFlags(int) 268 * @see android.view.KeyEvent#KEYCODE_MEDIA_FAST_FORWARD 269 */ 270 public final static int FLAG_KEY_MEDIA_FAST_FORWARD = 1 << 6; 271 /** 272 * Flag indicating a RemoteControlClient makes use of the "next" media key. 273 * 274 * @see #setTransportControlFlags(int) 275 * @see android.view.KeyEvent#KEYCODE_MEDIA_NEXT 276 */ 277 public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7; 278 /** 279 * Flag indicating a RemoteControlClient can receive changes in the media playback position 280 * through the {@link OnPlaybackPositionUpdateListener} interface. This flag must be set 281 * in order for components that display the RemoteControlClient information, to display and 282 * let the user control media playback position. 283 * @see #setTransportControlFlags(int) 284 * @see #setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener) 285 * @see #setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener) 286 */ 287 public final static int FLAG_KEY_MEDIA_POSITION_UPDATE = 1 << 8; 288 /** 289 * Flag indicating a RemoteControlClient supports ratings. 290 * This flag must be set in order for components that display the RemoteControlClient 291 * information, to display ratings information, and, if ratings are declared editable 292 * (by calling {@link MediaMetadataEditor#addEditableKey(int)} with the 293 * {@link MediaMetadataEditor#RATING_KEY_BY_USER} key), it will enable the user to rate 294 * the media, with values being received through the interface set with 295 * {@link #setMetadataUpdateListener(OnMetadataUpdateListener)}. 296 * @see #setTransportControlFlags(int) 297 */ 298 public final static int FLAG_KEY_MEDIA_RATING = 1 << 9; 299 300 /** 301 * @hide 302 * The flags for when no media keys are declared supported. 303 * Intentionally hidden as an application shouldn't set the transport control flags 304 * to this value. 305 */ 306 public final static int FLAGS_KEY_MEDIA_NONE = 0; 307 308 /** 309 * @hide 310 * Flag used to signal some type of metadata exposed by the RemoteControlClient is requested. 311 */ 312 public final static int FLAG_INFORMATION_REQUEST_METADATA = 1 << 0; 313 /** 314 * @hide 315 * Flag used to signal that the transport control buttons supported by the 316 * RemoteControlClient are requested. 317 * This can for instance happen when playback is at the end of a playlist, and the "next" 318 * operation is not supported anymore. 319 */ 320 public final static int FLAG_INFORMATION_REQUEST_KEY_MEDIA = 1 << 1; 321 /** 322 * @hide 323 * Flag used to signal that the playback state of the RemoteControlClient is requested. 324 */ 325 public final static int FLAG_INFORMATION_REQUEST_PLAYSTATE = 1 << 2; 326 /** 327 * @hide 328 * Flag used to signal that the album art for the RemoteControlClient is requested. 329 */ 330 public final static int FLAG_INFORMATION_REQUEST_ALBUM_ART = 1 << 3; 331 332 private MediaSession mSession; 333 334 /** 335 * Class constructor. 336 * @param mediaButtonIntent The intent that will be sent for the media button events sent 337 * by remote controls. 338 * This intent needs to have been constructed with the {@link Intent#ACTION_MEDIA_BUTTON} 339 * action, and have a component that will handle the intent (set with 340 * {@link Intent#setComponent(ComponentName)}) registered with 341 * {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} 342 * before this new RemoteControlClient can itself be registered with 343 * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}. 344 * @see AudioManager#registerMediaButtonEventReceiver(ComponentName) 345 * @see AudioManager#registerRemoteControlClient(RemoteControlClient) 346 */ RemoteControlClient(PendingIntent mediaButtonIntent)347 public RemoteControlClient(PendingIntent mediaButtonIntent) { 348 mRcMediaIntent = mediaButtonIntent; 349 } 350 351 /** 352 * Class constructor for a remote control client whose internal event handling 353 * happens on a user-provided Looper. 354 * @param mediaButtonIntent The intent that will be sent for the media button events sent 355 * by remote controls. 356 * This intent needs to have been constructed with the {@link Intent#ACTION_MEDIA_BUTTON} 357 * action, and have a component that will handle the intent (set with 358 * {@link Intent#setComponent(ComponentName)}) registered with 359 * {@link AudioManager#registerMediaButtonEventReceiver(ComponentName)} 360 * before this new RemoteControlClient can itself be registered with 361 * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}. 362 * @param looper The Looper running the event loop. 363 * @see AudioManager#registerMediaButtonEventReceiver(ComponentName) 364 * @see AudioManager#registerRemoteControlClient(RemoteControlClient) 365 */ RemoteControlClient(PendingIntent mediaButtonIntent, Looper looper)366 public RemoteControlClient(PendingIntent mediaButtonIntent, Looper looper) { 367 mRcMediaIntent = mediaButtonIntent; 368 } 369 370 /** 371 * @hide 372 */ registerWithSession(MediaSessionLegacyHelper helper)373 public void registerWithSession(MediaSessionLegacyHelper helper) { 374 helper.addRccListener(mRcMediaIntent, mTransportListener); 375 mSession = helper.getSession(mRcMediaIntent); 376 setTransportControlFlags(mTransportControlFlags); 377 } 378 379 /** 380 * @hide 381 */ unregisterWithSession(MediaSessionLegacyHelper helper)382 public void unregisterWithSession(MediaSessionLegacyHelper helper) { 383 helper.removeRccListener(mRcMediaIntent); 384 mSession = null; 385 } 386 387 /** 388 * Get a {@link MediaSession} associated with this RCC. It will only have a 389 * session while it is registered with 390 * {@link AudioManager#registerRemoteControlClient}. The session returned 391 * should not be modified directly by the application but may be used with 392 * other APIs that require a session. 393 * 394 * @return A media session object or null. 395 */ getMediaSession()396 public MediaSession getMediaSession() { 397 return mSession; 398 } 399 400 /** 401 * Class used to modify metadata in a {@link RemoteControlClient} object. 402 * Use {@link RemoteControlClient#editMetadata(boolean)} to create an instance of an editor, 403 * on which you set the metadata for the RemoteControlClient instance. Once all the information 404 * has been set, use {@link #apply()} to make it the new metadata that should be displayed 405 * for the associated client. Once the metadata has been "applied", you cannot reuse this 406 * instance of the MetadataEditor. 407 * 408 * @deprecated Use {@link MediaMetadata} and {@link MediaSession} instead. 409 */ 410 @Deprecated public class MetadataEditor extends MediaMetadataEditor { 411 412 // only use RemoteControlClient.editMetadata() to get a MetadataEditor instance MetadataEditor()413 private MetadataEditor() { } 414 /** 415 * @hide 416 */ clone()417 public Object clone() throws CloneNotSupportedException { 418 throw new CloneNotSupportedException(); 419 } 420 421 /** 422 * The metadata key for the content artwork / album art. 423 */ 424 public final static int BITMAP_KEY_ARTWORK = 100; 425 426 /** 427 * @hide 428 * TODO(jmtrivi) have lockscreen move to the new key name and remove 429 */ 430 public final static int METADATA_KEY_ARTWORK = BITMAP_KEY_ARTWORK; 431 432 /** 433 * Adds textual information to be displayed. 434 * Note that none of the information added after {@link #apply()} has been called, 435 * will be displayed. 436 * @param key The identifier of a the metadata field to set. Valid values are 437 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM}, 438 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST}, 439 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, 440 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST}, 441 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR}, 442 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION}, 443 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER}, 444 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE}, 445 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE}, 446 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, 447 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER}. 448 * @param value The text for the given key, or {@code null} to signify there is no valid 449 * information for the field. 450 * @return Returns a reference to the same MetadataEditor object, so you can chain put 451 * calls together. 452 */ putString(int key, String value)453 public synchronized MetadataEditor putString(int key, String value) 454 throws IllegalArgumentException { 455 super.putString(key, value); 456 if (mMetadataBuilder != null) { 457 // MediaMetadata supports all the same fields as MetadataEditor 458 String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key); 459 // But just in case, don't add things we don't understand 460 if (metadataKey != null) { 461 mMetadataBuilder.putText(metadataKey, value); 462 } 463 } 464 465 return this; 466 } 467 468 /** 469 * Adds numerical information to be displayed. 470 * Note that none of the information added after {@link #apply()} has been called, 471 * will be displayed. 472 * @param key the identifier of a the metadata field to set. Valid values are 473 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER}, 474 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER}, 475 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} (with a value 476 * expressed in milliseconds), 477 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}. 478 * @param value The long value for the given key 479 * @return Returns a reference to the same MetadataEditor object, so you can chain put 480 * calls together. 481 * @throws IllegalArgumentException 482 */ putLong(int key, long value)483 public synchronized MetadataEditor putLong(int key, long value) 484 throws IllegalArgumentException { 485 super.putLong(key, value); 486 if (mMetadataBuilder != null) { 487 // MediaMetadata supports all the same fields as MetadataEditor 488 String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key); 489 // But just in case, don't add things we don't understand 490 if (metadataKey != null) { 491 mMetadataBuilder.putLong(metadataKey, value); 492 } 493 } 494 return this; 495 } 496 497 /** 498 * Sets the album / artwork picture to be displayed on the remote control. 499 * @param key the identifier of the bitmap to set. The only valid value is 500 * {@link #BITMAP_KEY_ARTWORK} 501 * @param bitmap The bitmap for the artwork, or null if there isn't any. 502 * @return Returns a reference to the same MetadataEditor object, so you can chain put 503 * calls together. 504 * @throws IllegalArgumentException 505 * @see android.graphics.Bitmap 506 */ 507 @Override putBitmap(int key, Bitmap bitmap)508 public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap) 509 throws IllegalArgumentException { 510 super.putBitmap(key, bitmap); 511 if (mMetadataBuilder != null) { 512 // MediaMetadata supports all the same fields as MetadataEditor 513 String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key); 514 // But just in case, don't add things we don't understand 515 if (metadataKey != null) { 516 mMetadataBuilder.putBitmap(metadataKey, bitmap); 517 } 518 } 519 return this; 520 } 521 522 @Override putObject(int key, Object object)523 public synchronized MetadataEditor putObject(int key, Object object) 524 throws IllegalArgumentException { 525 super.putObject(key, object); 526 if (mMetadataBuilder != null && 527 (key == MediaMetadataEditor.RATING_KEY_BY_USER || 528 key == MediaMetadataEditor.RATING_KEY_BY_OTHERS)) { 529 String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key); 530 if (metadataKey != null) { 531 mMetadataBuilder.putRating(metadataKey, (Rating) object); 532 } 533 } 534 return this; 535 } 536 537 /** 538 * Clears all the metadata that has been set since the MetadataEditor instance was created 539 * (with {@link RemoteControlClient#editMetadata(boolean)}). 540 * Note that clearing the metadata doesn't reset the editable keys 541 * (use {@link MediaMetadataEditor#removeEditableKeys()} instead). 542 */ 543 @Override clear()544 public synchronized void clear() { 545 super.clear(); 546 } 547 548 /** 549 * Associates all the metadata that has been set since the MetadataEditor instance was 550 * created with {@link RemoteControlClient#editMetadata(boolean)}, or since 551 * {@link #clear()} was called, with the RemoteControlClient. Once "applied", 552 * this MetadataEditor cannot be reused to edit the RemoteControlClient's metadata. 553 */ apply()554 public synchronized void apply() { 555 if (mApplied) { 556 Log.e(TAG, "Can't apply a previously applied MetadataEditor"); 557 return; 558 } 559 synchronized (mCacheLock) { 560 // Still build the old metadata so when creating a new editor 561 // you get the expected values. 562 // assign the edited data 563 mMetadata = new Bundle(mEditorMetadata); 564 // add the information about editable keys 565 mMetadata.putLong(String.valueOf(KEY_EDITABLE_MASK), mEditableKeys); 566 if ((mOriginalArtwork != null) && (!mOriginalArtwork.equals(mEditorArtwork))) { 567 mOriginalArtwork.recycle(); 568 } 569 mOriginalArtwork = mEditorArtwork; 570 mEditorArtwork = null; 571 572 // USE_SESSIONS 573 if (mSession != null && mMetadataBuilder != null) { 574 mMediaMetadata = mMetadataBuilder.build(); 575 mSession.setMetadata(mMediaMetadata); 576 } 577 mApplied = true; 578 } 579 } 580 } 581 582 /** 583 * Creates a {@link MetadataEditor}. 584 * @param startEmpty Set to false if you want the MetadataEditor to contain the metadata that 585 * was previously applied to the RemoteControlClient, or true if it is to be created empty. 586 * @return a new MetadataEditor instance. 587 */ editMetadata(boolean startEmpty)588 public MetadataEditor editMetadata(boolean startEmpty) { 589 MetadataEditor editor = new MetadataEditor(); 590 if (startEmpty) { 591 editor.mEditorMetadata = new Bundle(); 592 editor.mEditorArtwork = null; 593 editor.mMetadataChanged = true; 594 editor.mArtworkChanged = true; 595 editor.mEditableKeys = 0; 596 } else { 597 editor.mEditorMetadata = new Bundle(mMetadata); 598 editor.mEditorArtwork = mOriginalArtwork; 599 editor.mMetadataChanged = false; 600 editor.mArtworkChanged = false; 601 } 602 // USE_SESSIONS 603 if (startEmpty || mMediaMetadata == null) { 604 editor.mMetadataBuilder = new MediaMetadata.Builder(); 605 } else { 606 editor.mMetadataBuilder = new MediaMetadata.Builder(mMediaMetadata); 607 } 608 return editor; 609 } 610 611 /** 612 * Sets the current playback state. 613 * @param state The current playback state, one of the following values: 614 * {@link #PLAYSTATE_STOPPED}, 615 * {@link #PLAYSTATE_PAUSED}, 616 * {@link #PLAYSTATE_PLAYING}, 617 * {@link #PLAYSTATE_FAST_FORWARDING}, 618 * {@link #PLAYSTATE_REWINDING}, 619 * {@link #PLAYSTATE_SKIPPING_FORWARDS}, 620 * {@link #PLAYSTATE_SKIPPING_BACKWARDS}, 621 * {@link #PLAYSTATE_BUFFERING}, 622 * {@link #PLAYSTATE_ERROR}. 623 */ setPlaybackState(int state)624 public void setPlaybackState(int state) { 625 setPlaybackStateInt(state, PLAYBACK_POSITION_ALWAYS_UNKNOWN, PLAYBACK_SPEED_1X, 626 false /* legacy API, converting to method with position and speed */); 627 } 628 629 /** 630 * Sets the current playback state and the matching media position for the current playback 631 * speed. 632 * @param state The current playback state, one of the following values: 633 * {@link #PLAYSTATE_STOPPED}, 634 * {@link #PLAYSTATE_PAUSED}, 635 * {@link #PLAYSTATE_PLAYING}, 636 * {@link #PLAYSTATE_FAST_FORWARDING}, 637 * {@link #PLAYSTATE_REWINDING}, 638 * {@link #PLAYSTATE_SKIPPING_FORWARDS}, 639 * {@link #PLAYSTATE_SKIPPING_BACKWARDS}, 640 * {@link #PLAYSTATE_BUFFERING}, 641 * {@link #PLAYSTATE_ERROR}. 642 * @param timeInMs a 0 or positive value for the current media position expressed in ms 643 * (same unit as for when sending the media duration, if applicable, with 644 * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} in the 645 * {@link RemoteControlClient.MetadataEditor}). Negative values imply that position is not 646 * known (e.g. listening to a live stream of a radio) or not applicable (e.g. when state 647 * is {@link #PLAYSTATE_BUFFERING} and nothing had played yet). 648 * @param playbackSpeed a value expressed as a ratio of 1x playback: 1.0f is normal playback, 649 * 2.0f is 2x, 0.5f is half-speed, -2.0f is rewind at 2x speed. 0.0f means nothing is 650 * playing (e.g. when state is {@link #PLAYSTATE_ERROR}). 651 */ setPlaybackState(int state, long timeInMs, float playbackSpeed)652 public void setPlaybackState(int state, long timeInMs, float playbackSpeed) { 653 setPlaybackStateInt(state, timeInMs, playbackSpeed, true); 654 } 655 setPlaybackStateInt(int state, long timeInMs, float playbackSpeed, boolean hasPosition)656 private void setPlaybackStateInt(int state, long timeInMs, float playbackSpeed, 657 boolean hasPosition) { 658 synchronized(mCacheLock) { 659 if ((mPlaybackState != state) || (mPlaybackPositionMs != timeInMs) 660 || (mPlaybackSpeed != playbackSpeed)) { 661 // store locally 662 mPlaybackState = state; 663 // distinguish between an application not knowing the current playback position 664 // at the moment and an application using the API where only the playback state 665 // is passed, not the playback position. 666 if (hasPosition) { 667 if (timeInMs < 0) { 668 mPlaybackPositionMs = PLAYBACK_POSITION_INVALID; 669 } else { 670 mPlaybackPositionMs = timeInMs; 671 } 672 } else { 673 mPlaybackPositionMs = PLAYBACK_POSITION_ALWAYS_UNKNOWN; 674 } 675 mPlaybackSpeed = playbackSpeed; 676 // keep track of when the state change occurred 677 mPlaybackStateChangeTimeMs = SystemClock.elapsedRealtime(); 678 679 // USE_SESSIONS 680 if (mSession != null) { 681 int pbState = getStateFromRccState(state); 682 long position = hasPosition ? mPlaybackPositionMs 683 : PlaybackState.PLAYBACK_POSITION_UNKNOWN; 684 685 PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState); 686 bob.setState(pbState, position, playbackSpeed, SystemClock.elapsedRealtime()); 687 bob.setErrorMessage(null); 688 mSessionPlaybackState = bob.build(); 689 mSession.setPlaybackState(mSessionPlaybackState); 690 } 691 } 692 } 693 } 694 695 /** 696 * Sets the flags for the media transport control buttons that this client supports. 697 * @param transportControlFlags A combination of the following flags: 698 * {@link #FLAG_KEY_MEDIA_PREVIOUS}, 699 * {@link #FLAG_KEY_MEDIA_REWIND}, 700 * {@link #FLAG_KEY_MEDIA_PLAY}, 701 * {@link #FLAG_KEY_MEDIA_PLAY_PAUSE}, 702 * {@link #FLAG_KEY_MEDIA_PAUSE}, 703 * {@link #FLAG_KEY_MEDIA_STOP}, 704 * {@link #FLAG_KEY_MEDIA_FAST_FORWARD}, 705 * {@link #FLAG_KEY_MEDIA_NEXT}, 706 * {@link #FLAG_KEY_MEDIA_POSITION_UPDATE}, 707 * {@link #FLAG_KEY_MEDIA_RATING}. 708 */ setTransportControlFlags(int transportControlFlags)709 public void setTransportControlFlags(int transportControlFlags) { 710 synchronized(mCacheLock) { 711 // store locally 712 mTransportControlFlags = transportControlFlags; 713 714 // USE_SESSIONS 715 if (mSession != null) { 716 PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState); 717 bob.setActions(getActionsFromRccControlFlags(transportControlFlags)); 718 mSessionPlaybackState = bob.build(); 719 mSession.setPlaybackState(mSessionPlaybackState); 720 } 721 } 722 } 723 724 /** 725 * Interface definition for a callback to be invoked when one of the metadata values has 726 * been updated. 727 * Implement this interface to receive metadata updates after registering your listener 728 * through {@link RemoteControlClient#setMetadataUpdateListener(OnMetadataUpdateListener)}. 729 */ 730 public interface OnMetadataUpdateListener { 731 /** 732 * Called on the implementer to notify that the metadata field for the given key has 733 * been updated to the new value. 734 * @param key the identifier of the updated metadata field. 735 * @param newValue the Object storing the new value for the key. 736 */ onMetadataUpdate(int key, Object newValue)737 public abstract void onMetadataUpdate(int key, Object newValue); 738 } 739 740 /** 741 * Sets the listener to be called whenever the metadata is updated. 742 * New metadata values will be received in the same thread as the one in which 743 * RemoteControlClient was created. 744 * @param l the metadata update listener 745 */ setMetadataUpdateListener(OnMetadataUpdateListener l)746 public void setMetadataUpdateListener(OnMetadataUpdateListener l) { 747 synchronized(mCacheLock) { 748 mMetadataUpdateListener = l; 749 } 750 } 751 752 753 /** 754 * Interface definition for a callback to be invoked when the media playback position is 755 * requested to be updated. 756 * @see RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE 757 */ 758 public interface OnPlaybackPositionUpdateListener { 759 /** 760 * Called on the implementer to notify it that the playback head should be set at the given 761 * position. If the position can be changed from its current value, the implementor of 762 * the interface must also update the playback position using 763 * {@link #setPlaybackState(int, long, float)} to reflect the actual new 764 * position being used, regardless of whether it differs from the requested position. 765 * Failure to do so would cause the system to not know the new actual playback position, 766 * and user interface components would fail to show the user where playback resumed after 767 * the position was updated. 768 * @param newPositionMs the new requested position in the current media, expressed in ms. 769 */ onPlaybackPositionUpdate(long newPositionMs)770 void onPlaybackPositionUpdate(long newPositionMs); 771 } 772 773 /** 774 * Interface definition for a callback to be invoked when the media playback position is 775 * queried. 776 * @see RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE 777 */ 778 public interface OnGetPlaybackPositionListener { 779 /** 780 * Called on the implementer of the interface to query the current playback position. 781 * @return a negative value if the current playback position (or the last valid playback 782 * position) is not known, or a zero or positive value expressed in ms indicating the 783 * current position, or the last valid known position. 784 */ onGetPlaybackPosition()785 long onGetPlaybackPosition(); 786 } 787 788 /** 789 * Sets the listener to be called whenever the media playback position is requested 790 * to be updated. 791 * Notifications will be received in the same thread as the one in which RemoteControlClient 792 * was created. 793 * @param l the position update listener to be called 794 */ setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l)795 public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l) { 796 synchronized(mCacheLock) { 797 mPositionUpdateListener = l; 798 } 799 } 800 801 /** 802 * Sets the listener to be called whenever the media current playback position is needed. 803 * Queries will be received in the same thread as the one in which RemoteControlClient 804 * was created. 805 * @param l the listener to be called to retrieve the playback position 806 */ setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener l)807 public void setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener l) { 808 synchronized(mCacheLock) { 809 mPositionProvider = l; 810 } 811 } 812 813 /** 814 * @hide 815 * Flag to reflect that the application controlling this RemoteControlClient sends playback 816 * position updates. The playback position being "readable" is considered from the application's 817 * point of view. 818 */ 819 @UnsupportedAppUsage 820 public static int MEDIA_POSITION_READABLE = 1 << 0; 821 /** 822 * @hide 823 * Flag to reflect that the application controlling this RemoteControlClient can receive 824 * playback position updates. The playback position being "writable" 825 * is considered from the application's point of view. 826 */ 827 @UnsupportedAppUsage 828 public static int MEDIA_POSITION_WRITABLE = 1 << 1; 829 830 /** @hide */ 831 public final static int DEFAULT_PLAYBACK_VOLUME_HANDLING = PLAYBACK_VOLUME_VARIABLE; 832 /** @hide */ 833 // hard-coded to the same number of steps as AudioService.MAX_STREAM_VOLUME[STREAM_MUSIC] 834 public final static int DEFAULT_PLAYBACK_VOLUME = 15; 835 836 /** 837 * Lock for all cached data 838 */ 839 private final Object mCacheLock = new Object(); 840 /** 841 * Cache for the playback state. 842 * Access synchronized on mCacheLock 843 */ 844 private int mPlaybackState = PLAYSTATE_NONE; 845 /** 846 * Time of last play state change 847 * Access synchronized on mCacheLock 848 */ 849 private long mPlaybackStateChangeTimeMs = 0; 850 /** 851 * Last playback position in ms reported by the user 852 */ 853 private long mPlaybackPositionMs = PLAYBACK_POSITION_INVALID; 854 /** 855 * Last playback speed reported by the user 856 */ 857 private float mPlaybackSpeed = PLAYBACK_SPEED_1X; 858 /** 859 * Cache for the artwork bitmap. 860 * Access synchronized on mCacheLock 861 * Artwork and metadata are not kept in one Bundle because the bitmap sometimes needs to be 862 * accessed to be resized, in which case a copy will be made. This would add overhead in 863 * Bundle operations. 864 */ 865 private Bitmap mOriginalArtwork; 866 /** 867 * Cache for the transport control mask. 868 * Access synchronized on mCacheLock 869 */ 870 private int mTransportControlFlags = FLAGS_KEY_MEDIA_NONE; 871 /** 872 * Cache for the metadata strings. 873 * Access synchronized on mCacheLock 874 * This is re-initialized in apply() and so cannot be final. 875 */ 876 private Bundle mMetadata = new Bundle(); 877 /** 878 * Listener registered by user of RemoteControlClient to receive requests for playback position 879 * update requests. 880 */ 881 private OnPlaybackPositionUpdateListener mPositionUpdateListener; 882 /** 883 * Provider registered by user of RemoteControlClient to provide the current playback position. 884 */ 885 private OnGetPlaybackPositionListener mPositionProvider; 886 /** 887 * Listener registered by user of RemoteControlClient to receive edit changes to metadata 888 * it exposes. 889 */ 890 private OnMetadataUpdateListener mMetadataUpdateListener; 891 /** 892 * The current remote control client generation ID across the system, as known by this object 893 */ 894 private int mCurrentClientGenId = -1; 895 896 /** 897 * The media button intent description associated with this remote control client 898 * (can / should include target component for intent handling, used when persisting media 899 * button event receiver across reboots). 900 */ 901 private final PendingIntent mRcMediaIntent; 902 903 /** 904 * Reflects whether any "plugged in" IRemoteControlDisplay has mWantsPositonSync set to true. 905 */ 906 // TODO consider using a ref count for IRemoteControlDisplay requiring sync instead 907 private boolean mNeedsPositionSync = false; 908 909 /** 910 * Cache for the current playback state using Session APIs. 911 */ 912 private PlaybackState mSessionPlaybackState = null; 913 914 /** 915 * Cache for metadata using Session APIs. This is re-initialized in apply(). 916 */ 917 private MediaMetadata mMediaMetadata; 918 919 /** 920 * @hide 921 * Accessor to media button intent description (includes target component) 922 */ getRcMediaIntent()923 public PendingIntent getRcMediaIntent() { 924 return mRcMediaIntent; 925 } 926 927 /** 928 * @hide 929 * Default value for the unique identifier 930 */ 931 public final static int RCSE_ID_UNREGISTERED = -1; 932 933 // USE_SESSIONS 934 private MediaSession.Callback mTransportListener = new MediaSession.Callback() { 935 936 @Override 937 public void onSeekTo(long pos) { 938 RemoteControlClient.this.onSeekTo(mCurrentClientGenId, pos); 939 } 940 941 @Override 942 public void onSetRating(Rating rating) { 943 if ((mTransportControlFlags & FLAG_KEY_MEDIA_RATING) != 0) { 944 onUpdateMetadata(mCurrentClientGenId, MetadataEditor.RATING_KEY_BY_USER, rating); 945 } 946 } 947 }; 948 949 //=========================================================== 950 // Message handlers 951 onSeekTo(int generationId, long timeMs)952 private void onSeekTo(int generationId, long timeMs) { 953 synchronized (mCacheLock) { 954 if ((mCurrentClientGenId == generationId) && (mPositionUpdateListener != null)) { 955 mPositionUpdateListener.onPlaybackPositionUpdate(timeMs); 956 } 957 } 958 } 959 onUpdateMetadata(int generationId, int key, Object value)960 private void onUpdateMetadata(int generationId, int key, Object value) { 961 synchronized (mCacheLock) { 962 if ((mCurrentClientGenId == generationId) && (mMetadataUpdateListener != null)) { 963 mMetadataUpdateListener.onMetadataUpdate(key, value); 964 } 965 } 966 } 967 968 //=========================================================== 969 // Internal utilities 970 971 /** 972 * Returns whether, for the given playback state, the playback position is expected to 973 * be changing. 974 * @param playstate the playback state to evaluate 975 * @return true during any form of playback, false if it's not playing anything while in this 976 * playback state 977 */ playbackPositionShouldMove(int playstate)978 static boolean playbackPositionShouldMove(int playstate) { 979 switch(playstate) { 980 case PLAYSTATE_STOPPED: 981 case PLAYSTATE_PAUSED: 982 case PLAYSTATE_BUFFERING: 983 case PLAYSTATE_ERROR: 984 case PLAYSTATE_SKIPPING_FORWARDS: 985 case PLAYSTATE_SKIPPING_BACKWARDS: 986 return false; 987 case PLAYSTATE_PLAYING: 988 case PLAYSTATE_FAST_FORWARDING: 989 case PLAYSTATE_REWINDING: 990 default: 991 return true; 992 } 993 } 994 995 /** 996 * Period for playback position drift checks, 15s when playing at 1x or slower. 997 */ 998 private final static long POSITION_REFRESH_PERIOD_PLAYING_MS = 15000; 999 1000 /** 1001 * Minimum period for playback position drift checks, never more often when every 2s, when 1002 * fast forwarding or rewinding. 1003 */ 1004 private final static long POSITION_REFRESH_PERIOD_MIN_MS = 2000; 1005 1006 /** 1007 * The value above which the difference between client-reported playback position and 1008 * estimated position is considered a drift. 1009 */ 1010 private final static long POSITION_DRIFT_MAX_MS = 500; 1011 1012 /** 1013 * Compute the period at which the estimated playback position should be compared against the 1014 * actual playback position. Is a funciton of playback speed. 1015 * @param speed 1.0f is normal playback speed 1016 * @return the period in ms 1017 */ getCheckPeriodFromSpeed(float speed)1018 private static long getCheckPeriodFromSpeed(float speed) { 1019 if (Math.abs(speed) <= 1.0f) { 1020 return POSITION_REFRESH_PERIOD_PLAYING_MS; 1021 } else { 1022 return Math.max((long)(POSITION_REFRESH_PERIOD_PLAYING_MS / Math.abs(speed)), 1023 POSITION_REFRESH_PERIOD_MIN_MS); 1024 } 1025 } 1026 1027 /** 1028 * Get the {@link PlaybackState} state for the given 1029 * {@link RemoteControlClient} state. 1030 * 1031 * @param rccState The state used by {@link RemoteControlClient}. 1032 * @return The equivalent state used by {@link PlaybackState}. 1033 */ getStateFromRccState(int rccState)1034 private static int getStateFromRccState(int rccState) { 1035 switch (rccState) { 1036 case PLAYSTATE_BUFFERING: 1037 return PlaybackState.STATE_BUFFERING; 1038 case PLAYSTATE_ERROR: 1039 return PlaybackState.STATE_ERROR; 1040 case PLAYSTATE_FAST_FORWARDING: 1041 return PlaybackState.STATE_FAST_FORWARDING; 1042 case PLAYSTATE_NONE: 1043 return PlaybackState.STATE_NONE; 1044 case PLAYSTATE_PAUSED: 1045 return PlaybackState.STATE_PAUSED; 1046 case PLAYSTATE_PLAYING: 1047 return PlaybackState.STATE_PLAYING; 1048 case PLAYSTATE_REWINDING: 1049 return PlaybackState.STATE_REWINDING; 1050 case PLAYSTATE_SKIPPING_BACKWARDS: 1051 return PlaybackState.STATE_SKIPPING_TO_PREVIOUS; 1052 case PLAYSTATE_SKIPPING_FORWARDS: 1053 return PlaybackState.STATE_SKIPPING_TO_NEXT; 1054 case PLAYSTATE_STOPPED: 1055 return PlaybackState.STATE_STOPPED; 1056 default: 1057 return -1; 1058 } 1059 } 1060 1061 /** 1062 * Get the {@link RemoteControlClient} state for the given 1063 * {@link PlaybackState} state. 1064 * 1065 * @param state The state used by {@link PlaybackState}. 1066 * @return The equivalent state used by {@link RemoteControlClient}. 1067 */ getRccStateFromState(int state)1068 static int getRccStateFromState(int state) { 1069 switch (state) { 1070 case PlaybackState.STATE_BUFFERING: 1071 return PLAYSTATE_BUFFERING; 1072 case PlaybackState.STATE_ERROR: 1073 return PLAYSTATE_ERROR; 1074 case PlaybackState.STATE_FAST_FORWARDING: 1075 return PLAYSTATE_FAST_FORWARDING; 1076 case PlaybackState.STATE_NONE: 1077 return PLAYSTATE_NONE; 1078 case PlaybackState.STATE_PAUSED: 1079 return PLAYSTATE_PAUSED; 1080 case PlaybackState.STATE_PLAYING: 1081 return PLAYSTATE_PLAYING; 1082 case PlaybackState.STATE_REWINDING: 1083 return PLAYSTATE_REWINDING; 1084 case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: 1085 return PLAYSTATE_SKIPPING_BACKWARDS; 1086 case PlaybackState.STATE_SKIPPING_TO_NEXT: 1087 return PLAYSTATE_SKIPPING_FORWARDS; 1088 case PlaybackState.STATE_STOPPED: 1089 return PLAYSTATE_STOPPED; 1090 default: 1091 return -1; 1092 } 1093 } 1094 getActionsFromRccControlFlags(int rccFlags)1095 private static long getActionsFromRccControlFlags(int rccFlags) { 1096 long actions = 0; 1097 long flag = 1; 1098 while (flag <= rccFlags) { 1099 if ((flag & rccFlags) != 0) { 1100 actions |= getActionForRccFlag((int) flag); 1101 } 1102 flag = flag << 1; 1103 } 1104 return actions; 1105 } 1106 getRccControlFlagsFromActions(long actions)1107 static int getRccControlFlagsFromActions(long actions) { 1108 int rccFlags = 0; 1109 long action = 1; 1110 while (action <= actions && action < Integer.MAX_VALUE) { 1111 if ((action & actions) != 0) { 1112 rccFlags |= getRccFlagForAction(action); 1113 } 1114 action = action << 1; 1115 } 1116 return rccFlags; 1117 } 1118 getActionForRccFlag(int flag)1119 private static long getActionForRccFlag(int flag) { 1120 switch (flag) { 1121 case FLAG_KEY_MEDIA_PREVIOUS: 1122 return PlaybackState.ACTION_SKIP_TO_PREVIOUS; 1123 case FLAG_KEY_MEDIA_REWIND: 1124 return PlaybackState.ACTION_REWIND; 1125 case FLAG_KEY_MEDIA_PLAY: 1126 return PlaybackState.ACTION_PLAY; 1127 case FLAG_KEY_MEDIA_PLAY_PAUSE: 1128 return PlaybackState.ACTION_PLAY_PAUSE; 1129 case FLAG_KEY_MEDIA_PAUSE: 1130 return PlaybackState.ACTION_PAUSE; 1131 case FLAG_KEY_MEDIA_STOP: 1132 return PlaybackState.ACTION_STOP; 1133 case FLAG_KEY_MEDIA_FAST_FORWARD: 1134 return PlaybackState.ACTION_FAST_FORWARD; 1135 case FLAG_KEY_MEDIA_NEXT: 1136 return PlaybackState.ACTION_SKIP_TO_NEXT; 1137 case FLAG_KEY_MEDIA_POSITION_UPDATE: 1138 return PlaybackState.ACTION_SEEK_TO; 1139 case FLAG_KEY_MEDIA_RATING: 1140 return PlaybackState.ACTION_SET_RATING; 1141 } 1142 return 0; 1143 } 1144 getRccFlagForAction(long action)1145 private static int getRccFlagForAction(long action) { 1146 // We only care about the lower set of actions that can map to rcc 1147 // flags. 1148 int testAction = action < Integer.MAX_VALUE ? (int) action : 0; 1149 switch (testAction) { 1150 case (int) PlaybackState.ACTION_SKIP_TO_PREVIOUS: 1151 return FLAG_KEY_MEDIA_PREVIOUS; 1152 case (int) PlaybackState.ACTION_REWIND: 1153 return FLAG_KEY_MEDIA_REWIND; 1154 case (int) PlaybackState.ACTION_PLAY: 1155 return FLAG_KEY_MEDIA_PLAY; 1156 case (int) PlaybackState.ACTION_PLAY_PAUSE: 1157 return FLAG_KEY_MEDIA_PLAY_PAUSE; 1158 case (int) PlaybackState.ACTION_PAUSE: 1159 return FLAG_KEY_MEDIA_PAUSE; 1160 case (int) PlaybackState.ACTION_STOP: 1161 return FLAG_KEY_MEDIA_STOP; 1162 case (int) PlaybackState.ACTION_FAST_FORWARD: 1163 return FLAG_KEY_MEDIA_FAST_FORWARD; 1164 case (int) PlaybackState.ACTION_SKIP_TO_NEXT: 1165 return FLAG_KEY_MEDIA_NEXT; 1166 case (int) PlaybackState.ACTION_SEEK_TO: 1167 return FLAG_KEY_MEDIA_POSITION_UPDATE; 1168 case (int) PlaybackState.ACTION_SET_RATING: 1169 return FLAG_KEY_MEDIA_RATING; 1170 } 1171 return 0; 1172 } 1173 } 1174