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.ActionBarActivity;
     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 ActionBarActivity {
     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(mMediaRouterCB);
    227             fragment.setRouteSelector(mSelector);
    228             fm.beginTransaction().add(fragment, DISCOVERY_FRAGMENT_TAG).commit();
    229         } else {
    230             fragment.setCallback(mMediaRouterCB);
    231             fragment.setRouteSelector(mSelector);
    232         }
    233 
    234         // Populate an array adapter with streaming media items.
    235         String[] mediaNames = getResources().getStringArray(R.array.media_names);
    236         String[] mediaUris = getResources().getStringArray(R.array.media_uris);
    237         mLibraryItems = new LibraryAdapter();
    238         for (int i = 0; i < mediaNames.length; i++) {
    239             mLibraryItems.add(new MediaItem(
    240                     "[streaming] " + mediaNames[i], Uri.parse(mediaUris[i]), "video/mp4"));
    241         }
    242 
    243         // Scan local external storage directory for media files.
    244         File externalDir = Environment.getExternalStorageDirectory();
    245         if (externalDir != null) {
    246             File list[] = externalDir.listFiles();
    247             if (list != null) {
    248                 for (int i = 0; i < list.length; i++) {
    249                     String filename = list[i].getName();
    250                     if (filename.matches(".*\\.(m4v|mp4)")) {
    251                         mLibraryItems.add(new MediaItem(
    252                                 "[local] " + filename, Uri.fromFile(list[i]), "video/mp4"));
    253                     }
    254                 }
    255             }
    256         }
    257 
    258         mPlayListItems = new PlaylistAdapter();
    259 
    260         // Initialize the layout.
    261         setContentView(R.layout.sample_media_router);
    262 
    263         TabHost tabHost = (TabHost) findViewById(R.id.tabHost);
    264         tabHost.setup();
    265         String tabName = getResources().getString(R.string.library_tab_text);
    266         TabSpec spec1 = tabHost.newTabSpec(tabName);
    267         spec1.setContent(R.id.tab1);
    268         spec1.setIndicator(tabName);
    269 
    270         tabName = getResources().getString(R.string.playlist_tab_text);
    271         TabSpec spec2 = tabHost.newTabSpec(tabName);
    272         spec2.setIndicator(tabName);
    273         spec2.setContent(R.id.tab2);
    274 
    275         tabName = getResources().getString(R.string.statistics_tab_text);
    276         TabSpec spec3 = tabHost.newTabSpec(tabName);
    277         spec3.setIndicator(tabName);
    278         spec3.setContent(R.id.tab3);
    279 
    280         tabHost.addTab(spec1);
    281         tabHost.addTab(spec2);
    282         tabHost.addTab(spec3);
    283         tabHost.setOnTabChangedListener(new OnTabChangeListener() {
    284             @Override
    285             public void onTabChanged(String arg0) {
    286                 updateUi();
    287             }
    288         });
    289 
    290         mLibraryView = (ListView) findViewById(R.id.media);
    291         mLibraryView.setAdapter(mLibraryItems);
    292         mLibraryView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    293         mLibraryView.setOnItemClickListener(new OnItemClickListener() {
    294             @Override
    295             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    296                 updateButtons();
    297             }
    298         });
    299 
    300         mPlayListView = (ListView) findViewById(R.id.playlist);
    301         mPlayListView.setAdapter(mPlayListItems);
    302         mPlayListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    303         mPlayListView.setOnItemClickListener(new OnItemClickListener() {
    304             @Override
    305             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    306                 updateButtons();
    307             }
    308         });
    309 
    310         mInfoTextView = (TextView) findViewById(R.id.info);
    311 
    312         mPauseResumeButton = (ImageButton) findViewById(R.id.pause_resume_button);
    313         mPauseResumeButton.setOnClickListener(new OnClickListener() {
    314             @Override
    315             public void onClick(View v) {
    316                 mPaused = !mPaused;
    317                 if (mPaused) {
    318                     mSessionManager.pause();
    319                 } else {
    320                     mSessionManager.resume();
    321                 }
    322             }
    323         });
    324 
    325         mStopButton = (ImageButton) findViewById(R.id.stop_button);
    326         mStopButton.setOnClickListener(new OnClickListener() {
    327             @Override
    328             public void onClick(View v) {
    329                 mPaused = false;
    330                 mSessionManager.stop();
    331             }
    332         });
    333 
    334         mSeekBar = (SeekBar) findViewById(R.id.seekbar);
    335         mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
    336             @Override
    337             public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    338                 PlaylistItem item = getCheckedPlaylistItem();
    339                 if (fromUser && item != null && item.getDuration() > 0) {
    340                     long pos = progress * item.getDuration() / 100;
    341                     mSessionManager.seek(item.getItemId(), pos);
    342                     item.setPosition(pos);
    343                     item.setTimestamp(SystemClock.elapsedRealtime());
    344                 }
    345             }
    346 
    347             @Override
    348             public void onStartTrackingTouch(SeekBar seekBar) {
    349                 mSeeking = true;
    350             }
    351 
    352             @Override
    353             public void onStopTrackingTouch(SeekBar seekBar) {
    354                 mSeeking = false;
    355                 updateUi();
    356             }
    357         });
    358 
    359         // Schedule Ui update
    360         mHandler.postDelayed(mUpdateSeekRunnable, 1000);
    361 
    362         // Build the PendingIntent for the remote control client
    363         mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    364         mEventReceiver =
    365                 new ComponentName(getPackageName(), SampleMediaButtonReceiver.class.getName());
    366         Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
    367         mediaButtonIntent.setComponent(mEventReceiver);
    368         mMediaPendingIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, 0);
    369 
    370         // Create and register the remote control client
    371         registerRemoteControlClient();
    372 
    373         // Set up playback manager and player
    374         mPlayer = Player.create(MainActivity.this, 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 registerRemoteControlClient() {
    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, AudioManager.STREAM_MUSIC,
    394                     AudioManager.AUDIOFOCUS_GAIN);
    395             mAudioManager.registerMediaButtonEventReceiver(mEventReceiver);
    396             mRemoteControlClient = new RemoteControlClient(mMediaPendingIntent);
    397             mAudioManager.registerRemoteControlClient(mRemoteControlClient);
    398             mMediaRouter.addRemoteControlClient(mRemoteControlClient);
    399             SampleMediaButtonReceiver.setActivity(MainActivity.this);
    400             mRemoteControlClient.setTransportControlFlags(RemoteControlClient
    401                     .FLAG_KEY_MEDIA_PLAY_PAUSE);
    402             mRemoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING);
    403         }
    404     }
    405 
    406     private void unregisterRemoteControlClient() {
    407         // Unregister the RCC with AudioManager and MediaRouter
    408         if (mRemoteControlClient != null) {
    409             mRemoteControlClient.setTransportControlFlags(0);
    410             mAudioManager.abandonAudioFocus(mAfChangeListener);
    411             mAudioManager.unregisterMediaButtonEventReceiver(mEventReceiver);
    412             mAudioManager.unregisterRemoteControlClient(mRemoteControlClient);
    413             mMediaRouter.removeRemoteControlClient(mRemoteControlClient);
    414             SampleMediaButtonReceiver.setActivity(null);
    415             mRemoteControlClient = null;
    416         }
    417     }
    418 
    419     public boolean handleMediaKey(KeyEvent event) {
    420         if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
    421             switch (event.getKeyCode()) {
    422                 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: {
    423                     Log.d(TAG, "Received Play/Pause event from RemoteControlClient");
    424                     mPaused = !mPaused;
    425                     if (mPaused) {
    426                         mSessionManager.pause();
    427                     } else {
    428                         mSessionManager.resume();
    429                     }
    430                     return true;
    431                 }
    432                 case KeyEvent.KEYCODE_MEDIA_PLAY: {
    433                     Log.d(TAG, "Received Play event from RemoteControlClient");
    434                     if (mPaused) {
    435                         mPaused = false;
    436                         mSessionManager.resume();
    437                     }
    438                     return true;
    439                 }
    440                 case KeyEvent.KEYCODE_MEDIA_PAUSE: {
    441                     Log.d(TAG, "Received Pause event from RemoteControlClient");
    442                     if (!mPaused) {
    443                         mPaused = true;
    444                         mSessionManager.pause();
    445                     }
    446                     return true;
    447                 }
    448                 case KeyEvent.KEYCODE_MEDIA_STOP: {
    449                     Log.d(TAG, "Received Stop event from RemoteControlClient");
    450                     mPaused = false;
    451                     mSessionManager.stop();
    452                     return true;
    453                 }
    454                 default:
    455                     break;
    456             }
    457         }
    458         return false;
    459     }
    460 
    461     @Override
    462     public boolean onKeyDown(int keyCode, KeyEvent event) {
    463         return handleMediaKey(event) || super.onKeyDown(keyCode, event);
    464     }
    465 
    466     @Override
    467     public boolean onKeyUp(int keyCode, KeyEvent event) {
    468         return handleMediaKey(event) || super.onKeyUp(keyCode, event);
    469     }
    470 
    471     @Override
    472     public void onStart() {
    473         // Be sure to call the super class.
    474         super.onStart();
    475     }
    476 
    477     @Override
    478     public void onPause() {
    479         // pause media player for local playback case only
    480         if (!mPlayer.isRemotePlayback() && !mPaused) {
    481             mNeedResume = true;
    482             mSessionManager.pause();
    483         }
    484         super.onPause();
    485     }
    486 
    487     @Override
    488     public void onResume() {
    489         // resume media player for local playback case only
    490         if (!mPlayer.isRemotePlayback() && mNeedResume) {
    491             mSessionManager.resume();
    492             mNeedResume = false;
    493         }
    494         super.onResume();
    495     }
    496 
    497     @Override
    498     public void onDestroy() {
    499         // Unregister the remote control client
    500         unregisterRemoteControlClient();
    501 
    502         mPaused = false;
    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 
    521         // Return true to show the menu.
    522         return true;
    523     }
    524 
    525     private void updateProgress() {
    526         // Estimate content position from last status time and elapsed time.
    527         // (Note this might be slightly out of sync with remote side, however
    528         // it avoids frequent polling the MRP.)
    529         int progress = 0;
    530         PlaylistItem item = getCheckedPlaylistItem();
    531         if (item != null) {
    532             int state = item.getState();
    533             long duration = item.getDuration();
    534             if (duration <= 0) {
    535                 if (state == MediaItemStatus.PLAYBACK_STATE_PLAYING ||
    536                         state == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
    537                     mSessionManager.updateStatus();
    538                 }
    539             } else {
    540                 long position = item.getPosition();
    541                 long timeDelta =
    542                         mPaused ? 0 : (SystemClock.elapsedRealtime() - item.getTimestamp());
    543                 progress = (int) (100.0 * (position + timeDelta) / duration);
    544             }
    545         }
    546         mSeekBar.setProgress(progress);
    547     }
    548 
    549     private void updateUi() {
    550         updatePlaylist();
    551         updateRouteDescription();
    552         updateButtons();
    553     }
    554 
    555     private void updatePlaylist() {
    556         mPlayListItems.clear();
    557         for (PlaylistItem item : mSessionManager.getPlaylist()) {
    558             mPlayListItems.add(item);
    559         }
    560         mPlayListView.invalidate();
    561     }
    562 
    563 
    564     private void updateRouteDescription() {
    565         RouteInfo route = mMediaRouter.getSelectedRoute();
    566         mInfoTextView.setText(
    567                 "Currently selected route:" + "\nName: " + route.getName() + "\nProvider: " +
    568                         route.getProvider().getPackageName() + "\nDescription: " +
    569                         route.getDescription() + "\nStatistics: " +
    570                         mSessionManager.getStatistics());
    571     }
    572 
    573     private void updateButtons() {
    574         MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute();
    575         // show pause or resume icon depending on current state
    576         mPauseResumeButton.setImageResource(
    577                 mPaused ? R.drawable.ic_action_play : R.drawable.ic_action_pause);
    578         // disable pause/resume/stop if no session
    579         mPauseResumeButton.setEnabled(mSessionManager.hasSession());
    580         mStopButton.setEnabled(mSessionManager.hasSession());
    581         // only enable seek bar when duration is known
    582         PlaylistItem item = getCheckedPlaylistItem();
    583         mSeekBar.setEnabled(item != null && item.getDuration() > 0);
    584         if (mRemoteControlClient != null) {
    585             mRemoteControlClient.setPlaybackState(mPaused ? RemoteControlClient.PLAYSTATE_PAUSED :
    586                     RemoteControlClient.PLAYSTATE_PLAYING);
    587         }
    588     }
    589 
    590     private PlaylistItem getCheckedPlaylistItem() {
    591         int count = mPlayListView.getCount();
    592         int index = mPlayListView.getCheckedItemPosition();
    593         if (count > 0) {
    594             if (index < 0 || index >= count) {
    595                 index = 0;
    596                 mPlayListView.setItemChecked(0, true);
    597             }
    598             return mPlayListItems.getItem(index);
    599         }
    600         return null;
    601     }
    602 
    603     public static final class DiscoveryFragment extends MediaRouteDiscoveryFragment {
    604         private static final String TAG = "DiscoveryFragment";
    605         private Callback mCallback;
    606 
    607         public DiscoveryFragment() {
    608             mCallback = null;
    609         }
    610 
    611         public DiscoveryFragment(Callback cb) {
    612             mCallback = cb;
    613         }
    614 
    615         public void setCallback(Callback cb) {
    616             mCallback = cb;
    617         }
    618 
    619         @Override
    620         public Callback onCreateCallback() {
    621             return mCallback;
    622         }
    623 
    624         @Override
    625         public int onPrepareCallbackFlags() {
    626             // Add the CALLBACK_FLAG_UNFILTERED_EVENTS flag to ensure that we will
    627             // observe and log all route events including those that are for routes
    628             // that do not match our selector.  This is only for demonstration purposes
    629             // and should not be needed by most applications.
    630             return super.onPrepareCallbackFlags() | MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS;
    631         }
    632     }
    633 
    634     private static final class MediaItem {
    635         public final String mName;
    636         public final Uri mUri;
    637         public final String mMime;
    638 
    639         public MediaItem(String name, Uri uri, String mime) {
    640             mName = name;
    641             mUri = uri;
    642             mMime = mime;
    643         }
    644 
    645         @Override
    646         public String toString() {
    647             return mName;
    648         }
    649     }
    650 
    651     private final class LibraryAdapter extends ArrayAdapter<MediaItem> {
    652         public LibraryAdapter() {
    653             super(MainActivity.this, R.layout.media_item);
    654         }
    655 
    656         @Override
    657         public View getView(int position, View convertView, ViewGroup parent) {
    658             final View v;
    659             if (convertView == null) {
    660                 v = getLayoutInflater().inflate(R.layout.media_item, null);
    661             } else {
    662                 v = convertView;
    663             }
    664 
    665             final MediaItem item = getItem(position);
    666 
    667             TextView tv = (TextView) v.findViewById(R.id.item_text);
    668             tv.setText(item.mName);
    669 
    670             ImageButton b = (ImageButton) v.findViewById(R.id.item_action);
    671             b.setImageResource(R.drawable.ic_suggestions_add);
    672             b.setTag(item);
    673             b.setOnClickListener(new OnClickListener() {
    674                 @Override
    675                 public void onClick(View v) {
    676                     if (item != null) {
    677                         mSessionManager.add(item.mUri, item.mMime);
    678                         Toast.makeText(MainActivity.this, R.string.playlist_item_added_text,
    679                                 Toast.LENGTH_SHORT).show();
    680                     }
    681                 }
    682             });
    683 
    684             return v;
    685         }
    686     }
    687 
    688     private final class PlaylistAdapter extends ArrayAdapter<PlaylistItem> {
    689         public PlaylistAdapter() {
    690             super(MainActivity.this, R.layout.media_item);
    691         }
    692 
    693         @Override
    694         public View getView(int position, View convertView, ViewGroup parent) {
    695             final View v;
    696             if (convertView == null) {
    697                 v = getLayoutInflater().inflate(R.layout.media_item, null);
    698             } else {
    699                 v = convertView;
    700             }
    701 
    702             final PlaylistItem item = getItem(position);
    703 
    704             TextView tv = (TextView) v.findViewById(R.id.item_text);
    705             tv.setText(item.toString());
    706 
    707             ImageButton b = (ImageButton) v.findViewById(R.id.item_action);
    708             b.setImageResource(R.drawable.ic_suggestions_delete);
    709             b.setTag(item);
    710             b.setOnClickListener(new OnClickListener() {
    711                 @Override
    712                 public void onClick(View v) {
    713                     if (item != null) {
    714                         mSessionManager.remove(item.getItemId());
    715                         Toast.makeText(MainActivity.this, R.string.playlist_item_removed_text,
    716                                 Toast.LENGTH_SHORT).show();
    717                     }
    718                 }
    719             });
    720 
    721             return v;
    722         }
    723     }
    724 }
    725