Update the MediaRouter support demo for new UIs

Updates the MediaRouter support demo for the new material UI spec.

bug:17879842
Change-Id: I3340e65384050fbf561cf506cb5b80028d69d849
This commit is contained in:
RoboErik
2014-11-11 16:31:22 -08:00
committed by Erik Pasternak
parent ed6aaf3f72
commit c00f64fbb9
9 changed files with 179 additions and 141 deletions

View File

@@ -86,6 +86,14 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".media.SampleMediaRouteSettingsActivity"
android:label="@string/sample_media_route_settings_activity"
android:theme="@style/Theme.AppCompat.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<service android:name=".media.SampleMediaRouteProviderService" <service android:name=".media.SampleMediaRouteProviderService"
android:label="@string/sample_media_route_provider_service" android:label="@string/sample_media_route_provider_service"
android:process=":mrp"> android:process=":mrp">

View File

@@ -25,6 +25,7 @@
<string name="sample_media_router_text">This activity demonstrates how to <string name="sample_media_router_text">This activity demonstrates how to
use MediaRouter from the support library. Select a route from the action bar.</string> use MediaRouter from the support library. Select a route from the action bar.</string>
<string name="media_route_menu_title">Play on...</string> <string name="media_route_menu_title">Play on...</string>
<string name="sample_media_route_settings_activity">Sample route settings</string>
<string name="library_tab_text">Library</string> <string name="library_tab_text">Library</string>
<string name="playlist_tab_text">Playlist</string> <string name="playlist_tab_text">Playlist</string>

View File

