1 /* 2 * Copyright (C) 2012 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.android.settings.wfd; 18 19 import android.app.AlertDialog; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.database.ContentObserver; 26 import android.hardware.display.DisplayManager; 27 import android.hardware.display.WifiDisplay; 28 import android.hardware.display.WifiDisplayStatus; 29 import android.media.MediaRouter; 30 import android.media.MediaRouter.RouteInfo; 31 import android.net.Uri; 32 import android.net.wifi.WpsInfo; 33 import android.net.wifi.p2p.WifiP2pManager; 34 import android.net.wifi.p2p.WifiP2pManager.ActionListener; 35 import android.net.wifi.p2p.WifiP2pManager.Channel; 36 import android.os.Bundle; 37 import android.os.Handler; 38 import android.os.Looper; 39 import android.provider.Settings; 40 import android.support.v14.preference.SwitchPreference; 41 import android.support.v7.preference.ListPreference; 42 import android.support.v7.preference.Preference; 43 import android.support.v7.preference.Preference.OnPreferenceChangeListener; 44 import android.support.v7.preference.PreferenceCategory; 45 import android.support.v7.preference.PreferenceGroup; 46 import android.support.v7.preference.PreferenceScreen; 47 import android.support.v7.preference.PreferenceViewHolder; 48 import android.util.Slog; 49 import android.util.TypedValue; 50 import android.view.Menu; 51 import android.view.MenuInflater; 52 import android.view.MenuItem; 53 import android.view.View; 54 import android.view.View.OnClickListener; 55 import android.widget.Button; 56 import android.widget.EditText; 57 import android.widget.ImageView; 58 import android.widget.TextView; 59 60 import com.android.internal.app.MediaRouteDialogPresenter; 61 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 62 import com.android.settings.R; 63 import com.android.settings.SettingsPreferenceFragment; 64 import com.android.settings.dashboard.SummaryLoader; 65 66 /** 67 * The Settings screen for WifiDisplay configuration and connection management. 68 * 69 * The wifi display routes are integrated together with other remote display routes 70 * from the media router. It may happen that wifi display isn't actually available 71 * on the system. In that case, the enable option will not be shown but other 72 * remote display routes will continue to be made available. 73 */ 74 public final class WifiDisplaySettings extends SettingsPreferenceFragment { 75 private static final String TAG = "WifiDisplaySettings"; 76 private static final boolean DEBUG = false; 77 78 private static final int MENU_ID_ENABLE_WIFI_DISPLAY = Menu.FIRST; 79 80 private static final int CHANGE_SETTINGS = 1 << 0; 81 private static final int CHANGE_ROUTES = 1 << 1; 82 private static final int CHANGE_WIFI_DISPLAY_STATUS = 1 << 2; 83 private static final int CHANGE_ALL = -1; 84 85 private static final int ORDER_CERTIFICATION = 1; 86 private static final int ORDER_CONNECTED = 2; 87 private static final int ORDER_AVAILABLE = 3; 88 private static final int ORDER_UNAVAILABLE = 4; 89 90 private final Handler mHandler; 91 92 private MediaRouter mRouter; 93 private DisplayManager mDisplayManager; 94 95 private boolean mStarted; 96 private int mPendingChanges; 97 98 private boolean mWifiDisplayOnSetting; 99 private WifiDisplayStatus mWifiDisplayStatus; 100 101 private TextView mEmptyView; 102 103 /* certification */ 104 private boolean mWifiDisplayCertificationOn; 105 private WifiP2pManager mWifiP2pManager; 106 private Channel mWifiP2pChannel; 107 private PreferenceGroup mCertCategory; 108 private boolean mListen; 109 private boolean mAutoGO; 110 private int mWpsConfig = WpsInfo.INVALID; 111 private int mListenChannel; 112 private int mOperatingChannel; 113 WifiDisplaySettings()114 public WifiDisplaySettings() { 115 mHandler = new Handler(); 116 } 117 118 @Override getMetricsCategory()119 public int getMetricsCategory() { 120 return MetricsEvent.WFD_WIFI_DISPLAY; 121 } 122 123 @Override onCreate(Bundle icicle)124 public void onCreate(Bundle icicle) { 125 super.onCreate(icicle); 126 127 final Context context = getActivity(); 128 mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); 129 mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); 130 mWifiP2pManager = (WifiP2pManager) context.getSystemService(Context.WIFI_P2P_SERVICE); 131 mWifiP2pChannel = mWifiP2pManager.initialize(context, Looper.getMainLooper(), null); 132 133 addPreferencesFromResource(R.xml.wifi_display_settings); 134 setHasOptionsMenu(true); 135 } 136 137 @Override getHelpResource()138 protected int getHelpResource() { 139 return R.string.help_url_remote_display; 140 } 141 142 @Override onActivityCreated(Bundle savedInstanceState)143 public void onActivityCreated(Bundle savedInstanceState) { 144 super.onActivityCreated(savedInstanceState); 145 146 mEmptyView = (TextView) getView().findViewById(android.R.id.empty); 147 mEmptyView.setText(R.string.wifi_display_no_devices_found); 148 setEmptyView(mEmptyView); 149 } 150 151 @Override onStart()152 public void onStart() { 153 super.onStart(); 154 mStarted = true; 155 156 final Context context = getActivity(); 157 IntentFilter filter = new IntentFilter(); 158 filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED); 159 context.registerReceiver(mReceiver, filter); 160 161 getContentResolver().registerContentObserver(Settings.Global.getUriFor( 162 Settings.Global.WIFI_DISPLAY_ON), false, mSettingsObserver); 163 getContentResolver().registerContentObserver(Settings.Global.getUriFor( 164 Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON), false, mSettingsObserver); 165 getContentResolver().registerContentObserver(Settings.Global.getUriFor( 166 Settings.Global.WIFI_DISPLAY_WPS_CONFIG), false, mSettingsObserver); 167 168 mRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mRouterCallback, 169 MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); 170 171 update(CHANGE_ALL); 172 } 173 174 @Override onStop()175 public void onStop() { 176 super.onStop(); 177 mStarted = false; 178 179 final Context context = getActivity(); 180 context.unregisterReceiver(mReceiver); 181 182 getContentResolver().unregisterContentObserver(mSettingsObserver); 183 184 mRouter.removeCallback(mRouterCallback); 185 186 unscheduleUpdate(); 187 } 188 189 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)190 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 191 if (mWifiDisplayStatus != null && mWifiDisplayStatus.getFeatureState() 192 != WifiDisplayStatus.FEATURE_STATE_UNAVAILABLE) { 193 MenuItem item = menu.add(Menu.NONE, MENU_ID_ENABLE_WIFI_DISPLAY, 0, 194 R.string.wifi_display_enable_menu_item); 195 item.setCheckable(true); 196 item.setChecked(mWifiDisplayOnSetting); 197 } 198 super.onCreateOptionsMenu(menu, inflater); 199 } 200 201 @Override onOptionsItemSelected(MenuItem item)202 public boolean onOptionsItemSelected(MenuItem item) { 203 switch (item.getItemId()) { 204 case MENU_ID_ENABLE_WIFI_DISPLAY: 205 mWifiDisplayOnSetting = !item.isChecked(); 206 item.setChecked(mWifiDisplayOnSetting); 207 Settings.Global.putInt(getContentResolver(), 208 Settings.Global.WIFI_DISPLAY_ON, mWifiDisplayOnSetting ? 1 : 0); 209 return true; 210 } 211 return super.onOptionsItemSelected(item); 212 } 213 isAvailable(Context context)214 public static boolean isAvailable(Context context) { 215 return context.getSystemService(Context.DISPLAY_SERVICE) != null 216 && context.getSystemService(Context.WIFI_P2P_SERVICE) != null; 217 } 218 scheduleUpdate(int changes)219 private void scheduleUpdate(int changes) { 220 if (mStarted) { 221 if (mPendingChanges == 0) { 222 mHandler.post(mUpdateRunnable); 223 } 224 mPendingChanges |= changes; 225 } 226 } 227 unscheduleUpdate()228 private void unscheduleUpdate() { 229 if (mPendingChanges != 0) { 230 mPendingChanges = 0; 231 mHandler.removeCallbacks(mUpdateRunnable); 232 } 233 } 234 update(int changes)235 private void update(int changes) { 236 boolean invalidateOptions = false; 237 238 // Update settings. 239 if ((changes & CHANGE_SETTINGS) != 0) { 240 mWifiDisplayOnSetting = Settings.Global.getInt(getContentResolver(), 241 Settings.Global.WIFI_DISPLAY_ON, 0) != 0; 242 mWifiDisplayCertificationOn = Settings.Global.getInt(getContentResolver(), 243 Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON, 0) != 0; 244 mWpsConfig = Settings.Global.getInt(getContentResolver(), 245 Settings.Global.WIFI_DISPLAY_WPS_CONFIG, WpsInfo.INVALID); 246 247 // The wifi display enabled setting may have changed. 248 invalidateOptions = true; 249 } 250 251 // Update wifi display state. 252 if ((changes & CHANGE_WIFI_DISPLAY_STATUS) != 0) { 253 mWifiDisplayStatus = mDisplayManager.getWifiDisplayStatus(); 254 255 // The wifi display feature state may have changed. 256 invalidateOptions = true; 257 } 258 259 // Rebuild the routes. 260 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 261 preferenceScreen.removeAll(); 262 263 // Add all known remote display routes. 264 final int routeCount = mRouter.getRouteCount(); 265 for (int i = 0; i < routeCount; i++) { 266 MediaRouter.RouteInfo route = mRouter.getRouteAt(i); 267 if (route.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) { 268 preferenceScreen.addPreference(createRoutePreference(route)); 269 } 270 } 271 272 // Additional features for wifi display routes. 273 if (mWifiDisplayStatus != null 274 && mWifiDisplayStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) { 275 // Add all unpaired wifi displays. 276 for (WifiDisplay display : mWifiDisplayStatus.getDisplays()) { 277 if (!display.isRemembered() && display.isAvailable() 278 && !display.equals(mWifiDisplayStatus.getActiveDisplay())) { 279 preferenceScreen.addPreference(new UnpairedWifiDisplayPreference( 280 getPrefContext(), display)); 281 } 282 } 283 284 // Add the certification menu if enabled in developer options. 285 if (mWifiDisplayCertificationOn) { 286 buildCertificationMenu(preferenceScreen); 287 } 288 } 289 290 // Invalidate menu options if needed. 291 if (invalidateOptions) { 292 getActivity().invalidateOptionsMenu(); 293 } 294 } 295 createRoutePreference(MediaRouter.RouteInfo route)296 private RoutePreference createRoutePreference(MediaRouter.RouteInfo route) { 297 WifiDisplay display = findWifiDisplay(route.getDeviceAddress()); 298 if (display != null) { 299 return new WifiDisplayRoutePreference(getPrefContext(), route, display); 300 } else { 301 return new RoutePreference(getPrefContext(), route); 302 } 303 } 304 findWifiDisplay(String deviceAddress)305 private WifiDisplay findWifiDisplay(String deviceAddress) { 306 if (mWifiDisplayStatus != null && deviceAddress != null) { 307 for (WifiDisplay display : mWifiDisplayStatus.getDisplays()) { 308 if (display.getDeviceAddress().equals(deviceAddress)) { 309 return display; 310 } 311 } 312 } 313 return null; 314 } 315 buildCertificationMenu(final PreferenceScreen preferenceScreen)316 private void buildCertificationMenu(final PreferenceScreen preferenceScreen) { 317 if (mCertCategory == null) { 318 mCertCategory = new PreferenceCategory(getPrefContext()); 319 mCertCategory.setTitle(R.string.wifi_display_certification_heading); 320 mCertCategory.setOrder(ORDER_CERTIFICATION); 321 } else { 322 mCertCategory.removeAll(); 323 } 324 preferenceScreen.addPreference(mCertCategory); 325 326 // display session info if there is an active p2p session 327 if (!mWifiDisplayStatus.getSessionInfo().getGroupId().isEmpty()) { 328 Preference p = new Preference(getPrefContext()); 329 p.setTitle(R.string.wifi_display_session_info); 330 p.setSummary(mWifiDisplayStatus.getSessionInfo().toString()); 331 mCertCategory.addPreference(p); 332 333 // show buttons for Pause/Resume when a WFD session is established 334 if (mWifiDisplayStatus.getSessionInfo().getSessionId() != 0) { 335 mCertCategory.addPreference(new Preference(getPrefContext()) { 336 @Override 337 public void onBindViewHolder(PreferenceViewHolder view) { 338 super.onBindViewHolder(view); 339 340 Button b = (Button) view.findViewById(R.id.left_button); 341 b.setText(R.string.wifi_display_pause); 342 b.setOnClickListener(new OnClickListener() { 343 @Override 344 public void onClick(View v) { 345 mDisplayManager.pauseWifiDisplay(); 346 } 347 }); 348 349 b = (Button) view.findViewById(R.id.right_button); 350 b.setText(R.string.wifi_display_resume); 351 b.setOnClickListener(new OnClickListener() { 352 @Override 353 public void onClick(View v) { 354 mDisplayManager.resumeWifiDisplay(); 355 } 356 }); 357 } 358 }); 359 mCertCategory.setLayoutResource(R.layout.two_buttons_panel); 360 } 361 } 362 363 // switch for Listen Mode 364 SwitchPreference pref = new SwitchPreference(getPrefContext()) { 365 @Override 366 protected void onClick() { 367 mListen = !mListen; 368 setListenMode(mListen); 369 setChecked(mListen); 370 } 371 }; 372 pref.setTitle(R.string.wifi_display_listen_mode); 373 pref.setChecked(mListen); 374 mCertCategory.addPreference(pref); 375 376 // switch for Autonomous GO 377 pref = new SwitchPreference(getPrefContext()) { 378 @Override 379 protected void onClick() { 380 mAutoGO = !mAutoGO; 381 if (mAutoGO) { 382 startAutoGO(); 383 } else { 384 stopAutoGO(); 385 } 386 setChecked(mAutoGO); 387 } 388 }; 389 pref.setTitle(R.string.wifi_display_autonomous_go); 390 pref.setChecked(mAutoGO); 391 mCertCategory.addPreference(pref); 392 393 // Drop down list for choosing WPS method (PBC/KEYPAD/DISPLAY) 394 ListPreference lp = new ListPreference(getPrefContext()); 395 lp.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 396 @Override 397 public boolean onPreferenceChange(Preference preference, Object value) { 398 int wpsConfig = Integer.parseInt((String) value); 399 if (wpsConfig != mWpsConfig) { 400 mWpsConfig = wpsConfig; 401 getActivity().invalidateOptionsMenu(); 402 Settings.Global.putInt(getActivity().getContentResolver(), 403 Settings.Global.WIFI_DISPLAY_WPS_CONFIG, mWpsConfig); 404 } 405 return true; 406 } 407 }); 408 mWpsConfig = Settings.Global.getInt(getActivity().getContentResolver(), 409 Settings.Global.WIFI_DISPLAY_WPS_CONFIG, WpsInfo.INVALID); 410 String[] wpsEntries = {"Default", "PBC", "KEYPAD", "DISPLAY"}; 411 String[] wpsValues = { 412 "" + WpsInfo.INVALID, 413 "" + WpsInfo.PBC, 414 "" + WpsInfo.KEYPAD, 415 "" + WpsInfo.DISPLAY}; 416 lp.setKey("wps"); 417 lp.setTitle(R.string.wifi_display_wps_config); 418 lp.setEntries(wpsEntries); 419 lp.setEntryValues(wpsValues); 420 lp.setValue("" + mWpsConfig); 421 lp.setSummary("%1$s"); 422 mCertCategory.addPreference(lp); 423 424 // Drop down list for choosing listen channel 425 lp = new ListPreference(getPrefContext()); 426 lp.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 427 @Override 428 public boolean onPreferenceChange(Preference preference, Object value) { 429 int channel = Integer.parseInt((String) value); 430 if (channel != mListenChannel) { 431 mListenChannel = channel; 432 getActivity().invalidateOptionsMenu(); 433 setWifiP2pChannels(mListenChannel, mOperatingChannel); 434 } 435 return true; 436 } 437 }); 438 String[] lcEntries = {"Auto", "1", "6", "11"}; 439 String[] lcValues = {"0", "1", "6", "11"}; 440 lp.setKey("listening_channel"); 441 lp.setTitle(R.string.wifi_display_listen_channel); 442 lp.setEntries(lcEntries); 443 lp.setEntryValues(lcValues); 444 lp.setValue("" + mListenChannel); 445 lp.setSummary("%1$s"); 446 mCertCategory.addPreference(lp); 447 448 // Drop down list for choosing operating channel 449 lp = new ListPreference(getPrefContext()); 450 lp.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 451 @Override 452 public boolean onPreferenceChange(Preference preference, Object value) { 453 int channel = Integer.parseInt((String) value); 454 if (channel != mOperatingChannel) { 455 mOperatingChannel = channel; 456 getActivity().invalidateOptionsMenu(); 457 setWifiP2pChannels(mListenChannel, mOperatingChannel); 458 } 459 return true; 460 } 461 }); 462 String[] ocEntries = {"Auto", "1", "6", "11", "36"}; 463 String[] ocValues = {"0", "1", "6", "11", "36"}; 464 lp.setKey("operating_channel"); 465 lp.setTitle(R.string.wifi_display_operating_channel); 466 lp.setEntries(ocEntries); 467 lp.setEntryValues(ocValues); 468 lp.setValue("" + mOperatingChannel); 469 lp.setSummary("%1$s"); 470 mCertCategory.addPreference(lp); 471 } 472 startAutoGO()473 private void startAutoGO() { 474 if (DEBUG) { 475 Slog.d(TAG, "Starting Autonomous GO..."); 476 } 477 mWifiP2pManager.createGroup(mWifiP2pChannel, new ActionListener() { 478 @Override 479 public void onSuccess() { 480 if (DEBUG) { 481 Slog.d(TAG, "Successfully started AutoGO."); 482 } 483 } 484 485 @Override 486 public void onFailure(int reason) { 487 Slog.e(TAG, "Failed to start AutoGO with reason " + reason + "."); 488 } 489 }); 490 } 491 stopAutoGO()492 private void stopAutoGO() { 493 if (DEBUG) { 494 Slog.d(TAG, "Stopping Autonomous GO..."); 495 } 496 mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() { 497 @Override 498 public void onSuccess() { 499 if (DEBUG) { 500 Slog.d(TAG, "Successfully stopped AutoGO."); 501 } 502 } 503 504 @Override 505 public void onFailure(int reason) { 506 Slog.e(TAG, "Failed to stop AutoGO with reason " + reason + "."); 507 } 508 }); 509 } 510 setListenMode(final boolean enable)511 private void setListenMode(final boolean enable) { 512 if (DEBUG) { 513 Slog.d(TAG, "Setting listen mode to: " + enable); 514 } 515 mWifiP2pManager.listen(mWifiP2pChannel, enable, new ActionListener() { 516 @Override 517 public void onSuccess() { 518 if (DEBUG) { 519 Slog.d(TAG, "Successfully " + (enable ? "entered" : "exited") 520 + " listen mode."); 521 } 522 } 523 524 @Override 525 public void onFailure(int reason) { 526 Slog.e(TAG, "Failed to " + (enable ? "entered" : "exited") 527 + " listen mode with reason " + reason + "."); 528 } 529 }); 530 } 531 setWifiP2pChannels(final int lc, final int oc)532 private void setWifiP2pChannels(final int lc, final int oc) { 533 if (DEBUG) { 534 Slog.d(TAG, "Setting wifi p2p channel: lc=" + lc + ", oc=" + oc); 535 } 536 mWifiP2pManager.setWifiP2pChannels(mWifiP2pChannel, 537 lc, oc, new ActionListener() { 538 @Override 539 public void onSuccess() { 540 if (DEBUG) { 541 Slog.d(TAG, "Successfully set wifi p2p channels."); 542 } 543 } 544 545 @Override 546 public void onFailure(int reason) { 547 Slog.e(TAG, "Failed to set wifi p2p channels with reason " + reason + "."); 548 } 549 }); 550 } 551 toggleRoute(MediaRouter.RouteInfo route)552 private void toggleRoute(MediaRouter.RouteInfo route) { 553 if (route.isSelected()) { 554 MediaRouteDialogPresenter.showDialogFragment(getActivity(), 555 MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, null); 556 } else { 557 route.select(); 558 } 559 } 560 pairWifiDisplay(WifiDisplay display)561 private void pairWifiDisplay(WifiDisplay display) { 562 if (display.canConnect()) { 563 mDisplayManager.connectWifiDisplay(display.getDeviceAddress()); 564 } 565 } 566 showWifiDisplayOptionsDialog(final WifiDisplay display)567 private void showWifiDisplayOptionsDialog(final WifiDisplay display) { 568 View view = getActivity().getLayoutInflater().inflate(R.layout.wifi_display_options, null); 569 final EditText nameEditText = (EditText) view.findViewById(R.id.name); 570 nameEditText.setText(display.getFriendlyDisplayName()); 571 572 DialogInterface.OnClickListener done = new DialogInterface.OnClickListener() { 573 @Override 574 public void onClick(DialogInterface dialog, int which) { 575 String name = nameEditText.getText().toString().trim(); 576 if (name.isEmpty() || name.equals(display.getDeviceName())) { 577 name = null; 578 } 579 mDisplayManager.renameWifiDisplay(display.getDeviceAddress(), name); 580 } 581 }; 582 DialogInterface.OnClickListener forget = new DialogInterface.OnClickListener() { 583 @Override 584 public void onClick(DialogInterface dialog, int which) { 585 mDisplayManager.forgetWifiDisplay(display.getDeviceAddress()); 586 } 587 }; 588 589 AlertDialog dialog = new AlertDialog.Builder(getActivity()) 590 .setCancelable(true) 591 .setTitle(R.string.wifi_display_options_title) 592 .setView(view) 593 .setPositiveButton(R.string.wifi_display_options_done, done) 594 .setNegativeButton(R.string.wifi_display_options_forget, forget) 595 .create(); 596 dialog.show(); 597 } 598 599 private final Runnable mUpdateRunnable = new Runnable() { 600 @Override 601 public void run() { 602 final int changes = mPendingChanges; 603 mPendingChanges = 0; 604 update(changes); 605 } 606 }; 607 608 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 609 @Override 610 public void onReceive(Context context, Intent intent) { 611 String action = intent.getAction(); 612 if (action.equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) { 613 scheduleUpdate(CHANGE_WIFI_DISPLAY_STATUS); 614 } 615 } 616 }; 617 618 private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { 619 @Override 620 public void onChange(boolean selfChange, Uri uri) { 621 scheduleUpdate(CHANGE_SETTINGS); 622 } 623 }; 624 625 private final MediaRouter.Callback mRouterCallback = new MediaRouter.SimpleCallback() { 626 @Override 627 public void onRouteAdded(MediaRouter router, RouteInfo info) { 628 scheduleUpdate(CHANGE_ROUTES); 629 } 630 631 @Override 632 public void onRouteChanged(MediaRouter router, RouteInfo info) { 633 scheduleUpdate(CHANGE_ROUTES); 634 } 635 636 @Override 637 public void onRouteRemoved(MediaRouter router, RouteInfo info) { 638 scheduleUpdate(CHANGE_ROUTES); 639 } 640 641 @Override 642 public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { 643 scheduleUpdate(CHANGE_ROUTES); 644 } 645 646 @Override 647 public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { 648 scheduleUpdate(CHANGE_ROUTES); 649 } 650 }; 651 652 private class RoutePreference extends Preference 653 implements Preference.OnPreferenceClickListener { 654 private final MediaRouter.RouteInfo mRoute; 655 RoutePreference(Context context, MediaRouter.RouteInfo route)656 public RoutePreference(Context context, MediaRouter.RouteInfo route) { 657 super(context); 658 659 mRoute = route; 660 setTitle(route.getName()); 661 setSummary(route.getDescription()); 662 setEnabled(route.isEnabled()); 663 if (route.isSelected()) { 664 setOrder(ORDER_CONNECTED); 665 if (route.isConnecting()) { 666 setSummary(R.string.wifi_display_status_connecting); 667 } else { 668 setSummary(R.string.wifi_display_status_connected); 669 } 670 } else { 671 if (isEnabled()) { 672 setOrder(ORDER_AVAILABLE); 673 } else { 674 setOrder(ORDER_UNAVAILABLE); 675 if (route.getStatusCode() == MediaRouter.RouteInfo.STATUS_IN_USE) { 676 setSummary(R.string.wifi_display_status_in_use); 677 } else { 678 setSummary(R.string.wifi_display_status_not_available); 679 } 680 } 681 } 682 setOnPreferenceClickListener(this); 683 } 684 685 @Override onPreferenceClick(Preference preference)686 public boolean onPreferenceClick(Preference preference) { 687 toggleRoute(mRoute); 688 return true; 689 } 690 } 691 692 private class WifiDisplayRoutePreference extends RoutePreference 693 implements View.OnClickListener { 694 private final WifiDisplay mDisplay; 695 WifiDisplayRoutePreference(Context context, MediaRouter.RouteInfo route, WifiDisplay display)696 public WifiDisplayRoutePreference(Context context, MediaRouter.RouteInfo route, 697 WifiDisplay display) { 698 super(context, route); 699 700 mDisplay = display; 701 setWidgetLayoutResource(R.layout.wifi_display_preference); 702 } 703 704 @Override onBindViewHolder(PreferenceViewHolder view)705 public void onBindViewHolder(PreferenceViewHolder view) { 706 super.onBindViewHolder(view); 707 708 ImageView deviceDetails = (ImageView) view.findViewById(R.id.deviceDetails); 709 if (deviceDetails != null) { 710 deviceDetails.setOnClickListener(this); 711 if (!isEnabled()) { 712 TypedValue value = new TypedValue(); 713 getContext().getTheme().resolveAttribute(android.R.attr.disabledAlpha, 714 value, true); 715 deviceDetails.setImageAlpha((int) (value.getFloat() * 255)); 716 deviceDetails.setEnabled(true); // always allow button to be pressed 717 } 718 } 719 } 720 721 @Override onClick(View v)722 public void onClick(View v) { 723 showWifiDisplayOptionsDialog(mDisplay); 724 } 725 } 726 727 private class UnpairedWifiDisplayPreference extends Preference 728 implements Preference.OnPreferenceClickListener { 729 private final WifiDisplay mDisplay; 730 UnpairedWifiDisplayPreference(Context context, WifiDisplay display)731 public UnpairedWifiDisplayPreference(Context context, WifiDisplay display) { 732 super(context); 733 734 mDisplay = display; 735 setTitle(display.getFriendlyDisplayName()); 736 setSummary(com.android.internal.R.string.wireless_display_route_description); 737 setEnabled(display.canConnect()); 738 if (isEnabled()) { 739 setOrder(ORDER_AVAILABLE); 740 } else { 741 setOrder(ORDER_UNAVAILABLE); 742 setSummary(R.string.wifi_display_status_in_use); 743 } 744 setOnPreferenceClickListener(this); 745 } 746 747 @Override onPreferenceClick(Preference preference)748 public boolean onPreferenceClick(Preference preference) { 749 pairWifiDisplay(mDisplay); 750 return true; 751 } 752 } 753 754 private static class SummaryProvider implements SummaryLoader.SummaryProvider { 755 756 private final Context mContext; 757 private final SummaryLoader mSummaryLoader; 758 private final MediaRouter mRouter; 759 private final MediaRouter.Callback mRouterCallback = new MediaRouter.SimpleCallback() { 760 @Override 761 public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { 762 updateSummary(); 763 } 764 765 @Override 766 public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { 767 updateSummary(); 768 } 769 770 @Override 771 public void onRouteAdded(MediaRouter router, RouteInfo info) { 772 updateSummary(); 773 } 774 775 @Override 776 public void onRouteRemoved(MediaRouter router, RouteInfo info) { 777 updateSummary(); 778 } 779 780 @Override 781 public void onRouteChanged(MediaRouter router, RouteInfo info) { 782 updateSummary(); 783 } 784 }; 785 SummaryProvider(Context context, SummaryLoader summaryLoader)786 public SummaryProvider(Context context, SummaryLoader summaryLoader) { 787 mContext = context; 788 mSummaryLoader = summaryLoader; 789 mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); 790 } 791 792 @Override setListening(boolean listening)793 public void setListening(boolean listening) { 794 if (listening) { 795 mRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mRouterCallback); 796 updateSummary(); 797 } else { 798 mRouter.removeCallback(mRouterCallback); 799 } 800 } 801 updateSummary()802 private void updateSummary() { 803 String summary = mContext.getString(R.string.disconnected); 804 805 final int routeCount = mRouter.getRouteCount(); 806 for (int i = 0; i < routeCount; i++) { 807 final MediaRouter.RouteInfo route = mRouter.getRouteAt(i); 808 if (route.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY) 809 && route.isSelected() && !route.isConnecting()) { 810 summary = mContext.getString(R.string.wifi_display_status_connected); 811 break; 812 } 813 } 814 mSummaryLoader.setSummary(this, summary); 815 } 816 } 817 818 public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY 819 = (activity, summaryLoader) -> new SummaryProvider(activity, summaryLoader); 820 } 821