Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.supportv7.media;
     18 
     19 import com.example.android.supportv7.R;
     20 
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.res.Resources;
     25 import android.app.PendingIntent;
     26 import android.media.AudioManager;
     27 import android.media.AudioManager.OnAudioFocusChangeListener;
     28 import android.media.MediaMetadataRetriever;
     29 import android.media.RemoteControlClient;
     30 import android.net.Uri;
     31 import android.os.Build;
     32 import android.os.Environment;
     33 import android.os.Handler;
     34 import android.os.Bundle;
     35 import android.os.SystemClock;
     36 import android.support.v4.app.FragmentManager;
     37 import android.support.v4.view.MenuItemCompat;
     38 import android.support.v7.app.ActionBarActivity;
     39 import android.support.v7.app.MediaRouteActionProvider;
     40 import android.support.v7.app.MediaRouteControllerDialog;
     41 import android.support.v7.app.MediaRouteControllerDialogFragment;
     42 import android.support.v7.app.MediaRouteDiscoveryFragment;
     43 import android.support.v7.app.MediaRouteDialogFactory;
     44 import android.support.v7.media.MediaControlIntent;
     45 import android.support.v7.media.MediaRouter;
     46 import android.support.v7.media.MediaRouter.Callback;
     47 import android.support.v7.media.MediaRouter.RouteInfo;
     48 import android.support.v7.media.MediaRouter.ProviderInfo;
     49 import android.support.v7.media.MediaRouteSelector;
     50 import android.support.v7.media.MediaItemStatus;
     51 import android.util.Log;
     52 import android.view.KeyEvent;
     53 import android.view.Menu;
     54 import android.view.MenuItem;
     55 import android.view.View;
     56 import android.view.View.OnClickListener;
     57 import android.view.ViewGroup;
     58 import android.widget.AdapterView;
     59 import android.widget.AdapterView.OnItemClickListener;
     60 import android.widget.ArrayAdapter;
     61 import android.widget.ImageButton;
     62 import android.widget.ListView;
     63 import android.widget.TextView;
     64 import android.widget.TabHost;
     65 import android.widget.TabHost.TabSpec;
     66 import android.widget.TabHost.OnTabChangeListener;
     67 import android.widget.SeekBar;
     68 import android.widget.SeekBar.OnSeekBarChangeListener;
     69 import java.io.File;
     70 
     71 /**
     72  * <h3>Media Router Support Activity</h3>
     73  *
     74  * <p>
     75  * This demonstrates how to use the {@link MediaRouter} API to build an
     76  * application that allows the user to send content to various rendering
     77  * targets.
     78  * </p>
     79  */
     80 public class SampleMediaRouterActivity extends ActionBarActivity {
     81     private static final String TAG = "SampleMediaRouterActivity";
     82     private static final String DISCOVERY_FRAGMENT_TAG = "DiscoveryFragment";
     83 
     84     private MediaRouter mMediaRouter;
     85     private MediaRouteSelector mSelector;
     86     private LibraryAdapter mLibraryItems;
     87     private PlaylistAdapter mPlayListItems;
     88     private TextView mInfoTextView;
     89     private ListView mLibraryView;
     90     private ListView mPlayListView;
     91     private ImageButton mPauseResumeButton;
     92     private ImageButton mStopButton;
     93     private SeekBar mSeekBar;
     94     private boolean mNeedResume;
     95     private boolean mSeeking;
     96     private SampleMediaRouteControllerDialog mControllerDialog;
     97 
     98     private final Handler mHandler = new Handler();
     99     private final Runnable mUpdateSeekRunnable = new Runnable() {
    100         @Override
    101         public void run() {
    102             updateProgress();
    103             // update Ui every 1 second
    104             mHandler.postDelayed(this, 1000);
    105         }
    106     };
    107 
    108     private final SessionManager mSessionManager = new SessionManager("app");
    109     private Player mPlayer;
    110 
    111     private final MediaRouter.Callback mMediaRouterCB = new MediaRouter.Callback() {
    112         // Return a custom callback that will simply log all of the route events
    113         // for demonstration purposes.
    114         @Override
    115         public void onRouteAdded(MediaRouter router, RouteInfo route) {
    116             Log.d(TAG, "onRouteAdded: route=" + route);
    117         }
    118 
    119         @Override
    120         public void onRouteChanged(MediaRouter router, RouteInfo route) {
    121             Log.d(TAG, "onRouteChanged: route=" + route);
    122         }
    123 
    124         @Override
    125         public void onRouteRemoved(MediaRouter router, RouteInfo route) {
    126             Log.d(TAG, "onRouteRemoved: route=" + route);
    127         }
    128 
    129         @Override
    130         public void onRouteSelected(MediaRouter router, RouteInfo route) {
    131             Log.d(TAG, "onRouteSelected: route=" + route);
    132 
    133             mPlayer = Player.create(SampleMediaRouterActivity.this, route);
    134             mPlayer.updatePresentation();
    135             mSessionManager.setPlayer(mPlayer);
    136             mSessionManager.unsuspend();
    137 
    138             registerRCC();
    139             updateUi();
    140         }
    141 
    142         @Override
    143         public void onRouteUnselected(MediaRouter router, RouteInfo route) {
    144             Log.d(TAG, "onRouteUnselected: route=" + route);
    145             unregisterRCC();
    146 
    147             PlaylistItem item = getCheckedPlaylistItem();
    148             if (item != null) {
    149                 long pos = item.getPosition() + (mSessionManager.isPaused() ?
    150                         0 : (SystemClock.elapsedRealtime() - item.getTimestamp()));
    151                 mSessionManager.suspend(pos);
    152             }
    153             mPlayer.updatePresentation();
    154             mPlayer.release();
    155             mControllerDialog = null;
    156         }
    157 
    158         @Override
    159         public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
    160             Log.d(TAG, "onRouteVolumeChanged: route=" + route);
    161         }
    162 
    163         @Override
    164         public void onRoutePresentationDisplayChanged(
    165                 MediaRouter router, RouteInfo route) {
    166             Log.d(TAG, "onRoutePresentationDisplayChanged: route=" + route);
    167             mPlayer.updatePresentation();
    168         }
    169 
    170         @Override
    171         public void onProviderAdded(MediaRouter router, ProviderInfo provider) {
    172             Log.d(TAG, "onRouteProviderAdded: provider=" + provider);
    173         }
    174 
    175         @Override
    176         public void onProviderRemoved(MediaRouter router, ProviderInfo provider) {
    177             Log.d(TAG, "onRouteProviderRemoved: provider=" + provider);
    178         }
    179 
    180         @Override
    181         public void onProviderChanged(MediaRouter router, ProviderInfo provider) {
    182             Log.d(TAG, "onRouteProviderChanged: provider=" + provider);
    183         }
    184     };
    185 
    186     private RemoteControlClient mRemoteControlClient;
    187     private ComponentName mEventReceiver;
    188     private AudioManager mAudioManager;
    189     private PendingIntent mMediaPendingIntent;
    190     private final OnAudioFocusChangeListener mAfChangeListener =
    191             new OnAudioFocusChangeListener() {
    192         @Override
    193         public void onAudioFocusChange(int focusChange) {
    194             if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
    195                 Log.d(TAG, "onAudioFocusChange: LOSS_TRANSIENT");
    196             } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
    197                 Log.d(TAG, "onAudioFocusChange: AUDIOFOCUS_GAIN");
    198             } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
    199                 Log.d(TAG, "onAudioFocusChange: AUDIOFOCUS_LOSS");
    200             }
    201         }
    202     };
    203 
    204     @Override
    205     protected void onCreate(Bundle savedInstanceState) {
    206         // Be sure to call the super class.
    207         super.onCreate(savedInstanceState);
    208 
    209         // Get the media router service.
    210         mMediaRouter = MediaRouter.getInstance(this);
    211 
    212         // Create a route selector for the type of routes that we care about.
    213         mSelector = new MediaRouteSelector.Builder()
    214                 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
    215                 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
    216                 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
    217                 .addControlCategory(SampleMediaRouteProvider.CATEGORY_SAMPLE_ROUTE)
    218                 .build();
    219 
    220         // Add a fragment to take care of media route discovery.
    221         // This fragment automatically adds or removes a callback whenever the activity
    222         // is started or stopped.
    223         FragmentManager fm = getSupportFragmentManager();
    224         DiscoveryFragment fragment = (DiscoveryFragment)fm.findFragmentByTag(
    225                 DISCOVERY_FRAGMENT_TAG);
    226         if (fragment == null) {
    227             fragment = new DiscoveryFragment(mMediaRouterCB);
    228             fragment.setRouteSelector(mSelector);
    229             fm.beginTransaction()
    230                     .add(fragment, DISCOVERY_FRAGMENT_TAG)
    231                     .commit();
    232         } else {
    233             fragment.setCallback(mMediaRouterCB);
    234             fragment.setRouteSelector(mSelector);
    235         }
    236 
    237         // Populate an array adapter with streaming media items.
    238         String[] mediaNames = getResources().getStringArray(R.array.media_names);
    239         String[] mediaUris = getResources().getStringArray(R.array.media_uris);
    240         mLibraryItems = new LibraryAdapter();
    241         for (int i = 0; i < mediaNames.length; i++) {
    242             mLibraryItems.add(new MediaItem(
    243                     "[streaming] "+mediaNames[i], Uri.parse(mediaUris[i]), "video/mp4"));
    244         }
    245 
    246         // Scan local external storage directory for media files.
    247         File externalDir = Environment.getExternalStorageDirectory();
    248         if (externalDir != null) {
    249             File list[] = externalDir.listFiles();
    250             if (list != null) {
    251                 for (int i = 0; i < list.length; i++) {
    252                     String filename = list[i].getName();
    253                     if (filename.matches(".*\\.(m4v|mp4)")) {
    254                         mLibraryItems.add(new MediaItem("[local] " + filename,
    255                                 Uri.fromFile(list[i]), "video/mp4"));
    256                     }
    257                 }
    258             }
    259         }
    260 
    261         mPlayListItems = new PlaylistAdapter();
    262 
    263         // Initialize the layout.
    264         setContentView(R.layout.sample_media_router);
    265 
    266         TabHost tabHost=(TabHost)findViewById(R.id.tabHost);
    267         tabHost.setup();
    268         String tabName = getResources().getString(R.string.library_tab_text);
    269         TabSpec spec1=tabHost.newTabSpec(tabName);
    270         spec1.setContent(R.id.tab1);
    271         spec1.setIndicator(tabName);
    272 
    273         tabName = getResources().getString(R.string.playlist_tab_text);
    274         TabSpec spec2=tabHost.newTabSpec(tabName);
    275         spec2.setIndicator(tabName);
    276         spec2.setContent(R.id.tab2);
    277 
    278         tabName = getResources().getString(R.string.info_tab_text);
    279         TabSpec spec3=tabHost.newTabSpec(tabName);
    280         spec3.setIndicator(tabName);
    281         spec3.setContent(R.id.tab3);
    282 
    283         tabHost.addTab(spec1);
    284         tabHost.addTab(spec2);
    285         tabHost.addTab(spec3);
    286         tabHost.setOnTabChangedListener(new OnTabChangeListener() {
    287             @Override
    288             public void onTabChanged(String arg0) {
    289                 updateUi();
    290             }
    291         });
    292 
    293         mLibraryView = (ListView) findViewById(R.id.media);
    294         mLibraryView.setAdapter(mLibraryItems);
    295         mLibraryView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    296         mLibraryView.setOnItemClickListener(new OnItemClickListener() {
    297             @Override
    298             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    299                 updateButtons();
    300             }
    301         });
    302 
    303         mPlayListView = (ListView) findViewById(R.id.playlist);
    304         mPlayListView.setAdapter(mPlayListItems);
    305         mPlayListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    306         mPlayListView.setOnItemClickListener(new OnItemClickListener() {
    307             @Override
    308             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    309                 updateButtons();
    310             }
    311         });
    312 
    313         mInfoTextView = (TextView) findViewById(R.id.info);
    314 
    315         mPauseResumeButton = (ImageButton)findViewById(R.id.pause_resume_button);
    316         mPauseResumeButton.setOnClickListener(new OnClickListener() {
    317             @Override
    318             public void onClick(View v) {
    319                 if (mSessionManager.isPaused()) {
    320                     mSessionManager.resume();
    321                 } else {
    322                     mSessionManager.pause();
    323                 }
    324             }
    325         });
    326 
    327         mStopButton = (ImageButton)findViewById(R.id.stop_button);
    328         mStopButton.setOnClickListener(new OnClickListener() {
    329             @Override
    330             public void onClick(View v) {
    331                 mSessionManager.stop();
    332             }
    333         });
    334 
    335         mSeekBar = (SeekBar) findViewById(R.id.seekbar);
    336         mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
    337             @Override
    338             public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    339                 PlaylistItem item = getCheckedPlaylistItem();
    340                 if (fromUser && item != null && item.getDuration() > 0) {
    341                     long pos = progress * item.getDuration() / 100;
    342                     mSessionManager.seek(item.getItemId(), pos);
    343                     item.setPosition(pos);
    344                     item.setTimestamp(SystemClock.elapsedRealtime());
    345                 }
    346             }
    347             @Override
    348             public void onStartTrackingTouch(SeekBar seekBar) {
    349                 mSeeking = true;
    350             }
    351             @Override
    352             public void onStopTrackingTouch(SeekBar seekBar) {
    353                 mSeeking = false;
    354                 updateUi();
    355             }
    356         });
    357 
    358         // Schedule Ui update
    359         mHandler.postDelayed(mUpdateSeekRunnable, 1000);
    360 
    361         // Build the PendingIntent for the remote control client
    362         mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    363         mEventReceiver = new ComponentName(getPackageName(),
    364                 SampleMediaButtonReceiver.class.getName());
    365         Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
    366         mediaButtonIntent.setComponent(mEventReceiver);
    367         mMediaPendingIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, 0);
    368 
    369         // Create and register the remote control client
    370         registerRCC();
    371 
    372         // Set up playback manager and player
    373         mPlayer = Player.create(SampleMediaRouterActivity.this,
    374                 mMediaRouter.getSelectedRoute());
    375         mSessionManager.setPlayer(mPlayer);
    376         mSessionManager.setCallback(new SessionManager.Callback() {
    377             @Override
    378             public void onStatusChanged() {
    379                 updateUi();
    380             }
    381 
    382             @Override
    383             public void onItemChanged(PlaylistItem item) {
    384             }
    385         });
    386 
    387         updateUi();
    388     }
    389 
    390     private void registerRCC() {
    391         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
    392             // Create the RCC and register with AudioManager and MediaRouter
    393             mAudioManager.requestAudioFocus(mAfChangeListener,
    394                     AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
    395             mAudioManager.registerMediaButtonEventReceiver(mEventReceiver);
    396             mRemoteControlClient = new RemoteControlClient(mMediaPendingIntent);
    397             mAudioManager.registerRemoteControlClient(mRemoteControlClient);
    398             mMediaRouter.addRemoteControlClient(mRemoteControlClient);
    399             SampleMediaButtonReceiver.setActivity(SampleMediaRouterActivity.this);
    400             mRemoteControlClient.setTransportControlFlags(
    401                     RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE);
    402             mRemoteControlClient.setPlaybackState(
    403                     RemoteControlClient.PLAYSTATE_PLAYING);
    404         }
    405     }
    406 
    407     private void unregisterRCC() {
    408         // Unregister the RCC with AudioManager and MediaRouter
    409         if (mRemoteControlClient != null) {
    410             mRemoteControlClient.setTransportControlFlags(0);
    411             mAudioManager.abandonAudioFocus(mAfChangeListener);
    412             mAudioManager.unregisterMediaButtonEventReceiver(mEventReceiver);
    413             mAudioManager.unregisterRemoteControlClient(mRemoteControlClient);
    414             mMediaRouter.removeRemoteControlClient(mRemoteControlClient);
    415             SampleMediaButtonReceiver.setActivity(null);
    416             mRemoteControlClient = null;
    417         }
    418     }
    419 
    420     public boolean handleMediaKey(KeyEvent event) {
    421         if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
    422             switch (event.getKeyCode()) {
    423                 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
    424                 {
    425                     Log.d(TAG, "Received Play/Pause event from RemoteControlClient");
    426                     if (mSessionManager.isPaused()) {
    427                         mSessionManager.resume();
    428                     } else {
    429                         mSessionManager.pause();
    430                     }
    431                     return true;
    432                 }
    433                 case KeyEvent.KEYCODE_MEDIA_PLAY:
    434                 {
    435                     Log.d(TAG, "Received Play event from RemoteControlClient");
    436                     if (mSessionManager.isPaused()) {
    437                         mSessionManager.resume();
    438                     }
    439                     return true;
    440                 }
    441                 case KeyEvent.KEYCODE_MEDIA_PAUSE:
    442                 {
    443                     Log.d(TAG, "Received Pause event from RemoteControlClient");
    444                     if (!mSessionManager.isPaused()) {
    445                         mSessionManager.pause();
    446                     }
    447                     return true;
    448                 }
    449                 case KeyEvent.KEYCODE_MEDIA_STOP:
    450                 {
    451                     Log.d(TAG, "Received Stop event from RemoteControlClient");
    452                     mSessionManager.stop();
    453                     return true;
    454                 }
    455                 default:
    456                     break;
    457             }
    458         }
    459         return false;
    460     }
    461 
    462     @Override
    463     public boolean onKeyDown(int keyCode, KeyEvent event) {
    464         return handleMediaKey(event) || super.onKeyDown(keyCode, event);
    465     }
    466 
    467     @Override
    468     public boolean onKeyUp(int keyCode, KeyEvent event) {
    469         return handleMediaKey(event) || super.onKeyUp(keyCode, event);
    470     }
    471 
    472     @Override
    473     public void onStart() {
    474         // Be sure to call the super class.
    475         super.onStart();
    476     }
    477 
    478     @Override
    479     public void onPause() {
    480         // pause media player for local playback case only
    481         if (!mPlayer.isRemotePlayback() && !mSessionManager.isPaused()) {
    482             mNeedResume = true;
    483             mSessionManager.pause();
    484         }
    485         super.onPause();
    486     }
    487 
    488     @Override
    489     public void onResume() {
    490         // resume media player for local playback case only
    491         if (!mPlayer.isRemotePlayback() && mNeedResume) {
    492             mSessionManager.resume();
    493             mNeedResume = false;
    494         }
    495         super.onResume();
    496     }
    497 
    498     @Override
    499     public void onDestroy() {
    500         // Unregister the remote control client
    501         unregisterRCC();
    502 
    503         mSessionManager.stop();
    504         mPlayer.release();
    505         super.onDestroy();
    506     }
    507 
    508     @Override
    509     public boolean onCreateOptionsMenu(Menu menu) {
    510         // Be sure to call the super class.
    511         super.onCreateOptionsMenu(menu);
    512 
    513         // Inflate the menu and configure the media router action provider.
    514         getMenuInflater().inflate(R.menu.sample_media_router_menu, menu);
    515 
    516         MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
    517         MediaRouteActionProvider mediaRouteActionProvider =
    518                 (MediaRouteActionProvider)MenuItemCompat.getActionProvider(mediaRouteMenuItem);
    519         mediaRouteActionProvider.setRouteSelector(mSelector);
    520         mediaRouteActionProvider.setDialogFactory(new MediaRouteDialogFactory() {
    521             @Override
    522             public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() {
    523                 return new MediaRouteControllerDialogFragment() {
    524                     @Override
    525                     public MediaRouteControllerDialog onCreateControllerDialog(
    526                             Context context, Bundle savedInstanceState) {
    527                         mControllerDialog = new SampleMediaRouteControllerDialog(
    528                                 context, mSessionManager, mPlayer);
    529                         return mControllerDialog;
    530                     }
    531                 };
    532             }
    533         });
    534 
    535         // Return true to show the menu.
    536         return true;
    537     }
    538 
    539     private void updateProgress() {
    540         // Estimate content position from last status time and elapsed time.
    541         // (Note this might be slightly out of sync with remote side, however
    542         // it avoids frequent polling the MRP.)
    543         int progress = 0;
    544         PlaylistItem item = getCheckedPlaylistItem();
    545         if (item != null) {
    546             int state = item.getState();
    547             long duration = item.getDuration();
    548             if (duration <= 0) {
    549                 if (state == MediaItemStatus.PLAYBACK_STATE_PLAYING
    550                         || state == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
    551                     mSessionManager.updateStatus();
    552                 }
    553             } else {
    554                 long position = item.getPosition();
    555                 long timeDelta = mSessionManager.isPaused() ? 0 :
    556                         (SystemClock.elapsedRealtime() - item.getTimestamp());
    557                 progress = (int)(100.0 * (position + timeDelta) / duration);
    558             }
    559         }
    560         mSeekBar.setProgress(progress);
    561     }
    562 
    563     private void updateUi() {
    564         updatePlaylist();
    565         updateRouteDescription();
    566         updateButtons();
    567         if (mControllerDialog != null) {
    568             mControllerDialog.updateUi();
    569         }
    570     }
    571 
    572     private void updatePlaylist() {
    573         mPlayListItems.clear();
    574         for (PlaylistItem item : mSessionManager.getPlaylist()) {
    575             mPlayListItems.add(item);
    576         }
    577         mPlayListView.invalidate();
    578     }
    579 
    580     private void updateRouteDescription() {
    581         RouteInfo route = mMediaRouter.getSelectedRoute();
    582         mInfoTextView.setText("Currently selected route:"
    583                 + "\nName: " + route.getName()
    584                 + "\nProvider: " + route.getProvider().getPackageName()
    585                 + "\nDescription: " + route.getDescription());
    586     }
    587 
    588     private void updateButtons() {
    589         MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute();
    590         // show pause or resume icon depending on current state
    591         mPauseResumeButton.setImageResource(mSessionManager.isPaused() ?
    592                 R.drawable.ic_media_play : R.drawable.ic_media_pause);
    593         // only enable seek bar when duration is known
    594         PlaylistItem item = getCheckedPlaylistItem();
    595         mSeekBar.setEnabled(item != null && item.getDuration() > 0);
    596         if (mRemoteControlClient != null) {
    597             mRemoteControlClient.setPlaybackState(mSessionManager.isPaused() ?
    598                     RemoteControlClient.PLAYSTATE_PAUSED :
    599                         RemoteControlClient.PLAYSTATE_PLAYING);
    600         }
    601     }
    602 
    603     private PlaylistItem getCheckedPlaylistItem() {
    604         int count = mPlayListView.getCount();
    605         int index = mPlayListView.getCheckedItemPosition();
    606         if (count > 0) {
    607             if (index < 0 || index >= count) {
    608                 index = 0;
    609                 mPlayListView.setItemChecked(0, true);
    610             }
    611             return mPlayListItems.getItem(index);
    612         }
    613         return null;
    614     }
    615 
    616     public static final class DiscoveryFragment extends MediaRouteDiscoveryFragment {
    617         private static final String TAG = "DiscoveryFragment";
    618         private Callback mCallback;
    619 
    620         public DiscoveryFragment() {
    621             mCallback = null;
    622         }
    623 
    624         public DiscoveryFragment(Callback cb) {
    625             mCallback = cb;
    626         }
    627 
    628         public void setCallback(Callback cb) {
    629             mCallback = cb;
    630         }
    631 
    632         @Override
    633         public Callback onCreateCallback() {
    634             return mCallback;
    635         }
    636 
    637         @Override
    638         public int onPrepareCallbackFlags() {
    639             // Add the CALLBACK_FLAG_UNFILTERED_EVENTS flag to ensure that we will
    640             // observe and log all route events including those that are for routes
    641             // that do not match our selector.  This is only for demonstration purposes
    642             // and should not be needed by most applications.
    643             return super.onPrepareCallbackFlags()
    644                     | MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS;
    645         }
    646     }
    647 
    648     private static final class MediaItem {
    649         public final String mName;
    650         public final Uri mUri;
    651         public final String mMime;
    652 
    653         public MediaItem(String name, Uri uri, String mime) {
    654             mName = name;
    655             mUri = uri;
    656             mMime = mime;
    657         }
    658 
    659         @Override
    660         public String toString() {
    661             return mName;
    662         }
    663     }
    664 
    665     private final class LibraryAdapter extends ArrayAdapter<MediaItem> {
    666         public LibraryAdapter() {
    667             super(SampleMediaRouterActivity.this, R.layout.media_item);
    668         }
    669 
    670         @Override
    671         public View getView(int position, View convertView, ViewGroup parent) {
    672             final View v;
    673             if (convertView == null) {
    674                 v = getLayoutInflater().inflate(R.layout.media_item, null);
    675             } else {
    676                 v = convertView;
    677             }
    678 
    679             final MediaItem item = getItem(position);
    680 
    681             TextView tv = (TextView)v.findViewById(R.id.item_text);
    682             tv.setText(item.mName);
    683 
    684             ImageButton b = (ImageButton)v.findViewById(R.id.item_action);
    685             b.setImageResource(R.drawable.ic_menu_add);
    686             b.setTag(item);
    687             b.setOnClickListener(new OnClickListener() {
    688                 @Override
    689                 public void onClick(View v) {
    690                     if (item != null) {
    691                         mSessionManager.add(item.mUri, item.mMime);
    692                     }
    693                 }
    694             });
    695 
    696             return v;
    697         }
    698     }
    699 
    700     private final class PlaylistAdapter extends ArrayAdapter<PlaylistItem> {
    701         public PlaylistAdapter() {
    702             super(SampleMediaRouterActivity.this, R.layout.media_item);
    703         }
    704 
    705         @Override
    706         public View getView(int position, View convertView, ViewGroup parent) {
    707             final View v;
    708             if (convertView == null) {
    709                 v = getLayoutInflater().inflate(R.layout.media_item, null);
    710             } else {
    711                 v = convertView;
    712             }
    713 
    714             final PlaylistItem item = getItem(position);
    715 
    716             TextView tv = (TextView)v.findViewById(R.id.item_text);
    717             tv.setText(item.toString());
    718 
    719             ImageButton b = (ImageButton)v.findViewById(R.id.item_action);
    720             b.setImageResource(R.drawable.ic_menu_delete);
    721             b.setTag(item);
    722             b.setOnClickListener(new OnClickListener() {
    723                 @Override
    724                 public void onClick(View v) {
    725                     if (item != null) {
    726                         mSessionManager.remove(item.getItemId());
    727                     }
    728                 }
    729             });
    730 
    731             return v;
    732         }
    733     }
    734 
    735     /**
    736      * Trivial subclass of this activity used to provide another copy of the
    737      * same activity using a light theme instead of the dark theme.
    738      */
    739     public static class Light extends SampleMediaRouterActivity {
    740     }
    741 
    742     /**
    743      * Trivial subclass of this activity used to provide another copy of the
    744      * same activity using a light theme with dark action bar instead of the dark theme.
    745      */
    746     public static class LightWithDarkActionBar extends SampleMediaRouterActivity {
    747     }
    748 }
    749