MediaController.java revision 2f5b057da7d05d5d699a272aa24fd7c97cdda820
101fe661ae5da3739215d93922412df4b24c859a2RoboErik/* 201fe661ae5da3739215d93922412df4b24c859a2RoboErik * Copyright (C) 2014 The Android Open Source Project 301fe661ae5da3739215d93922412df4b24c859a2RoboErik * 401fe661ae5da3739215d93922412df4b24c859a2RoboErik * Licensed under the Apache License, Version 2.0 (the "License"); 501fe661ae5da3739215d93922412df4b24c859a2RoboErik * you may not use this file except in compliance with the License. 601fe661ae5da3739215d93922412df4b24c859a2RoboErik * You may obtain a copy of the License at 701fe661ae5da3739215d93922412df4b24c859a2RoboErik * 801fe661ae5da3739215d93922412df4b24c859a2RoboErik * http://www.apache.org/licenses/LICENSE-2.0 901fe661ae5da3739215d93922412df4b24c859a2RoboErik * 1001fe661ae5da3739215d93922412df4b24c859a2RoboErik * Unless required by applicable law or agreed to in writing, software 1101fe661ae5da3739215d93922412df4b24c859a2RoboErik * distributed under the License is distributed on an "AS IS" BASIS, 1201fe661ae5da3739215d93922412df4b24c859a2RoboErik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1301fe661ae5da3739215d93922412df4b24c859a2RoboErik * See the License for the specific language governing permissions and 1401fe661ae5da3739215d93922412df4b24c859a2RoboErik * limitations under the License. 1501fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 1601fe661ae5da3739215d93922412df4b24c859a2RoboErik 172f5b057da7d05d5d699a272aa24fd7c97cdda820RoboErikpackage android.media.session; 1801fe661ae5da3739215d93922412df4b24c859a2RoboErik 1901fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.content.Intent; 202f5b057da7d05d5d699a272aa24fd7c97cdda820RoboErikimport android.media.session.IMediaController; 212f5b057da7d05d5d699a272aa24fd7c97cdda820RoboErikimport android.media.session.IMediaControllerCallback; 222f5b057da7d05d5d699a272aa24fd7c97cdda820RoboErikimport android.media.MediaMetadataRetriever; 232f5b057da7d05d5d699a272aa24fd7c97cdda820RoboErikimport android.media.RemoteControlClient; 2401fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Bundle; 2501fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Handler; 2601fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Looper; 2701fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Message; 2801fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.RemoteException; 2901fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.text.TextUtils; 3001fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.util.Log; 3101fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.view.KeyEvent; 3201fe661ae5da3739215d93922412df4b24c859a2RoboErik 3301fe661ae5da3739215d93922412df4b24c859a2RoboErikimport java.util.ArrayList; 3401fe661ae5da3739215d93922412df4b24c859a2RoboErik 3501fe661ae5da3739215d93922412df4b24c859a2RoboErik/** 3601fe661ae5da3739215d93922412df4b24c859a2RoboErik * Allows an app to interact with an ongoing media session. Media buttons and 3701fe661ae5da3739215d93922412df4b24c859a2RoboErik * other commands can be sent to the session. A callback may be registered to 3801fe661ae5da3739215d93922412df4b24c859a2RoboErik * receive updates from the session, such as metadata and play state changes. 3901fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p> 4001fe661ae5da3739215d93922412df4b24c859a2RoboErik * A MediaController can be created through {@link MediaSessionManager} if you 4101fe661ae5da3739215d93922412df4b24c859a2RoboErik * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or directly if 4201fe661ae5da3739215d93922412df4b24c859a2RoboErik * you have a {@link MediaSessionToken} from the session owner. 4301fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p> 4401fe661ae5da3739215d93922412df4b24c859a2RoboErik * MediaController objects are thread-safe. 4501fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 4601fe661ae5da3739215d93922412df4b24c859a2RoboErikpublic final class MediaController { 4701fe661ae5da3739215d93922412df4b24c859a2RoboErik private static final String TAG = "MediaController"; 4801fe661ae5da3739215d93922412df4b24c859a2RoboErik 4901fe661ae5da3739215d93922412df4b24c859a2RoboErik private static final int MESSAGE_EVENT = 1; 5001fe661ae5da3739215d93922412df4b24c859a2RoboErik private static final int MESSAGE_PLAYBACK_STATE = 2; 5101fe661ae5da3739215d93922412df4b24c859a2RoboErik private static final int MESSAGE_METADATA = 3; 5201fe661ae5da3739215d93922412df4b24c859a2RoboErik private static final int MESSAGE_ROUTE = 4; 5301fe661ae5da3739215d93922412df4b24c859a2RoboErik 5401fe661ae5da3739215d93922412df4b24c859a2RoboErik private static final String KEY_EVENT = "event"; 5501fe661ae5da3739215d93922412df4b24c859a2RoboErik private static final String KEY_EXTRAS = "extras"; 5601fe661ae5da3739215d93922412df4b24c859a2RoboErik 5701fe661ae5da3739215d93922412df4b24c859a2RoboErik private final IMediaController mSessionBinder; 5801fe661ae5da3739215d93922412df4b24c859a2RoboErik 5901fe661ae5da3739215d93922412df4b24c859a2RoboErik private final CallbackStub mCbStub = new CallbackStub(); 6001fe661ae5da3739215d93922412df4b24c859a2RoboErik private final ArrayList<Callback> mCbs = new ArrayList<Callback>(); 6101fe661ae5da3739215d93922412df4b24c859a2RoboErik private final Object mLock = new Object(); 6201fe661ae5da3739215d93922412df4b24c859a2RoboErik 6301fe661ae5da3739215d93922412df4b24c859a2RoboErik private boolean mCbRegistered = false; 6401fe661ae5da3739215d93922412df4b24c859a2RoboErik 6501fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 6601fe661ae5da3739215d93922412df4b24c859a2RoboErik * If you have a {@link MediaSessionToken} from the owner of the session a 6701fe661ae5da3739215d93922412df4b24c859a2RoboErik * controller can be created directly. It is up to the session creator to 6801fe661ae5da3739215d93922412df4b24c859a2RoboErik * handle token distribution if desired. 6901fe661ae5da3739215d93922412df4b24c859a2RoboErik * 7001fe661ae5da3739215d93922412df4b24c859a2RoboErik * @see MediaSession#getSessionToken() 7101fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param token A token from the creator of the session 7201fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 7301fe661ae5da3739215d93922412df4b24c859a2RoboErik public MediaController(MediaSessionToken token) { 7401fe661ae5da3739215d93922412df4b24c859a2RoboErik mSessionBinder = token.getBinder(); 7501fe661ae5da3739215d93922412df4b24c859a2RoboErik } 7601fe661ae5da3739215d93922412df4b24c859a2RoboErik 7701fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 7801fe661ae5da3739215d93922412df4b24c859a2RoboErik * @hide 7901fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 8001fe661ae5da3739215d93922412df4b24c859a2RoboErik public MediaController(IMediaController sessionBinder) { 8101fe661ae5da3739215d93922412df4b24c859a2RoboErik mSessionBinder = sessionBinder; 8201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 8301fe661ae5da3739215d93922412df4b24c859a2RoboErik 8401fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 8501fe661ae5da3739215d93922412df4b24c859a2RoboErik * Sends a generic command to the session. It is up to the session creator 8601fe661ae5da3739215d93922412df4b24c859a2RoboErik * to decide what commands and parameters they will support. As such, 8701fe661ae5da3739215d93922412df4b24c859a2RoboErik * commands should only be sent to sessions that the controller owns. 8801fe661ae5da3739215d93922412df4b24c859a2RoboErik * 8901fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param command The command to send 9001fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param params Any parameters to include with the command 9101fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 9201fe661ae5da3739215d93922412df4b24c859a2RoboErik public void sendCommand(String command, Bundle params) { 9301fe661ae5da3739215d93922412df4b24c859a2RoboErik if (TextUtils.isEmpty(command)) { 9401fe661ae5da3739215d93922412df4b24c859a2RoboErik throw new IllegalArgumentException("command cannot be null or empty"); 9501fe661ae5da3739215d93922412df4b24c859a2RoboErik } 9601fe661ae5da3739215d93922412df4b24c859a2RoboErik try { 9701fe661ae5da3739215d93922412df4b24c859a2RoboErik mSessionBinder.sendCommand(command, params); 9801fe661ae5da3739215d93922412df4b24c859a2RoboErik } catch (RemoteException e) { 9901fe661ae5da3739215d93922412df4b24c859a2RoboErik Log.d(TAG, "Dead object in sendCommand.", e); 10001fe661ae5da3739215d93922412df4b24c859a2RoboErik } 10101fe661ae5da3739215d93922412df4b24c859a2RoboErik } 10201fe661ae5da3739215d93922412df4b24c859a2RoboErik 10301fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 10401fe661ae5da3739215d93922412df4b24c859a2RoboErik * Send the specified media button to the session. Only media keys can be 10501fe661ae5da3739215d93922412df4b24c859a2RoboErik * sent using this method. 10601fe661ae5da3739215d93922412df4b24c859a2RoboErik * 10701fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param keycode The media button keycode, such as 1089524ed59a44b6850f3f708e50ef0708d2a462316RoboErik * {@link KeyEvent#KEYCODE_MEDIA_PLAY}. 10901fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 11001fe661ae5da3739215d93922412df4b24c859a2RoboErik public void sendMediaButton(int keycode) { 11101fe661ae5da3739215d93922412df4b24c859a2RoboErik if (!KeyEvent.isMediaKey(keycode)) { 11201fe661ae5da3739215d93922412df4b24c859a2RoboErik throw new IllegalArgumentException("May only send media buttons through " 11301fe661ae5da3739215d93922412df4b24c859a2RoboErik + "sendMediaButton"); 11401fe661ae5da3739215d93922412df4b24c859a2RoboErik } 11501fe661ae5da3739215d93922412df4b24c859a2RoboErik // TODO do something better than key down/up events 11601fe661ae5da3739215d93922412df4b24c859a2RoboErik KeyEvent event = new KeyEvent(KeyEvent.ACTION_UP, keycode); 11701fe661ae5da3739215d93922412df4b24c859a2RoboErik try { 11801fe661ae5da3739215d93922412df4b24c859a2RoboErik mSessionBinder.sendMediaButton(event); 11901fe661ae5da3739215d93922412df4b24c859a2RoboErik } catch (RemoteException e) { 12001fe661ae5da3739215d93922412df4b24c859a2RoboErik Log.d(TAG, "Dead object in sendMediaButton", e); 12101fe661ae5da3739215d93922412df4b24c859a2RoboErik } 12201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 12301fe661ae5da3739215d93922412df4b24c859a2RoboErik 12401fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 12501fe661ae5da3739215d93922412df4b24c859a2RoboErik * Adds a callback to receive updates from the Session. Updates will be 12601fe661ae5da3739215d93922412df4b24c859a2RoboErik * posted on the caller's thread. 12701fe661ae5da3739215d93922412df4b24c859a2RoboErik * 12801fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param cb The callback object, must not be null 12901fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 13001fe661ae5da3739215d93922412df4b24c859a2RoboErik public void addCallback(Callback cb) { 13101fe661ae5da3739215d93922412df4b24c859a2RoboErik addCallback(cb, null); 13201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 13301fe661ae5da3739215d93922412df4b24c859a2RoboErik 13401fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 13501fe661ae5da3739215d93922412df4b24c859a2RoboErik * Adds a callback to receive updates from the session. Updates will be 13601fe661ae5da3739215d93922412df4b24c859a2RoboErik * posted on the specified handler. 13701fe661ae5da3739215d93922412df4b24c859a2RoboErik * 13801fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param cb Cannot be null. 13901fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param handler The handler to post updates on, if null the callers thread 14001fe661ae5da3739215d93922412df4b24c859a2RoboErik * will be used 14101fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 14201fe661ae5da3739215d93922412df4b24c859a2RoboErik public void addCallback(Callback cb, Handler handler) { 14301fe661ae5da3739215d93922412df4b24c859a2RoboErik if (handler == null) { 14401fe661ae5da3739215d93922412df4b24c859a2RoboErik handler = new Handler(); 14501fe661ae5da3739215d93922412df4b24c859a2RoboErik } 14601fe661ae5da3739215d93922412df4b24c859a2RoboErik synchronized (mLock) { 14701fe661ae5da3739215d93922412df4b24c859a2RoboErik addCallbackLocked(cb, handler); 14801fe661ae5da3739215d93922412df4b24c859a2RoboErik } 14901fe661ae5da3739215d93922412df4b24c859a2RoboErik } 15001fe661ae5da3739215d93922412df4b24c859a2RoboErik 15101fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 15201fe661ae5da3739215d93922412df4b24c859a2RoboErik * Stop receiving updates on the specified callback. If an update has 15301fe661ae5da3739215d93922412df4b24c859a2RoboErik * already been posted you may still receive it after calling this method. 15401fe661ae5da3739215d93922412df4b24c859a2RoboErik * 15501fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param cb The callback to remove 15601fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 15701fe661ae5da3739215d93922412df4b24c859a2RoboErik public void removeCallback(Callback cb) { 15801fe661ae5da3739215d93922412df4b24c859a2RoboErik synchronized (mLock) { 15901fe661ae5da3739215d93922412df4b24c859a2RoboErik removeCallbackLocked(cb); 16001fe661ae5da3739215d93922412df4b24c859a2RoboErik } 16101fe661ae5da3739215d93922412df4b24c859a2RoboErik } 16201fe661ae5da3739215d93922412df4b24c859a2RoboErik 16301fe661ae5da3739215d93922412df4b24c859a2RoboErik /* 16401fe661ae5da3739215d93922412df4b24c859a2RoboErik * @hide 16501fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 16601fe661ae5da3739215d93922412df4b24c859a2RoboErik IMediaController getSessionBinder() { 16701fe661ae5da3739215d93922412df4b24c859a2RoboErik return mSessionBinder; 16801fe661ae5da3739215d93922412df4b24c859a2RoboErik } 16901fe661ae5da3739215d93922412df4b24c859a2RoboErik 17001fe661ae5da3739215d93922412df4b24c859a2RoboErik private void addCallbackLocked(Callback cb, Handler handler) { 17101fe661ae5da3739215d93922412df4b24c859a2RoboErik if (cb == null) { 17201fe661ae5da3739215d93922412df4b24c859a2RoboErik throw new IllegalArgumentException("Callback cannot be null"); 17301fe661ae5da3739215d93922412df4b24c859a2RoboErik } 17401fe661ae5da3739215d93922412df4b24c859a2RoboErik if (handler == null) { 17501fe661ae5da3739215d93922412df4b24c859a2RoboErik throw new IllegalArgumentException("Handler cannot be null"); 17601fe661ae5da3739215d93922412df4b24c859a2RoboErik } 17701fe661ae5da3739215d93922412df4b24c859a2RoboErik if (mCbs.contains(cb)) { 17801fe661ae5da3739215d93922412df4b24c859a2RoboErik Log.w(TAG, "Callback is already added, ignoring"); 17901fe661ae5da3739215d93922412df4b24c859a2RoboErik return; 18001fe661ae5da3739215d93922412df4b24c859a2RoboErik } 18101fe661ae5da3739215d93922412df4b24c859a2RoboErik cb.setHandler(handler); 18201fe661ae5da3739215d93922412df4b24c859a2RoboErik mCbs.add(cb); 18301fe661ae5da3739215d93922412df4b24c859a2RoboErik 18401fe661ae5da3739215d93922412df4b24c859a2RoboErik // Only register one cb binder, track callbacks internally and notify 18501fe661ae5da3739215d93922412df4b24c859a2RoboErik if (!mCbRegistered) { 18601fe661ae5da3739215d93922412df4b24c859a2RoboErik try { 18701fe661ae5da3739215d93922412df4b24c859a2RoboErik mSessionBinder.registerCallbackListener(mCbStub); 18801fe661ae5da3739215d93922412df4b24c859a2RoboErik mCbRegistered = true; 18901fe661ae5da3739215d93922412df4b24c859a2RoboErik } catch (RemoteException e) { 19001fe661ae5da3739215d93922412df4b24c859a2RoboErik Log.d(TAG, "Dead object in registerCallback", e); 19101fe661ae5da3739215d93922412df4b24c859a2RoboErik } 19201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 19301fe661ae5da3739215d93922412df4b24c859a2RoboErik } 19401fe661ae5da3739215d93922412df4b24c859a2RoboErik 19501fe661ae5da3739215d93922412df4b24c859a2RoboErik private void removeCallbackLocked(Callback cb) { 19601fe661ae5da3739215d93922412df4b24c859a2RoboErik if (cb == null) { 19701fe661ae5da3739215d93922412df4b24c859a2RoboErik throw new IllegalArgumentException("Callback cannot be null"); 19801fe661ae5da3739215d93922412df4b24c859a2RoboErik } 19901fe661ae5da3739215d93922412df4b24c859a2RoboErik mCbs.remove(cb); 20001fe661ae5da3739215d93922412df4b24c859a2RoboErik 20101fe661ae5da3739215d93922412df4b24c859a2RoboErik if (mCbs.size() == 0 && mCbRegistered) { 20201fe661ae5da3739215d93922412df4b24c859a2RoboErik try { 20301fe661ae5da3739215d93922412df4b24c859a2RoboErik mSessionBinder.unregisterCallbackListener(mCbStub); 20401fe661ae5da3739215d93922412df4b24c859a2RoboErik } catch (RemoteException e) { 20501fe661ae5da3739215d93922412df4b24c859a2RoboErik Log.d(TAG, "Dead object in unregisterCallback", e); 20601fe661ae5da3739215d93922412df4b24c859a2RoboErik } 20701fe661ae5da3739215d93922412df4b24c859a2RoboErik mCbRegistered = false; 20801fe661ae5da3739215d93922412df4b24c859a2RoboErik } 20901fe661ae5da3739215d93922412df4b24c859a2RoboErik } 21001fe661ae5da3739215d93922412df4b24c859a2RoboErik 21101fe661ae5da3739215d93922412df4b24c859a2RoboErik private void pushOnEventLocked(String event, Bundle extras) { 21201fe661ae5da3739215d93922412df4b24c859a2RoboErik for (int i = mCbs.size() - 1; i >= 0; i--) { 21301fe661ae5da3739215d93922412df4b24c859a2RoboErik mCbs.get(i).postEvent(event, extras); 21401fe661ae5da3739215d93922412df4b24c859a2RoboErik } 21501fe661ae5da3739215d93922412df4b24c859a2RoboErik } 21601fe661ae5da3739215d93922412df4b24c859a2RoboErik 21701fe661ae5da3739215d93922412df4b24c859a2RoboErik private void pushOnMetadataUpdateLocked(Bundle metadata) { 21801fe661ae5da3739215d93922412df4b24c859a2RoboErik for (int i = mCbs.size() - 1; i >= 0; i--) { 21901fe661ae5da3739215d93922412df4b24c859a2RoboErik mCbs.get(i).postMetadataUpdate(metadata); 22001fe661ae5da3739215d93922412df4b24c859a2RoboErik } 22101fe661ae5da3739215d93922412df4b24c859a2RoboErik } 22201fe661ae5da3739215d93922412df4b24c859a2RoboErik 22301fe661ae5da3739215d93922412df4b24c859a2RoboErik private void pushOnPlaybackUpdateLocked(int newState) { 22401fe661ae5da3739215d93922412df4b24c859a2RoboErik for (int i = mCbs.size() - 1; i >= 0; i--) { 22501fe661ae5da3739215d93922412df4b24c859a2RoboErik mCbs.get(i).postPlaybackStateChange(newState); 22601fe661ae5da3739215d93922412df4b24c859a2RoboErik } 22701fe661ae5da3739215d93922412df4b24c859a2RoboErik } 22801fe661ae5da3739215d93922412df4b24c859a2RoboErik 22901fe661ae5da3739215d93922412df4b24c859a2RoboErik private void pushOnRouteChangedLocked(Bundle routeDescriptor) { 23001fe661ae5da3739215d93922412df4b24c859a2RoboErik for (int i = mCbs.size() - 1; i >= 0; i--) { 23101fe661ae5da3739215d93922412df4b24c859a2RoboErik mCbs.get(i).postRouteChanged(routeDescriptor); 23201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 23301fe661ae5da3739215d93922412df4b24c859a2RoboErik } 23401fe661ae5da3739215d93922412df4b24c859a2RoboErik 23501fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 23601fe661ae5da3739215d93922412df4b24c859a2RoboErik * MediaSession callbacks will be posted on the thread that created the 23701fe661ae5da3739215d93922412df4b24c859a2RoboErik * Callback object. 23801fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 23901fe661ae5da3739215d93922412df4b24c859a2RoboErik public static abstract class Callback { 24001fe661ae5da3739215d93922412df4b24c859a2RoboErik private Handler mHandler; 24101fe661ae5da3739215d93922412df4b24c859a2RoboErik 24201fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 24301fe661ae5da3739215d93922412df4b24c859a2RoboErik * Override to handle custom events sent by the session owner. 24401fe661ae5da3739215d93922412df4b24c859a2RoboErik * Controllers should only handle these for sessions they own. 24501fe661ae5da3739215d93922412df4b24c859a2RoboErik * 24601fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param event 24701fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 24801fe661ae5da3739215d93922412df4b24c859a2RoboErik public void onEvent(String event, Bundle extras) { 24901fe661ae5da3739215d93922412df4b24c859a2RoboErik } 25001fe661ae5da3739215d93922412df4b24c859a2RoboErik 25101fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 25201fe661ae5da3739215d93922412df4b24c859a2RoboErik * Override to handle updates to the playback state. Valid values are in 25301fe661ae5da3739215d93922412df4b24c859a2RoboErik * {@link RemoteControlClient}. TODO put playstate values somewhere more 25401fe661ae5da3739215d93922412df4b24c859a2RoboErik * generic. 25501fe661ae5da3739215d93922412df4b24c859a2RoboErik * 25601fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param state 25701fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 25801fe661ae5da3739215d93922412df4b24c859a2RoboErik public void onPlaybackStateChange(int state) { 25901fe661ae5da3739215d93922412df4b24c859a2RoboErik } 26001fe661ae5da3739215d93922412df4b24c859a2RoboErik 26101fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 26201fe661ae5da3739215d93922412df4b24c859a2RoboErik * Override to handle metadata changes for this session's media. The 26301fe661ae5da3739215d93922412df4b24c859a2RoboErik * default supported fields are those in {@link MediaMetadataRetriever}. 26401fe661ae5da3739215d93922412df4b24c859a2RoboErik * 26501fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param metadata 26601fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 26701fe661ae5da3739215d93922412df4b24c859a2RoboErik public void onMetadataUpdate(Bundle metadata) { 26801fe661ae5da3739215d93922412df4b24c859a2RoboErik } 26901fe661ae5da3739215d93922412df4b24c859a2RoboErik 27001fe661ae5da3739215d93922412df4b24c859a2RoboErik /** 27101fe661ae5da3739215d93922412df4b24c859a2RoboErik * Override to handle route changes for this session. 27201fe661ae5da3739215d93922412df4b24c859a2RoboErik * 27301fe661ae5da3739215d93922412df4b24c859a2RoboErik * @param route 27401fe661ae5da3739215d93922412df4b24c859a2RoboErik */ 27501fe661ae5da3739215d93922412df4b24c859a2RoboErik public void onRouteChanged(Bundle route) { 27601fe661ae5da3739215d93922412df4b24c859a2RoboErik } 27701fe661ae5da3739215d93922412df4b24c859a2RoboErik 27801fe661ae5da3739215d93922412df4b24c859a2RoboErik private void setHandler(Handler handler) { 27901fe661ae5da3739215d93922412df4b24c859a2RoboErik mHandler = new MessageHandler(handler.getLooper(), this); 28001fe661ae5da3739215d93922412df4b24c859a2RoboErik } 28101fe661ae5da3739215d93922412df4b24c859a2RoboErik 28201fe661ae5da3739215d93922412df4b24c859a2RoboErik private void postEvent(String event, Bundle extras) { 28301fe661ae5da3739215d93922412df4b24c859a2RoboErik Bundle eventBundle = new Bundle(); 28401fe661ae5da3739215d93922412df4b24c859a2RoboErik eventBundle.putString(KEY_EVENT, event); 28501fe661ae5da3739215d93922412df4b24c859a2RoboErik eventBundle.putBundle(KEY_EXTRAS, extras); 28601fe661ae5da3739215d93922412df4b24c859a2RoboErik Message msg = mHandler.obtainMessage(MESSAGE_EVENT, eventBundle); 28701fe661ae5da3739215d93922412df4b24c859a2RoboErik mHandler.sendMessage(msg); 28801fe661ae5da3739215d93922412df4b24c859a2RoboErik } 28901fe661ae5da3739215d93922412df4b24c859a2RoboErik 29001fe661ae5da3739215d93922412df4b24c859a2RoboErik private void postPlaybackStateChange(final int state) { 29101fe661ae5da3739215d93922412df4b24c859a2RoboErik Message msg = mHandler.obtainMessage(MESSAGE_PLAYBACK_STATE, state, 0); 29201fe661ae5da3739215d93922412df4b24c859a2RoboErik mHandler.sendMessage(msg); 29301fe661ae5da3739215d93922412df4b24c859a2RoboErik } 29401fe661ae5da3739215d93922412df4b24c859a2RoboErik 29501fe661ae5da3739215d93922412df4b24c859a2RoboErik private void postMetadataUpdate(final Bundle metadata) { 29601fe661ae5da3739215d93922412df4b24c859a2RoboErik Message msg = mHandler.obtainMessage(MESSAGE_METADATA, metadata); 29701fe661ae5da3739215d93922412df4b24c859a2RoboErik mHandler.sendMessage(msg); 29801fe661ae5da3739215d93922412df4b24c859a2RoboErik } 29901fe661ae5da3739215d93922412df4b24c859a2RoboErik 30001fe661ae5da3739215d93922412df4b24c859a2RoboErik private void postRouteChanged(final Bundle descriptor) { 30101fe661ae5da3739215d93922412df4b24c859a2RoboErik Message msg = mHandler.obtainMessage(MESSAGE_ROUTE, descriptor); 30201fe661ae5da3739215d93922412df4b24c859a2RoboErik mHandler.sendMessage(msg); 30301fe661ae5da3739215d93922412df4b24c859a2RoboErik } 30401fe661ae5da3739215d93922412df4b24c859a2RoboErik } 30501fe661ae5da3739215d93922412df4b24c859a2RoboErik 30601fe661ae5da3739215d93922412df4b24c859a2RoboErik private final class CallbackStub extends IMediaControllerCallback.Stub { 30701fe661ae5da3739215d93922412df4b24c859a2RoboErik 30801fe661ae5da3739215d93922412df4b24c859a2RoboErik @Override 30901fe661ae5da3739215d93922412df4b24c859a2RoboErik public void onEvent(String event, Bundle extras) throws RemoteException { 31001fe661ae5da3739215d93922412df4b24c859a2RoboErik synchronized (mLock) { 31101fe661ae5da3739215d93922412df4b24c859a2RoboErik pushOnEventLocked(event, extras); 31201fe661ae5da3739215d93922412df4b24c859a2RoboErik } 31301fe661ae5da3739215d93922412df4b24c859a2RoboErik } 31401fe661ae5da3739215d93922412df4b24c859a2RoboErik 31501fe661ae5da3739215d93922412df4b24c859a2RoboErik @Override 31601fe661ae5da3739215d93922412df4b24c859a2RoboErik public void onMetadataUpdate(Bundle metadata) throws RemoteException { 31701fe661ae5da3739215d93922412df4b24c859a2RoboErik synchronized (mLock) { 31801fe661ae5da3739215d93922412df4b24c859a2RoboErik pushOnMetadataUpdateLocked(metadata); 31901fe661ae5da3739215d93922412df4b24c859a2RoboErik } 32001fe661ae5da3739215d93922412df4b24c859a2RoboErik } 32101fe661ae5da3739215d93922412df4b24c859a2RoboErik 32201fe661ae5da3739215d93922412df4b24c859a2RoboErik @Override 32301fe661ae5da3739215d93922412df4b24c859a2RoboErik public void onPlaybackUpdate(final int newState) throws RemoteException { 32401fe661ae5da3739215d93922412df4b24c859a2RoboErik synchronized (mLock) { 32501fe661ae5da3739215d93922412df4b24c859a2RoboErik pushOnPlaybackUpdateLocked(newState); 32601fe661ae5da3739215d93922412df4b24c859a2RoboErik } 32701fe661ae5da3739215d93922412df4b24c859a2RoboErik } 32801fe661ae5da3739215d93922412df4b24c859a2RoboErik 32901fe661ae5da3739215d93922412df4b24c859a2RoboErik @Override 33001fe661ae5da3739215d93922412df4b24c859a2RoboErik public void onRouteChanged(Bundle mediaRouteDescriptor) throws RemoteException { 33101fe661ae5da3739215d93922412df4b24c859a2RoboErik synchronized (mLock) { 33201fe661ae5da3739215d93922412df4b24c859a2RoboErik pushOnRouteChangedLocked(mediaRouteDescriptor); 33301fe661ae5da3739215d93922412df4b24c859a2RoboErik } 33401fe661ae5da3739215d93922412df4b24c859a2RoboErik } 33501fe661ae5da3739215d93922412df4b24c859a2RoboErik 33601fe661ae5da3739215d93922412df4b24c859a2RoboErik } 33701fe661ae5da3739215d93922412df4b24c859a2RoboErik 33801fe661ae5da3739215d93922412df4b24c859a2RoboErik private final static class MessageHandler extends Handler { 33901fe661ae5da3739215d93922412df4b24c859a2RoboErik private final MediaController.Callback mCb; 34001fe661ae5da3739215d93922412df4b24c859a2RoboErik 34101fe661ae5da3739215d93922412df4b24c859a2RoboErik public MessageHandler(Looper looper, MediaController.Callback cb) { 34201fe661ae5da3739215d93922412df4b24c859a2RoboErik super(looper); 34301fe661ae5da3739215d93922412df4b24c859a2RoboErik mCb = cb; 34401fe661ae5da3739215d93922412df4b24c859a2RoboErik } 34501fe661ae5da3739215d93922412df4b24c859a2RoboErik 34601fe661ae5da3739215d93922412df4b24c859a2RoboErik @Override 34701fe661ae5da3739215d93922412df4b24c859a2RoboErik public void handleMessage(Message msg) { 34801fe661ae5da3739215d93922412df4b24c859a2RoboErik switch (msg.what) { 34901fe661ae5da3739215d93922412df4b24c859a2RoboErik case MESSAGE_EVENT: 35001fe661ae5da3739215d93922412df4b24c859a2RoboErik Bundle eventBundle = (Bundle) msg.obj; 35101fe661ae5da3739215d93922412df4b24c859a2RoboErik String event = eventBundle.getString(KEY_EVENT); 35201fe661ae5da3739215d93922412df4b24c859a2RoboErik Bundle extras = eventBundle.getBundle(KEY_EXTRAS); 35301fe661ae5da3739215d93922412df4b24c859a2RoboErik mCb.onEvent(event, extras); 35401fe661ae5da3739215d93922412df4b24c859a2RoboErik break; 35501fe661ae5da3739215d93922412df4b24c859a2RoboErik case MESSAGE_PLAYBACK_STATE: 35601fe661ae5da3739215d93922412df4b24c859a2RoboErik mCb.onPlaybackStateChange(msg.arg1); 35701fe661ae5da3739215d93922412df4b24c859a2RoboErik break; 35801fe661ae5da3739215d93922412df4b24c859a2RoboErik case MESSAGE_METADATA: 35901fe661ae5da3739215d93922412df4b24c859a2RoboErik mCb.onMetadataUpdate((Bundle) msg.obj); 36001fe661ae5da3739215d93922412df4b24c859a2RoboErik break; 36101fe661ae5da3739215d93922412df4b24c859a2RoboErik case MESSAGE_ROUTE: 36201fe661ae5da3739215d93922412df4b24c859a2RoboErik mCb.onRouteChanged((Bundle) msg.obj); 36301fe661ae5da3739215d93922412df4b24c859a2RoboErik } 36401fe661ae5da3739215d93922412df4b24c859a2RoboErik } 36501fe661ae5da3739215d93922412df4b24c859a2RoboErik } 36601fe661ae5da3739215d93922412df4b24c859a2RoboErik 36701fe661ae5da3739215d93922412df4b24c859a2RoboErik} 368