@@ -28,6 +28,7 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v7.media.MediaRouter.RouteInfo; import android.support.v7.media.MediaRouter.RouteInfo;
import android.support.v7.media.MediaItemStatus; import android.support.v7.media.MediaItemStatus;
import android.util.Log; import android.util.Log;
@@ -56,12 +57,6 @@ public abstract class LocalPlayer extends Player implements
private static final String TAG = "LocalPlayer"; private static final String TAG = "LocalPlayer";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int STATE_IDLE = 0;
private static final int STATE_PLAY_PENDING = 1;
private static final int STATE_READY = 2;
private static final int STATE_PLAYING = 3;
private static final int STATE_PAUSED = 4;
private final Context mContext; private final Context mContext;
private final Handler mHandler = new Handler(); private final Handler mHandler = new Handler();
private MediaPlayer mMediaPlayer; private MediaPlayer mMediaPlayer;
@@ -109,6 +104,11 @@ public abstract class LocalPlayer extends Player implements
} }
} }
@Override
public MediaSessionCompat getMediaSession() {
return mMediaSession;
}
// Player // Player
@Override @Override
public void play(final PlaylistItem item) { public void play(final PlaylistItem item) {

View File

@@ -16,11 +16,14 @@
package com.example.android.supportv7.media; package com.example.android.supportv7.media;
import android.net.Uri;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.support.v7.media.MediaControlIntent; import android.support.v7.media.MediaControlIntent;
import android.support.v7.media.MediaRouter.RouteInfo; import android.support.v7.media.MediaRouter.RouteInfo;
import android.util.Log;
/** /**
* Abstraction of common playback operations of media items, such as play, * Abstraction of common playback operations of media items, such as play,
@@ -28,7 +31,19 @@ import android.support.v7.media.MediaRouter.RouteInfo;
* of media items. * of media items.
*/ */
public abstract class Player { public abstract class Player {
private static final String TAG = "SampleMediaRoutePlayer";
protected static final int STATE_IDLE = 0;
protected static final int STATE_PLAY_PENDING = 1;
protected static final int STATE_READY = 2;
protected static final int STATE_PLAYING = 3;
protected static final int STATE_PAUSED = 4;
private static final long PLAYBACK_ACTIONS = PlaybackStateCompat.ACTION_PAUSE
| PlaybackStateCompat.ACTION_PLAY;
protected Callback mCallback; protected Callback mCallback;
protected MediaSessionCompat mMediaSession;
protected MediaSessionCallback mSessionCallback;
public abstract boolean isRemotePlayback(); public abstract boolean isRemotePlayback();
public abstract boolean isQueuingSupported(); public abstract boolean isQueuingSupported();
@@ -71,10 +86,71 @@ public abstract class Player {
} else { } else {
player = new LocalPlayer.OverlayPlayer(context); player = new LocalPlayer.OverlayPlayer(context);
} }
player.initMediaSession(context);
player.connect(route); player.connect(route);
return player; return player;
} }
public MediaSessionCompat getMediaSession() {
return mMediaSession;
}
protected void updateMetadata() {
MediaMetadataCompat.Builder bob = new MediaMetadataCompat.Builder();
bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, getDescription());
bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Subtitle of the thing");
bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION,
"Description of the thing");
bob.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, getSnapshot());
mMediaSession.setMetadata(bob.build());
}
protected void publishState(int state) {
PlaybackStateCompat.Builder bob = new PlaybackStateCompat.Builder();
bob.setActions(PLAYBACK_ACTIONS);
switch (state) {
case STATE_PLAYING:
bob.setState(PlaybackStateCompat.STATE_PLAYING, -1, 1);
break;
case STATE_READY:
case STATE_PAUSED:
bob.setState(PlaybackStateCompat.STATE_PAUSED, -1, 0);
break;
case STATE_IDLE:
bob.setState(PlaybackStateCompat.STATE_STOPPED, -1, 0);
break;
}
PlaybackStateCompat pbState = bob.build();
Log.d(TAG, "Setting state to " + pbState);
mMediaSession.setPlaybackState(pbState);
if (state != STATE_IDLE) {
mMediaSession.setActive(true);
} else {
mMediaSession.setActive(false);
}
}
private void initMediaSession(Context context) {
mSessionCallback = new MediaSessionCallback();
mMediaSession = new MediaSessionCompat(context, "Support7Demos");
mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
| MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mMediaSession.setCallback(mSessionCallback);
updateMetadata();
}
private final class MediaSessionCallback extends MediaSessionCompat.Callback {
@Override
public void onPlay() {
resume();
}
@Override
public void onPause() {
pause();
}
}
public interface Callback { public interface Callback {
void onError(); void onError();
void onCompletion(); void onCompletion();

View File

@@ -140,6 +140,9 @@ public class RemotePlayer extends Player {
} }
if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) { if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
pause(); pause();
publishState(STATE_PAUSED);
} else {
publishState(STATE_PLAYING);
} }
if (mCallback != null) { if (mCallback != null) {
mCallback.onPlaylistChanged(); mCallback.onPlaylistChanged();
@@ -214,6 +217,7 @@ public class RemotePlayer extends Player {
if (mCallback != null) { if (mCallback != null) {
mCallback.onPlaylistChanged(); mCallback.onPlaylistChanged();
} }
publishState(STATE_PAUSED);
} }
@Override @Override
@@ -239,6 +243,7 @@ public class RemotePlayer extends Player {
if (mCallback != null) { if (mCallback != null) {
mCallback.onPlaylistChanged(); mCallback.onPlaylistChanged();
} }
publishState(STATE_PLAYING);
} }
@Override @Override
@@ -254,6 +259,7 @@ public class RemotePlayer extends Player {
// ignore if no session // ignore if no session
return; return;
} }
publishState(STATE_IDLE);
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "stop"); Log.d(TAG, "stop");
} }

View File

@@ -1,121 +0,0 @@
/*
* Copyright (C) 2013 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.example.android.supportv7.media;
import com.example.android.supportv7.R;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.v7.app.MediaRouteControllerDialog;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* This class serves as an example on how to customize the media router control
* dialog. It is derived from the standard MediaRouteControllerDialog with the
* following overrides:
*
* 1. Shows thumbnail/snapshot of the current item
*
* 2. For variable volume routes, only allow volume control via Volume Up/Down
* keys (to prevent accidental tapping on the volume adjust seekbar that sets
* volume to maximum)
*
* 3. Provides transport control buttons (play/pause, stop)
*/
public class SampleMediaRouteControllerDialog extends MediaRouteControllerDialog {
private static final String TAG = "SampleMediaRouteControllerDialog";
private final SampleMediaRouterActivity mActivity;
private final SessionManager mSessionManager;
private final Player mPlayer;
private ImageButton mPauseResumeButton;
private ImageButton mStopButton;
private ImageView mThumbnail;
private TextView mTextView;
private LinearLayout mInfoLayout;
private LinearLayout mVolumeLayout;
public SampleMediaRouteControllerDialog(Context context,
SessionManager manager, Player player) {
super(context);
mActivity = (SampleMediaRouterActivity) context;
mSessionManager = manager;
mPlayer = player;
}
@Override
public View onCreateMediaControlView(Bundle savedInstanceState) {
// Thumbnail and Track info
View v = getLayoutInflater().inflate(R.layout.sample_media_controller, null);
mInfoLayout = (LinearLayout)v.findViewById(R.id.media_route_info);
mTextView = (TextView)v.findViewById(R.id.track_info);
mThumbnail = (ImageView)v.findViewById(R.id.snapshot);
// Transport controls
mPauseResumeButton = (ImageButton)v.findViewById(R.id.pause_resume_button);
mPauseResumeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mActivity != null) {
mActivity.handleMediaKey(new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
}
}
});
mStopButton = (ImageButton)v.findViewById(R.id.stop_button);
mStopButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mActivity != null) {
mActivity.handleMediaKey(new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_MEDIA_STOP));
}
}
});
// update session status (will callback to updateUi at the end)
mSessionManager.updateStatus();
return v;
}
public void updateUi() {
String trackInfo = mPlayer.getDescription();
Bitmap snapshot = mPlayer.getSnapshot();
if (mPlayer.isRemotePlayback() && !trackInfo.isEmpty() && snapshot != null) {
mInfoLayout.setVisibility(View.VISIBLE);
mThumbnail.setImageBitmap(snapshot);
mTextView.setText(trackInfo);
} else {
mInfoLayout.setVisibility(View.GONE);
}
// show pause or resume icon depending on current state
mPauseResumeButton.setImageResource(mSessionManager.isPaused() ?
R.drawable.ic_media_play : R.drawable.ic_media_pause);
}
}

