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