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