1bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
28ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik/*
38ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * Copyright (C) 2014 The Android Open Source Project
48ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik *
58ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * Licensed under the Apache License, Version 2.0 (the "License");
68ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * you may not use this file except in compliance with the License.
78ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * You may obtain a copy of the License at
88ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik *
98ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik *      http://www.apache.org/licenses/LICENSE-2.0
108ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik *
118ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * Unless required by applicable law or agreed to in writing, software
128ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * distributed under the License is distributed on an "AS IS" BASIS,
138ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
148ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * See the License for the specific language governing permissions and
158ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik * limitations under the License.
168ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik */
17bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErikpackage com.android.onemedia;
18bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
1942ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikimport android.media.MediaMetadata;
2042ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikimport android.media.session.MediaController;
21550116576cce028d3c435f7c3ae9f6e3b92b5cf2RoboErikimport android.media.session.MediaSession;
2242ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikimport android.media.session.MediaSessionManager;
238ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport android.media.session.PlaybackState;
248ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport android.os.Bundle;
25bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErikimport android.os.Handler;
26bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErikimport android.os.IBinder;
27bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErikimport android.os.RemoteException;
28550116576cce028d3c435f7c3ae9f6e3b92b5cf2RoboErikimport android.app.Activity;
29bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErikimport android.content.ComponentName;
30bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErikimport android.content.Context;
31bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErikimport android.content.Intent;
32bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErikimport android.content.ServiceConnection;
3345f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandlerimport android.graphics.Bitmap;
34bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErikimport android.util.Log;
35bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
36bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErikimport com.android.onemedia.playback.RequestUtils;
37bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
38bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErikpublic class PlayerController {
398ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private static final String TAG = "PlayerController";
40bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
41bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    public static final int STATE_DISCONNECTED = 0;
42bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    public static final int STATE_CONNECTED = 1;
43bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
4442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik    protected MediaController mController;
45bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    protected IPlayerService mBinder;
46c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    protected MediaController.TransportControls mTransportControls;
47bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
48bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    private final Intent mServiceIntent;
49550116576cce028d3c435f7c3ae9f6e3b92b5cf2RoboErik    private Activity mContext;
50bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    private Listener mListener;
51bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    private SessionCallback mControllerCb;
5242ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik    private MediaSessionManager mManager;
53bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    private Handler mHandler = new Handler();
54bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
55bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    private boolean mResumed;
5645f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler    private Bitmap mArt;
57bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
58550116576cce028d3c435f7c3ae9f6e3b92b5cf2RoboErik    public PlayerController(Activity context, Intent serviceIntent) {
59bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        mContext = context;
60bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        if (serviceIntent == null) {
61bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            mServiceIntent = new Intent(mContext, PlayerService.class);
62bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        } else {
63bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            mServiceIntent = serviceIntent;
64bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        }
65bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        mControllerCb = new SessionCallback();
6642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        mManager = (MediaSessionManager) context
67bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik                .getSystemService(Context.MEDIA_SESSION_SERVICE);
68bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
69bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        mResumed = false;
70bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    }
71bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
72bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    public void setListener(Listener listener) {
73bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        mListener = listener;
74bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        Log.d(TAG, "Listener set to " + listener + " session is " + mController);
75bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        if (mListener != null) {
76bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            mHandler = new Handler();
778ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mListener.onConnectionStateChange(
78bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik                    mController == null ? STATE_DISCONNECTED : STATE_CONNECTED);
79bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        }
80bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    }
81bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
82bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    public void onResume() {
83bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        mResumed = true;
84bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        Log.d(TAG, "onResume. Binding to service with intent " + mServiceIntent.toString());
85bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        bindToService();
86bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    }
87bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
88bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    public void onPause() {
89bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        mResumed = false;
90bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        Log.d(TAG, "onPause, unbinding from service");
91bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        unbindFromService();
92bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    }
93bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
9445f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler    public void setArt(Bitmap art) {
9545f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler        mArt = art;
9645f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler        if (mBinder != null) {
9745f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler            try {
9845f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler                mBinder.setIcon(art);
9945f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler            } catch (RemoteException e) {
10045f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler            }
10145f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler        }
10245f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler    }
10345f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler
104bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    public void play() {
1058ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (mTransportControls != null) {
1068ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mTransportControls.play();
1078ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
108bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    }
109bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
110bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    public void pause() {
1118ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (mTransportControls != null) {
1128ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mTransportControls.pause();
1138ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
114bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    }
115bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
116bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    public void setContent(String source) {
117bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        RequestUtils.ContentBuilder bob = new RequestUtils.ContentBuilder();
118bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        bob.setSource(source);
119bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        try {
120bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            mBinder.sendRequest(RequestUtils.ACTION_SET_CONTENT, bob.build(), null);
121bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        } catch (RemoteException e) {
122bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            Log.d(TAG, "setContent failed, service may have died.", e);
123bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        }
124bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    }
125bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
126bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    public void setNextContent(String source) {
127bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        RequestUtils.ContentBuilder bob = new RequestUtils.ContentBuilder();
128bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        bob.setSource(source);
129bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        try {
130bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            mBinder.sendRequest(RequestUtils.ACTION_SET_NEXT_CONTENT, bob.build(), null);
131bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        } catch (RemoteException e) {
132bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            Log.d(TAG, "setNexctContent failed, service may have died.", e);
133bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        }
134bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    }
135bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
13607c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    public void showRoutePicker() {
137ff0215dd64e5fd4ff366930747e7943fcc40c4abJeff Brown        // TODO
13807c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    }
13907c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik
14045f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler    public MediaSession.Token getSessionToken() {
14145f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler        if (mBinder != null) {
14245f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler            try {
14345f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler                return mBinder.getSessionToken();
14445f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler            } catch (RemoteException e) {
14545f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler            }
14645f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler        }
14745f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler        return null;
14845f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler    }
14945f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler
150bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    private void unbindFromService() {
151bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        mContext.unbindService(mServiceConnection);
152bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    }
153bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
154bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    private void bindToService() {
155bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        mContext.bindService(mServiceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
156bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    }
157bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
158bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    private ServiceConnection mServiceConnection = new ServiceConnection() {
159bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        @Override
160bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        public void onServiceDisconnected(ComponentName name) {
161bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            if (mController != null) {
16214f717a506a0d22facbec07386b06634e0c6a8eeRoboErik                mController.unregisterCallback(mControllerCb);
163bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            }
164bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            mBinder = null;
165bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            mController = null;
1668ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mTransportControls = null;
167550116576cce028d3c435f7c3ae9f6e3b92b5cf2RoboErik            mContext.setMediaController(null);
168bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            Log.d(TAG, "Disconnected from PlayerService");
169bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
170bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            if (mListener != null) {
1718ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                mListener.onConnectionStateChange(STATE_DISCONNECTED);
172bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            }
173bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        }
174bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
175bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        @Override
176bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        public void onServiceConnected(ComponentName name, IBinder service) {
177bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            mBinder = IPlayerService.Stub.asInterface(service);
178bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            Log.d(TAG, "service is " + service + " binder is " + mBinder);
179550116576cce028d3c435f7c3ae9f6e3b92b5cf2RoboErik            MediaSession.Token token;
180bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            try {
181550116576cce028d3c435f7c3ae9f6e3b92b5cf2RoboErik                token = mBinder.getSessionToken();
182bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            } catch (RemoteException e) {
183bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik                Log.e(TAG, "Error getting session", e);
184bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik                return;
185bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            }
18640495e054591ed4bc110c710c46996ef163664c1RoboErik            mController = new MediaController(mContext, token);
187550116576cce028d3c435f7c3ae9f6e3b92b5cf2RoboErik            mContext.setMediaController(mController);
18814f717a506a0d22facbec07386b06634e0c6a8eeRoboErik            mController.registerCallback(mControllerCb, mHandler);
189c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            mTransportControls = mController.getTransportControls();
19045f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler            if (mArt != null) {
19145f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler                setArt(mArt);
19245f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler            }
193bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            Log.d(TAG, "Ready to use PlayerService");
194bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
195bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            if (mListener != null) {
1968ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                mListener.onConnectionStateChange(STATE_CONNECTED);
1978ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                if (mTransportControls != null) {
198c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mListener.onPlaybackStateChange(mController.getPlaybackState());
1998ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                }
200bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            }
201bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        }
202bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    };
203bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
20442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik    private class SessionCallback extends MediaController.Callback {
205bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        @Override
2068ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onPlaybackStateChanged(PlaybackState state) {
2078ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (state == null) {
2088ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                return;
2098ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
2108ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            Log.d(TAG, "Received playback state change to state " + state.getState());
211bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            if (mListener != null) {
2128ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                mListener.onPlaybackStateChange(state);
213bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik            }
214bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik        }
2158ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
2168ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        @Override
2178ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onMetadataChanged(MediaMetadata metadata) {
2188ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (metadata == null) {
2198ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                return;
2208ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
22175847b98f39e521a57042c50e69be9e142788d32RoboErik            Log.d(TAG, "Received metadata change, " + metadata.getDescription());
22245f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler            if (mListener != null) {
22345f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler                mListener.onMetadataChange(metadata);
22445f7ee8201efbda59b57b1fe637a1b9ffef25bb6Daniel Sandler            }
2258ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
226bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    }
227bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
228bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    public interface Listener {
2298ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onPlaybackStateChange(PlaybackState state);
2308ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onMetadataChange(MediaMetadata metadata);
2318ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onConnectionStateChange(int state);
232bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik    }
233bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik
234bfa153b64b4e8c2faa39a15e87fc9f0300335f20RoboErik}
235