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