diff options
Diffstat (limited to 'tests')
6 files changed, 421 insertions, 0 deletions
diff --git a/tests/OneMedia/Android.mk b/tests/OneMedia/Android.mk index 4d39728..4feac68 100644 --- a/tests/OneMedia/Android.mk +++ b/tests/OneMedia/Android.mk @@ -9,6 +9,9 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) \ LOCAL_PACKAGE_NAME := OneMedia LOCAL_CERTIFICATE := platform +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-media-protocols + LOCAL_PROGUARD_ENABLED := disabled include $(BUILD_PACKAGE) diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml index beafeb4..95072a4 100644 --- a/tests/OneMedia/AndroidManifest.xml +++ b/tests/OneMedia/AndroidManifest.xml @@ -25,6 +25,15 @@ android:name="com.android.onemedia.OnePlayerService" android:exported="true" android:process="com.android.onemedia.service" /> + <service + android:name=".provider.OneMediaRouteProvider" + android:permission="android.permission.BIND_MEDIA_ROUTE_SERVICE" + android:exported="true" + android:process="com.android.onemedia.provider"> + <intent-filter> + <action android:name="android.media.routing.MediaRouteService" /> + </intent-filter> + </service> </application> </manifest> diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java index 2455c9c..141a209 100644 --- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java +++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java @@ -19,17 +19,25 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.media.MediaMetadata; +import android.media.routing.MediaRouteSelector; +import android.media.routing.MediaRouter; +import android.media.routing.MediaRouter.ConnectionRequest; +import android.media.routing.MediaRouter.DestinationInfo; +import android.media.routing.MediaRouter.RouteInfo; import android.media.session.MediaSession; import android.media.session.MediaSession.QueueItem; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; import android.os.Bundle; +import android.support.media.protocols.MediaPlayerProtocol; +import android.support.media.protocols.MediaPlayerProtocol.MediaStatus; import android.os.RemoteException; import android.os.SystemClock; 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.RequestUtils; @@ -40,6 +48,7 @@ public class PlayerSession { private static final String TAG = "PlayerSession"; protected MediaSession mSession; + protected MediaRouter mRouter; protected Context mContext; protected Renderer mRenderer; protected MediaSession.Callback mCallback; @@ -75,11 +84,22 @@ public class PlayerSession { .getSystemService(Context.MEDIA_SESSION_SERVICE); Log.d(TAG, "Creating session for package " + mContext.getBasePackageName()); + mRouter = new MediaRouter(mContext); + mRouter.addSelector(new MediaRouteSelector.Builder() + .addRequiredProtocol(MediaPlayerProtocol.class) + .build()); + mRouter.addSelector(new MediaRouteSelector.Builder() + .setRequiredFeatures(MediaRouter.ROUTE_FEATURE_LIVE_AUDIO) + .setOptionalFeatures(MediaRouter.ROUTE_FEATURE_LIVE_VIDEO) + .build()); + mRouter.setRoutingCallback(new RoutingCallback(), null); + mSession = new MediaSession(mContext, "OneMedia"); mSession.setCallback(mCallback); mSession.setPlaybackState(mPlaybackState); mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS); + mSession.setMediaRouter(mRouter); mSession.setActive(true); updateMetadata(); } @@ -97,6 +117,10 @@ public class PlayerSession { mSession.release(); mSession = null; } + if (mRouter != null) { + mRouter.release(); + mRouter = null; + } } public void setListener(Listener listener) { @@ -254,4 +278,63 @@ public class PlayerSession { mRenderer.onPause(); } } + + private class RoutingCallback extends MediaRouter.RoutingCallback { + @Override + public void onConnectionStateChanged(int state) { + if (state == MediaRouter.CONNECTION_STATE_CONNECTING) { + if (mRenderer != null) { + mRenderer.onStop(); + } + mRenderer = null; + updateState(PlaybackState.STATE_CONNECTING); + return; + } + + MediaRouter.ConnectionInfo connection = mRouter.getConnection(); + if (connection != null) { + MediaPlayerProtocol protocol = + connection.getProtocolObject(MediaPlayerProtocol.class); + if (protocol != null) { + Log.d(TAG, "Connected to route using media player protocol"); + + protocol.setCallback(new PlayerCallback(), null); + mRenderer = new OneMRPRenderer(protocol); + updateState(PlaybackState.STATE_NONE); + return; + } + } + + // Use local route + mRenderer = new LocalRenderer(mContext, null); + mRenderer.registerListener(mRenderListener); + updateState(PlaybackState.STATE_NONE); + } + } + + private class PlayerCallback extends MediaPlayerProtocol.Callback { + @Override + public void onStatusUpdated(MediaStatus status, Bundle extras) { + if (status != null) { + Log.d(TAG, "Received status update: " + status.toBundle()); + switch (status.getPlayerState()) { + case MediaStatus.PLAYER_STATE_BUFFERING: + updateState(PlaybackState.STATE_BUFFERING); + break; + case MediaStatus.PLAYER_STATE_IDLE: + updateState(PlaybackState.STATE_STOPPED); + break; + case MediaStatus.PLAYER_STATE_PAUSED: + updateState(PlaybackState.STATE_PAUSED); + break; + case MediaStatus.PLAYER_STATE_PLAYING: + updateState(PlaybackState.STATE_PLAYING); + break; + case MediaStatus.PLAYER_STATE_UNKNOWN: + updateState(PlaybackState.STATE_NONE); + break; + } + } + } + } } 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..55eb92c --- /dev/null +++ b/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java @@ -0,0 +1,47 @@ +package com.android.onemedia.playback; + +import android.os.Bundle; +import android.support.media.protocols.MediaPlayerProtocol; +import android.support.media.protocols.MediaPlayerProtocol.MediaInfo; + +/** + * Renderer for communicating with the OneMRP route + */ +public class OneMRPRenderer extends Renderer { + private final MediaPlayerProtocol mProtocol; + + public OneMRPRenderer(MediaPlayerProtocol protocol) { + super(null, null); + mProtocol = protocol; + } + + @Override + public void setContent(Bundle request) { + MediaInfo mediaInfo = new MediaInfo(request.getString(RequestUtils.EXTRA_KEY_SOURCE), + MediaInfo.STREAM_TYPE_BUFFERED, "audio/mp3"); + mProtocol.load(mediaInfo, true, 0, null); + } + + @Override + public boolean onStop() { + mProtocol.stop(null); + return true; + } + + @Override + public boolean onPlay() { + mProtocol.play(null); + return true; + } + + @Override + public boolean onPause() { + mProtocol.pause(null); + return true; + } + + @Override + public long getSeekPosition() { + return -1; + } +} 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..5845e48 --- /dev/null +++ b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java @@ -0,0 +1,270 @@ +/* + * 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.routing.MediaRouteSelector; +import android.media.routing.MediaRouteService; +import android.media.routing.MediaRouter.ConnectionInfo; +import android.media.routing.MediaRouter.ConnectionRequest; +import android.media.routing.MediaRouter.DestinationInfo; +import android.media.routing.MediaRouter.DiscoveryRequest; +import android.media.routing.MediaRouter.RouteInfo; +import android.media.session.PlaybackState; +import android.os.Bundle; +import android.os.Handler; +import android.os.Process; +import android.support.media.protocols.MediaPlayerProtocol; +import android.support.media.protocols.MediaPlayerProtocol.MediaInfo; +import android.support.media.protocols.MediaPlayerProtocol.MediaStatus; +import android.os.Looper; +import android.os.ResultReceiver; +import android.os.SystemClock; +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; + +/** + * Test of MediaRouteProvider. Show a dummy provider with a simple interface for + * playing music. + */ +public class OneMediaRouteProvider extends MediaRouteService { + private static final String TAG = "OneMRP"; + private static final boolean DEBUG = true; + + private static final String TEST_DESTINATION_ID = "testDestination"; + private static final String TEST_ROUTE_ID = "testRoute"; + + private Renderer mRenderer; + private RenderListener mRenderListener; + private PlaybackState mPlaybackState; + private Handler mHandler; + + private OneStub mStub; + + @Override + public void onCreate() { + mHandler = new Handler(); + mRenderer = new LocalRenderer(this, null); + mRenderListener = new RenderListener(); + PlaybackState.Builder bob = new PlaybackState.Builder(); + bob.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY); + mPlaybackState = bob.build(); + + mRenderer.registerListener(mRenderListener); + } + + @Override + public ClientSession onCreateClientSession(ClientInfo client) { + if (client.getUid() != Process.myUid()) { + // for testing purposes, only allow connections from this application + // since this provider is not fully featured + return null; + } + return new OneSession(client); + } + + private final class OneSession extends ClientSession { + private final ClientInfo mClient; + + public OneSession(ClientInfo client) { + mClient = client; + } + + @Override + public boolean onStartDiscovery(DiscoveryRequest req, DiscoveryCallback callback) { + for (MediaRouteSelector selector : req.getSelectors()) { + if (isMatch(selector)) { + DestinationInfo destination = new DestinationInfo.Builder( + TEST_DESTINATION_ID, getServiceMetadata(), "OneMedia") + .setDescription("Test route from OneMedia app.") + .build(); + ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>(); + routes.add(new RouteInfo.Builder( + TEST_ROUTE_ID, destination, selector).build()); + callback.onDestinationFound(destination, routes); + return true; + } + } + return false; + } + + @Override + public void onStopDiscovery() { + } + + @Override + public boolean onConnect(ConnectionRequest req, ConnectionCallback callback) { + if (req.getRoute().getId().equals(TEST_ROUTE_ID)) { + mStub = new OneStub(); + ConnectionInfo connection = new ConnectionInfo.Builder(req.getRoute()) + .setProtocolStub(MediaPlayerProtocol.class, mStub) + .build(); + callback.onConnected(connection); + return true; + } + return false; + } + + @Override + public void onDisconnect() { + mStub = null; + } + + private boolean isMatch(MediaRouteSelector selector) { + if (!selector.containsProtocol(MediaPlayerProtocol.class)) { + return false; + } + for (String protocol : selector.getRequiredProtocols()) { + if (!protocol.equals(MediaPlayerProtocol.class.getName())) { + return false; + } + } + return true; + } + } + + private final class OneStub extends MediaPlayerProtocol.Stub { + MediaInfo mMediaInfo; + + public OneStub() { + super(mHandler); + } + + @Override + public void onLoad(MediaInfo mediaInfo, boolean autoplay, long playPosition, + Bundle extras) { + if (DEBUG) { + Log.d(TAG, "Attempting to play " + mediaInfo.getContentId()); + } + // look up the route and send a play command to it + mMediaInfo = mediaInfo; + Bundle bundle = new Bundle(); + bundle.putString(RequestUtils.EXTRA_KEY_SOURCE, mediaInfo.getContentId()); + mRenderer.setContent(bundle); + } + + @Override + public void onPlay(Bundle extras) { + mRenderer.onPlay(); + } + + @Override + public void onPause(Bundle extras) { + mRenderer.onPause(); + } + } + + 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); + sendStatusUpdate(PlaybackState.STATE_ERROR); + } + + @Override + public void onStateChanged(int newState) { + long position = -1; + if (mRenderer != null) { + position = mRenderer.getSeekPosition(); + } + int pbState; + float rate = 0; + String errorMsg = null; + switch (newState) { + case Renderer.STATE_ENDED: + case Renderer.STATE_STOPPED: + pbState = PlaybackState.STATE_STOPPED; + break; + case Renderer.STATE_INIT: + case Renderer.STATE_PREPARING: + pbState = PlaybackState.STATE_BUFFERING; + break; + case Renderer.STATE_ERROR: + pbState = PlaybackState.STATE_ERROR; + break; + case Renderer.STATE_PAUSED: + pbState = PlaybackState.STATE_PAUSED; + break; + case Renderer.STATE_PLAYING: + pbState = PlaybackState.STATE_PLAYING; + rate = 1; + break; + default: + pbState = PlaybackState.STATE_ERROR; + errorMsg = "unknown state"; + break; + } + PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState); + bob.setState(pbState, position, rate, SystemClock.elapsedRealtime()); + bob.setErrorMessage(errorMsg); + mPlaybackState = bob.build(); + + sendStatusUpdate(mPlaybackState.getState()); + } + + @Override + public void onBufferingUpdate(int percent) { + } + + @Override + public void onFocusLost() { + Log.d(TAG, "Focus lost, pausing"); + // Don't update state here, we'll get a separate call to + // onStateChanged when it pauses + mRenderer.onPause(); + } + + @Override + public void onNextStarted() { + } + + private void sendStatusUpdate(int state) { + if (mStub != null) { + MediaStatus status = new MediaStatus(1, mStub.mMediaInfo); + switch (state) { + case PlaybackState.STATE_BUFFERING: + case PlaybackState.STATE_FAST_FORWARDING: + case PlaybackState.STATE_REWINDING: + case PlaybackState.STATE_SKIPPING_TO_NEXT: + case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: + status.setPlayerState(MediaStatus.PLAYER_STATE_BUFFERING); + break; + case PlaybackState.STATE_CONNECTING: + case PlaybackState.STATE_STOPPED: + status.setPlayerState(MediaStatus.PLAYER_STATE_IDLE); + break; + case PlaybackState.STATE_PAUSED: + status.setPlayerState(MediaStatus.PLAYER_STATE_PAUSED); + break; + case PlaybackState.STATE_PLAYING: + status.setPlayerState(MediaStatus.PLAYER_STATE_PLAYING); + break; + case PlaybackState.STATE_NONE: + case PlaybackState.STATE_ERROR: + default: + status.setPlayerState(MediaStatus.PLAYER_STATE_UNKNOWN); + break; + } + mStub.sendStatusUpdatedEvent(status, null); + } + } + } +} diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java index d64eefa..783c78e 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java @@ -18,7 +18,9 @@ package com.android.test.voiceinteraction; import android.app.Activity; import android.app.VoiceInteractor; +import android.content.ComponentName; import android.os.Bundle; +import android.service.voice.VoiceInteractionService; import android.util.Log; import android.view.Gravity; import android.view.View; @@ -42,6 +44,13 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis return; } + if (!VoiceInteractionService.isActiveService(this, + new ComponentName(this, MainInteractionService.class))) { + Log.w(TAG, "Not current voice interactor!"); + finish(); + return; + } + setContentView(R.layout.test_interaction); mAbortButton = (Button)findViewById(R.id.abort); mAbortButton.setOnClickListener(this); |