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