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