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