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