17ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi/*
27ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * Copyright (C) 2013 The Android Open Source Project
37ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi *
47ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * Licensed under the Apache License, Version 2.0 (the "License");
57ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * you may not use this file except in compliance with the License.
67ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * You may obtain a copy of the License at
77ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi *
87ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi *      http://www.apache.org/licenses/LICENSE-2.0
97ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi *
107ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * Unless required by applicable law or agreed to in writing, software
117ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * distributed under the License is distributed on an "AS IS" BASIS,
127ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * See the License for the specific language governing permissions and
147ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * limitations under the License.
157ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi */
167ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
177ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivipackage android.media;
187ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
19a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Triviimport android.Manifest;
20f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powellimport android.app.ActivityManager;
217ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.app.PendingIntent;
227ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.app.PendingIntent.CanceledException;
237ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.content.Context;
247ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.content.Intent;
257ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.graphics.Bitmap;
267ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.media.IRemoteControlDisplay;
277ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.media.MediaMetadataEditor;
287ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.os.Bundle;
297ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.os.Handler;
307ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.os.Looper;
317ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.os.Message;
327ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.os.RemoteException;
337ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.os.ServiceManager;
34f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powellimport android.os.SystemClock;
35f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powellimport android.util.DisplayMetrics;
367ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.util.Log;
377ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Triviimport android.view.KeyEvent;
387ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
394348a73bbe4e66ab691aea5971107046df806511Jean-Michel Triviimport java.lang.ref.WeakReference;
404348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi
417ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi/**
427ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * The RemoteController class is used to control media playback, display and update media metadata
437ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * and playback status, published by applications using the {@link RemoteControlClient} class.
447ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * <p>
457ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * A RemoteController shall be registered through
467ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * {@link AudioManager#registerRemoteController(RemoteController)} in order for the system to send
47f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi * media event updates to the {@link OnClientUpdateListener} listener set in the class constructor.
48f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi * Implement the methods of the interface to receive the information published by the active
49f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi * {@link RemoteControlClient} instances.
50f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi * <br>By default an {@link OnClientUpdateListener} implementation will not receive bitmaps for
51f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi * album art. Use {@link #setArtworkConfiguration(int, int)} to receive images as well.
527ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi * <p>
53f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi * Registration requires the {@link OnClientUpdateListener} listener to be one of the enabled
54f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi * notification listeners (see {@link android.service.notification.NotificationListenerService}).
557ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi */
56a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivipublic final class RemoteController
577ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi{
587ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private final static int MAX_BITMAP_DIMENSION = 512;
597ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private final static int TRANSPORT_UNKNOWN = 0;
607ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private final static String TAG = "RemoteController";
617ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private final static boolean DEBUG = false;
627ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private final static Object mGenLock = new Object();
637ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private final static Object mInfoLock = new Object();
64a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi    private final RcDisplay mRcd;
65a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi    private final Context mContext;
66a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi    private final AudioManager mAudioManager;
67f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell    private final int mMaxBitmapDimension;
687ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private MetadataEditor mMetadataEditor;
697ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
707ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
717ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * Synchronized on mGenLock
727ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
737ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private int mClientGenerationIdCurrent = 0;
747ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
757ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
767ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * Synchronized on mInfoLock
777ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
787ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private boolean mIsRegistered = false;
797ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private PendingIntent mClientPendingIntentCurrent;
807ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private OnClientUpdateListener mOnClientUpdateListener;
817ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private PlaybackInfo mLastPlaybackInfo;
828ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi    private int mArtworkWidth = -1;
838ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi    private int mArtworkHeight = -1;
84f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    private boolean mEnabled = true;
857ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
867ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
87a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * Class constructor.
88f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @param context the {@link Context}, must be non-null.
89f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @param updateListener the listener to be called whenever new client information is available,
90f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     *     must be non-null.
91f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @throws IllegalArgumentException
927ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
93f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    public RemoteController(Context context, OnClientUpdateListener updateListener)
94f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            throws IllegalArgumentException {
95f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        this(context, updateListener, null);
967ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
977ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
987ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
99a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * Class constructor.
100f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @param context the {@link Context}, must be non-null.
101f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @param updateListener the listener to be called whenever new client information is available,
102f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     *     must be non-null.
103a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * @param looper the {@link Looper} on which to run the event loop,
104a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     or null to use the current thread's looper.
1057ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @throws java.lang.IllegalArgumentException
1067ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
107f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    public RemoteController(Context context, OnClientUpdateListener updateListener, Looper looper)
108f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            throws IllegalArgumentException {
109a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi        if (context == null) {
1107ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            throw new IllegalArgumentException("Invalid null Context");
1117ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
112f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        if (updateListener == null) {
113f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            throw new IllegalArgumentException("Invalid null OnClientUpdateListener");
114f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        }
115a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi        if (looper != null) {
116a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi            mEventHandler = new EventHandler(this, looper);
117a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi        } else {
118a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi            Looper l = Looper.myLooper();
119a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi            if (l != null) {
120a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi                mEventHandler = new EventHandler(this, l);
121a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi            } else {
122a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi                throw new IllegalArgumentException("Calling thread not associated with a looper");
123a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi            }
124a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi        }
125f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        mOnClientUpdateListener = updateListener;
126a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi        mContext = context;
1274348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi        mRcd = new RcDisplay(this);
128a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
129f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell
130f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell        if (ActivityManager.isLowRamDeviceStatic()) {
131f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            mMaxBitmapDimension = MAX_BITMAP_DIMENSION;
132f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell        } else {
133f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            final DisplayMetrics dm = context.getResources().getDisplayMetrics();
134f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            mMaxBitmapDimension = Math.max(dm.widthPixels, dm.heightPixels);
135f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell        }
1367ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
1377ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
1387ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
1397ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
140f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * Interface definition for the callbacks to be invoked whenever media events, metadata
141a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * and playback status are available.
1427ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
143f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    public interface OnClientUpdateListener {
1447ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        /**
145f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi         * Called whenever all information, previously received through the other
146a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * methods of the listener, is no longer valid and is about to be refreshed.
147a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * This is typically called whenever a new {@link RemoteControlClient} has been selected
148a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * by the system to have its media information published.
149a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * @param clearing true if there is no selected RemoteControlClient and no information
150a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         *     is available.
1517ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi         */
152f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        public void onClientChange(boolean clearing);
153a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi
1547ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        /**
155f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi         * Called whenever the playback state has changed.
156a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * It is called when no information is known about the playback progress in the media and
157a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * the playback speed.
158a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * @param state one of the playback states authorized
159a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         *     in {@link RemoteControlClient#setPlaybackState(int)}.
1607ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi         */
161f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        public void onClientPlaybackStateUpdate(int state);
1627ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        /**
163f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi         * Called whenever the playback state has changed, and playback position
164f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi         * and speed are known.
165a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * @param state one of the playback states authorized
166a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         *     in {@link RemoteControlClient#setPlaybackState(int)}.
167a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * @param stateChangeTimeMs the system time at which the state change was reported,
16870cb8021ef7d1804982032c464685139629f68a2Adam Powell         *     expressed in ms. Based on {@link android.os.SystemClock#elapsedRealtime()}.
169a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * @param currentPosMs a positive value for the current media playback position expressed
170a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         *     in ms, a negative value if the position is temporarily unknown.
171a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * @param speed  a value expressed as a ratio of 1x playback: 1.0f is normal playback,
172a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         *    2.0f is 2x, 0.5f is half-speed, -2.0f is rewind at 2x speed. 0.0f means nothing is
173a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         *    playing (e.g. when state is {@link RemoteControlClient#PLAYSTATE_ERROR}).
1747ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi         */
1757ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs,
176f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi                long currentPosMs, float speed);
1777ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        /**
178f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi         * Called whenever the transport control flags have changed.
179a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * @param transportControlFlags one of the flags authorized
180a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         *     in {@link RemoteControlClient#setTransportControlFlags(int)}.
1817ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi         */
182f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        public void onClientTransportControlUpdate(int transportControlFlags);
1837ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        /**
184f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi         * Called whenever new metadata is available.
185a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * See the {@link MediaMetadataEditor#putLong(int, long)},
186a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         *  {@link MediaMetadataEditor#putString(int, String)},
187a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         *  {@link MediaMetadataEditor#putBitmap(int, Bitmap)}, and
188a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         *  {@link MediaMetadataEditor#putObject(int, Object)} methods for the various keys that
189a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         *  can be queried.
190a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * @param metadataEditor the container of the new metadata.
1917ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi         */
192f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        public void onClientMetadataUpdate(MetadataEditor metadataEditor);
1937ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    };
1947ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
1957ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
196f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell    /**
197f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     * @hide
198f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     */
199f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell    public String getRemoteControlClientPackageName() {
200f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell        return mClientPendingIntentCurrent != null ?
201f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell                mClientPendingIntentCurrent.getCreatorPackage() : null;
202f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell    }
203f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell
204f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell    /**
205f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     * Return the estimated playback position of the current media track or a negative value
206f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     * if not available.
207f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     *
208f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     * <p>The value returned is estimated by the current process and may not be perfect.
209f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     * The time returned by this method is calculated from the last state change time based
210f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     * on the current play position at that time and the last known playback speed.
211f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     * An application may call {@link #setSynchronizationMode(int)} to apply
212f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     * a synchronization policy that will periodically re-sync the estimated position
213f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     * with the RemoteControlClient.</p>
214f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     *
215f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     * @return the current estimated playback position in milliseconds or a negative value
216f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     *         if not available
217f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     *
218f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     * @see OnClientUpdateListener#onClientPlaybackStateUpdate(int, long, long, float)
219f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell     */
220f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell    public long getEstimatedMediaPosition() {
221f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell        if (mLastPlaybackInfo != null) {
222f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            if (!RemoteControlClient.playbackPositionShouldMove(mLastPlaybackInfo.mState)) {
223f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell                return mLastPlaybackInfo.mCurrentPosMs;
224f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            }
225f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell
226f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            // Take the current position at the time of state change and estimate.
227f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            final long thenPos = mLastPlaybackInfo.mCurrentPosMs;
228f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            if (thenPos < 0) {
229f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell                return -1;
230f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            }
231f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell
232f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            final long now = SystemClock.elapsedRealtime();
233f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            final long then = mLastPlaybackInfo.mStateChangeTimeMs;
234f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            final long sinceThen = now - then;
235f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            final long scaledSinceThen = (long) (sinceThen * mLastPlaybackInfo.mSpeed);
236f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            return thenPos + scaledSinceThen;
237f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell        }
238f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell        return -1;
239f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell    }
2407ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
241f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi
2427ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
243a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * Send a simulated key event for a media button to be received by the current client.
244a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * To simulate a key press, you must first send a KeyEvent built with
245a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * a {@link KeyEvent#ACTION_DOWN} action, then another event with the {@link KeyEvent#ACTION_UP}
246a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * action.
247a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * <p>The key event will be sent to the registered receiver
248a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * (see {@link AudioManager#registerMediaButtonEventReceiver(PendingIntent)}) whose associated
249a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * {@link RemoteControlClient}'s metadata and playback state is published (there may be
250a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * none under some circumstances).
2517ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @param keyEvent a {@link KeyEvent} instance whose key code is one of
252a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     {@link KeyEvent#KEYCODE_MUTE},
253a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     {@link KeyEvent#KEYCODE_HEADSETHOOK},
254a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     {@link KeyEvent#KEYCODE_MEDIA_PLAY},
255a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     {@link KeyEvent#KEYCODE_MEDIA_PAUSE},
256a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE},
257a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     {@link KeyEvent#KEYCODE_MEDIA_STOP},
258a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     {@link KeyEvent#KEYCODE_MEDIA_NEXT},
259a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS},
260a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     {@link KeyEvent#KEYCODE_MEDIA_REWIND},
261a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     {@link KeyEvent#KEYCODE_MEDIA_RECORD},
262a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     {@link KeyEvent#KEYCODE_MEDIA_FAST_FORWARD},
263a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     {@link KeyEvent#KEYCODE_MEDIA_CLOSE},
264a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     {@link KeyEvent#KEYCODE_MEDIA_EJECT},
265a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     *     or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}.
266f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @return true if the event was successfully sent, false otherwise.
267f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @throws IllegalArgumentException
2687ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
269f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    public boolean sendMediaKeyEvent(KeyEvent keyEvent) throws IllegalArgumentException {
2707ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        if (!MediaFocusControl.isMediaKeyCode(keyEvent.getKeyCode())) {
271f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            throw new IllegalArgumentException("not a media key event");
2727ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
2737ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        final PendingIntent pi;
2747ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized(mInfoLock) {
275a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi            if (!mIsRegistered) {
276a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi                Log.e(TAG, "Cannot use sendMediaKeyEvent() from an unregistered RemoteController");
277f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi                return false;
278f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            }
279f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            if (!mEnabled) {
280f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi                Log.e(TAG, "Cannot use sendMediaKeyEvent() from a disabled RemoteController");
281f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi                return false;
282a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi            }
2837ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            pi = mClientPendingIntentCurrent;
2847ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
285a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi        if (pi != null) {
286a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi            Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
287a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi            intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
288a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi            try {
289a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi                pi.send(mContext, 0, intent);
290a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi            } catch (CanceledException e) {
291a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi                Log.e(TAG, "Error sending intent for media button down: ", e);
292f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi                return false;
2937ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
2947ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        } else {
295a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi            Log.i(TAG, "No-op when sending key click, no receiver right now");
296f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            return false;
2977ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
298f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        return true;
2997ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
3007ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
3017ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
3027ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
303a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * Sets the new playback position.
304a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * This method can only be called on a registered RemoteController.
305a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * @param timeMs a 0 or positive value for the new playback position, expressed in ms.
306f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @return true if the command to set the playback position was successfully sent.
307f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @throws IllegalArgumentException
3087ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
309f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    public boolean seekTo(long timeMs) throws IllegalArgumentException {
310f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        if (!mEnabled) {
311f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            Log.e(TAG, "Cannot use seekTo() from a disabled RemoteController");
312f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            return false;
313f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        }
3147ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        if (timeMs < 0) {
315f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            throw new IllegalArgumentException("illegal negative time value");
3167ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
3177ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        final int genId;
3187ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized (mGenLock) {
3197ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            genId = mClientGenerationIdCurrent;
3207ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
3217ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        mAudioManager.setRemoteControlClientPlaybackPosition(genId, timeMs);
322f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        return true;
3237ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
3247ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
3257ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
3267ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
3277ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @hide
3287ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @param wantBitmap
3297ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @param width
3307ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @param height
331f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @return true if successful
332f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @throws IllegalArgumentException
3337ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
334f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    public boolean setArtworkConfiguration(boolean wantBitmap, int width, int height)
335f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            throws IllegalArgumentException {
3367ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized (mInfoLock) {
3378ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi            if (wantBitmap) {
3388ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi                if ((width > 0) && (height > 0)) {
339f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell                    if (width > mMaxBitmapDimension) { width = mMaxBitmapDimension; }
340f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell                    if (height > mMaxBitmapDimension) { height = mMaxBitmapDimension; }
3418ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi                    mArtworkWidth = width;
3428ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi                    mArtworkHeight = height;
3438ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi                } else {
344f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi                    throw new IllegalArgumentException("Invalid dimensions");
3458ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi                }
3467ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            } else {
3478ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi                mArtworkWidth = -1;
3488ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi                mArtworkHeight = -1;
3497ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
3508ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi            if (mIsRegistered) {
3518ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi                mAudioManager.remoteControlDisplayUsesBitmapSize(mRcd,
3528ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi                        mArtworkWidth, mArtworkHeight);
3538ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi            } // else new values have been stored, and will be read by AudioManager with
3548ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi              //    RemoteController.getArtworkSize() when AudioManager.registerRemoteController()
3558ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi              //    is called.
3567ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
357f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        return true;
3587ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
3597ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
3607ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
361a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * Set the maximum artwork image dimensions to be received in the metadata.
362a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * No bitmaps will be received unless this has been specified.
363a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * @param width the maximum width in pixels
364a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * @param height  the maximum height in pixels
365f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @return true if the artwork dimension was successfully set.
366f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @throws IllegalArgumentException
3677ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
368f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    public boolean setArtworkConfiguration(int width, int height) throws IllegalArgumentException {
369a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi        return setArtworkConfiguration(true, width, height);
3707ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
3717ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
3727ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
373a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * Prevents this RemoteController from receiving artwork images.
374f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @return true if receiving artwork images was successfully disabled.
3757ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
376f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    public boolean clearArtworkConfiguration() {
377a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi        return setArtworkConfiguration(false, -1, -1);
3787ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
3797ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
3807ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
3817ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
3827ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * Default playback position synchronization mode where the RemoteControlClient is not
3837ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * asked regularly for its playback position to see if it has drifted from the estimated
3847ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * position.
3857ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
3867ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    public static final int POSITION_SYNCHRONIZATION_NONE = 0;
3877ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
3887ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
3897ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * The playback position synchronization mode where the RemoteControlClient instances which
3907ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * expose their playback position to the framework, will be regularly polled to check
3917ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * whether any drift has been noticed between their estimated position and the one they report.
3927ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * Note that this mode should only ever be used when needing to display very accurate playback
3937ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * position, as regularly polling a RemoteControlClient for its position may have an impact
3947ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * on battery life (if applicable) when this query will trigger network transactions in the
3957ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * case of remote playback.
3967ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
3977ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    public static final int POSITION_SYNCHRONIZATION_CHECK = 1;
3987ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
3997ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
4007ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * Set the playback position synchronization mode.
4017ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * Must be called on a registered RemoteController.
4027ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @param sync {@link #POSITION_SYNCHRONIZATION_NONE} or {@link #POSITION_SYNCHRONIZATION_CHECK}
403f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @return true if the synchronization mode was successfully set.
404f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @throws IllegalArgumentException
4057ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
406f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    public boolean setSynchronizationMode(int sync) throws IllegalArgumentException {
4077ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        if ((sync != POSITION_SYNCHRONIZATION_NONE) || (sync != POSITION_SYNCHRONIZATION_CHECK)) {
408f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            throw new IllegalArgumentException("Unknown synchronization mode " + sync);
4097ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
4107ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        if (!mIsRegistered) {
4117ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            Log.e(TAG, "Cannot set synchronization mode on an unregistered RemoteController");
412f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            return false;
4137ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
4147ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        mAudioManager.remoteControlDisplayWantsPlaybackPositionSync(mRcd,
4157ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                POSITION_SYNCHRONIZATION_CHECK == sync);
416f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        return true;
4177ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
4187ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
4197ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
4207ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
4217ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * Creates a {@link MetadataEditor} for updating metadata values of the editable keys of
4227ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * the current {@link RemoteControlClient}.
423a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * This method can only be called on a registered RemoteController.
4247ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @return a new MetadataEditor instance.
4257ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
4267ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    public MetadataEditor editMetadata() {
4277ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        MetadataEditor editor = new MetadataEditor();
4287ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        editor.mEditorMetadata = new Bundle();
4297ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        editor.mEditorArtwork = null;
4307ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        editor.mMetadataChanged = true;
4317ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        editor.mArtworkChanged = true;
4327ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        editor.mEditableKeys = 0;
4337ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        return editor;
4347ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
4357ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
4367ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
4377ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
438a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi     * A class to read the metadata published by a {@link RemoteControlClient}, or send a
4397ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * {@link RemoteControlClient} new values for keys that can be edited.
4407ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
4417ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    public class MetadataEditor extends MediaMetadataEditor {
4427ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        /**
4437ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi         * @hide
4447ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi         */
4457ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        protected MetadataEditor() { }
4467ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
4477ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        /**
4487ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi         * @hide
4497ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi         */
4507ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        protected MetadataEditor(Bundle metadata, long editableKeys) {
4517ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            mEditorMetadata = metadata;
4527ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            mEditableKeys = editableKeys;
453f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell
454f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            mEditorArtwork = (Bitmap) metadata.getParcelable(
455f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell                    String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK));
456f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            if (mEditorArtwork != null) {
457f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell                cleanupBitmapFromBundle(MediaMetadataEditor.BITMAP_KEY_ARTWORK);
458f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell            }
459f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell
4607ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            mMetadataChanged = true;
4617ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            mArtworkChanged = true;
4627ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            mApplied = false;
4637ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
4647ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
4656d298811ef642bc4f179ca3e9f4de028fb1ddb74Jean-Michel Trivi        private void cleanupBitmapFromBundle(int key) {
4666d298811ef642bc4f179ca3e9f4de028fb1ddb74Jean-Michel Trivi            if (METADATA_KEYS_TYPE.get(key, METADATA_TYPE_INVALID) == METADATA_TYPE_BITMAP) {
4676d298811ef642bc4f179ca3e9f4de028fb1ddb74Jean-Michel Trivi                mEditorMetadata.remove(String.valueOf(key));
4686d298811ef642bc4f179ca3e9f4de028fb1ddb74Jean-Michel Trivi            }
4696d298811ef642bc4f179ca3e9f4de028fb1ddb74Jean-Michel Trivi        }
4706d298811ef642bc4f179ca3e9f4de028fb1ddb74Jean-Michel Trivi
4717ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        /**
472a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * Applies all of the metadata changes that have been set since the MediaMetadataEditor
473a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * instance was created with {@link RemoteController#editMetadata()}
474a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi         * or since {@link #clear()} was called.
4757ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi         */
4767ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        public synchronized void apply() {
4777ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            // "applying" a metadata bundle in RemoteController is only for sending edited
4787ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            // key values back to the RemoteControlClient, so here we only care about the only
4797ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            // editable key we support: RATING_KEY_BY_USER
4807ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (!mMetadataChanged) {
4817ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                return;
4827ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
4837ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            final int genId;
4847ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            synchronized(mGenLock) {
4857ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                genId = mClientGenerationIdCurrent;
4867ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
4877ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            synchronized(mInfoLock) {
4887ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                if (mEditorMetadata.containsKey(
4897ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                        String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
4907ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    Rating rating = (Rating) getObject(
4917ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                            MediaMetadataEditor.RATING_KEY_BY_USER, null);
4927ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    mAudioManager.updateRemoteControlClientMetadata(genId,
4937ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                          MediaMetadataEditor.RATING_KEY_BY_USER,
4947ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                          rating);
4957ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                } else {
4967ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    Log.e(TAG, "no metadata to apply");
4977ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                }
4987ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                // NOT setting mApplied to true as this type of MetadataEditor will be applied
4997ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                // multiple times, whenever the user of a RemoteController needs to change the
5007ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                // metadata (e.g. user changes the rating of a song more than once during playback)
5017ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                mApplied = false;
5027ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
5037ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
5047ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
5057ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
5067ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
5077ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
5087ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    //==================================================
5097ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    // Implementation of IRemoteControlDisplay interface
5104348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi    private static class RcDisplay extends IRemoteControlDisplay.Stub {
5114348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi        private final WeakReference<RemoteController> mController;
5124348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi
5134348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi        RcDisplay(RemoteController rc) {
5144348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            mController = new WeakReference<RemoteController>(rc);
5154348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi        }
516a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi
5177ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        public void setCurrentClientId(int genId, PendingIntent clientMediaIntent,
5187ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                boolean clearing) {
5194348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            final RemoteController rc = mController.get();
5204348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            if (rc == null) {
5214348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                return;
5224348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            }
5237ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            boolean isNew = false;
5247ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            synchronized(mGenLock) {
5254348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                if (rc.mClientGenerationIdCurrent != genId) {
5264348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                    rc.mClientGenerationIdCurrent = genId;
5277ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    isNew = true;
5287ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                }
5297ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
5307ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (clientMediaIntent != null) {
5314348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                sendMsg(rc.mEventHandler, MSG_NEW_PENDING_INTENT, SENDMSG_REPLACE,
5327ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                        genId /*arg1*/, 0, clientMediaIntent /*obj*/, 0 /*delay*/);
5337ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
5347ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (isNew || clearing) {
5354348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                sendMsg(rc.mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
5367ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                        genId /*arg1*/, clearing ? 1 : 0, null /*obj*/, 0 /*delay*/);
5377ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
5387ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
5397ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
540f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        public void setEnabled(boolean enabled) {
5414348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            final RemoteController rc = mController.get();
5424348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            if (rc == null) {
5434348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                return;
5444348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            }
5454348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            sendMsg(rc.mEventHandler, MSG_DISPLAY_ENABLE, SENDMSG_REPLACE,
546f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi                    enabled ? 1 : 0 /*arg1*/, 0, null /*obj*/, 0 /*delay*/);
547f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        }
548f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi
5497ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        public void setPlaybackState(int genId, int state,
5507ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                long stateChangeTimeMs, long currentPosMs, float speed) {
5514348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            final RemoteController rc = mController.get();
5524348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            if (rc == null) {
5534348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                return;
5544348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            }
5557ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (DEBUG) {
5567ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                Log.d(TAG, "> new playback state: genId="+genId
5577ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                        + " state="+ state
5587ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                        + " changeTime="+ stateChangeTimeMs
5597ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                        + " pos=" + currentPosMs
5607ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                        + "ms speed=" + speed);
5617ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
5627ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
5637ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            synchronized(mGenLock) {
5644348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                if (rc.mClientGenerationIdCurrent != genId) {
5657ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    return;
5667ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                }
5677ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
5687ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            final PlaybackInfo playbackInfo =
5697ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    new PlaybackInfo(state, stateChangeTimeMs, currentPosMs, speed);
5704348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            sendMsg(rc.mEventHandler, MSG_NEW_PLAYBACK_INFO, SENDMSG_REPLACE,
5717ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    genId /*arg1*/, 0, playbackInfo /*obj*/, 0 /*delay*/);
5727ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
5737ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
5747ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
5757ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        public void setTransportControlInfo(int genId, int transportControlFlags,
5767ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                int posCapabilities) {
5774348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            final RemoteController rc = mController.get();
5784348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            if (rc == null) {
5794348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                return;
5804348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            }
5817ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            synchronized(mGenLock) {
5824348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                if (rc.mClientGenerationIdCurrent != genId) {
5837ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    return;
5847ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                }
5857ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
5864348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            sendMsg(rc.mEventHandler, MSG_NEW_TRANSPORT_INFO, SENDMSG_REPLACE,
5877ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    genId /*arg1*/, transportControlFlags /*arg2*/,
5887ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    null /*obj*/, 0 /*delay*/);
5897ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
5907ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
5917ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        public void setMetadata(int genId, Bundle metadata) {
5924348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            final RemoteController rc = mController.get();
5934348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            if (rc == null) {
5944348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                return;
5954348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            }
5967ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (DEBUG) { Log.e(TAG, "setMetadata("+genId+")"); }
5977ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (metadata == null) {
5987ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                return;
5997ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
6007ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            synchronized(mGenLock) {
6014348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                if (rc.mClientGenerationIdCurrent != genId) {
6027ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    return;
6037ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                }
6047ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
6054348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            sendMsg(rc.mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
6067ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    genId /*arg1*/, 0 /*arg2*/,
6077ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    metadata /*obj*/, 0 /*delay*/);
6087ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
6097ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
6107ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        public void setArtwork(int genId, Bitmap artwork) {
6114348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            final RemoteController rc = mController.get();
6124348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            if (rc == null) {
6134348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                return;
6144348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            }
6157ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (DEBUG) { Log.v(TAG, "setArtwork("+genId+")"); }
6167ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            synchronized(mGenLock) {
6174348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                if (rc.mClientGenerationIdCurrent != genId) {
6187ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    return;
6197ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                }
6207ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
6217ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            Bundle metadata = new Bundle(1);
6227ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            metadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK), artwork);
6234348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            sendMsg(rc.mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
6247ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    genId /*arg1*/, 0 /*arg2*/,
6257ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    metadata /*obj*/, 0 /*delay*/);
6267ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
6277ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
6287ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        public void setAllMetadata(int genId, Bundle metadata, Bitmap artwork) {
6294348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            final RemoteController rc = mController.get();
6304348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            if (rc == null) {
6314348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                return;
6324348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            }
6337ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (DEBUG) { Log.e(TAG, "setAllMetadata("+genId+")"); }
6347ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if ((metadata == null) && (artwork == null)) {
6357ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                return;
6367ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
6377ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            synchronized(mGenLock) {
6384348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi                if (rc.mClientGenerationIdCurrent != genId) {
6397ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    return;
6407ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                }
6417ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
6427ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (metadata == null) {
6437ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                metadata = new Bundle(1);
6447ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
6457ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (artwork != null) {
6467ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                metadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK),
6477ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                        artwork);
6487ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
6494348a73bbe4e66ab691aea5971107046df806511Jean-Michel Trivi            sendMsg(rc.mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
6507ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    genId /*arg1*/, 0 /*arg2*/,
6517ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    metadata /*obj*/, 0 /*delay*/);
6527ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
6537ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
6547ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
6557ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    //==================================================
6567ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    // Event handling
657a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi    private final EventHandler mEventHandler;
6587ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private final static int MSG_NEW_PENDING_INTENT = 0;
6597ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private final static int MSG_NEW_PLAYBACK_INFO =  1;
6607ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private final static int MSG_NEW_TRANSPORT_INFO = 2;
6617ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private final static int MSG_NEW_METADATA       = 3; // msg always has non-null obj parameter
662a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi    private final static int MSG_CLIENT_CHANGE      = 4;
663f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    private final static int MSG_DISPLAY_ENABLE     = 5;
6647ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
6657ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private class EventHandler extends Handler {
6667ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
6677ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        public EventHandler(RemoteController rc, Looper looper) {
6687ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            super(looper);
6697ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
6707ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
6717ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        @Override
6727ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        public void handleMessage(Message msg) {
6737ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            switch(msg.what) {
6747ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                case MSG_NEW_PENDING_INTENT:
6757ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    onNewPendingIntent(msg.arg1, (PendingIntent) msg.obj);
6767ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    break;
6777ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                case MSG_NEW_PLAYBACK_INFO:
6787ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    onNewPlaybackInfo(msg.arg1, (PlaybackInfo) msg.obj);
6797ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    break;
6807ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                case MSG_NEW_TRANSPORT_INFO:
6817ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    onNewTransportInfo(msg.arg1, msg.arg2);
6827ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    break;
6837ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                case MSG_NEW_METADATA:
6847ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    onNewMetadata(msg.arg1, (Bundle)msg.obj);
6857ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    break;
686a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi                case MSG_CLIENT_CHANGE:
687a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi                    onClientChange(msg.arg1, msg.arg2 == 1);
6887ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    break;
689f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi                case MSG_DISPLAY_ENABLE:
690f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi                    onDisplayEnable(msg.arg1 == 1);
691f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi                    break;
6927ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                default:
6937ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    Log.e(TAG, "unknown event " + msg.what);
6947ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
6957ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
6967ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
6977ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
6987ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /** If the msg is already queued, replace it with this one. */
6997ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private static final int SENDMSG_REPLACE = 0;
7007ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /** If the msg is already queued, ignore this one and leave the old. */
7017ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private static final int SENDMSG_NOOP = 1;
7027ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /** If the msg is already queued, queue this one and leave the old. */
7037ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private static final int SENDMSG_QUEUE = 2;
7047ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
7057ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private static void sendMsg(Handler handler, int msg, int existingMsgPolicy,
7067ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            int arg1, int arg2, Object obj, int delayMs) {
7077ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        if (handler == null) {
7087ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            Log.e(TAG, "null event handler, will not deliver message " + msg);
7097ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            return;
7107ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
7117ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        if (existingMsgPolicy == SENDMSG_REPLACE) {
7127ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            handler.removeMessages(msg);
7137ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
7147ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            return;
7157ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
7167ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delayMs);
7177ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
7187ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
7197ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private void onNewPendingIntent(int genId, PendingIntent pi) {
7207ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized(mGenLock) {
7217ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (mClientGenerationIdCurrent != genId) {
7227ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                return;
7237ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
7247ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
7257ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized(mInfoLock) {
7267ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            mClientPendingIntentCurrent = pi;
7277ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
7287ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
7297ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
7307ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private void onNewPlaybackInfo(int genId, PlaybackInfo pi) {
7317ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized(mGenLock) {
7327ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (mClientGenerationIdCurrent != genId) {
7337ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                return;
7347ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
7357ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
7367ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        final OnClientUpdateListener l;
7377ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized(mInfoLock) {
7387ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            l = this.mOnClientUpdateListener;
7397ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            mLastPlaybackInfo = pi;
7407ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
7417ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        if (l != null) {
7427ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (pi.mCurrentPosMs == RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
7437ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                l.onClientPlaybackStateUpdate(pi.mState);
7447ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            } else {
7457ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                l.onClientPlaybackStateUpdate(pi.mState, pi.mStateChangeTimeMs, pi.mCurrentPosMs,
7467ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                        pi.mSpeed);
7477ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
7487ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
7497ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
7507ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
7517ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private void onNewTransportInfo(int genId, int transportControlFlags) {
7527ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized(mGenLock) {
7537ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (mClientGenerationIdCurrent != genId) {
7547ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                return;
7557ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
7567ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
7577ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        final OnClientUpdateListener l;
7587ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized(mInfoLock) {
7597ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            l = mOnClientUpdateListener;
7607ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
7617ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        if (l != null) {
7627ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            l.onClientTransportControlUpdate(transportControlFlags);
7637ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
7647ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
7657ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
7667ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
7677ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @param genId
7687ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @param metadata guaranteed to be always non-null
7697ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
7707ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private void onNewMetadata(int genId, Bundle metadata) {
7717ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized(mGenLock) {
7727ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (mClientGenerationIdCurrent != genId) {
7737ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                return;
7747ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
7757ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
7767ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        final OnClientUpdateListener l;
7777ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        final MetadataEditor metadataEditor;
7787ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        // prepare the received Bundle to be used inside a MetadataEditor
7797ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        final long editableKeys = metadata.getLong(
7807ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                String.valueOf(MediaMetadataEditor.KEY_EDITABLE_MASK), 0);
7817ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        if (editableKeys != 0) {
7827ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            metadata.remove(String.valueOf(MediaMetadataEditor.KEY_EDITABLE_MASK));
7837ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
7847ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized(mInfoLock) {
7857ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            l = mOnClientUpdateListener;
7867ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if ((mMetadataEditor != null) && (mMetadataEditor.mEditorMetadata != null)) {
7877ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                if (mMetadataEditor.mEditorMetadata != metadata) {
7887ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    // existing metadata, merge existing and new
7897ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                    mMetadataEditor.mEditorMetadata.putAll(metadata);
7907ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                }
791f8895248e2ac4dbb46622f3e04c7256f03175b4fAdam Powell
7926d298811ef642bc4f179ca3e9f4de028fb1ddb74Jean-Michel Trivi                mMetadataEditor.putBitmap(MediaMetadataEditor.BITMAP_KEY_ARTWORK,
7936d298811ef642bc4f179ca3e9f4de028fb1ddb74Jean-Michel Trivi                        (Bitmap)metadata.getParcelable(
7946d298811ef642bc4f179ca3e9f4de028fb1ddb74Jean-Michel Trivi                                String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK)));
7956d298811ef642bc4f179ca3e9f4de028fb1ddb74Jean-Michel Trivi                mMetadataEditor.cleanupBitmapFromBundle(MediaMetadataEditor.BITMAP_KEY_ARTWORK);
7967ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            } else {
7977ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                mMetadataEditor = new MetadataEditor(metadata, editableKeys);
7987ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
7997ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            metadataEditor = mMetadataEditor;
8007ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
8017ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        if (l != null) {
8027ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            l.onClientMetadataUpdate(metadataEditor);
8037ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
8047ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
8057ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
806a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi    private void onClientChange(int genId, boolean clearing) {
8077ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized(mGenLock) {
8087ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            if (mClientGenerationIdCurrent != genId) {
8097ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi                return;
8107ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            }
8117ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
8127ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        final OnClientUpdateListener l;
8137ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized(mInfoLock) {
8147ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            l = mOnClientUpdateListener;
8153b435fb2f0c868c468f3fb2cb2e3ea8314a1755aAdam Powell            mMetadataEditor = null;
8167ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
8177ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        if (l != null) {
818a83487e8c618f3c267c3fe3a72d4eb9f1388d07eJean-Michel Trivi            l.onClientChange(clearing);
8197ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
8207ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
8217ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
822f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    private void onDisplayEnable(boolean enabled) {
82319566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi        final OnClientUpdateListener l;
824f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        synchronized(mInfoLock) {
825f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi            mEnabled = enabled;
82619566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            l = this.mOnClientUpdateListener;
82719566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi        }
82819566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi        if (!enabled) {
82919566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            // when disabling, reset all info sent to the user
83019566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            final int genId;
83119566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            synchronized (mGenLock) {
83219566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi                genId = mClientGenerationIdCurrent;
83319566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            }
83419566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            // send "stopped" state, happened "now", playback position is 0, speed 0.0f
83519566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            final PlaybackInfo pi = new PlaybackInfo(RemoteControlClient.PLAYSTATE_STOPPED,
83619566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi                    SystemClock.elapsedRealtime() /*stateChangeTimeMs*/,
83719566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi                    0 /*currentPosMs*/, 0.0f /*speed*/);
83819566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            sendMsg(mEventHandler, MSG_NEW_PLAYBACK_INFO, SENDMSG_REPLACE,
83919566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi                    genId /*arg1*/, 0 /*arg2, ignored*/, pi /*obj*/, 0 /*delay*/);
84019566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            // send "blank" transport control info: no controls are supported
84119566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            sendMsg(mEventHandler, MSG_NEW_TRANSPORT_INFO, SENDMSG_REPLACE,
84219566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi                    genId /*arg1*/, 0 /*arg2, no flags*/,
84319566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi                    null /*obj, ignored*/, 0 /*delay*/);
84419566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            // send dummy metadata with empty string for title and artist, duration of 0
84519566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            Bundle metadata = new Bundle(3);
84619566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            metadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_TITLE), "");
84719566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            metadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ARTIST), "");
84819566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            metadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_DURATION), 0);
84919566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi            sendMsg(mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
85019566543c4833f50e7c22aff0c707388f8a338d2Jean-Michel Trivi                    genId /*arg1*/, 0 /*arg2, ignored*/, metadata /*obj*/, 0 /*delay*/);
851f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        }
852f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    }
8537ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
8547ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    //==================================================
8557ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    private static class PlaybackInfo {
8567ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        int mState;
8577ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        long mStateChangeTimeMs;
8587ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        long mCurrentPosMs;
8597ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        float mSpeed;
8607ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
8617ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        PlaybackInfo(int state, long stateChangeTimeMs, long currentPosMs, float speed) {
8627ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            mState = state;
8637ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            mStateChangeTimeMs = stateChangeTimeMs;
8647ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            mCurrentPosMs = currentPosMs;
8657ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            mSpeed = speed;
8667ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
8677ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
8687ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
8697ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
8707ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @hide
8717ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * Used by AudioManager to mark this instance as registered.
8727ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @param registered
8737ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
874f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    void setIsRegistered(boolean registered) {
8757ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        synchronized (mInfoLock) {
8767ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi            mIsRegistered = registered;
8777ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        }
8787ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
8797ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi
8807ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    /**
8817ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @hide
8827ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * Used by AudioManager to access binder to be registered/unregistered inside MediaFocusControl
8837ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     * @return
8847ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi     */
885f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    RcDisplay getRcDisplay() {
8867ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi        return mRcd;
8877ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi    }
8888ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi
8898ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi    /**
8908ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi     * @hide
8918ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi     * Used by AudioManager to read the current artwork dimension
8928ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi     * @return array containing width (index 0) and height (index 1) of currently set artwork size
8938ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi     */
894f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    int[] getArtworkSize() {
8958ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi        synchronized (mInfoLock) {
8968ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi            int[] size = { mArtworkWidth, mArtworkHeight };
8978ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi            return size;
8988ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi        }
8998ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi    }
9008ab8c2d06185dfefb5c69e614271f684e77eef63Jean-Michel Trivi
901f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    /**
902f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @hide
903f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * Used by AudioManager to access user listener receiving the client update notifications
904f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     * @return
905f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi     */
906f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    OnClientUpdateListener getUpdateListener() {
907f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi        return mOnClientUpdateListener;
908f108cdd9ee5efe354d87edd02a07b323298c116cJean-Michel Trivi    }
9097ddd226e7c6e759feaf2747a90be1cc06acf37a3Jean-Michel Trivi}
910