am cdc171cd: Merge "Update the MediaRouter support demo for new UIs" into lmp-mr1-dev
* commit 'cdc171cd042d9ed86b4a8f5d12e561e62cd66106': Update the MediaRouter support demo for new UIs
This commit is contained in:
@@ -86,6 +86,14 @@
|
||||
</intent-filter>
|
||||
</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"
|
||||
android:label="@string/sample_media_route_provider_service"
|
||||
android:process=":mrp">
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<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>
|
||||
<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="playlist_tab_text">Playlist</string>
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.support.v4.media.session.MediaSessionCompat;
|
||||
import android.support.v7.media.MediaRouter.RouteInfo;
|
||||
import android.support.v7.media.MediaItemStatus;
|
||||
import android.util.Log;
|
||||
@@ -56,12 +57,6 @@ public abstract class LocalPlayer extends Player implements
|
||||
private static final String TAG = "LocalPlayer";
|
||||
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 Handler mHandler = new Handler();
|
||||
private MediaPlayer mMediaPlayer;
|
||||
@@ -109,6 +104,11 @@ public abstract class LocalPlayer extends Player implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaSessionCompat getMediaSession() {
|
||||
return mMediaSession;
|
||||
}
|
||||
|
||||
// Player
|
||||
@Override
|
||||
public void play(final PlaylistItem item) {
|
||||
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
package com.example.android.supportv7.media;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.content.Context;
|
||||
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.MediaRouter.RouteInfo;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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 MediaSessionCompat mMediaSession;
|
||||
protected MediaSessionCallback mSessionCallback;
|
||||
|
||||
public abstract boolean isRemotePlayback();
|
||||
public abstract boolean isQueuingSupported();
|
||||
@@ -71,10 +86,71 @@ public abstract class Player {
|
||||
} else {
|
||||
player = new LocalPlayer.OverlayPlayer(context);
|
||||
}
|
||||
player.initMediaSession(context);
|
||||
player.connect(route);
|
||||
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 {
|
||||
void onError();
|
||||
void onCompletion();
|
||||
|
||||
@@ -140,6 +140,9 @@ public class RemotePlayer extends Player {
|
||||
}
|
||||
if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
|
||||
pause();
|
||||
publishState(STATE_PAUSED);
|
||||
} else {
|
||||
publishState(STATE_PLAYING);
|
||||
}
|
||||
if (mCallback != null) {
|
||||
mCallback.onPlaylistChanged();
|
||||
@@ -214,6 +217,7 @@ public class RemotePlayer extends Player {
|
||||
if (mCallback != null) {
|
||||
mCallback.onPlaylistChanged();
|
||||
}
|
||||
publishState(STATE_PAUSED);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -239,6 +243,7 @@ public class RemotePlayer extends Player {
|
||||
if (mCallback != null) {
|
||||
mCallback.onPlaylistChanged();
|
||||
}
|
||||
publishState(STATE_PLAYING);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -254,6 +259,7 @@ public class RemotePlayer extends Player {
|
||||
// ignore if no session
|
||||
return;
|
||||
}
|
||||
publishState(STATE_IDLE);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "stop");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IntentFilter.MalformedMimeTypeException;
|
||||
import android.content.IntentSender;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.AudioManager;
|
||||
@@ -173,6 +174,10 @@ final class SampleMediaRouteProvider extends MediaRouteProvider {
|
||||
|
||||
private void publishRoutes() {
|
||||
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(
|
||||
FIXED_VOLUME_ROUTE_ID,
|
||||
@@ -183,6 +188,8 @@ final class SampleMediaRouteProvider extends MediaRouteProvider {
|
||||
.setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
|
||||
.setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED)
|
||||
.setVolume(VOLUME_MAX)
|
||||
.setCanDisconnect(true)
|
||||
.setSettingsActivity(is)
|
||||
.build();
|
||||
|
||||
MediaRouteDescriptor routeDescriptor2 = new MediaRouteDescriptor.Builder(
|
||||
@@ -195,6 +202,7 @@ final class SampleMediaRouteProvider extends MediaRouteProvider {
|
||||
.setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
|
||||
.setVolumeMax(VOLUME_MAX)
|
||||
.setVolume(mVolume)
|
||||
.setSettingsActivity(is)
|
||||
.build();
|
||||
|
||||
MediaRouteDescriptor routeDescriptor3 = new MediaRouteDescriptor.Builder(
|
||||
@@ -207,6 +215,7 @@ final class SampleMediaRouteProvider extends MediaRouteProvider {
|
||||
.setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
|
||||
.setVolumeMax(VOLUME_MAX)
|
||||
.setVolume(mVolume)
|
||||
.setCanDisconnect(true)
|
||||
.build();
|
||||
|
||||
MediaRouteDescriptor routeDescriptor4 = new MediaRouteDescriptor.Builder(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import com.example.android.supportv7.R;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.app.PendingIntent;
|
||||
@@ -37,6 +38,7 @@ import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.support.v7.app.MediaRouteActionProvider;
|
||||
import android.support.v7.app.MediaRouteButton;
|
||||
import android.support.v7.app.MediaRouteControllerDialog;
|
||||
import android.support.v7.app.MediaRouteControllerDialogFragment;
|
||||
import android.support.v7.app.MediaRouteDiscoveryFragment;
|
||||
@@ -66,6 +68,7 @@ import android.widget.TabHost.TabSpec;
|
||||
import android.widget.TabHost.OnTabChangeListener;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
@@ -93,7 +96,6 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
|
||||
private SeekBar mSeekBar;
|
||||
private boolean mNeedResume;
|
||||
private boolean mSeeking;
|
||||
private SampleMediaRouteControllerDialog mControllerDialog;
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
private final Runnable mUpdateSeekRunnable = new Runnable() {
|
||||
@@ -152,7 +154,6 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
|
||||
}
|
||||
mPlayer.updatePresentation();
|
||||
mPlayer.release();
|
||||
mControllerDialog = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -372,6 +373,8 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
|
||||
// Set up playback manager and player
|
||||
mPlayer = Player.create(SampleMediaRouterActivity.this,
|
||||
mMediaRouter.getSelectedRoute());
|
||||
mMediaRouter.setMediaSessionCompat(mPlayer.getMediaSession());
|
||||
|
||||
mSessionManager.setPlayer(mPlayer);
|
||||
mSessionManager.setCallback(new SessionManager.Callback() {
|
||||
@Override
|
||||
@@ -520,15 +523,7 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
|
||||
mediaRouteActionProvider.setDialogFactory(new MediaRouteDialogFactory() {
|
||||
@Override
|
||||
public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() {
|
||||
return new MediaRouteControllerDialogFragment() {
|
||||
@Override
|
||||
public MediaRouteControllerDialog onCreateControllerDialog(
|
||||
Context context, Bundle savedInstanceState) {
|
||||
mControllerDialog = new SampleMediaRouteControllerDialog(
|
||||
context, mSessionManager, mPlayer);
|
||||
return mControllerDialog;
|
||||
}
|
||||
};
|
||||
return new ControllerDialogFragment(mPlayer);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -564,8 +559,8 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
|
||||
updatePlaylist();
|
||||
updateRouteDescription();
|
||||
updateButtons();
|
||||
if (mControllerDialog != null) {
|
||||
mControllerDialog.updateUi();
|
||||
if (mPlayer != null) {
|
||||
mPlayer.updateMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -745,4 +740,41 @@ public class SampleMediaRouterActivity extends ActionBarActivity {
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user