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