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