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