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.Settings; 41 import android.util.Slog; 42 import android.util.TypedValue; 43 import android.view.Menu; 44 import android.view.MenuInflater; 45 import android.view.MenuItem; 46 import android.view.View; 47 import android.view.View.OnClickListener; 48 import android.widget.Button; 49 import android.widget.EditText; 50 import android.widget.ImageView; 51 import android.widget.TextView; 52 53 import androidx.appcompat.app.AlertDialog; 54 import androidx.preference.ListPreference; 55 import androidx.preference.Preference; 56 import androidx.preference.Preference.OnPreferenceChangeListener; 57 import androidx.preference.PreferenceCategory; 58 import androidx.preference.PreferenceGroup; 59 import androidx.preference.PreferenceScreen; 60 import androidx.preference.PreferenceViewHolder; 61 import androidx.preference.SwitchPreference; 62 63 import com.android.internal.app.MediaRouteDialogPresenter; 64 import com.android.settings.R; 65 import com.android.settings.SettingsPreferenceFragment; 66 import com.android.settings.search.BaseSearchIndexProvider; 67 import com.android.settingslib.search.Indexable; 68 import com.android.settingslib.search.SearchIndexable; 69 import com.android.settingslib.widget.TwoTargetPreference; 70 71 /** 72 * The Settings screen for WifiDisplay configuration and connection management. 73 * 74 * The wifi display routes are integrated together with other remote display routes 75 * from the media router. It may happen that wifi display isn't actually available 76 * on the system. In that case, the enable option will not be shown but other 77 * remote display routes will continue to be made available. 78 */ 79 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) 80 public final class WifiDisplaySettings extends SettingsPreferenceFragment implements Indexable { 81 private static final String TAG = "WifiDisplaySettings"; 82 private static final boolean DEBUG = false; 83 84 private static final int MENU_ID_ENABLE_WIFI_DISPLAY = Menu.FIRST; 85 86 private static final int CHANGE_SETTINGS = 1 << 0; 87 private static final int CHANGE_ROUTES = 1 << 1; 88 private static final int CHANGE_WIFI_DISPLAY_STATUS = 1 << 2; 89 private static final int CHANGE_ALL = -1; 90 91 private static final int ORDER_CERTIFICATION = 1; 92 private static final int ORDER_CONNECTED = 2; 93 private static final int ORDER_AVAILABLE = 3; 94 private static final int ORDER_UNAVAILABLE = 4; 95 96 private final Handler mHandler; 97 98 private MediaRouter mRouter; 99 private DisplayManager mDisplayManager; 100 101 private boolean mStarted; 102 private int mPendingChanges; 103 104 private boolean mWifiDisplayOnSetting; 105 private WifiDisplayStatus mWifiDisplayStatus; 106 107 private TextView mEmptyView; 108 109 /* certification */ 110 private boolean mWifiDisplayCertificationOn; 111 private WifiP2pManager mWifiP2pManager; 112 private Channel mWifiP2pChannel; 113 private PreferenceGroup mCertCategory; 114 private boolean mListen; 115 private boolean mAutoGO; 116 private int mWpsConfig = WpsInfo.INVALID; 117 private int mListenChannel; 118 private int mOperatingChannel; 119 WifiDisplaySettings()120 public WifiDisplaySettings() { 121 mHandler = new Handler(); 122 } 123 124 @Override getMetricsCategory()125 public int getMetricsCategory() { 126 return SettingsEnums.WFD_WIFI_DISPLAY; 127 } 128 129 @Override onCreate(Bundle icicle)130 public void onCreate(Bundle icicle) { 131 super.onCreate(icicle); 132 133 final Context context = getActivity(); 134 mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); 135 mRouter.setRouterGroupId(MediaRouter.MIRRORING_GROUP_ID); 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 getHelpResource()145 public int getHelpResource() { 146 return R.string.help_url_remote_display; 147 } 148 149 @Override onActivityCreated(Bundle savedInstanceState)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 onStart()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 onStop()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 onCreateOptionsMenu(Menu menu, MenuInflater inflater)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 onOptionsItemSelected(MenuItem item)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 isAvailable(Context context)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 scheduleUpdate(int changes)227 private void scheduleUpdate(int changes) { 228 if (mStarted) { 229 if (mPendingChanges == 0) { 230 mHandler.post(mUpdateRunnable); 231 } 232 mPendingChanges |= changes; 233 } 234 } 235 unscheduleUpdate()236 private void unscheduleUpdate() { 237 if (mPendingChanges != 0) { 238 mPendingChanges = 0; 239 mHandler.removeCallbacks(mUpdateRunnable); 240 } 241 } 242 update(int changes)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 createRoutePreference(MediaRouter.RouteInfo route)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 findWifiDisplay(String deviceAddress)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 buildCertificationMenu(final PreferenceScreen preferenceScreen)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 startAutoGO()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 stopAutoGO()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 setListenMode(final boolean enable)519 private void setListenMode(final boolean enable) { 520 if (DEBUG) { 521 Slog.d(TAG, "Setting listen mode to: " + enable); 522 } 523 final ActionListener listener = 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 if (enable) { 539 mWifiP2pManager.startListening(mWifiP2pChannel, listener); 540 } else { 541 mWifiP2pManager.stopListening(mWifiP2pChannel, listener); 542 } 543 } 544 setWifiP2pChannels(final int lc, final int oc)545 private void setWifiP2pChannels(final int lc, final int oc) { 546 if (DEBUG) { 547 Slog.d(TAG, "Setting wifi p2p channel: lc=" + lc + ", oc=" + oc); 548 } 549 mWifiP2pManager.setWifiP2pChannels(mWifiP2pChannel, 550 lc, oc, new ActionListener() { 551 @Override 552 public void onSuccess() { 553 if (DEBUG) { 554 Slog.d(TAG, "Successfully set wifi p2p channels."); 555 } 556 } 557 558 @Override 559 public void onFailure(int reason) { 560 Slog.e(TAG, "Failed to set wifi p2p channels with reason " + reason + "."); 561 } 562 }); 563 } 564 toggleRoute(MediaRouter.RouteInfo route)565 private void toggleRoute(MediaRouter.RouteInfo route) { 566 if (route.isSelected()) { 567 MediaRouteDialogPresenter.showDialogFragment(getActivity(), 568 MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, null); 569 } else { 570 route.select(); 571 } 572 } 573 pairWifiDisplay(WifiDisplay display)574 private void pairWifiDisplay(WifiDisplay display) { 575 if (display.canConnect()) { 576 mDisplayManager.connectWifiDisplay(display.getDeviceAddress()); 577 } 578 } 579 showWifiDisplayOptionsDialog(final WifiDisplay display)580 private void showWifiDisplayOptionsDialog(final WifiDisplay display) { 581 View view = getActivity().getLayoutInflater().inflate(R.layout.wifi_display_options, null); 582 final EditText nameEditText = (EditText) view.findViewById(R.id.name); 583 nameEditText.setText(display.getFriendlyDisplayName()); 584 585 DialogInterface.OnClickListener done = new DialogInterface.OnClickListener() { 586 @Override 587 public void onClick(DialogInterface dialog, int which) { 588 String name = nameEditText.getText().toString().trim(); 589 if (name.isEmpty() || name.equals(display.getDeviceName())) { 590 name = null; 591 } 592 mDisplayManager.renameWifiDisplay(display.getDeviceAddress(), name); 593 } 594 }; 595 DialogInterface.OnClickListener forget = new DialogInterface.OnClickListener() { 596 @Override 597 public void onClick(DialogInterface dialog, int which) { 598 mDisplayManager.forgetWifiDisplay(display.getDeviceAddress()); 599 } 600 }; 601 602 AlertDialog dialog = new AlertDialog.Builder(getActivity()) 603 .setCancelable(true) 604 .setTitle(R.string.wifi_display_options_title) 605 .setView(view) 606 .setPositiveButton(R.string.wifi_display_options_done, done) 607 .setNegativeButton(R.string.wifi_display_options_forget, forget) 608 .create(); 609 dialog.show(); 610 } 611 612 private final Runnable mUpdateRunnable = new Runnable() { 613 @Override 614 public void run() { 615 final int changes = mPendingChanges; 616 mPendingChanges = 0; 617 update(changes); 618 } 619 }; 620 621 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 622 @Override 623 public void onReceive(Context context, Intent intent) { 624 String action = intent.getAction(); 625 if (action.equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) { 626 scheduleUpdate(CHANGE_WIFI_DISPLAY_STATUS); 627 } 628 } 629 }; 630 631 private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { 632 @Override 633 public void onChange(boolean selfChange, Uri uri) { 634 scheduleUpdate(CHANGE_SETTINGS); 635 } 636 }; 637 638 private final MediaRouter.Callback mRouterCallback = new MediaRouter.SimpleCallback() { 639 @Override 640 public void onRouteAdded(MediaRouter router, RouteInfo info) { 641 scheduleUpdate(CHANGE_ROUTES); 642 } 643 644 @Override 645 public void onRouteChanged(MediaRouter router, RouteInfo info) { 646 scheduleUpdate(CHANGE_ROUTES); 647 } 648 649 @Override 650 public void onRouteRemoved(MediaRouter router, RouteInfo info) { 651 scheduleUpdate(CHANGE_ROUTES); 652 } 653 654 @Override 655 public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { 656 scheduleUpdate(CHANGE_ROUTES); 657 } 658 659 @Override 660 public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { 661 scheduleUpdate(CHANGE_ROUTES); 662 } 663 }; 664 665 private class RoutePreference extends TwoTargetPreference 666 implements Preference.OnPreferenceClickListener { 667 private final MediaRouter.RouteInfo mRoute; 668 RoutePreference(Context context, MediaRouter.RouteInfo route)669 public RoutePreference(Context context, MediaRouter.RouteInfo route) { 670 super(context); 671 672 mRoute = route; 673 setTitle(route.getName()); 674 setSummary(route.getDescription()); 675 setEnabled(route.isEnabled()); 676 if (route.isSelected()) { 677 setOrder(ORDER_CONNECTED); 678 if (route.isConnecting()) { 679 setSummary(R.string.wifi_display_status_connecting); 680 } else { 681 setSummary(R.string.wifi_display_status_connected); 682 } 683 } else { 684 if (isEnabled()) { 685 setOrder(ORDER_AVAILABLE); 686 } else { 687 setOrder(ORDER_UNAVAILABLE); 688 if (route.getStatusCode() == MediaRouter.RouteInfo.STATUS_IN_USE) { 689 setSummary(R.string.wifi_display_status_in_use); 690 } else { 691 setSummary(R.string.wifi_display_status_not_available); 692 } 693 } 694 } 695 setOnPreferenceClickListener(this); 696 } 697 698 @Override onPreferenceClick(Preference preference)699 public boolean onPreferenceClick(Preference preference) { 700 toggleRoute(mRoute); 701 return true; 702 } 703 } 704 705 private class WifiDisplayRoutePreference extends RoutePreference 706 implements View.OnClickListener { 707 private final WifiDisplay mDisplay; 708 709 @Override getSecondTargetResId()710 protected int getSecondTargetResId() { 711 return R.layout.preference_widget_gear; 712 } 713 WifiDisplayRoutePreference(Context context, MediaRouter.RouteInfo route, WifiDisplay display)714 public WifiDisplayRoutePreference(Context context, MediaRouter.RouteInfo route, 715 WifiDisplay display) { 716 super(context, route); 717 mDisplay = display; 718 } 719 720 @Override onBindViewHolder(PreferenceViewHolder holder)721 public void onBindViewHolder(PreferenceViewHolder holder) { 722 super.onBindViewHolder(holder); 723 724 final ImageView gear = (ImageView) holder.findViewById(R.id.settings_button); 725 if (gear != null) { 726 gear.setOnClickListener(this); 727 if (!isEnabled()) { 728 TypedValue value = new TypedValue(); 729 getContext().getTheme().resolveAttribute(android.R.attr.disabledAlpha, 730 value, true); 731 gear.setImageAlpha((int) (value.getFloat() * 255)); 732 gear.setEnabled(true); // always allow button to be pressed 733 } 734 } 735 } 736 737 @Override onClick(View v)738 public void onClick(View v) { 739 showWifiDisplayOptionsDialog(mDisplay); 740 } 741 } 742 743 private class UnpairedWifiDisplayPreference extends Preference 744 implements Preference.OnPreferenceClickListener { 745 private final WifiDisplay mDisplay; 746 UnpairedWifiDisplayPreference(Context context, WifiDisplay display)747 public UnpairedWifiDisplayPreference(Context context, WifiDisplay display) { 748 super(context); 749 750 mDisplay = display; 751 setTitle(display.getFriendlyDisplayName()); 752 setSummary(com.android.internal.R.string.wireless_display_route_description); 753 setEnabled(display.canConnect()); 754 if (isEnabled()) { 755 setOrder(ORDER_AVAILABLE); 756 } else { 757 setOrder(ORDER_UNAVAILABLE); 758 setSummary(R.string.wifi_display_status_in_use); 759 } 760 setOnPreferenceClickListener(this); 761 } 762 763 @Override onPreferenceClick(Preference preference)764 public boolean onPreferenceClick(Preference preference) { 765 pairWifiDisplay(mDisplay); 766 return true; 767 } 768 } 769 770 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 771 new BaseSearchIndexProvider(R.xml.wifi_display_settings); 772 } 773