diff options
author | RoboErik <epastern@google.com> | 2014-03-20 13:33:52 -0700 |
---|---|---|
committer | RoboErik <epastern@google.com> | 2014-04-17 15:02:33 -0700 |
commit | 07c7077c54717dbbf2c401ea32d00fa6df6d77c6 (patch) | |
tree | 49f9e8019caa93c702c6d2d8213767de3d1499c6 /tests | |
parent | 46175e152891eabd9051523f2f077de5a5562996 (diff) | |
download | frameworks_base-07c7077c54717dbbf2c401ea32d00fa6df6d77c6.zip frameworks_base-07c7077c54717dbbf2c401ea32d00fa6df6d77c6.tar.gz frameworks_base-07c7077c54717dbbf2c401ea32d00fa6df6d77c6.tar.bz2 |
Add RouteProviders to the new Media APIs
Compiles and works with OneMedia. This currently is a rough test of
the system for finding, connecting to, and sending messages to routes.
This will just connect to the first route it finds when a request to
open the route picker is made (and disconnect when another request is
made).
Change-Id: I5de5521a079471b9e02664be4654c0591dfd9a6d
Diffstat (limited to 'tests')
17 files changed, 481 insertions, 60 deletions
diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml index 7d6ba1d..504d471 100644 --- a/tests/OneMedia/AndroidManifest.xml +++ b/tests/OneMedia/AndroidManifest.xml @@ -25,6 +25,15 @@ android:name="com.android.onemedia.OnePlayerService" android:exported="false" android:process="com.android.onemedia.service" /> + <service + android:name=".provider.OneMediaRouteProvider" + android:permission="android.permission.BIND_ROUTE_PROVIDER" + android:exported="true" + android:process="com.android.onemedia.provider"> + <intent-filter> + <action android:name="com.android.media.session.MediaRouteProvider" /> + </intent-filter> + </service> </application> </manifest> diff --git a/tests/OneMedia/res/layout/activity_one_player.xml b/tests/OneMedia/res/layout/activity_one_player.xml index 4208355..516562f 100644 --- a/tests/OneMedia/res/layout/activity_one_player.xml +++ b/tests/OneMedia/res/layout/activity_one_player.xml @@ -53,6 +53,12 @@ android:layout_weight="1" android:text="@string/play_button" /> </LinearLayout> + <Button + android:id="@+id/route_button" + style="@style/BottomBarButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/route_button" /> <TextView android:id="@+id/status" android:layout_width="match_parent" diff --git a/tests/OneMedia/res/values/strings.xml b/tests/OneMedia/res/values/strings.xml index 1b0cebb..3735c8d 100644 --- a/tests/OneMedia/res/values/strings.xml +++ b/tests/OneMedia/res/values/strings.xml @@ -7,6 +7,7 @@ <string name="start_button">Start</string> <string name="play_button">Play</string> + <string name="route_button">Change route</string> <string name="media_content_hint">Content</string> <string name="media_next_hint">Next content</string> <string name="has_video">Is video</string> diff --git a/tests/OneMedia/src/com/android/onemedia/IPlayerCallback.aidl b/tests/OneMedia/src/com/android/onemedia/IPlayerCallback.aidl index 2b14384..189fa6a 100644 --- a/tests/OneMedia/src/com/android/onemedia/IPlayerCallback.aidl +++ b/tests/OneMedia/src/com/android/onemedia/IPlayerCallback.aidl @@ -15,8 +15,8 @@ package com.android.onemedia; -import android.media.session.MediaSessionToken; +import android.media.session.SessionToken; interface IPlayerCallback { - void onSessionChanged(in MediaSessionToken session); + void onSessionChanged(in SessionToken session); }
\ No newline at end of file diff --git a/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl b/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl index efdbe9a..15ea25f 100644 --- a/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl +++ b/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl @@ -15,14 +15,14 @@ package com.android.onemedia; -import android.media.session.MediaSessionToken; +import android.media.session.SessionToken; import android.os.Bundle; import com.android.onemedia.IPlayerCallback; import com.android.onemedia.playback.IRequestCallback; interface IPlayerService { - MediaSessionToken getSessionToken(); + SessionToken getSessionToken(); void registerCallback(in IPlayerCallback cb); void unregisterCallback(in IPlayerCallback cb); void sendRequest(String action, in Bundle params, in IRequestCallback cb); diff --git a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java index 3114ca9..b9a6470 100644 --- a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java +++ b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java @@ -37,6 +37,7 @@ public class OnePlayerActivity extends Activity { private Button mStartButton; private Button mPlayButton; + private Button mRouteButton; private TextView mStatusView; private EditText mContentText; @@ -54,6 +55,7 @@ public class OnePlayerActivity extends Activity { mStartButton = (Button) findViewById(R.id.start_button); mPlayButton = (Button) findViewById(R.id.play_button); + mRouteButton = (Button) findViewById(R.id.route_button); mStatusView = (TextView) findViewById(R.id.status); mContentText = (EditText) findViewById(R.id.content); mNextContentText = (EditText) findViewById(R.id.next_content); @@ -61,6 +63,7 @@ public class OnePlayerActivity extends Activity { mStartButton.setOnClickListener(mButtonListener); mPlayButton.setOnClickListener(mButtonListener); + mRouteButton.setOnClickListener(mButtonListener); } @@ -107,6 +110,9 @@ public class OnePlayerActivity extends Activity { Log.d(TAG, "Start button pressed, in state " + mPlaybackState); mPlayer.setContent(mContentText.getText().toString()); break; + case R.id.route_button: + mPlayer.showRoutePicker(); + break; } } @@ -117,6 +123,7 @@ public class OnePlayerActivity extends Activity { public void onPlaybackStateChange(PlaybackState state) { mPlaybackState = state.getState(); boolean enablePlay = false; + boolean enableControls = true; StringBuilder statusBuilder = new StringBuilder(); switch (mPlaybackState) { case PlaybackState.PLAYSTATE_PLAYING: @@ -143,12 +150,17 @@ public class OnePlayerActivity extends Activity { case PlaybackState.PLAYSTATE_NONE: statusBuilder.append("none"); break; + case PlaybackState.PLAYSTATE_CONNECTING: + statusBuilder.append("connecting"); + enableControls = false; + break; default: statusBuilder.append(mPlaybackState); } statusBuilder.append(" -- At position: ").append(state.getPosition()); mStatusView.setText(statusBuilder.toString()); mPlayButton.setEnabled(enablePlay); + setControlsEnabled(enableControls); } @Override diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerController.java b/tests/OneMedia/src/com/android/onemedia/PlayerController.java index e831ec6..e3f5c0c 100644 --- a/tests/OneMedia/src/com/android/onemedia/PlayerController.java +++ b/tests/OneMedia/src/com/android/onemedia/PlayerController.java @@ -16,9 +16,10 @@ */ package com.android.onemedia; -import android.media.session.MediaController; +import android.media.session.SessionController; import android.media.session.MediaMetadata; -import android.media.session.MediaSessionManager; +import android.media.session.RouteInfo; +import android.media.session.SessionManager; import android.media.session.PlaybackState; import android.media.session.TransportController; import android.os.Bundle; @@ -39,7 +40,7 @@ public class PlayerController { public static final int STATE_DISCONNECTED = 0; public static final int STATE_CONNECTED = 1; - protected MediaController mController; + protected SessionController mController; protected IPlayerService mBinder; protected TransportController mTransportControls; @@ -48,7 +49,7 @@ public class PlayerController { private Listener mListener; private TransportListener mTransportListener = new TransportListener(); private SessionCallback mControllerCb; - private MediaSessionManager mManager; + private SessionManager mManager; private Handler mHandler = new Handler(); private boolean mResumed; @@ -61,7 +62,7 @@ public class PlayerController { mServiceIntent = serviceIntent; } mControllerCb = new SessionCallback(); - mManager = (MediaSessionManager) context + mManager = (SessionManager) context .getSystemService(Context.MEDIA_SESSION_SERVICE); mResumed = false; @@ -121,6 +122,10 @@ public class PlayerController { } } + public void showRoutePicker() { + mController.showRoutePicker(); + } + private void unbindFromService() { mContext.unbindService(mServiceConnection); } @@ -150,7 +155,7 @@ public class PlayerController { mBinder = IPlayerService.Stub.asInterface(service); Log.d(TAG, "service is " + service + " binder is " + mBinder); try { - mController = MediaController.fromToken(mBinder.getSessionToken()); + mController = SessionController.fromToken(mBinder.getSessionToken()); } catch (RemoteException e) { Log.e(TAG, "Error getting session", e); return; @@ -171,9 +176,9 @@ public class PlayerController { } }; - private class SessionCallback extends MediaController.Callback { + private class SessionCallback extends SessionController.Callback { @Override - public void onRouteChanged(Bundle route) { + public void onRouteChanged(RouteInfo route) { // TODO } } diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerService.java b/tests/OneMedia/src/com/android/onemedia/PlayerService.java index 0ad6dd1..8b53ddf 100644 --- a/tests/OneMedia/src/com/android/onemedia/PlayerService.java +++ b/tests/OneMedia/src/com/android/onemedia/PlayerService.java @@ -17,7 +17,7 @@ package com.android.onemedia; import android.app.Service; import android.content.Intent; -import android.media.session.MediaSessionToken; +import android.media.session.SessionToken; import android.media.session.PlaybackState; import android.os.Bundle; import android.os.IBinder; @@ -149,7 +149,7 @@ public class PlayerService extends Service { } @Override - public MediaSessionToken getSessionToken() throws RemoteException { + public SessionToken getSessionToken() throws RemoteException { return mSession.getSessionToken(); } } diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java index a2d7897..5dc3904 100644 --- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java +++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java @@ -17,9 +17,13 @@ package com.android.onemedia; import android.content.Context; import android.content.Intent; -import android.media.session.MediaSession; -import android.media.session.MediaSessionManager; -import android.media.session.MediaSessionToken; +import android.media.session.Route; +import android.media.session.RouteInfo; +import android.media.session.RouteOptions; +import android.media.session.RoutePlaybackControls; +import android.media.session.Session; +import android.media.session.SessionManager; +import android.media.session.SessionToken; import android.media.session.PlaybackState; import android.media.session.TransportPerformer; import android.os.Bundle; @@ -27,41 +31,55 @@ import android.util.Log; import android.view.KeyEvent; import com.android.onemedia.playback.LocalRenderer; +import com.android.onemedia.playback.OneMRPRenderer; import com.android.onemedia.playback.Renderer; -import com.android.onemedia.playback.RendererFactory; +import com.android.onemedia.playback.RequestUtils; + +import java.util.ArrayList; public class PlayerSession { private static final String TAG = "PlayerSession"; - protected MediaSession mSession; + protected Session mSession; protected Context mContext; - protected RendererFactory mRendererFactory; - protected LocalRenderer mRenderer; - protected MediaSession.Callback mCallback; + protected Renderer mRenderer; + protected Session.Callback mCallback; protected Renderer.Listener mRenderListener; protected TransportPerformer mPerformer; protected PlaybackState mPlaybackState; protected Listener mListener; + protected ArrayList<RouteOptions> mRouteOptions; + protected Route mRoute; + protected RoutePlaybackControls mRouteControls; + protected RouteListener mRouteListener; + + private String mContent; public PlayerSession(Context context) { mContext = context; - mRendererFactory = new RendererFactory(); mRenderer = new LocalRenderer(context, null); - mCallback = new ControllerCb(); + mCallback = new SessionCb(); mRenderListener = new RenderListener(); mPlaybackState = new PlaybackState(); mPlaybackState.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY); mRenderer.registerListener(mRenderListener); + + // TODO need an easier way to build route options + mRouteOptions = new ArrayList<RouteOptions>(); + RouteOptions.Builder bob = new RouteOptions.Builder(); + bob.addInterface(RoutePlaybackControls.NAME); + mRouteOptions.add(bob.build()); + mRouteListener = new RouteListener(); } public void createSession() { if (mSession != null) { mSession.release(); } - MediaSessionManager man = (MediaSessionManager) mContext + SessionManager man = (SessionManager) mContext .getSystemService(Context.MEDIA_SESSION_SERVICE); Log.d(TAG, "Creating session for package " + mContext.getBasePackageName()); mSession = man.createSession("OneMedia"); @@ -69,6 +87,7 @@ public class PlayerSession { mPerformer = mSession.setTransportPerformerEnabled(); mPerformer.addListener(new TransportListener()); mPerformer.setPlaybackState(mPlaybackState); + mSession.setRouteOptions(mRouteOptions); mSession.publish(); } @@ -86,18 +105,24 @@ public class PlayerSession { mListener = listener; } - public MediaSessionToken getSessionToken() { + public SessionToken getSessionToken() { return mSession.getSessionToken(); } public void setContent(Bundle request) { mRenderer.setContent(request); + mContent = request.getString(RequestUtils.EXTRA_KEY_SOURCE); } public void setNextContent(Bundle request) { mRenderer.setNextContent(request); } + private void updateState(int newState) { + mPlaybackState.setState(newState); + mPerformer.setPlaybackState(mPlaybackState); + } + public interface Listener { public void onPlayStateChanged(PlaybackState state); } @@ -145,7 +170,11 @@ public class PlayerSession { mPlaybackState.setErrorMessage("unkown state"); break; } - mPlaybackState.setPosition(mRenderer.getSeekPosition()); + if (mRenderer != null) { + mPlaybackState.setPosition(mRenderer.getSeekPosition()); + } else { + mPlaybackState.setPosition(-1); + } mPerformer.setPlaybackState(mPlaybackState); if (mListener != null) { mListener.onPlayStateChanged(mPlaybackState); @@ -173,8 +202,7 @@ public class PlayerSession { } - private class ControllerCb extends MediaSession.Callback { - + private class SessionCb extends Session.Callback { @Override public void onMediaButton(Intent mediaRequestIntent) { if (Intent.ACTION_MEDIA_BUTTON.equals(mediaRequestIntent.getAction())) { @@ -192,6 +220,40 @@ public class PlayerSession { } } } + + @Override + public void onRequestRouteChange(RouteInfo route) { + if (mRenderer != null) { + mRenderer.onStop(); + } + if (route == null) { + // Use local route + mRoute = null; + mRenderer = new LocalRenderer(mContext, null); + mRenderer.registerListener(mRenderListener); + updateState(PlaybackState.PLAYSTATE_NONE); + } else { + // Use remote route + mSession.connect(route, mRouteOptions.get(0)); + mRenderer = null; + updateState(PlaybackState.PLAYSTATE_CONNECTING); + } + } + + @Override + public void onRouteConnected(Route route) { + mRoute = route; + mRouteControls = RoutePlaybackControls.from(route); + mRouteControls.addListener(mRouteListener); + Log.d(TAG, "Connected to route, registering listener"); + mRenderer = new OneMRPRenderer(mRouteControls); + updateState(PlaybackState.PLAYSTATE_NONE); + } + + @Override + public void onRouteDisconnected(Route route, int reason) { + + } } private class TransportListener extends TransportPerformer.Listener { @@ -206,4 +268,12 @@ public class PlayerSession { } } + private class RouteListener extends RoutePlaybackControls.Listener { + @Override + public void onPlaybackStateChange(int state) { + Log.d(TAG, "Updating state to " + state); + updateState(state); + } + } + } diff --git a/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java b/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java index 7f62f66..c8a8d6c 100644 --- a/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java +++ b/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.onemedia.playback; import org.apache.http.Header; @@ -370,6 +385,8 @@ public class LocalRenderer extends Renderer implements OnPreparedListener, * Prepares the player for the given playback request. If the holder is null * it is assumed this is an audio only source. If playOnReady is set to true * the media will begin playing as soon as it can. + * + * @see RequestUtils for the set of valid keys. */ public void setContent(Bundle request, SurfaceHolder holder) { String source = request.getString(RequestUtils.EXTRA_KEY_SOURCE); diff --git a/tests/OneMedia/src/com/android/onemedia/playback/MediaItem.java b/tests/OneMedia/src/com/android/onemedia/playback/MediaItem.java index f9e6794..05516d2 100644 --- a/tests/OneMedia/src/com/android/onemedia/playback/MediaItem.java +++ b/tests/OneMedia/src/com/android/onemedia/playback/MediaItem.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.onemedia.playback; import android.os.Bundle; diff --git a/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java b/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java new file mode 100644 index 0000000..9b0a2b2 --- /dev/null +++ b/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java @@ -0,0 +1,44 @@ +package com.android.onemedia.playback; + +import android.media.session.RoutePlaybackControls; +import android.os.Bundle; + +/** + * Renderer for communicating with the OneMRP route + */ +public class OneMRPRenderer extends Renderer { + private final RoutePlaybackControls mControls; + + public OneMRPRenderer(RoutePlaybackControls controls) { + super(null, null); + mControls = controls; + } + + @Override + public void setContent(Bundle request) { + mControls.playNow(request.getString(RequestUtils.EXTRA_KEY_SOURCE)); + } + + @Override + public boolean onStop() { + mControls.pause(); + return true; + } + + @Override + public boolean onPlay() { + mControls.resume(); + return true; + } + + @Override + public boolean onPause() { + mControls.pause(); + return true; + } + + @Override + public long getSeekPosition() { + return -1; + } +} diff --git a/tests/OneMedia/src/com/android/onemedia/playback/PlaybackError.java b/tests/OneMedia/src/com/android/onemedia/playback/PlaybackError.java index 72d936c..ac9da23 100644 --- a/tests/OneMedia/src/com/android/onemedia/playback/PlaybackError.java +++ b/tests/OneMedia/src/com/android/onemedia/playback/PlaybackError.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.onemedia.playback; import android.os.Bundle; diff --git a/tests/OneMedia/src/com/android/onemedia/playback/Renderer.java b/tests/OneMedia/src/com/android/onemedia/playback/Renderer.java index 2451bdf..09debcf 100644 --- a/tests/OneMedia/src/com/android/onemedia/playback/Renderer.java +++ b/tests/OneMedia/src/com/android/onemedia/playback/Renderer.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.onemedia.playback; import android.content.Context; @@ -77,39 +92,54 @@ public abstract class Renderer { } public boolean onPlay() { - throw new UnsupportedOperationException("play is not supported."); + // TODO consider making these log warnings instead of crashes (or + // Log.wtf) + // throw new UnsupportedOperationException("play is not supported."); + return false; } public boolean onPause() { - throw new UnsupportedOperationException("pause is not supported."); + // throw new UnsupportedOperationException("pause is not supported."); + return false; } public boolean onNext() { - throw new UnsupportedOperationException("next is not supported."); + // throw new UnsupportedOperationException("next is not supported."); + return false; } public boolean onPrevious() { - throw new UnsupportedOperationException("previous is not supported."); + // throw new + // UnsupportedOperationException("previous is not supported."); + return false; } public boolean onStop() { - throw new UnsupportedOperationException("stop is not supported."); + // throw new UnsupportedOperationException("stop is not supported."); + return false; } public boolean onSeekTo(int time) { - throw new UnsupportedOperationException("seekTo is not supported."); + // throw new UnsupportedOperationException("seekTo is not supported."); + return false; } public long getSeekPosition() { - throw new UnsupportedOperationException("getSeekPosition is not supported."); + // throw new + // UnsupportedOperationException("getSeekPosition is not supported."); + return -1; } public long getDuration() { - throw new UnsupportedOperationException("getDuration is not supported."); + // throw new + // UnsupportedOperationException("getDuration is not supported."); + return -1; } public int getPlayState() { - throw new UnsupportedOperationException("getPlayState is not supported."); + // throw new + // UnsupportedOperationException("getPlayState is not supported."); + return 0; } public void onDestroy() { diff --git a/tests/OneMedia/src/com/android/onemedia/playback/RendererFactory.java b/tests/OneMedia/src/com/android/onemedia/playback/RendererFactory.java deleted file mode 100644 index f333fce..0000000 --- a/tests/OneMedia/src/com/android/onemedia/playback/RendererFactory.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.android.onemedia.playback; - -import android.content.Context; -import android.media.MediaRouter; -import android.os.Bundle; -import android.util.Log; - -/** - * TODO: Insert description here. - */ -public class RendererFactory { - private static final String TAG = "RendererFactory"; - - public Renderer createRenderer(MediaRouter.RouteInfo route, Context context, Bundle params) { - if (route.getPlaybackType() == MediaRouter.RouteInfo.PLAYBACK_TYPE_LOCAL) { - return new LocalRenderer(context, params); - } - Log.e(TAG, "Unable to create renderer for route of playback type " - + route.getPlaybackType()); - return null; - } -} diff --git a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java index 9b50dad..dd0d982 100644 --- a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java +++ b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.onemedia.playback; import android.os.Bundle; diff --git a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java new file mode 100644 index 0000000..6edcd7d --- /dev/null +++ b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.onemedia.provider; + +import android.media.routeprovider.RouteConnection; +import android.media.routeprovider.RouteInterfaceHandler; +import android.media.routeprovider.RoutePlaybackControlsHandler; +import android.media.routeprovider.RouteProviderService; +import android.media.routeprovider.RouteRequest; +import android.media.session.RouteInfo; +import android.media.session.RoutePlaybackControls; +import android.media.session.RouteInterface; +import android.media.session.PlaybackState; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.ResultReceiver; +import android.util.Log; + +import com.android.onemedia.playback.LocalRenderer; +import com.android.onemedia.playback.Renderer; +import com.android.onemedia.playback.RequestUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Test of MediaRouteProvider. Show a dummy provider with a simple interface for + * playing music. + */ +public class OneMediaRouteProvider extends RouteProviderService { + private static final String TAG = "OneMRP"; + private static final boolean DEBUG = true; + + private Renderer mRenderer; + private RenderListener mRenderListener; + private PlaybackState mPlaybackState; + private RouteConnection mConnection; + private RoutePlaybackControlsHandler mControls; + private String mRouteId; + private Handler mHandler; + + @Override + public void onCreate() { + mHandler = new Handler(); + mRouteId = UUID.randomUUID().toString(); + mRenderer = new LocalRenderer(this, null); + mRenderListener = new RenderListener(); + mPlaybackState = new PlaybackState(); + mPlaybackState.setActions(PlaybackState.ACTION_PAUSE + | PlaybackState.ACTION_PLAY); + + mRenderer.registerListener(mRenderListener); + + if (DEBUG) { + Log.d(TAG, "onCreate, routeId is " + mRouteId); + } + } + + @Override + public List<RouteInfo> getMatchingRoutes(List<RouteRequest> requests) { + RouteInfo.Builder bob = new RouteInfo.Builder(); + bob.setName("OneMedia").setId(mRouteId); + // TODO add a helper library for generating route info with the correct + // options + Log.d(TAG, "Requests:"); + for (RouteRequest request : requests) { + List<String> ifaces = request.getConnectionOptions().getInterfaceNames(); + Log.d(TAG, " request ifaces:" + ifaces.toString()); + if (ifaces != null && ifaces.size() == 1 + && RoutePlaybackControls.NAME.equals(ifaces.get(0))) { + bob.addRouteOptions(request.getConnectionOptions()); + } + } + ArrayList<RouteInfo> result = new ArrayList<RouteInfo>(); + if (bob.getOptionsSize() > 0) { + RouteInfo info = bob.build(); + result.add(info); + } + if (DEBUG) { + Log.d(TAG, "getRoutes returning " + result.toString()); + } + return result; + } + + @Override + public RouteConnection connect(RouteInfo route, RouteRequest request) { + if (mConnection != null) { + disconnect(mConnection); + } + RouteConnection connection = new RouteConnection(this, route); + mControls = RoutePlaybackControlsHandler.addTo(connection); + mControls.addListener(new PlayHandler(mRouteId), mHandler); + if (DEBUG) { + Log.d(TAG, "Connected to route"); + } + return connection; + } + + private class PlayHandler extends RoutePlaybackControlsHandler.Listener { + private final String mRouteId; + + public PlayHandler(String routeId) { + mRouteId = routeId; + } + + @Override + public void playNow(String content, ResultReceiver cb) { + if (DEBUG) { + Log.d(TAG, "Attempting to play " + content); + } + // look up the route and send a play command to it + Bundle bundle = new Bundle(); + bundle.putString(RequestUtils.EXTRA_KEY_SOURCE, content); + mRenderer.setContent(bundle); + RouteInterfaceHandler.sendResult(cb, RouteInterface.RESULT_SUCCESS, null); + } + + @Override + public boolean resume() { + mRenderer.onPlay(); + return true; + } + + @Override + public boolean pause() { + mRenderer.onPause(); + return true; + } + } + + private class RenderListener implements Renderer.Listener { + + @Override + public void onError(int type, int extra, Bundle extras, Throwable error) { + Log.d(TAG, "Sending onError with type " + type + " and extra " + extra); + if (mControls != null) { + mControls.sendPlaybackChangeEvent(PlaybackState.PLAYSTATE_ERROR); + } + } + + @Override + public void onStateChanged(int newState) { + if (newState != Renderer.STATE_ERROR) { + mPlaybackState.setErrorMessage(null); + } + switch (newState) { + case Renderer.STATE_ENDED: + case Renderer.STATE_STOPPED: + mPlaybackState.setState(PlaybackState.PLAYSTATE_STOPPED); + break; + case Renderer.STATE_INIT: + case Renderer.STATE_PREPARING: + mPlaybackState.setState(PlaybackState.PLAYSTATE_BUFFERING); + break; + case Renderer.STATE_ERROR: + mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR); + break; + case Renderer.STATE_PAUSED: + mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED); + break; + case Renderer.STATE_PLAYING: + mPlaybackState.setState(PlaybackState.PLAYSTATE_PLAYING); + break; + default: + mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR); + mPlaybackState.setErrorMessage("unkown state"); + break; + } + mPlaybackState.setPosition(mRenderer.getSeekPosition()); + + mControls.sendPlaybackChangeEvent(mPlaybackState.getState()); + } + + @Override + public void onBufferingUpdate(int percent) { + } + + @Override + public void onFocusLost() { + Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED); + mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED); + mPlaybackState.setPosition(mRenderer.getSeekPosition()); + } + + @Override + public void onNextStarted() { + } + } +} |