View File

@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.IntentFilter.MalformedMimeTypeException; import android.content.IntentFilter.MalformedMimeTypeException;
import android.content.IntentSender;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.media.AudioManager; import android.media.AudioManager;
@@ -173,6 +174,10 @@ final class SampleMediaRouteProvider extends MediaRouteProvider {
private void publishRoutes() { private void publishRoutes() {
Resources r = getContext().getResources(); Resources r = getContext().getResources();
Intent settingsIntent = new Intent(Intent.ACTION_MAIN);
settingsIntent.setClass(getContext(), SampleMediaRouteSettingsActivity.class);
IntentSender is = PendingIntent.getActivity(getContext(), 99, settingsIntent,
Intent.FLAG_ACTIVITY_NEW_TASK).getIntentSender();
MediaRouteDescriptor routeDescriptor1 = new MediaRouteDescriptor.Builder( MediaRouteDescriptor routeDescriptor1 = new MediaRouteDescriptor.Builder(
FIXED_VOLUME_ROUTE_ID, FIXED_VOLUME_ROUTE_ID,
@@ -183,6 +188,8 @@ final class SampleMediaRouteProvider extends MediaRouteProvider {
.setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE) .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
.setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED) .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED)
.setVolume(VOLUME_MAX) .setVolume(VOLUME_MAX)
.setCanDisconnect(true)
.setSettingsActivity(is)
.build(); .build();
MediaRouteDescriptor routeDescriptor2 = new MediaRouteDescriptor.Builder( MediaRouteDescriptor routeDescriptor2 = new MediaRouteDescriptor.Builder(
@@ -195,6 +202,7 @@ final class SampleMediaRouteProvider extends MediaRouteProvider {
.setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
.setVolumeMax(VOLUME_MAX) .setVolumeMax(VOLUME_MAX)
.setVolume(mVolume) .setVolume(mVolume)
.setSettingsActivity(is)
.build(); .build();
MediaRouteDescriptor routeDescriptor3 = new MediaRouteDescriptor.Builder( MediaRouteDescriptor routeDescriptor3 = new MediaRouteDescriptor.Builder(
@@ -207,6 +215,7 @@ final class SampleMediaRouteProvider extends MediaRouteProvider {
.setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
.setVolumeMax(VOLUME_MAX) .setVolumeMax(VOLUME_MAX)
.setVolume(mVolume) .setVolume(mVolume)
.setCanDisconnect(true)
.build(); .build();
MediaRouteDescriptor routeDescriptor4 = new MediaRouteDescriptor.Builder( MediaRouteDescriptor routeDescriptor4 = new MediaRouteDescriptor.Builder(

View File

@@ -0,0 +1,27 @@
/*
* 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.example.android.supportv7.media;
import android.support.v7.app.ActionBarActivity;
/**
* This activity is a dummy settings activity for the
* {@link SampleMediaRouteProvider}.
*/
public class SampleMediaRouteSettingsActivity extends ActionBarActivity {
}

View File

@@ -20,6 +20,7 @@ import com.example.android.supportv7.R;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.app.PendingIntent; import android.app.PendingIntent;
@@ -37,6 +38,7 @@ import android.support.v4.app.FragmentManager;
import android.support.v4.view.MenuItemCompat; import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.MediaRouteActionProvider; import android.support.v7.app.MediaRouteActionProvider;
import android.support.v7.app.MediaRouteButton;
import android.support.v7.app.MediaRouteControllerDialog; import android.support.v7.app.MediaRouteControllerDialog;
import android.support.v7.app.MediaRouteControllerDialogFragment; import android.support.v7.app.MediaRouteControllerDialogFragment;
import android.support.v7.app.MediaRouteDiscoveryFragment; import android.support.v7.app.MediaRouteDiscoveryFragment;
@@ -66,6 +68,7 @@ import android.widget.TabHost.TabSpec;
import android.widget.TabHost.OnTabChangeListener; import android.widget.TabHost.OnTabChangeListener;
import android.widget.SeekBar; import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.SeekBar.OnSeekBarChangeListener;
import java.io.File; import java.io.File;
/** /**
@@ -93,7 +96,6 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
private SeekBar mSeekBar; private SeekBar mSeekBar;
private boolean mNeedResume; private boolean mNeedResume;
private boolean mSeeking; private boolean mSeeking;
private SampleMediaRouteControllerDialog mControllerDialog;
private final Handler mHandler = new Handler(); private final Handler mHandler = new Handler();
private final Runnable mUpdateSeekRunnable = new Runnable() { private final Runnable mUpdateSeekRunnable = new Runnable() {
@@ -152,7 +154,6 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
} }
mPlayer.updatePresentation(); mPlayer.updatePresentation();
mPlayer.release(); mPlayer.release();
mControllerDialog = null;
} }
@Override @Override
@@ -372,6 +373,8 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
// Set up playback manager and player // Set up playback manager and player
mPlayer = Player.create(SampleMediaRouterActivity.this, mPlayer = Player.create(SampleMediaRouterActivity.this,
mMediaRouter.getSelectedRoute()); mMediaRouter.getSelectedRoute());
mMediaRouter.setMediaSessionCompat(mPlayer.getMediaSession());
mSessionManager.setPlayer(mPlayer); mSessionManager.setPlayer(mPlayer);
mSessionManager.setCallback(new SessionManager.Callback() { mSessionManager.setCallback(new SessionManager.Callback() {
@Override @Override
@@ -520,15 +523,7 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
mediaRouteActionProvider.setDialogFactory(new MediaRouteDialogFactory() { mediaRouteActionProvider.setDialogFactory(new MediaRouteDialogFactory() {
@Override @Override
public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() { public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() {
return new MediaRouteControllerDialogFragment() { return new ControllerDialogFragment(mPlayer);
@Override
public MediaRouteControllerDialog onCreateControllerDialog(
Context context, Bundle savedInstanceState) {
mControllerDialog = new SampleMediaRouteControllerDialog(
context, mSessionManager, mPlayer);
return mControllerDialog;
}
};
} }
}); });
@@ -564,8 +559,8 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
updatePlaylist(); updatePlaylist();
updateRouteDescription(); updateRouteDescription();
updateButtons(); updateButtons();
if (mControllerDialog != null) { if (mPlayer != null) {
mControllerDialog.updateUi(); mPlayer.updateMetadata();
} }
} }
@@ -745,4 +740,41 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
*/ */
public static class LightWithDarkActionBar extends SampleMediaRouterActivity { public static class LightWithDarkActionBar extends SampleMediaRouterActivity {
} }
public static class ControllerDialogFragment extends MediaRouteControllerDialogFragment {
private MediaRouteControllerDialog mControllerDialog;
private Player mPlayer;
public ControllerDialogFragment() {
super();
}
public ControllerDialogFragment(Player player) {
mPlayer = player;
}
@Override
public MediaRouteControllerDialog onCreateControllerDialog(
Context context, Bundle savedInstanceState) {
mControllerDialog = super.onCreateControllerDialog(context,
savedInstanceState);
if (mPlayer != null) {
mControllerDialog.setMediaSession(mPlayer.getMediaSession().getSessionToken());
}
mControllerDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
mControllerDialog = null;
}
});
return mControllerDialog;
}
public void setPlayer(Player player) {
mPlayer = player;
if (mControllerDialog != null) {
mControllerDialog.setMediaSession(mPlayer.getMediaSession().getSessionToken());
}
}
}
} }