SampleMediaRouterActivity.java revision 57456222f52fbfc1dcfb1b99030aad6acc02ad23
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 com.example.android.supportv7.R; 20 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.content.BroadcastReceiver; 25import android.content.res.Resources; 26import android.content.DialogInterface; 27import android.app.PendingIntent; 28import android.app.Presentation; 29import android.net.Uri; 30import android.os.Handler; 31import android.os.Bundle; 32import android.os.SystemClock; 33import android.support.v4.app.FragmentManager; 34import android.support.v4.view.MenuItemCompat; 35import android.support.v7.app.ActionBarActivity; 36import android.support.v7.app.MediaRouteActionProvider; 37import android.support.v7.app.MediaRouteDiscoveryFragment; 38import android.support.v7.media.MediaControlIntent; 39import android.support.v7.media.MediaRouter; 40import android.support.v7.media.MediaRouter.Callback; 41import android.support.v7.media.MediaRouter.RouteInfo; 42import android.support.v7.media.MediaRouter.ProviderInfo; 43import android.support.v7.media.MediaRouteSelector; 44import android.support.v7.media.MediaItemStatus; 45import android.util.Log; 46import android.view.Gravity; 47import android.view.Menu; 48import android.view.MenuItem; 49import android.view.View; 50import android.view.View.OnClickListener; 51import android.view.ViewGroup; 52import android.view.Display; 53import android.view.Surface; 54import android.view.SurfaceView; 55import android.view.SurfaceHolder; 56import android.view.WindowManager; 57import android.widget.AdapterView; 58import android.widget.AdapterView.OnItemClickListener; 59import android.widget.ArrayAdapter; 60import android.widget.Button; 61import android.widget.ImageButton; 62import android.widget.ListView; 63import android.widget.TextView; 64import android.widget.Toast; 65import android.widget.FrameLayout; 66import android.widget.TabHost; 67import android.widget.TabHost.TabSpec; 68import android.widget.TabHost.OnTabChangeListener; 69import android.widget.SeekBar; 70import android.widget.SeekBar.OnSeekBarChangeListener; 71 72 73import java.util.ArrayList; 74import java.util.List; 75import java.io.File; 76 77/** 78 * <h3>Media Router Support Activity</h3> 79 * 80 * <p> 81 * This demonstrates how to use the {@link MediaRouter} API to build an 82 * application that allows the user to send content to various rendering 83 * targets. 84 * </p> 85 */ 86public class SampleMediaRouterActivity extends ActionBarActivity { 87 private static final String TAG = "MediaRouterSupport"; 88 private static final String DISCOVERY_FRAGMENT_TAG = "DiscoveryFragment"; 89 private static final String ACTION_STATUS_CHANGE = 90 "com.example.android.supportv7.media.ACTION_STATUS_CHANGE"; 91 92 private MediaRouter mMediaRouter; 93 private MediaRouteSelector mSelector; 94 private LibraryAdapter mLibraryItems; 95 private PlaylistAdapter mPlayListItems; 96 private TextView mInfoTextView; 97 private ListView mLibraryView; 98 private ListView mPlayListView; 99 private ImageButton mPauseResumeButton; 100 private ImageButton mStopButton; 101 private SeekBar mSeekBar; 102 private String mStatsInfo; 103 private boolean mPaused; 104 private boolean mNeedResume; 105 private boolean mSeeking; 106 private long mLastStatusTime; 107 private PlaylistAdapter mSavedPlaylist; 108 109 private final Handler mHandler = new Handler(); 110 private final Runnable mUpdateSeekRunnable = new Runnable() { 111 @Override 112 public void run() { 113 updateProgress(getCheckedMediaQueueItem()); 114 // update Ui every 1 second 115 mHandler.postDelayed(this, 1000); 116 } 117 }; 118 119 private final MediaPlayerWrapper mMediaPlayer = new MediaPlayerWrapper(this); 120 private final MediaPlayerWrapper.Callback mMediaPlayerCB = 121 new MediaPlayerWrapper.Callback() { 122 @Override 123 public void onError() { 124 mPlayer.onFinish(true); 125 } 126 127 @Override 128 public void onCompletion() { 129 mPlayer.onFinish(false); 130 } 131 132 @Override 133 public void onSizeChanged(int width, int height) { 134 mLocalPlayer.updateSize(width, height); 135 } 136 137 @Override 138 public void onStatusChanged() { 139 if (!mSeeking) { 140 updateUi(); 141 } 142 } 143 }; 144 145 private final RemotePlayer mRemotePlayer = new RemotePlayer(); 146 private final LocalPlayer mLocalPlayer = new LocalPlayer(); 147 private Player mPlayer; 148 private MediaSessionManager.Callback mPlayerCB; 149 150 private final MediaRouter.Callback mMediaRouterCB = new MediaRouter.Callback() { 151 // Return a custom callback that will simply log all of the route events 152 // for demonstration purposes. 153 @Override 154 public void onRouteAdded(MediaRouter router, RouteInfo route) { 155 Log.d(TAG, "onRouteAdded: route=" + route); 156 } 157 158 @Override 159 public void onRouteChanged(MediaRouter router, RouteInfo route) { 160 Log.d(TAG, "onRouteChanged: route=" + route); 161 mPlayer.showStatistics(); 162 } 163 164 @Override 165 public void onRouteRemoved(MediaRouter router, RouteInfo route) { 166 Log.d(TAG, "onRouteRemoved: route=" + route); 167 } 168 169 @Override 170 public void onRouteSelected(MediaRouter router, RouteInfo route) { 171 Log.d(TAG, "onRouteSelected: route=" + route); 172 173 Player player = mPlayer; 174 MediaSessionManager.Callback playerCB = mPlayerCB; 175 176 if (route.supportsControlCategory( 177 MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { 178 Intent enqueueIntent = new Intent(MediaControlIntent.ACTION_ENQUEUE); 179 enqueueIntent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 180 enqueueIntent.setDataAndType(Uri.parse("http://"), "video/mp4"); 181 182 Intent removeIntent = new Intent(MediaControlIntent.ACTION_REMOVE); 183 removeIntent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 184 185 // Remote Playback: 186 // If route supports remote queuing, let it manage the queue; 187 // otherwise, manage the queue locally and feed it one item at a time 188 if (route.supportsControlRequest(enqueueIntent) 189 && route.supportsControlRequest(removeIntent)) { 190 player = mRemotePlayer; 191 } else { 192 player = mLocalPlayer; 193 } 194 playerCB = mRemotePlayer; 195 mRemotePlayer.reset(); 196 } else { 197 // Local Playback: 198 // Use local player and feed media player one item at a time 199 player = mLocalPlayer; 200 playerCB = mMediaPlayer; 201 } 202 203 if (player != mPlayer || playerCB != mPlayerCB) { 204 // save current playlist 205 PlaylistAdapter playlist = new PlaylistAdapter(); 206 for (int i = 0; i < mPlayListItems.getCount(); i++) { 207 MediaQueueItem item = mPlayListItems.getItem(i); 208 if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING 209 || item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) { 210 long position = item.getContentPosition(); 211 long timeDelta = mPaused ? 0 : 212 (SystemClock.elapsedRealtime() - mLastStatusTime); 213 item.setContentPosition(position + timeDelta); 214 } 215 playlist.add(item); 216 } 217 218 // switch players 219 mPlayer.stop(); 220 mPaused = false; 221 mLocalPlayer.setCallback(playerCB); 222 mPlayerCB = playerCB; 223 mPlayer = player; 224 mPlayer.showStatistics(); 225 mLocalPlayer.updatePresentation(); 226 227 // migrate playlist to new route 228 int count = playlist.getCount(); 229 if (isRemoteQueue()) { 230 // if queuing is managed remotely, only enqueue the first 231 // item, as we need to have the returned session id to 232 // enqueue the rest of the playlist items 233 mSavedPlaylist = playlist; 234 count = 1; 235 } 236 for (int i = 0; i < count; i++) { 237 final MediaQueueItem item = playlist.getItem(i); 238 mPlayer.enqueue(item.getUri(), item.getContentPosition()); 239 } 240 } 241 updateUi(); 242 } 243 244 @Override 245 public void onRouteUnselected(MediaRouter router, RouteInfo route) { 246 Log.d(TAG, "onRouteUnselected: route=" + route); 247 mPlayer.showStatistics(); 248 } 249 250 @Override 251 public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) { 252 Log.d(TAG, "onRouteVolumeChanged: route=" + route); 253 } 254 255 @Override 256 public void onRoutePresentationDisplayChanged( 257 MediaRouter router, RouteInfo route) { 258 Log.d(TAG, "onRoutePresentationDisplayChanged: route=" + route); 259 } 260 261 @Override 262 public void onProviderAdded(MediaRouter router, ProviderInfo provider) { 263 Log.d(TAG, "onRouteProviderAdded: provider=" + provider); 264 } 265 266 @Override 267 public void onProviderRemoved(MediaRouter router, ProviderInfo provider) { 268 Log.d(TAG, "onRouteProviderRemoved: provider=" + provider); 269 } 270 271 @Override 272 public void onProviderChanged(MediaRouter router, ProviderInfo provider) { 273 Log.d(TAG, "onRouteProviderChanged: provider=" + provider); 274 } 275 }; 276 277 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 278 @Override 279 public void onReceive(Context context, Intent intent) { 280 Log.d(TAG, "Received status update: " + intent); 281 if (intent.getAction().equals(ACTION_STATUS_CHANGE)) { 282 String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID); 283 String iid = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID); 284 MediaItemStatus status = MediaItemStatus.fromBundle( 285 intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_STATUS)); 286 287 if (status.getPlaybackState() == 288 MediaItemStatus.PLAYBACK_STATE_FINISHED) { 289 mPlayer.onFinish(false); 290 } else if (status.getPlaybackState() == 291 MediaItemStatus.PLAYBACK_STATE_ERROR) { 292 mPlayer.onFinish(true); 293 showToast("Error while playing item" + 294 ", sid " + sid + ", iid " + iid); 295 } else { 296 if (!mSeeking) { 297 updateUi(); 298 } 299 } 300 } 301 } 302 }; 303 304 @Override 305 protected void onCreate(Bundle savedInstanceState) { 306 // Be sure to call the super class. 307 super.onCreate(savedInstanceState); 308 309 // Get the media router service. 310 mMediaRouter = MediaRouter.getInstance(this); 311 312 // Create a route selector for the type of routes that we care about. 313 mSelector = new MediaRouteSelector.Builder() 314 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) 315 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO) 316 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) 317 .addControlCategory(SampleMediaRouteProvider.CATEGORY_SAMPLE_ROUTE) 318 .build(); 319 320 // Add a fragment to take care of media route discovery. 321 // This fragment automatically adds or removes a callback whenever the activity 322 // is started or stopped. 323 FragmentManager fm = getSupportFragmentManager(); 324 DiscoveryFragment fragment = (DiscoveryFragment)fm.findFragmentByTag( 325 DISCOVERY_FRAGMENT_TAG); 326 if (fragment == null) { 327 fragment = new DiscoveryFragment(mMediaRouterCB); 328 fragment.setRouteSelector(mSelector); 329 fm.beginTransaction() 330 .add(fragment, DISCOVERY_FRAGMENT_TAG) 331 .commit(); 332 } else { 333 fragment.setCallback(mMediaRouterCB); 334 fragment.setRouteSelector(mSelector); 335 } 336 337 // Populate an array adapter with streaming media items. 338 String[] mediaNames = getResources().getStringArray(R.array.media_names); 339 String[] mediaUris = getResources().getStringArray(R.array.media_uris); 340 mLibraryItems = new LibraryAdapter(); 341 for (int i = 0; i < mediaNames.length; i++) { 342 mLibraryItems.add(new MediaItem( 343 "[streaming] "+mediaNames[i], Uri.parse(mediaUris[i]))); 344 } 345 346 // Scan local /sdcard/ directory for media files. 347 String sdcard = "/sdcard/"; 348 File file = new File(sdcard); 349 File list[] = file.listFiles(); 350 for (int i = 0; i < list.length; i++) { 351 String filename = list[i].getName(); 352 if (filename.matches(".*\\.(m4v|mp4)")) { 353 mLibraryItems.add(new MediaItem( 354 "[local] "+filename, Uri.parse("file:///sdcard/" + filename))); 355 } 356 } 357 358 mPlayListItems = new PlaylistAdapter(); 359 360 // Initialize the layout. 361 setContentView(R.layout.sample_media_router); 362 363 TabHost tabHost=(TabHost)findViewById(R.id.tabHost); 364 tabHost.setup(); 365 String tabName = getResources().getString(R.string.library_tab_text); 366 TabSpec spec1=tabHost.newTabSpec(tabName); 367 spec1.setContent(R.id.tab1); 368 spec1.setIndicator(tabName); 369 370 tabName = getResources().getString(R.string.playlist_tab_text); 371 TabSpec spec2=tabHost.newTabSpec(tabName); 372 spec2.setIndicator(tabName); 373 spec2.setContent(R.id.tab2); 374 375 tabName = getResources().getString(R.string.statistics_tab_text); 376 TabSpec spec3=tabHost.newTabSpec(tabName); 377 spec3.setIndicator(tabName); 378 spec3.setContent(R.id.tab3); 379 380 tabHost.addTab(spec1); 381 tabHost.addTab(spec2); 382 tabHost.addTab(spec3); 383 tabHost.setOnTabChangedListener(new OnTabChangeListener() { 384 @Override 385 public void onTabChanged(String arg0) { 386 if (arg0.equals(getResources().getString( 387 R.string.statistics_tab_text))) { 388 mPlayer.showStatistics(); 389 } 390 updateUi(); 391 } 392 }); 393 394 mLibraryView = (ListView) findViewById(R.id.media); 395 mLibraryView.setAdapter(mLibraryItems); 396 mLibraryView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 397 mLibraryView.setOnItemClickListener(new OnItemClickListener() { 398 @Override 399 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 400 updateButtons(); 401 } 402 }); 403 404 mPlayListView = (ListView) findViewById(R.id.playlist); 405 mPlayListView.setAdapter(mPlayListItems); 406 mPlayListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 407 mPlayListView.setOnItemClickListener(new OnItemClickListener() { 408 @Override 409 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 410 updateButtons(); 411 } 412 }); 413 414 mInfoTextView = (TextView) findViewById(R.id.info); 415 416 mPauseResumeButton = (ImageButton)findViewById(R.id.pause_resume_button); 417 mPauseResumeButton.setOnClickListener(new OnClickListener() { 418 @Override 419 public void onClick(View v) { 420 if (!mPaused) { 421 mPlayer.pause(); 422 } else { 423 mPlayer.resume(); 424 } 425 } 426 }); 427 428 mStopButton = (ImageButton)findViewById(R.id.stop_button); 429 mStopButton.setOnClickListener(new OnClickListener() { 430 @Override 431 public void onClick(View v) { 432 mPlayer.stop(); 433 clearContent(); 434 } 435 }); 436 437 mSeekBar = (SeekBar) findViewById(R.id.seekbar); 438 mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 439 @Override 440 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 441 MediaQueueItem item = getCheckedMediaQueueItem(); 442 if (fromUser && item != null && item.getContentDuration() > 0) { 443 long pos = progress * item.getContentDuration() / 100; 444 mPlayer.seek(item.getSessionId(), item.getItemId(), pos); 445 item.setContentPosition(pos); 446 mLastStatusTime = SystemClock.elapsedRealtime(); 447 } 448 } 449 @Override 450 public void onStartTrackingTouch(SeekBar seekBar) { 451 mSeeking = true; 452 } 453 @Override 454 public void onStopTrackingTouch(SeekBar seekBar) { 455 mSeeking = false; 456 updateUi(); 457 } 458 }); 459 460 // Schedule Ui update 461 mHandler.postDelayed(mUpdateSeekRunnable, 1000); 462 463 // Use local playback with media player by default 464 mLocalPlayer.onCreate(); 465 mMediaPlayer.setCallback(mMediaPlayerCB); 466 mLocalPlayer.setCallback(mMediaPlayer); 467 mPlayerCB = mMediaPlayer; 468 mPlayer = mLocalPlayer; 469 470 // Register broadcast receiver to receive status update from MRP 471 IntentFilter filter = new IntentFilter(); 472 filter.addAction(SampleMediaRouterActivity.ACTION_STATUS_CHANGE); 473 registerReceiver(mReceiver, filter); 474 } 475 476 @Override 477 public void onStart() { 478 // Be sure to call the super class. 479 super.onStart(); 480 mPlayer.showStatistics(); 481 } 482 483 @Override 484 public void onPause() { 485 // pause media player for local playback case only 486 if (!isRemotePlayback() && !mPaused) { 487 mNeedResume = true; 488 mPlayer.pause(); 489 } 490 super.onPause(); 491 } 492 493 @Override 494 public void onResume() { 495 // resume media player for local playback case only 496 if (!isRemotePlayback() && mNeedResume) { 497 mPlayer.resume(); 498 mNeedResume = false; 499 } 500 super.onResume(); 501 } 502 503 @Override 504 public void onDestroy() { 505 // Unregister broadcast receiver 506 unregisterReceiver(mReceiver); 507 mPlayer.stop(); 508 mMediaPlayer.release(); 509 super.onDestroy(); 510 } 511 512 @Override 513 public boolean onCreateOptionsMenu(Menu menu) { 514 // Be sure to call the super class. 515 super.onCreateOptionsMenu(menu); 516 517 // Inflate the menu and configure the media router action provider. 518 getMenuInflater().inflate(R.menu.sample_media_router_menu, menu); 519 520 MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item); 521 MediaRouteActionProvider mediaRouteActionProvider = 522 (MediaRouteActionProvider)MenuItemCompat.getActionProvider(mediaRouteMenuItem); 523 mediaRouteActionProvider.setRouteSelector(mSelector); 524 525 // Return true to show the menu. 526 return true; 527 } 528 529 private void updateRouteDescription() { 530 RouteInfo route = mMediaRouter.getSelectedRoute(); 531 mInfoTextView.setText("Currently selected route:" 532 + "\nName: " + route.getName() 533 + "\nProvider: " + route.getProvider().getPackageName() 534 + "\nDescription: " + route.getDescription() 535 + "\nStatistics: " + mStatsInfo); 536 updateButtons(); 537 mLocalPlayer.updatePresentation(); 538 } 539 540 private void clearContent() { 541 //TO-DO: clear surface view 542 } 543 544 private void updateButtons() { 545 MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(); 546 // show pause or resume icon depending on current state 547 mPauseResumeButton.setImageResource(mPaused ? 548 R.drawable.ic_media_play : R.drawable.ic_media_pause); 549 // only enable seek bar when duration is known 550 MediaQueueItem item = getCheckedMediaQueueItem(); 551 mSeekBar.setEnabled(item != null && item.getContentDuration() > 0); 552 } 553 554 private void updateProgress(MediaQueueItem queueItem) { 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 if (queueItem != null) { 560 int state = queueItem.getState(); 561 long duration = queueItem.getContentDuration(); 562 if (duration <= 0) { 563 if (state == MediaItemStatus.PLAYBACK_STATE_PLAYING 564 || state == MediaItemStatus.PLAYBACK_STATE_PAUSED) { 565 updateUi(); 566 } 567 } else { 568 long position = queueItem.getContentPosition(); 569 long timeDelta = mPaused ? 0 : 570 (SystemClock.elapsedRealtime() - mLastStatusTime); 571 progress = (int)(100.0 * (position + timeDelta) / duration); 572 } 573 } 574 mSeekBar.setProgress(progress); 575 } 576 577 private void updateUi() { 578 updatePlaylist(); 579 updateButtons(); 580 } 581 582 private void updatePlaylist() { 583 Log.d(TAG, "updatePlaylist"); 584 final PlaylistAdapter playlist = new PlaylistAdapter(); 585 // make a copy of current playlist 586 for (int i = 0; i < mPlayListItems.getCount(); i++) { 587 playlist.add(mPlayListItems.getItem(i)); 588 } 589 // clear mPlayListItems first, items will be added back when we get 590 // status back from provider. 591 mPlayListItems.clear(); 592 mPlayListView.invalidate(); 593 594 for (int i = 0; i < playlist.getCount(); i++) { 595 final MediaQueueItem item = playlist.getItem(i); 596 final boolean update = (i == playlist.getCount() - 1); 597 mPlayer.getStatus(item, update); 598 } 599 } 600 601 private MediaItem getCheckedMediaItem() { 602 int index = mLibraryView.getCheckedItemPosition(); 603 if (index >= 0 && index < mLibraryItems.getCount()) { 604 return mLibraryItems.getItem(index); 605 } 606 return null; 607 } 608 609 private MediaQueueItem getCheckedMediaQueueItem() { 610 int count = mPlayListView.getCount(); 611 int index = mPlayListView.getCheckedItemPosition(); 612 if (count > 0) { 613 if (index < 0 || index >= count) { 614 index = 0; 615 mPlayListView.setItemChecked(0, true); 616 } 617 return mPlayListItems.getItem(index); 618 } 619 return null; 620 } 621 622 private void enqueuePlaylist() { 623 if (mSavedPlaylist != null) { 624 final PlaylistAdapter playlist = mSavedPlaylist; 625 mSavedPlaylist = null; 626 // migrate playlist (except for the 1st item) to new route 627 for (int i = 1; i < playlist.getCount(); i++) { 628 final MediaQueueItem item = playlist.getItem(i); 629 mPlayer.enqueue(item.getUri(), item.getContentPosition()); 630 } 631 } 632 } 633 634 private boolean isRemoteQueue() { 635 return mPlayer == mRemotePlayer; 636 } 637 638 private boolean isRemotePlayback() { 639 return mPlayerCB == mRemotePlayer; 640 } 641 642 private void showToast(String msg) { 643 Toast toast = Toast.makeText(SampleMediaRouterActivity.this, 644 "[app] " + msg, Toast.LENGTH_LONG); 645 toast.setGravity(Gravity.TOP, 0, 100); 646 toast.show(); 647 } 648 649 private abstract class Player { 650 abstract void enqueue(final Uri uri, long pos); 651 abstract void remove(final MediaQueueItem item); 652 abstract void seek(String sid, String iid, long pos); 653 abstract void getStatus(final MediaQueueItem item, final boolean update); 654 abstract void pause(); 655 abstract void resume(); 656 abstract void stop(); 657 abstract void showStatistics(); 658 abstract void onFinish(boolean error); 659 } 660 661 private class LocalPlayer extends Player implements SurfaceHolder.Callback { 662 private final MediaSessionManager mSessionManager = new MediaSessionManager(); 663 private String mSessionId; 664 // The presentation to show on the secondary display. 665 private DemoPresentation mPresentation; 666 private SurfaceView mSurfaceView; 667 private FrameLayout mLayout; 668 private int mVideoWidth; 669 private int mVideoHeight; 670 671 public void onCreate() { 672 mLayout = (FrameLayout)findViewById(R.id.player); 673 mSurfaceView = (SurfaceView)findViewById(R.id.surface_view); 674 SurfaceHolder holder = mSurfaceView.getHolder(); 675 holder.addCallback(mLocalPlayer); 676 } 677 678 public void setCallback(MediaSessionManager.Callback cb) { 679 mSessionManager.setCallback(cb); 680 } 681 682 @Override 683 public void enqueue(final Uri uri, long pos) { 684 Log.d(TAG, "LocalPlayer: enqueue, uri=" + uri + ", pos=" + pos); 685 MediaQueueItem playlistItem = mSessionManager.enqueue(mSessionId, uri, null); 686 mSessionId = playlistItem.getSessionId(); 687 mPlayListItems.add(playlistItem); 688 if (pos > 0) { 689 // Seek to initial position if needed 690 mPlayer.seek(mSessionId, playlistItem.getItemId(), pos); 691 } 692 updateUi(); 693 } 694 695 @Override 696 public void remove(final MediaQueueItem item) { 697 Log.d(TAG, "LocalPlayer: remove, item=" + item); 698 mSessionManager.remove(item.getSessionId(), item.getItemId()); 699 updateUi(); 700 } 701 702 @Override 703 public void seek(String sid, String iid, long pos) { 704 Log.d(TAG, "LocalPlayer: seek, sid=" + sid + ", iid=" + iid); 705 mSessionManager.seek(sid, iid, pos); 706 } 707 708 @Override 709 public void getStatus(final MediaQueueItem item, final boolean update) { 710 Log.d(TAG, "LocalPlayer: getStatus, item=" + item + ", update=" + update); 711 MediaQueueItem playlistItem = 712 mSessionManager.getStatus(item.getSessionId(), item.getItemId()); 713 if (playlistItem != null) { 714 mLastStatusTime = playlistItem.getStatus().getTimestamp(); 715 mPlayListItems.add(item); 716 mPlayListView.invalidate(); 717 } 718 if (update) { 719 clearContent(); 720 updateButtons(); 721 } 722 } 723 724 @Override 725 public void pause() { 726 Log.d(TAG, "LocalPlayer: pause"); 727 mSessionManager.pause(mSessionId); 728 mPaused = true; 729 updateUi(); 730 } 731 732 @Override 733 public void resume() { 734 Log.d(TAG, "LocalPlayer: resume"); 735 mSessionManager.resume(mSessionId); 736 mPaused = false; 737 updateUi(); 738 } 739 740 @Override 741 public void stop() { 742 Log.d(TAG, "LocalPlayer: stop"); 743 mSessionManager.stop(mSessionId); 744 mSessionId = null; 745 mPaused = false; 746 // For demo purpose, invalidate remote session when local session 747 // is stopped (note this is not necessary, remote player could reuse 748 // the same session) 749 mRemotePlayer.reset(); 750 updateUi(); 751 } 752 753 @Override 754 public void showStatistics() { 755 Log.d(TAG, "LocalPlayer: showStatistics"); 756 mStatsInfo = null; 757 if (isRemotePlayback()) { 758 mRemotePlayer.showStatistics(); 759 } 760 updateRouteDescription(); 761 } 762 763 @Override 764 public void onFinish(boolean error) { 765 MediaQueueItem item = mSessionManager.finish(error); 766 updateUi(); 767 if (error) { 768 showToast("Failed to play item " + item.getUri()); 769 } 770 } 771 772 // SurfaceHolder.Callback 773 @Override 774 public void surfaceChanged(SurfaceHolder holder, int format, 775 int width, int height) { 776 Log.d(TAG, "surfaceChanged "+width+"x"+height); 777 mMediaPlayer.onWindowCreated(holder.getSurface()); 778 } 779 780 @Override 781 public void surfaceCreated(SurfaceHolder holder) { 782 Log.d(TAG, "surfaceCreated"); 783 mMediaPlayer.onWindowCreated(holder.getSurface()); 784 mLocalPlayer.updateSize(mVideoWidth, mVideoHeight); 785 } 786 787 @Override 788 public void surfaceDestroyed(SurfaceHolder holder) { 789 Log.d(TAG, "surfaceDestroyed"); 790 } 791 792 private void updateSize(int width, int height) { 793 if (width > 0 && height > 0) { 794 if (mPresentation == null) { 795 int surfaceWidth = mLayout.getWidth(); 796 int surfaceHeight = mLayout.getHeight(); 797 798 // Calculate the new size of mSurfaceView, so that video is centered 799 // inside the framelayout with proper letterboxing/pillarboxing 800 ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams(); 801 if (surfaceWidth * height < surfaceHeight * width) { 802 // Black bars on top&bottom, mSurfaceView has full layout width, 803 // while height is derived from video's aspect ratio 804 lp.width = surfaceWidth; 805 lp.height = surfaceWidth * height / width; 806 } else { 807 // Black bars on left&right, mSurfaceView has full layout height, 808 // while width is derived from video's aspect ratio 809 lp.width = surfaceHeight * width / height; 810 lp.height = surfaceHeight; 811 } 812 Log.d(TAG, "video rect is "+lp.width+"x"+lp.height); 813 mSurfaceView.setLayoutParams(lp); 814 } else { 815 mPresentation.updateSize(width, height); 816 } 817 mVideoWidth = width; 818 mVideoHeight = height; 819 } else { 820 mVideoWidth = mVideoHeight = 0; 821 } 822 } 823 824 private void updatePresentation() { 825 // Get the current route and its presentation display. 826 MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(); 827 Display presentationDisplay = route != null ? route.getPresentationDisplay() : null; 828 829 // Dismiss the current presentation if the display has changed. 830 if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) { 831 Log.i(TAG, "Dismissing presentation because the current route no longer " 832 + "has a presentation display."); 833 mPresentation.dismiss(); 834 mPresentation = null; 835 } 836 837 // Show a new presentation if needed. 838 if (mPresentation == null && presentationDisplay != null) { 839 Log.i(TAG, "Showing presentation on display: " + presentationDisplay); 840 mPresentation = new DemoPresentation( 841 SampleMediaRouterActivity.this, presentationDisplay); 842 mPresentation.setOnDismissListener(mOnDismissListener); 843 try { 844 mPresentation.show(); 845 } catch (WindowManager.InvalidDisplayException ex) { 846 Log.w(TAG, "Couldn't show presentation! Display was removed in " 847 + "the meantime.", ex); 848 mPresentation = null; 849 } 850 } 851 852 if (mPresentation != null || route.supportsControlCategory( 853 MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { 854 mSurfaceView.setVisibility(View.GONE); 855 mLayout.setVisibility(View.GONE); 856 } else { 857 mLayout.setVisibility(View.VISIBLE); 858 mSurfaceView.setVisibility(View.VISIBLE); 859 } 860 } 861 862 // Listens for when presentations are dismissed. 863 private final DialogInterface.OnDismissListener mOnDismissListener = 864 new DialogInterface.OnDismissListener() { 865 @Override 866 public void onDismiss(DialogInterface dialog) { 867 if (dialog == mPresentation) { 868 Log.i(TAG, "Presentation was dismissed."); 869 mPresentation = null; 870 updatePresentation(); 871 } 872 } 873 }; 874 875 private final class DemoPresentation extends Presentation { 876 private SurfaceView mSurfaceView; 877 878 public DemoPresentation(Context context, Display display) { 879 super(context, display); 880 } 881 882 @Override 883 protected void onCreate(Bundle savedInstanceState) { 884 // Be sure to call the super class. 885 super.onCreate(savedInstanceState); 886 887 // Get the resources for the context of the presentation. 888 // Notice that we are getting the resources from the context 889 // of the presentation. 890 Resources r = getContext().getResources(); 891 892 // Inflate the layout. 893 setContentView(R.layout.sample_media_router_presentation); 894 895 // Set up the surface view for visual interest. 896 mSurfaceView = (SurfaceView)findViewById(R.id.surface_view); 897 SurfaceHolder holder = mSurfaceView.getHolder(); 898 holder.addCallback(mLocalPlayer); 899 } 900 901 public void updateSize(int width, int height) { 902 int surfaceHeight=getWindow().getDecorView().getHeight(); 903 int surfaceWidth=getWindow().getDecorView().getWidth(); 904 ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams(); 905 if (surfaceWidth * height < surfaceHeight * width) { 906 lp.width = surfaceWidth; 907 lp.height = surfaceWidth * height / width; 908 } else { 909 lp.width = surfaceHeight * width / height; 910 lp.height = surfaceHeight; 911 } 912 Log.d(TAG, "video rect is "+lp.width+"x"+lp.height); 913 mSurfaceView.setLayoutParams(lp); 914 } 915 916 public void clearContent() { 917 //TO-DO: clear surface view 918 } 919 } 920 } 921 922 private class RemotePlayer extends Player implements MediaSessionManager.Callback { 923 private MediaQueueItem mQueueItem; 924 private MediaQueueItem mPlaylistItem; 925 private String mSessionId; 926 private String mItemId; 927 private long mPosition; 928 929 public void reset() { 930 mQueueItem = null; 931 mPlaylistItem = null; 932 mSessionId = null; 933 mItemId = null; 934 mPosition = 0; 935 } 936 937 // MediaSessionManager.Callback 938 @Override 939 public void onStart() { 940 resume(); 941 } 942 943 @Override 944 public void onPause() { 945 pause(); 946 } 947 948 @Override 949 public void onStop() { 950 stop(); 951 } 952 953 @Override 954 public void onSeek(long pos) { 955 // If we're currently performing a Play/Enqueue, do not seek 956 // until we get the result back (or we may not have valid session 957 // and item ids); otherwise do the seek now 958 if (mSessionId != null) { 959 seek(mSessionId, mItemId, pos); 960 } 961 // Set current position to seek-to position, actual position will 962 // be updated when next getStatus is completed. 963 mPosition = pos; 964 } 965 966 @Override 967 public void onGetStatus(MediaQueueItem item) { 968 if (mQueueItem != null) { 969 mPlaylistItem = item; 970 getStatus(mQueueItem, false); 971 } 972 } 973 974 @Override 975 public void onNewItem(Uri uri) { 976 mPosition = 0; 977 play(uri, false, 0); 978 } 979 980 // Player API 981 @Override 982 public void enqueue(final Uri uri, long pos) { 983 play(uri, true, pos); 984 } 985 986 @Override 987 public void remove(final MediaQueueItem item) { 988 MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(); 989 Intent intent = makeRemoveIntent(item); 990 if (route.supportsControlRequest(intent)) { 991 MediaRouter.ControlRequestCallback callback = 992 new MediaRouter.ControlRequestCallback() { 993 @Override 994 public void onResult(Bundle data) { 995 MediaItemStatus status = MediaItemStatus.fromBundle( 996 data.getBundle(MediaControlIntent.EXTRA_ITEM_STATUS)); 997 Log.d(TAG, "Remove request succeeded: status=" + status.toString()); 998 updateUi(); 999 } 1000 1001 @Override 1002 public void onError(String error, Bundle data) { 1003 Log.d(TAG, "Remove request failed: error=" + error + ", data=" + data); 1004 } 1005 }; 1006 1007 Log.d(TAG, "Sending remove request: intent=" + intent); 1008 route.sendControlRequest(intent, callback); 1009 } else { 1010 Log.d(TAG, "Remove request not supported!"); 1011 } 1012 } 1013 1014 @Override 1015 public void seek(String sid, String iid, long pos) { 1016 MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(); 1017 Intent intent = makeSeekIntent(sid, iid, pos); 1018 if (route.supportsControlRequest(intent)) { 1019 MediaRouter.ControlRequestCallback callback = 1020 new MediaRouter.ControlRequestCallback() { 1021 @Override 1022 public void onResult(Bundle data) { 1023 MediaItemStatus status = MediaItemStatus.fromBundle( 1024 data.getBundle(MediaControlIntent.EXTRA_ITEM_STATUS)); 1025 Log.d(TAG, "Seek request succeeded: status=" + status.toString()); 1026 } 1027 1028 @Override 1029 public void onError(String error, Bundle data) { 1030 Log.d(TAG, "Seek request failed: error=" + error + ", data=" + data); 1031 } 1032 }; 1033 1034 Log.d(TAG, "Sending seek request: intent=" + intent); 1035 route.sendControlRequest(intent, callback); 1036 } else { 1037 Log.d(TAG, "Seek request not supported!"); 1038 } 1039 } 1040 1041 @Override 1042 public void getStatus(final MediaQueueItem item, final boolean update) { 1043 MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(); 1044 Intent intent = makeGetStatusIntent(item); 1045 if (route.supportsControlRequest(intent)) { 1046 MediaRouter.ControlRequestCallback callback = 1047 new MediaRouter.ControlRequestCallback() { 1048 @Override 1049 public void onResult(Bundle data) { 1050 if (data != null) { 1051 String sid = data.getString(MediaControlIntent.EXTRA_SESSION_ID); 1052 String iid = data.getString(MediaControlIntent.EXTRA_ITEM_ID); 1053 MediaItemStatus status = MediaItemStatus.fromBundle( 1054 data.getBundle(MediaControlIntent.EXTRA_ITEM_STATUS)); 1055 Log.d(TAG, "GetStatus request succeeded: status=" + status.toString()); 1056 //showToast("GetStatus request succeeded " + item.mName); 1057 if (isRemoteQueue()) { 1058 int state = status.getPlaybackState(); 1059 if (state == MediaItemStatus.PLAYBACK_STATE_PLAYING 1060 || state == MediaItemStatus.PLAYBACK_STATE_PAUSED 1061 || state == MediaItemStatus.PLAYBACK_STATE_PENDING) { 1062 item.setState(state); 1063 item.setContentPosition(status.getContentPosition()); 1064 item.setContentDuration(status.getContentDuration()); 1065 mLastStatusTime = status.getTimestamp(); 1066 mPlayListItems.add(item); 1067 mPlayListView.invalidate(); 1068 // update buttons as the queue count might have changed 1069 if (update) { 1070 clearContent(); 1071 updateButtons(); 1072 } 1073 } 1074 } else { 1075 if (mPlaylistItem != null) { 1076 mPlaylistItem.setContentPosition(status.getContentPosition()); 1077 mPlaylistItem.setContentDuration(status.getContentDuration()); 1078 mPlaylistItem = null; 1079 updateButtons(); 1080 } 1081 } 1082 } 1083 } 1084 1085 @Override 1086 public void onError(String error, Bundle data) { 1087 Log.d(TAG, "GetStatus request failed: error=" + error + ", data=" + data); 1088 //showToast("Unable to get status "); 1089 if (isRemoteQueue()) { 1090 if (update) { 1091 clearContent(); 1092 updateButtons(); 1093 } 1094 } 1095 } 1096 }; 1097 1098 Log.d(TAG, "Sending GetStatus request: intent=" + intent); 1099 route.sendControlRequest(intent, callback); 1100 } else { 1101 Log.d(TAG, "GetStatus request not supported!"); 1102 } 1103 } 1104 1105 @Override 1106 public void pause() { 1107 Intent intent = makePauseIntent(); 1108 MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(); 1109 if (route.supportsControlRequest(intent)) { 1110 MediaRouter.ControlRequestCallback callback = 1111 new MediaRouter.ControlRequestCallback() { 1112 @Override 1113 public void onResult(Bundle data) { 1114 Log.d(TAG, "Pause request succeeded"); 1115 if (isRemoteQueue()) { 1116 mPaused = true; 1117 updateUi(); 1118 } 1119 } 1120 1121 @Override 1122 public void onError(String error, Bundle data) { 1123 Log.d(TAG, "Pause request failed: error=" + error); 1124 } 1125 }; 1126 1127 Log.d(TAG, "Sending pause request"); 1128 route.sendControlRequest(intent, callback); 1129 } else { 1130 Log.d(TAG, "Pause request not supported!"); 1131 } 1132 } 1133 1134 @Override 1135 public void resume() { 1136 Intent intent = makeResumeIntent(); 1137 MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(); 1138 if (route.supportsControlRequest(intent)) { 1139 MediaRouter.ControlRequestCallback callback = 1140 new MediaRouter.ControlRequestCallback() { 1141 @Override 1142 public void onResult(Bundle data) { 1143 Log.d(TAG, "Resume request succeeded"); 1144 if (isRemoteQueue()) { 1145 mPaused = false; 1146 updateUi(); 1147 } 1148 } 1149 1150 @Override 1151 public void onError(String error, Bundle data) { 1152 Log.d(TAG, "Resume request failed: error=" + error); 1153 } 1154 }; 1155 1156 Log.d(TAG, "Sending resume request"); 1157 route.sendControlRequest(intent, callback); 1158 } else { 1159 Log.d(TAG, "Resume request not supported!"); 1160 } 1161 } 1162 1163 @Override 1164 public void stop() { 1165 Intent intent = makeStopIntent(); 1166 MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(); 1167 if (route.supportsControlRequest(intent)) { 1168 MediaRouter.ControlRequestCallback callback = 1169 new MediaRouter.ControlRequestCallback() { 1170 @Override 1171 public void onResult(Bundle data) { 1172 Log.d(TAG, "Stop request succeeded"); 1173 if (isRemoteQueue()) { 1174 // Reset mSessionId, so that next Play/Enqueue 1175 // starts a new session 1176 mQueueItem = null; 1177 mSessionId = null; 1178 mPaused = false; 1179 updateUi(); 1180 } 1181 } 1182 1183 @Override 1184 public void onError(String error, Bundle data) { 1185 Log.d(TAG, "Stop request failed: error=" + error); 1186 } 1187 }; 1188 1189 Log.d(TAG, "Sending stop request"); 1190 route.sendControlRequest(intent, callback); 1191 } else { 1192 Log.d(TAG, "Stop request not supported!"); 1193 } 1194 } 1195 1196 @Override 1197 public void showStatistics() { 1198 MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(); 1199 Intent intent = makeStatisticsIntent(); 1200 if (route.supportsControlRequest(intent)) { 1201 MediaRouter.ControlRequestCallback callback = 1202 new MediaRouter.ControlRequestCallback() { 1203 @Override 1204 public void onResult(Bundle data) { 1205 Log.d(TAG, "Statistics request succeeded: data=" + data); 1206 if (data != null) { 1207 int playbackCount = data.getInt( 1208 SampleMediaRouteProvider.DATA_PLAYBACK_COUNT, -1); 1209 mStatsInfo = "Total playback count: " + playbackCount; 1210 } else { 1211 showToast("Statistics query did not return any data"); 1212 } 1213 updateRouteDescription(); 1214 } 1215 1216 @Override 1217 public void onError(String error, Bundle data) { 1218 Log.d(TAG, "Statistics request failed: error=" + error + ", data=" + data); 1219 showToast("Unable to query statistics, error: " + error); 1220 updateRouteDescription(); 1221 } 1222 }; 1223 1224 Log.d(TAG, "Sent statistics request: intent=" + intent); 1225 route.sendControlRequest(intent, callback); 1226 } else { 1227 Log.d(TAG, "Statistics request not supported!"); 1228 } 1229 1230 } 1231 1232 @Override 1233 public void onFinish(boolean error) { 1234 updateUi(); 1235 } 1236 1237 private void play(final Uri uri, boolean enqueue, final long pos) { 1238 // save the initial seek position 1239 mPosition = pos; 1240 MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(); 1241 Intent intent = makePlayIntent(uri, enqueue); 1242 final String request = enqueue ? "Enqueue" : "Play"; 1243 if (route.supportsControlRequest(intent)) { 1244 MediaRouter.ControlRequestCallback callback = 1245 new MediaRouter.ControlRequestCallback() { 1246 @Override 1247 public void onResult(Bundle data) { 1248 if (data != null) { 1249 String sid = data.getString(MediaControlIntent.EXTRA_SESSION_ID); 1250 String iid = data.getString(MediaControlIntent.EXTRA_ITEM_ID); 1251 MediaItemStatus status = MediaItemStatus.fromBundle( 1252 data.getBundle(MediaControlIntent.EXTRA_ITEM_STATUS)); 1253 Log.d(TAG, request + " request succeeded: data=" + data + 1254 ", sid=" + sid + ", iid=" + iid); 1255 1256 // perform delayed initial seek 1257 if (mSessionId == null && mPosition > 0) { 1258 seek(sid, iid, mPosition); 1259 } 1260 1261 mSessionId = sid; 1262 mItemId = iid; 1263 mQueueItem = new MediaQueueItem(sid, iid, null, null); 1264 1265 if (isRemoteQueue()) { 1266 MediaQueueItem playlistItem = 1267 new MediaQueueItem(sid, iid, uri, null); 1268 playlistItem.setState(status.getPlaybackState()); 1269 mPlayListItems.add(playlistItem); 1270 updateUi(); 1271 enqueuePlaylist(); 1272 } 1273 } 1274 } 1275 1276 @Override 1277 public void onError(String error, Bundle data) { 1278 Log.d(TAG, request + " request failed: error=" + error + ", data=" + data); 1279 showToast("Unable to " + request + uri + ", error: " + error); 1280 } 1281 }; 1282 1283 Log.d(TAG, "Sending " + request + " request: intent=" + intent); 1284 route.sendControlRequest(intent, callback); 1285 } else { 1286 Log.d(TAG, request + " request not supported!"); 1287 } 1288 } 1289 1290 private Intent makePlayIntent(Uri uri, boolean enqueue) { 1291 Intent intent = new Intent( 1292 enqueue ? MediaControlIntent.ACTION_ENQUEUE 1293 : MediaControlIntent.ACTION_PLAY); 1294 intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 1295 intent.setDataAndType(uri, "video/mp4"); 1296 1297 // Provide a valid session id, or none (which starts a new session) 1298 if (mSessionId != null) { 1299 intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId); 1300 } 1301 1302 // PendingIntent for receiving status update from MRP 1303 Intent statusIntent = new Intent(SampleMediaRouterActivity.ACTION_STATUS_CHANGE); 1304 intent.putExtra(MediaControlIntent.EXTRA_ITEM_STATUS_UPDATE_RECEIVER, 1305 PendingIntent.getBroadcast(SampleMediaRouterActivity.this, 1306 0, statusIntent, 0)); 1307 1308 return intent; 1309 } 1310 1311 private Intent makeRemoveIntent(MediaQueueItem item) { 1312 Intent intent = new Intent(MediaControlIntent.ACTION_REMOVE); 1313 intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 1314 intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, item.getSessionId()); 1315 intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, item.getItemId()); 1316 return intent; 1317 } 1318 1319 private Intent makeSeekIntent(String sid, String iid, long pos) { 1320 Intent intent = new Intent(MediaControlIntent.ACTION_SEEK); 1321 intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 1322 intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sid); 1323 intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, iid); 1324 intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, pos); 1325 return intent; 1326 } 1327 1328 private Intent makePauseIntent() { 1329 Intent intent = new Intent(MediaControlIntent.ACTION_PAUSE); 1330 intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 1331 if (mSessionId != null) { 1332 intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId); 1333 } 1334 return intent; 1335 } 1336 1337 private Intent makeResumeIntent() { 1338 Intent intent = new Intent(MediaControlIntent.ACTION_RESUME); 1339 intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 1340 if (mSessionId != null) { 1341 intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId); 1342 } 1343 return intent; 1344 } 1345 1346 private Intent makeStopIntent() { 1347 Intent intent = new Intent(MediaControlIntent.ACTION_STOP); 1348 intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 1349 if (mSessionId != null) { 1350 intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId); 1351 } 1352 return intent; 1353 } 1354 1355 private Intent makeGetStatusIntent(MediaQueueItem item) { 1356 Intent intent = new Intent(MediaControlIntent.ACTION_GET_STATUS); 1357 intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 1358 intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, item.getSessionId()); 1359 intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, item.getItemId()); 1360 return intent; 1361 } 1362 1363 private Intent makeStatisticsIntent() { 1364 Intent intent = new Intent(SampleMediaRouteProvider.ACTION_GET_STATISTICS); 1365 intent.addCategory(SampleMediaRouteProvider.CATEGORY_SAMPLE_ROUTE); 1366 return intent; 1367 } 1368 } 1369 1370 public static final class DiscoveryFragment extends MediaRouteDiscoveryFragment { 1371 private static final String TAG = "DiscoveryFragment"; 1372 private Callback mCallback; 1373 1374 public DiscoveryFragment() { 1375 mCallback = null; 1376 } 1377 1378 public DiscoveryFragment(Callback cb) { 1379 mCallback = cb; 1380 } 1381 1382 public void setCallback(Callback cb) { 1383 mCallback = cb; 1384 } 1385 1386 @Override 1387 public Callback onCreateCallback() { 1388 return mCallback; 1389 } 1390 1391 @Override 1392 public int onPrepareCallbackFlags() { 1393 // Add the CALLBACK_FLAG_UNFILTERED_EVENTS flag to ensure that we will 1394 // observe and log all route events including those that are for routes 1395 // that do not match our selector. This is only for demonstration purposes 1396 // and should not be needed by most applications. 1397 return super.onPrepareCallbackFlags() 1398 | MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS; 1399 } 1400 } 1401 1402 private static final class MediaItem { 1403 public final String mName; 1404 public final Uri mUri; 1405 1406 public MediaItem(String name, Uri uri) { 1407 mName = name; 1408 mUri = uri; 1409 } 1410 1411 @Override 1412 public String toString() { 1413 return mName; 1414 } 1415 } 1416 1417 private final class LibraryAdapter extends ArrayAdapter<MediaItem> { 1418 public LibraryAdapter() { 1419 super(SampleMediaRouterActivity.this, R.layout.media_item); 1420 } 1421 1422 @Override 1423 public View getView(int position, View convertView, ViewGroup parent) { 1424 final View v; 1425 if (convertView == null) { 1426 v = getLayoutInflater().inflate(R.layout.media_item, null); 1427 } else { 1428 v = convertView; 1429 } 1430 1431 final MediaItem item = getItem(position); 1432 1433 TextView tv = (TextView)v.findViewById(R.id.item_text); 1434 tv.setText(item.mName); 1435 1436 ImageButton b = (ImageButton)v.findViewById(R.id.item_action); 1437 b.setImageResource(R.drawable.ic_menu_add); 1438 b.setTag(item); 1439 b.setOnClickListener(new OnClickListener() { 1440 @Override 1441 public void onClick(View v) { 1442 if (item != null) { 1443 mPlayer.enqueue(item.mUri, 0); 1444 } 1445 } 1446 }); 1447 1448 return v; 1449 } 1450 } 1451 1452 private final class PlaylistAdapter extends ArrayAdapter<MediaQueueItem> { 1453 public PlaylistAdapter() { 1454 super(SampleMediaRouterActivity.this, R.layout.media_item); 1455 } 1456 1457 @Override 1458 public View getView(int position, View convertView, ViewGroup parent) { 1459 final View v; 1460 if (convertView == null) { 1461 v = getLayoutInflater().inflate(R.layout.media_item, null); 1462 } else { 1463 v = convertView; 1464 } 1465 1466 final MediaQueueItem item = getItem(position); 1467 1468 TextView tv = (TextView)v.findViewById(R.id.item_text); 1469 tv.setText(item.toString()); 1470 1471 ImageButton b = (ImageButton)v.findViewById(R.id.item_action); 1472 b.setImageResource(R.drawable.ic_menu_delete); 1473 b.setTag(item); 1474 b.setOnClickListener(new OnClickListener() { 1475 @Override 1476 public void onClick(View v) { 1477 if (item != null) { 1478 mPlayer.remove(item); 1479 } 1480 } 1481 }); 1482 1483 return v; 1484 } 1485 } 1486 1487 /** 1488 * Trivial subclass of this activity used to provide another copy of the 1489 * same activity using a light theme instead of the dark theme. 1490 */ 1491 public static class Light extends SampleMediaRouterActivity { 1492 } 1493 1494 /** 1495 * Trivial subclass of this activity used to provide another copy of the 1496 * same activity using a light theme with dark action bar instead of the dark theme. 1497 */ 1498 public static class LightWithDarkActionBar extends SampleMediaRouterActivity { 1499 } 1500} 1501