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