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