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