1 /* 2 * Copyright (C) 2010 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.wifi; 18 19 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 20 import static android.os.UserManager.DISALLOW_CONFIG_WIFI; 21 22 import android.annotation.NonNull; 23 import android.app.Activity; 24 import android.app.Dialog; 25 import android.app.settings.SettingsEnums; 26 import android.content.ContentResolver; 27 import android.content.Context; 28 import android.content.DialogInterface; 29 import android.content.Intent; 30 import android.content.res.Resources; 31 import android.net.ConnectivityManager; 32 import android.net.Network; 33 import android.net.NetworkInfo; 34 import android.net.NetworkInfo.State; 35 import android.net.NetworkRequest; 36 import android.net.NetworkTemplate; 37 import android.net.wifi.WifiConfiguration; 38 import android.net.wifi.WifiManager; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.os.Looper; 42 import android.os.PowerManager; 43 import android.provider.Settings; 44 import android.util.FeatureFlagUtils; 45 import android.util.Log; 46 import android.view.ContextMenu; 47 import android.view.ContextMenu.ContextMenuInfo; 48 import android.view.Menu; 49 import android.view.MenuItem; 50 import android.view.View; 51 import android.widget.Toast; 52 53 import androidx.annotation.VisibleForTesting; 54 import androidx.preference.Preference; 55 import androidx.preference.PreferenceCategory; 56 57 import com.android.settings.LinkifyUtils; 58 import com.android.settings.R; 59 import com.android.settings.RestrictedSettingsFragment; 60 import com.android.settings.SettingsActivity; 61 import com.android.settings.core.FeatureFlags; 62 import com.android.settings.core.SubSettingLauncher; 63 import com.android.settings.dashboard.SummaryLoader; 64 import com.android.settings.datausage.DataUsageUtils; 65 import com.android.settings.datausage.DataUsagePreference; 66 import com.android.settings.location.ScanningSettings; 67 import com.android.settings.search.BaseSearchIndexProvider; 68 import com.android.settings.search.Indexable; 69 import com.android.settings.search.SearchIndexableRaw; 70 import com.android.settings.widget.SummaryUpdater.OnSummaryChangeListener; 71 import com.android.settings.widget.SwitchBarController; 72 import com.android.settings.wifi.details.WifiNetworkDetailsFragment; 73 import com.android.settings.wifi.dpp.WifiDppUtils; 74 import com.android.settingslib.RestrictedLockUtils; 75 import com.android.settingslib.RestrictedLockUtilsInternal; 76 import com.android.settingslib.search.SearchIndexable; 77 import com.android.settingslib.wifi.AccessPoint; 78 import com.android.settingslib.wifi.AccessPoint.AccessPointListener; 79 import com.android.settingslib.wifi.AccessPointPreference; 80 import com.android.settingslib.wifi.WifiSavedConfigUtils; 81 import com.android.settingslib.wifi.WifiTracker; 82 import com.android.settingslib.wifi.WifiTrackerFactory; 83 84 import java.util.ArrayList; 85 import java.util.List; 86 87 /** 88 * Two types of UI are provided here. 89 * 90 * The first is for "usual Settings", appearing as any other Setup fragment. 91 * 92 * The second is for Setup Wizard, with a simplified interface that hides the action bar 93 * and menus. 94 */ 95 @SearchIndexable 96 public class WifiSettings extends RestrictedSettingsFragment 97 implements Indexable, WifiTracker.WifiListener, AccessPointListener, 98 WifiDialog.WifiDialogListener, DialogInterface.OnDismissListener { 99 100 private static final String TAG = "WifiSettings"; 101 102 private static final int MENU_ID_CONNECT = Menu.FIRST + 6; 103 @VisibleForTesting 104 static final int MENU_ID_FORGET = Menu.FIRST + 7; 105 private static final int MENU_ID_MODIFY = Menu.FIRST + 8; 106 107 public static final int WIFI_DIALOG_ID = 1; 108 109 @VisibleForTesting 110 static final int ADD_NETWORK_REQUEST = 2; 111 112 // Instance state keys 113 private static final String SAVE_DIALOG_MODE = "dialog_mode"; 114 private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state"; 115 116 private static final String PREF_KEY_EMPTY_WIFI_LIST = "wifi_empty_list"; 117 private static final String PREF_KEY_CONNECTED_ACCESS_POINTS = "connected_access_point"; 118 private static final String PREF_KEY_ACCESS_POINTS = "access_points"; 119 private static final String PREF_KEY_CONFIGURE_WIFI_SETTINGS = "configure_settings"; 120 private static final String PREF_KEY_SAVED_NETWORKS = "saved_networks"; 121 private static final String PREF_KEY_STATUS_MESSAGE = "wifi_status_message"; 122 @VisibleForTesting 123 static final String PREF_KEY_DATA_USAGE = "wifi_data_usage"; 124 125 private static final int REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER = 0; 126 isVerboseLoggingEnabled()127 private static boolean isVerboseLoggingEnabled() { 128 return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE); 129 } 130 131 private final Runnable mUpdateAccessPointsRunnable = () -> { 132 updateAccessPointPreferences(); 133 }; 134 private final Runnable mHideProgressBarRunnable = () -> { 135 setProgressBarVisible(false); 136 }; 137 138 protected WifiManager mWifiManager; 139 private ConnectivityManager mConnectivityManager; 140 private WifiManager.ActionListener mConnectListener; 141 private WifiManager.ActionListener mSaveListener; 142 private WifiManager.ActionListener mForgetListener; 143 private CaptivePortalNetworkCallback mCaptivePortalNetworkCallback; 144 145 /** 146 * The state of {@link #isUiRestricted()} at {@link #onCreate(Bundle)}}. This is neccesary to 147 * ensure that behavior is consistent if {@link #isUiRestricted()} changes. It could be changed 148 * by the Test DPC tool in AFW mode. 149 */ 150 private boolean mIsRestricted; 151 152 private WifiEnabler mWifiEnabler; 153 // An access point being edited is stored here. 154 private AccessPoint mSelectedAccessPoint; 155 156 private WifiDialog mDialog; 157 158 private View mProgressHeader; 159 160 // this boolean extra specifies whether to disable the Next button when not connected. Used by 161 // account creation outside of setup wizard. 162 private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect"; 163 // This string extra specifies a network to open the connect dialog on, so the user can enter 164 // network credentials. This is used by quick settings for secured networks, among other 165 // things. 166 public static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid"; 167 168 // should Next button only be enabled when we have a connection? 169 private boolean mEnableNextOnConnection; 170 171 // Save the dialog details 172 private int mDialogMode; 173 private AccessPoint mDlgAccessPoint; 174 private Bundle mAccessPointSavedState; 175 176 @VisibleForTesting 177 WifiTracker mWifiTracker; 178 private String mOpenSsid; 179 180 private AccessPointPreference.UserBadgeCache mUserBadgeCache; 181 182 private PreferenceCategory mConnectedAccessPointPreferenceCategory; 183 private PreferenceCategory mAccessPointsPreferenceCategory; 184 @VisibleForTesting 185 AddWifiNetworkPreference mAddWifiNetworkPreference; 186 @VisibleForTesting 187 Preference mConfigureWifiSettingsPreference; 188 @VisibleForTesting 189 Preference mSavedNetworksPreference; 190 @VisibleForTesting 191 DataUsagePreference mDataUsagePreference; 192 private LinkablePreference mStatusMessagePreference; 193 194 // For Search 195 public static final String DATA_KEY_REFERENCE = "main_toggle_wifi"; 196 197 /** 198 * Tracks whether the user initiated a connection via clicking in order to autoscroll to the 199 * network once connected. 200 */ 201 private boolean mClickedConnect; 202 203 /* End of "used in Wifi Setup context" */ 204 WifiSettings()205 public WifiSettings() { 206 super(DISALLOW_CONFIG_WIFI); 207 } 208 209 @Override onViewCreated(View view, Bundle savedInstanceState)210 public void onViewCreated(View view, Bundle savedInstanceState) { 211 super.onViewCreated(view, savedInstanceState); 212 final Activity activity = getActivity(); 213 if (activity != null) { 214 mProgressHeader = setPinnedHeaderView(R.layout.progress_header) 215 .findViewById(R.id.progress_bar_animation); 216 setProgressBarVisible(false); 217 } 218 ((SettingsActivity) activity).getSwitchBar().setSwitchBarText( 219 R.string.wifi_settings_master_switch_title, 220 R.string.wifi_settings_master_switch_title); 221 } 222 223 @Override onCreate(Bundle icicle)224 public void onCreate(Bundle icicle) { 225 super.onCreate(icicle); 226 227 // TODO(b/37429702): Add animations and preference comparator back after initial screen is 228 // loaded (ODR). 229 setAnimationAllowed(false); 230 231 addPreferences(); 232 233 mIsRestricted = isUiRestricted(); 234 } 235 addPreferences()236 private void addPreferences() { 237 addPreferencesFromResource(R.xml.wifi_settings); 238 239 mConnectedAccessPointPreferenceCategory = 240 (PreferenceCategory) findPreference(PREF_KEY_CONNECTED_ACCESS_POINTS); 241 mAccessPointsPreferenceCategory = 242 (PreferenceCategory) findPreference(PREF_KEY_ACCESS_POINTS); 243 mConfigureWifiSettingsPreference = findPreference(PREF_KEY_CONFIGURE_WIFI_SETTINGS); 244 mSavedNetworksPreference = findPreference(PREF_KEY_SAVED_NETWORKS); 245 mAddWifiNetworkPreference = new AddWifiNetworkPreference(getPrefContext()); 246 mStatusMessagePreference = (LinkablePreference) findPreference(PREF_KEY_STATUS_MESSAGE); 247 mUserBadgeCache = new AccessPointPreference.UserBadgeCache(getPackageManager()); 248 mDataUsagePreference = findPreference(PREF_KEY_DATA_USAGE); 249 mDataUsagePreference.setVisible(DataUsageUtils.hasWifiRadio(getContext())); 250 mDataUsagePreference.setTemplate(NetworkTemplate.buildTemplateWifiWildcard(), 251 0 /*subId*/, 252 null /*service*/); 253 } 254 255 @Override onActivityCreated(Bundle savedInstanceState)256 public void onActivityCreated(Bundle savedInstanceState) { 257 super.onActivityCreated(savedInstanceState); 258 259 mWifiTracker = WifiTrackerFactory.create( 260 getActivity(), this, getSettingsLifecycle(), true, true); 261 mWifiManager = mWifiTracker.getManager(); 262 263 final Activity activity = getActivity(); 264 if (activity != null) { 265 mConnectivityManager = getActivity().getSystemService(ConnectivityManager.class); 266 } 267 268 mConnectListener = new WifiConnectListener(getActivity()); 269 270 mSaveListener = new WifiManager.ActionListener() { 271 @Override 272 public void onSuccess() { 273 } 274 275 @Override 276 public void onFailure(int reason) { 277 Activity activity = getActivity(); 278 if (activity != null) { 279 Toast.makeText(activity, 280 R.string.wifi_failed_save_message, 281 Toast.LENGTH_SHORT).show(); 282 } 283 } 284 }; 285 286 mForgetListener = new WifiManager.ActionListener() { 287 @Override 288 public void onSuccess() { 289 } 290 291 @Override 292 public void onFailure(int reason) { 293 Activity activity = getActivity(); 294 if (activity != null) { 295 Toast.makeText(activity, 296 R.string.wifi_failed_forget_message, 297 Toast.LENGTH_SHORT).show(); 298 } 299 } 300 }; 301 302 if (savedInstanceState != null) { 303 mDialogMode = savedInstanceState.getInt(SAVE_DIALOG_MODE); 304 if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) { 305 mAccessPointSavedState = 306 savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE); 307 } 308 } 309 310 // if we're supposed to enable/disable the Next button based on our current connection 311 // state, start it off in the right state 312 Intent intent = getActivity().getIntent(); 313 mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false); 314 315 if (mEnableNextOnConnection) { 316 if (hasNextButton()) { 317 final ConnectivityManager connectivity = (ConnectivityManager) 318 getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); 319 if (connectivity != null) { 320 NetworkInfo info = connectivity.getNetworkInfo( 321 ConnectivityManager.TYPE_WIFI); 322 changeNextButtonState(info.isConnected()); 323 } 324 } 325 } 326 327 registerForContextMenu(getListView()); 328 setHasOptionsMenu(true); 329 330 if (intent.hasExtra(EXTRA_START_CONNECT_SSID)) { 331 mOpenSsid = intent.getStringExtra(EXTRA_START_CONNECT_SSID); 332 } 333 } 334 335 @Override onDestroyView()336 public void onDestroyView() { 337 super.onDestroyView(); 338 339 if (mWifiEnabler != null) { 340 mWifiEnabler.teardownSwitchController(); 341 } 342 } 343 344 @Override onStart()345 public void onStart() { 346 super.onStart(); 347 348 // On/off switch is hidden for Setup Wizard (returns null) 349 mWifiEnabler = createWifiEnabler(); 350 351 if (mIsRestricted) { 352 restrictUi(); 353 return; 354 } 355 356 onWifiStateChanged(mWifiManager.getWifiState()); 357 } 358 restrictUi()359 private void restrictUi() { 360 if (!isUiRestrictedByOnlyAdmin()) { 361 getEmptyTextView().setText(R.string.wifi_empty_list_user_restricted); 362 } 363 getPreferenceScreen().removeAll(); 364 } 365 366 /** 367 * @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard) 368 */ createWifiEnabler()369 private WifiEnabler createWifiEnabler() { 370 final SettingsActivity activity = (SettingsActivity) getActivity(); 371 return new WifiEnabler(activity, new SwitchBarController(activity.getSwitchBar()), 372 mMetricsFeatureProvider); 373 } 374 375 @Override onResume()376 public void onResume() { 377 final Activity activity = getActivity(); 378 super.onResume(); 379 380 // Because RestrictedSettingsFragment's onResume potentially requests authorization, 381 // which changes the restriction state, recalculate it. 382 final boolean alreadyImmutablyRestricted = mIsRestricted; 383 mIsRestricted = isUiRestricted(); 384 if (!alreadyImmutablyRestricted && mIsRestricted) { 385 restrictUi(); 386 } 387 388 if (mWifiEnabler != null) { 389 mWifiEnabler.resume(activity); 390 } 391 } 392 393 @Override onPause()394 public void onPause() { 395 super.onPause(); 396 if (mWifiEnabler != null) { 397 mWifiEnabler.pause(); 398 } 399 } 400 401 @Override onStop()402 public void onStop() { 403 getView().removeCallbacks(mUpdateAccessPointsRunnable); 404 getView().removeCallbacks(mHideProgressBarRunnable); 405 unregisterCaptivePortalNetworkCallback(); 406 super.onStop(); 407 } 408 409 @Override onActivityResult(int requestCode, int resultCode, Intent data)410 public void onActivityResult(int requestCode, int resultCode, Intent data) { 411 super.onActivityResult(requestCode, resultCode, data); 412 413 if (requestCode == ADD_NETWORK_REQUEST) { 414 handleAddNetworkRequest(resultCode, data); 415 return; 416 } else if (requestCode == REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER) { 417 if (resultCode == Activity.RESULT_OK) { 418 if (mDialog != null) { 419 mDialog.dismiss(); 420 } 421 mWifiTracker.resumeScanning(); 422 } 423 return; 424 } 425 426 final boolean formerlyRestricted = mIsRestricted; 427 mIsRestricted = isUiRestricted(); 428 if (formerlyRestricted && !mIsRestricted 429 && getPreferenceScreen().getPreferenceCount() == 0) { 430 // De-restrict the ui 431 addPreferences(); 432 } 433 } 434 435 @Override getMetricsCategory()436 public int getMetricsCategory() { 437 return SettingsEnums.WIFI; 438 } 439 440 @Override onSaveInstanceState(Bundle outState)441 public void onSaveInstanceState(Bundle outState) { 442 super.onSaveInstanceState(outState); 443 // If dialog has been shown, save its state. 444 if (mDialog != null) { 445 outState.putInt(SAVE_DIALOG_MODE, mDialogMode); 446 if (mDlgAccessPoint != null) { 447 mAccessPointSavedState = new Bundle(); 448 mDlgAccessPoint.saveWifiState(mAccessPointSavedState); 449 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState); 450 } 451 } 452 } 453 454 @Override onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info)455 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) { 456 Preference preference = (Preference) view.getTag(); 457 458 if (preference instanceof LongPressAccessPointPreference) { 459 mSelectedAccessPoint = 460 ((LongPressAccessPointPreference) preference).getAccessPoint(); 461 menu.setHeaderTitle(mSelectedAccessPoint.getTitle()); 462 if (mSelectedAccessPoint.isConnectable()) { 463 menu.add(Menu.NONE, MENU_ID_CONNECT, 0 /* order */, R.string.wifi_connect); 464 } 465 466 WifiConfiguration config = mSelectedAccessPoint.getConfig(); 467 // Some configs are ineditable 468 if (WifiUtils.isNetworkLockedDown(getActivity(), config)) { 469 return; 470 } 471 472 // "forget" for normal saved network. And "disconnect" for ephemeral network because it 473 // could only be disconnected and be put in blacklists so it won't be used again. 474 if (mSelectedAccessPoint.isSaved() || mSelectedAccessPoint.isEphemeral()) { 475 final int stringId = mSelectedAccessPoint.isEphemeral() ? 476 R.string.wifi_disconnect_button_text : R.string.forget; 477 menu.add(Menu.NONE, MENU_ID_FORGET, 0 /* order */, stringId); 478 } 479 480 if (mSelectedAccessPoint.isSaved() && !mSelectedAccessPoint.isActive()) { 481 menu.add(Menu.NONE, MENU_ID_MODIFY, 0 /* order */, R.string.wifi_modify); 482 } 483 } 484 } 485 486 @Override onContextItemSelected(MenuItem item)487 public boolean onContextItemSelected(MenuItem item) { 488 if (mSelectedAccessPoint == null) { 489 return super.onContextItemSelected(item); 490 } 491 switch (item.getItemId()) { 492 case MENU_ID_CONNECT: { 493 boolean isSavedNetwork = mSelectedAccessPoint.isSaved(); 494 if (isSavedNetwork) { 495 connect(mSelectedAccessPoint.getConfig(), isSavedNetwork); 496 } else if ((mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE) || 497 (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_OWE)) { 498 /** Bypass dialog for unsecured networks */ 499 mSelectedAccessPoint.generateOpenNetworkConfig(); 500 connect(mSelectedAccessPoint.getConfig(), isSavedNetwork); 501 } else { 502 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT); 503 } 504 return true; 505 } 506 case MENU_ID_FORGET: { 507 forget(); 508 return true; 509 } 510 case MENU_ID_MODIFY: { 511 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_MODIFY); 512 return true; 513 } 514 } 515 return super.onContextItemSelected(item); 516 } 517 518 @Override onPreferenceTreeClick(Preference preference)519 public boolean onPreferenceTreeClick(Preference preference) { 520 // If the preference has a fragment set, open that 521 if (preference.getFragment() != null) { 522 preference.setOnPreferenceClickListener(null); 523 return super.onPreferenceTreeClick(preference); 524 } 525 526 if (preference instanceof LongPressAccessPointPreference) { 527 mSelectedAccessPoint = ((LongPressAccessPointPreference) preference).getAccessPoint(); 528 if (mSelectedAccessPoint == null) { 529 return false; 530 } 531 if (mSelectedAccessPoint.isActive()) { 532 return super.onPreferenceTreeClick(preference); 533 } 534 /** 535 * Bypass dialog and connect to unsecured networks, or previously connected saved 536 * networks, or Passpoint provided networks. 537 */ 538 switch (WifiUtils.getConnectingType(mSelectedAccessPoint)) { 539 case WifiUtils.CONNECT_TYPE_OSU_PROVISION: 540 mSelectedAccessPoint.startOsuProvisioning(mConnectListener); 541 mClickedConnect = true; 542 break; 543 544 case WifiUtils.CONNECT_TYPE_OPEN_NETWORK: 545 mSelectedAccessPoint.generateOpenNetworkConfig(); 546 connect(mSelectedAccessPoint.getConfig(), mSelectedAccessPoint.isSaved()); 547 break; 548 549 case WifiUtils.CONNECT_TYPE_SAVED_NETWORK: 550 connect(mSelectedAccessPoint.getConfig(), true /* isSavedNetwork */); 551 break; 552 553 default: 554 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT); 555 break; 556 } 557 } else if (preference == mAddWifiNetworkPreference) { 558 onAddNetworkPressed(); 559 } else { 560 return super.onPreferenceTreeClick(preference); 561 } 562 return true; 563 } 564 showDialog(AccessPoint accessPoint, int dialogMode)565 private void showDialog(AccessPoint accessPoint, int dialogMode) { 566 if (accessPoint != null) { 567 WifiConfiguration config = accessPoint.getConfig(); 568 if (WifiUtils.isNetworkLockedDown(getActivity(), config) && accessPoint.isActive()) { 569 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), 570 RestrictedLockUtilsInternal.getDeviceOwner(getActivity())); 571 return; 572 } 573 } 574 575 if (mDialog != null) { 576 removeDialog(WIFI_DIALOG_ID); 577 mDialog = null; 578 } 579 580 // Save the access point and edit mode 581 mDlgAccessPoint = accessPoint; 582 mDialogMode = dialogMode; 583 584 showDialog(WIFI_DIALOG_ID); 585 } 586 587 @Override onCreateDialog(int dialogId)588 public Dialog onCreateDialog(int dialogId) { 589 switch (dialogId) { 590 case WIFI_DIALOG_ID: 591 // modify network 592 if (mDlgAccessPoint == null && mAccessPointSavedState != null) { 593 // restore AP from save state 594 mDlgAccessPoint = new AccessPoint(getActivity(), mAccessPointSavedState); 595 // Reset the saved access point data 596 mAccessPointSavedState = null; 597 } 598 mDialog = WifiDialog 599 .createModal(getActivity(), this, mDlgAccessPoint, mDialogMode); 600 mSelectedAccessPoint = mDlgAccessPoint; 601 return mDialog; 602 } 603 return super.onCreateDialog(dialogId); 604 } 605 606 @Override onDialogShowing()607 public void onDialogShowing() { 608 super.onDialogShowing(); 609 setOnDismissListener(this); 610 } 611 612 @Override onDismiss(DialogInterface dialog)613 public void onDismiss(DialogInterface dialog) { 614 // We don't keep any dialog object when dialog was dismissed. 615 mDialog = null; 616 } 617 618 @Override getDialogMetricsCategory(int dialogId)619 public int getDialogMetricsCategory(int dialogId) { 620 switch (dialogId) { 621 case WIFI_DIALOG_ID: 622 return SettingsEnums.DIALOG_WIFI_AP_EDIT; 623 default: 624 return 0; 625 } 626 } 627 628 /** 629 * Called to indicate the list of AccessPoints has been updated and 630 * getAccessPoints should be called to get the latest information. 631 */ 632 @Override onAccessPointsChanged()633 public void onAccessPointsChanged() { 634 Log.d(TAG, "onAccessPointsChanged (WifiTracker) callback initiated"); 635 updateAccessPointsDelayed(); 636 } 637 638 /** 639 * Updates access points from {@link WifiManager#getScanResults()}. Adds a delay to have 640 * progress bar displayed before starting to modify APs. 641 */ updateAccessPointsDelayed()642 private void updateAccessPointsDelayed() { 643 // Safeguard from some delayed event handling 644 if (getActivity() != null && !mIsRestricted && mWifiManager.isWifiEnabled()) { 645 final View view = getView(); 646 final Handler handler = view.getHandler(); 647 if (handler != null && handler.hasCallbacks(mUpdateAccessPointsRunnable)) { 648 return; 649 } 650 setProgressBarVisible(true); 651 view.postDelayed(mUpdateAccessPointsRunnable, 300 /* delay milliseconds */); 652 } 653 } 654 655 /** Called when the state of Wifi has changed. */ 656 @Override onWifiStateChanged(int state)657 public void onWifiStateChanged(int state) { 658 if (mIsRestricted) { 659 return; 660 } 661 662 final int wifiState = mWifiManager.getWifiState(); 663 switch (wifiState) { 664 case WifiManager.WIFI_STATE_ENABLED: 665 updateAccessPointPreferences(); 666 break; 667 668 case WifiManager.WIFI_STATE_ENABLING: 669 removeConnectedAccessPointPreference(); 670 removeAccessPointPreference(); 671 addMessagePreference(R.string.wifi_starting); 672 setProgressBarVisible(true); 673 break; 674 675 case WifiManager.WIFI_STATE_DISABLING: 676 removeConnectedAccessPointPreference(); 677 removeAccessPointPreference(); 678 addMessagePreference(R.string.wifi_stopping); 679 break; 680 681 case WifiManager.WIFI_STATE_DISABLED: 682 setOffMessage(); 683 setAdditionalSettingsSummaries(); 684 setProgressBarVisible(false); 685 break; 686 } 687 } 688 689 /** 690 * Called when the connection state of wifi has changed. 691 */ 692 @Override onConnectedChanged()693 public void onConnectedChanged() { 694 changeNextButtonState(mWifiTracker.isConnected()); 695 } 696 697 /** Helper method to return whether an AccessPoint is disabled due to a wrong password */ isDisabledByWrongPassword(AccessPoint accessPoint)698 private static boolean isDisabledByWrongPassword(AccessPoint accessPoint) { 699 WifiConfiguration config = accessPoint.getConfig(); 700 if (config == null) { 701 return false; 702 } 703 WifiConfiguration.NetworkSelectionStatus networkStatus = 704 config.getNetworkSelectionStatus(); 705 if (networkStatus == null || networkStatus.isNetworkEnabled()) { 706 return false; 707 } 708 int reason = networkStatus.getNetworkSelectionDisableReason(); 709 return WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD == reason; 710 } 711 updateAccessPointPreferences()712 private void updateAccessPointPreferences() { 713 // in case state has changed 714 if (!mWifiManager.isWifiEnabled()) { 715 return; 716 } 717 // AccessPoints are sorted by the WifiTracker 718 final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints(); 719 if (isVerboseLoggingEnabled()) { 720 Log.i(TAG, "updateAccessPoints called for: " + accessPoints); 721 } 722 723 boolean hasAvailableAccessPoints = false; 724 mStatusMessagePreference.setVisible(false); 725 mConnectedAccessPointPreferenceCategory.setVisible(true); 726 mAccessPointsPreferenceCategory.setVisible(true); 727 728 cacheRemoveAllPrefs(mAccessPointsPreferenceCategory); 729 730 int index = 731 configureConnectedAccessPointPreferenceCategory(accessPoints) ? 1 : 0; 732 int numAccessPoints = accessPoints.size(); 733 for (; index < numAccessPoints; index++) { 734 AccessPoint accessPoint = accessPoints.get(index); 735 // Ignore access points that are out of range. 736 if (accessPoint.isReachable()) { 737 String key = accessPoint.getKey(); 738 hasAvailableAccessPoints = true; 739 LongPressAccessPointPreference pref = 740 (LongPressAccessPointPreference) getCachedPreference(key); 741 if (pref != null) { 742 pref.setOrder(index); 743 continue; 744 } 745 LongPressAccessPointPreference preference = 746 createLongPressAccessPointPreference(accessPoint); 747 preference.setKey(key); 748 preference.setOrder(index); 749 if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr()) 750 && (accessPoint.getSecurity() != AccessPoint.SECURITY_NONE && 751 accessPoint.getSecurity() != AccessPoint.SECURITY_OWE)) { 752 if (!accessPoint.isSaved() || isDisabledByWrongPassword(accessPoint)) { 753 onPreferenceTreeClick(preference); 754 mOpenSsid = null; 755 } 756 } 757 mAccessPointsPreferenceCategory.addPreference(preference); 758 accessPoint.setListener(WifiSettings.this); 759 preference.refresh(); 760 } 761 } 762 removeCachedPrefs(mAccessPointsPreferenceCategory); 763 mAddWifiNetworkPreference.setOrder(index); 764 mAccessPointsPreferenceCategory.addPreference(mAddWifiNetworkPreference); 765 setAdditionalSettingsSummaries(); 766 767 if (!hasAvailableAccessPoints) { 768 setProgressBarVisible(true); 769 Preference pref = new Preference(getPrefContext()); 770 pref.setSelectable(false); 771 pref.setSummary(R.string.wifi_empty_list_wifi_on); 772 pref.setOrder(index++); 773 pref.setKey(PREF_KEY_EMPTY_WIFI_LIST); 774 mAccessPointsPreferenceCategory.addPreference(pref); 775 } else { 776 // Continuing showing progress bar for an additional delay to overlap with animation 777 getView().postDelayed(mHideProgressBarRunnable, 1700 /* delay millis */); 778 } 779 } 780 781 @NonNull createLongPressAccessPointPreference( AccessPoint accessPoint)782 private LongPressAccessPointPreference createLongPressAccessPointPreference( 783 AccessPoint accessPoint) { 784 return new LongPressAccessPointPreference(accessPoint, getPrefContext(), mUserBadgeCache, 785 false /* forSavedNetworks */, R.drawable.ic_wifi_signal_0, this); 786 } 787 788 @NonNull 789 @VisibleForTesting createConnectedAccessPointPreference( AccessPoint accessPoint, Context context)790 ConnectedAccessPointPreference createConnectedAccessPointPreference( 791 AccessPoint accessPoint, Context context) { 792 return new ConnectedAccessPointPreference(accessPoint, context, mUserBadgeCache, 793 R.drawable.ic_wifi_signal_0, false /* forSavedNetworks */, this); 794 } 795 796 /** 797 * Configure the ConnectedAccessPointPreferenceCategory and return true if the Category was 798 * shown. 799 */ configureConnectedAccessPointPreferenceCategory( List<AccessPoint> accessPoints)800 private boolean configureConnectedAccessPointPreferenceCategory( 801 List<AccessPoint> accessPoints) { 802 if (accessPoints.size() == 0) { 803 removeConnectedAccessPointPreference(); 804 return false; 805 } 806 807 AccessPoint connectedAp = accessPoints.get(0); 808 if (!connectedAp.isActive()) { 809 removeConnectedAccessPointPreference(); 810 return false; 811 } 812 813 // Is the preference category empty? 814 if (mConnectedAccessPointPreferenceCategory.getPreferenceCount() == 0) { 815 addConnectedAccessPointPreference(connectedAp); 816 return true; 817 } 818 819 // Is the previous currently connected SSID different from the new one? 820 ConnectedAccessPointPreference preference = 821 (ConnectedAccessPointPreference) 822 (mConnectedAccessPointPreferenceCategory.getPreference(0)); 823 // The AccessPoints need to be the same reference to ensure that updates are reflected 824 // in the UI. 825 if (preference.getAccessPoint() != connectedAp) { 826 removeConnectedAccessPointPreference(); 827 addConnectedAccessPointPreference(connectedAp); 828 return true; 829 } 830 831 // Else same AP is connected, simply refresh the connected access point preference 832 // (first and only access point in this category). 833 preference.refresh(); 834 // Update any potential changes to the connected network and ensure that the callback is 835 // registered after an onStop lifecycle event. 836 registerCaptivePortalNetworkCallback(getCurrentWifiNetwork(), preference); 837 return true; 838 } 839 840 /** 841 * Creates a Preference for the given {@link AccessPoint} and adds it to the 842 * {@link #mConnectedAccessPointPreferenceCategory}. 843 */ addConnectedAccessPointPreference(AccessPoint connectedAp)844 private void addConnectedAccessPointPreference(AccessPoint connectedAp) { 845 final ConnectedAccessPointPreference pref = 846 createConnectedAccessPointPreference(connectedAp, getPrefContext()); 847 registerCaptivePortalNetworkCallback(getCurrentWifiNetwork(), pref); 848 849 // Launch details page or captive portal on click. 850 pref.setOnPreferenceClickListener( 851 preference -> { 852 pref.getAccessPoint().saveWifiState(pref.getExtras()); 853 if (mCaptivePortalNetworkCallback != null 854 && mCaptivePortalNetworkCallback.isCaptivePortal()) { 855 mConnectivityManager.startCaptivePortalApp( 856 mCaptivePortalNetworkCallback.getNetwork()); 857 } else { 858 launchNetworkDetailsFragment(pref); 859 } 860 return true; 861 }); 862 863 pref.setOnGearClickListener( 864 preference -> { 865 pref.getAccessPoint().saveWifiState(pref.getExtras()); 866 launchNetworkDetailsFragment(pref); 867 }); 868 869 pref.refresh(); 870 871 mConnectedAccessPointPreferenceCategory.addPreference(pref); 872 mConnectedAccessPointPreferenceCategory.setVisible(true); 873 if (mClickedConnect) { 874 mClickedConnect = false; 875 scrollToPreference(mConnectedAccessPointPreferenceCategory); 876 } 877 } 878 registerCaptivePortalNetworkCallback( Network wifiNetwork, ConnectedAccessPointPreference pref)879 private void registerCaptivePortalNetworkCallback( 880 Network wifiNetwork, ConnectedAccessPointPreference pref) { 881 if (wifiNetwork == null || pref == null) { 882 Log.w(TAG, "Network or Preference were null when registering callback."); 883 return; 884 } 885 886 if (mCaptivePortalNetworkCallback != null 887 && mCaptivePortalNetworkCallback.isSameNetworkAndPreference(wifiNetwork, pref)) { 888 return; 889 } 890 891 unregisterCaptivePortalNetworkCallback(); 892 893 mCaptivePortalNetworkCallback = new CaptivePortalNetworkCallback(wifiNetwork, pref); 894 mConnectivityManager.registerNetworkCallback( 895 new NetworkRequest.Builder() 896 .clearCapabilities() 897 .addTransportType(TRANSPORT_WIFI) 898 .build(), 899 mCaptivePortalNetworkCallback, 900 new Handler(Looper.getMainLooper())); 901 } 902 unregisterCaptivePortalNetworkCallback()903 private void unregisterCaptivePortalNetworkCallback() { 904 if (mCaptivePortalNetworkCallback != null) { 905 try { 906 mConnectivityManager.unregisterNetworkCallback(mCaptivePortalNetworkCallback); 907 } catch (RuntimeException e) { 908 Log.e(TAG, "Unregistering CaptivePortalNetworkCallback failed.", e); 909 } 910 mCaptivePortalNetworkCallback = null; 911 } 912 } 913 launchAddNetworkFragment()914 private void launchAddNetworkFragment() { 915 new SubSettingLauncher(getContext()) 916 .setTitleRes(R.string.wifi_add_network) 917 .setDestination(AddNetworkFragment.class.getName()) 918 .setSourceMetricsCategory(getMetricsCategory()) 919 .setResultListener(this, ADD_NETWORK_REQUEST) 920 .launch(); 921 } 922 launchNetworkDetailsFragment(ConnectedAccessPointPreference pref)923 private void launchNetworkDetailsFragment(ConnectedAccessPointPreference pref) { 924 final AccessPoint accessPoint = pref.getAccessPoint(); 925 final Context context = getContext(); 926 final CharSequence title = 927 FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_DATAUSAGE_HEADER) 928 ? accessPoint.getTitle() 929 : context.getText(R.string.pref_title_network_details); 930 931 new SubSettingLauncher(getContext()) 932 .setTitleText(title) 933 .setDestination(WifiNetworkDetailsFragment.class.getName()) 934 .setArguments(pref.getExtras()) 935 .setSourceMetricsCategory(getMetricsCategory()) 936 .launch(); 937 } 938 getCurrentWifiNetwork()939 private Network getCurrentWifiNetwork() { 940 return mWifiManager != null ? mWifiManager.getCurrentNetwork() : null; 941 } 942 943 /** Removes all preferences and hide the {@link #mConnectedAccessPointPreferenceCategory}. */ removeConnectedAccessPointPreference()944 private void removeConnectedAccessPointPreference() { 945 mConnectedAccessPointPreferenceCategory.removeAll(); 946 mConnectedAccessPointPreferenceCategory.setVisible(false); 947 unregisterCaptivePortalNetworkCallback(); 948 } 949 removeAccessPointPreference()950 private void removeAccessPointPreference() { 951 mAccessPointsPreferenceCategory.removeAll(); 952 mAccessPointsPreferenceCategory.setVisible(false); 953 } 954 955 @VisibleForTesting setAdditionalSettingsSummaries()956 void setAdditionalSettingsSummaries() { 957 mConfigureWifiSettingsPreference.setSummary(getString( 958 isWifiWakeupEnabled() 959 ? R.string.wifi_configure_settings_preference_summary_wakeup_on 960 : R.string.wifi_configure_settings_preference_summary_wakeup_off)); 961 962 final List<AccessPoint> savedNetworks = 963 WifiSavedConfigUtils.getAllConfigs(getContext(), mWifiManager); 964 final int numSavedNetworks = (savedNetworks != null) ? savedNetworks.size() : 0; 965 mSavedNetworksPreference.setVisible(numSavedNetworks > 0); 966 if (numSavedNetworks > 0) { 967 mSavedNetworksPreference.setSummary( 968 getSavedNetworkSettingsSummaryText(savedNetworks, numSavedNetworks)); 969 } 970 } 971 getSavedNetworkSettingsSummaryText( List<AccessPoint> savedNetworks, int numSavedNetworks)972 private String getSavedNetworkSettingsSummaryText( 973 List<AccessPoint> savedNetworks, int numSavedNetworks) { 974 int numSavedPasspointNetworks = 0; 975 for (AccessPoint savedNetwork : savedNetworks) { 976 if (savedNetwork.isPasspointConfig() || savedNetwork.isPasspoint()) { 977 numSavedPasspointNetworks++; 978 } 979 } 980 final int numSavedNormalNetworks = numSavedNetworks - numSavedPasspointNetworks; 981 982 if (numSavedNetworks == numSavedNormalNetworks) { 983 return getResources().getQuantityString(R.plurals.wifi_saved_access_points_summary, 984 numSavedNormalNetworks, numSavedNormalNetworks); 985 } else if (numSavedNetworks == numSavedPasspointNetworks) { 986 return getResources().getQuantityString( 987 R.plurals.wifi_saved_passpoint_access_points_summary, 988 numSavedPasspointNetworks, numSavedPasspointNetworks); 989 } else { 990 return getResources().getQuantityString(R.plurals.wifi_saved_all_access_points_summary, 991 numSavedNetworks, numSavedNetworks); 992 } 993 } 994 isWifiWakeupEnabled()995 private boolean isWifiWakeupEnabled() { 996 final Context context = getContext(); 997 final PowerManager powerManager = context.getSystemService(PowerManager.class); 998 final ContentResolver contentResolver = context.getContentResolver(); 999 return Settings.Global.getInt(contentResolver, 1000 Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1 1001 && Settings.Global.getInt(contentResolver, 1002 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1 1003 && Settings.Global.getInt(contentResolver, 1004 Settings.Global.AIRPLANE_MODE_ON, 0) == 0 1005 && !powerManager.isPowerSaveMode(); 1006 } 1007 setOffMessage()1008 private void setOffMessage() { 1009 final CharSequence title = getText(R.string.wifi_empty_list_wifi_off); 1010 // Don't use WifiManager.isScanAlwaysAvailable() to check the Wi-Fi scanning mode. Instead, 1011 // read the system settings directly. Because when the device is in Airplane mode, even if 1012 // Wi-Fi scanning mode is on, WifiManager.isScanAlwaysAvailable() still returns "off". 1013 final boolean wifiScanningMode = Settings.Global.getInt(getActivity().getContentResolver(), 1014 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1; 1015 final CharSequence description = wifiScanningMode ? getText(R.string.wifi_scan_notify_text) 1016 : getText(R.string.wifi_scan_notify_text_scanning_off); 1017 final LinkifyUtils.OnClickListener clickListener = 1018 () -> new SubSettingLauncher(getContext()) 1019 .setDestination(ScanningSettings.class.getName()) 1020 .setTitleRes(R.string.location_scanning_screen_title) 1021 .setSourceMetricsCategory(getMetricsCategory()) 1022 .launch(); 1023 mStatusMessagePreference.setText(title, description, clickListener); 1024 removeConnectedAccessPointPreference(); 1025 removeAccessPointPreference(); 1026 mStatusMessagePreference.setVisible(true); 1027 } 1028 addMessagePreference(int messageId)1029 private void addMessagePreference(int messageId) { 1030 mStatusMessagePreference.setTitle(messageId); 1031 mStatusMessagePreference.setVisible(true); 1032 1033 } 1034 setProgressBarVisible(boolean visible)1035 protected void setProgressBarVisible(boolean visible) { 1036 if (mProgressHeader != null) { 1037 mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE); 1038 } 1039 } 1040 1041 /** 1042 * Renames/replaces "Next" button when appropriate. "Next" button usually exists in 1043 * Wifi setup screens, not in usual wifi settings screen. 1044 * 1045 * @param enabled true when the device is connected to a wifi network. 1046 */ changeNextButtonState(boolean enabled)1047 private void changeNextButtonState(boolean enabled) { 1048 if (mEnableNextOnConnection && hasNextButton()) { 1049 getNextButton().setEnabled(enabled); 1050 } 1051 } 1052 1053 @Override onForget(WifiDialog dialog)1054 public void onForget(WifiDialog dialog) { 1055 forget(); 1056 } 1057 1058 @Override onSubmit(WifiDialog dialog)1059 public void onSubmit(WifiDialog dialog) { 1060 if (mDialog != null) { 1061 submit(mDialog.getController()); 1062 } 1063 } 1064 1065 @Override onScan(WifiDialog dialog, String ssid)1066 public void onScan(WifiDialog dialog, String ssid) { 1067 // Launch QR code scanner to join a network. 1068 startActivityForResult(WifiDppUtils.getEnrolleeQrCodeScannerIntent(ssid), 1069 REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER); 1070 } 1071 submit(WifiConfigController configController)1072 /* package */ void submit(WifiConfigController configController) { 1073 1074 final WifiConfiguration config = configController.getConfig(); 1075 1076 if (config == null) { 1077 if (mSelectedAccessPoint != null 1078 && mSelectedAccessPoint.isSaved()) { 1079 connect(mSelectedAccessPoint.getConfig(), true /* isSavedNetwork */); 1080 } 1081 } else if (configController.getMode() == WifiConfigUiBase.MODE_MODIFY) { 1082 mWifiManager.save(config, mSaveListener); 1083 } else { 1084 mWifiManager.save(config, mSaveListener); 1085 if (mSelectedAccessPoint != null) { // Not an "Add network" 1086 connect(config, false /* isSavedNetwork */); 1087 } 1088 } 1089 1090 mWifiTracker.resumeScanning(); 1091 } 1092 forget()1093 /* package */ void forget() { 1094 mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_WIFI_FORGET); 1095 if (!mSelectedAccessPoint.isSaved()) { 1096 if (mSelectedAccessPoint.getNetworkInfo() != null && 1097 mSelectedAccessPoint.getNetworkInfo().getState() != State.DISCONNECTED) { 1098 // Network is active but has no network ID - must be ephemeral. 1099 mWifiManager.disableEphemeralNetwork( 1100 AccessPoint.convertToQuotedString(mSelectedAccessPoint.getSsidStr())); 1101 } else { 1102 // Should not happen, but a monkey seems to trigger it 1103 Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig()); 1104 return; 1105 } 1106 } else if (mSelectedAccessPoint.getConfig().isPasspoint()) { 1107 try { 1108 mWifiManager.removePasspointConfiguration(mSelectedAccessPoint.getConfig().FQDN); 1109 } catch (IllegalArgumentException e) { 1110 Log.e(TAG, "Failed to remove Passpoint configuration with error: " + e); 1111 return; 1112 } 1113 } else { 1114 mWifiManager.forget(mSelectedAccessPoint.getConfig().networkId, mForgetListener); 1115 } 1116 1117 mWifiTracker.resumeScanning(); 1118 1119 // We need to rename/replace "Next" button in wifi setup context. 1120 changeNextButtonState(false); 1121 } 1122 connect(final WifiConfiguration config, boolean isSavedNetwork)1123 protected void connect(final WifiConfiguration config, boolean isSavedNetwork) { 1124 // Log subtype if configuration is a saved network. 1125 mMetricsFeatureProvider.action(getContext(), SettingsEnums.ACTION_WIFI_CONNECT, 1126 isSavedNetwork); 1127 mWifiManager.connect(config, mConnectListener); 1128 mClickedConnect = true; 1129 } 1130 connect(final int networkId, boolean isSavedNetwork)1131 protected void connect(final int networkId, boolean isSavedNetwork) { 1132 // Log subtype if configuration is a saved network. 1133 mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_WIFI_CONNECT, 1134 isSavedNetwork); 1135 mWifiManager.connect(networkId, mConnectListener); 1136 } 1137 1138 @VisibleForTesting handleAddNetworkRequest(int result, Intent data)1139 void handleAddNetworkRequest(int result, Intent data) { 1140 if (result == Activity.RESULT_OK) { 1141 handleAddNetworkSubmitEvent(data); 1142 } 1143 mWifiTracker.resumeScanning(); 1144 } 1145 handleAddNetworkSubmitEvent(Intent data)1146 private void handleAddNetworkSubmitEvent(Intent data) { 1147 final WifiConfiguration wifiConfiguration = data.getParcelableExtra( 1148 AddNetworkFragment.WIFI_CONFIG_KEY); 1149 if (wifiConfiguration != null) { 1150 mWifiManager.save(wifiConfiguration, mSaveListener); 1151 } 1152 } 1153 1154 /** 1155 * Called when "add network" button is pressed. 1156 */ onAddNetworkPressed()1157 private void onAddNetworkPressed() { 1158 // No exact access point is selected. 1159 mSelectedAccessPoint = null; 1160 launchAddNetworkFragment(); 1161 } 1162 1163 @Override getHelpResource()1164 public int getHelpResource() { 1165 return R.string.help_url_wifi; 1166 } 1167 1168 @Override onAccessPointChanged(final AccessPoint accessPoint)1169 public void onAccessPointChanged(final AccessPoint accessPoint) { 1170 Log.d(TAG, "onAccessPointChanged (singular) callback initiated"); 1171 View view = getView(); 1172 if (view != null) { 1173 view.post(new Runnable() { 1174 @Override 1175 public void run() { 1176 Object tag = accessPoint.getTag(); 1177 if (tag != null) { 1178 ((AccessPointPreference) tag).refresh(); 1179 } 1180 } 1181 }); 1182 } 1183 } 1184 1185 @Override onLevelChanged(AccessPoint accessPoint)1186 public void onLevelChanged(AccessPoint accessPoint) { 1187 ((AccessPointPreference) accessPoint.getTag()).onLevelChanged(); 1188 } 1189 1190 public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 1191 new BaseSearchIndexProvider() { 1192 @Override 1193 public List<SearchIndexableRaw> getRawDataToIndex(Context context, 1194 boolean enabled) { 1195 final List<SearchIndexableRaw> result = new ArrayList<>(); 1196 final Resources res = context.getResources(); 1197 1198 // Add fragment title if we are showing this fragment 1199 if (res.getBoolean(R.bool.config_show_wifi_settings)) { 1200 SearchIndexableRaw data = new SearchIndexableRaw(context); 1201 data.title = res.getString(R.string.wifi_settings); 1202 data.screenTitle = res.getString(R.string.wifi_settings); 1203 data.keywords = res.getString(R.string.keywords_wifi); 1204 data.key = DATA_KEY_REFERENCE; 1205 result.add(data); 1206 } 1207 1208 return result; 1209 } 1210 }; 1211 1212 private static class SummaryProvider 1213 implements SummaryLoader.SummaryProvider, OnSummaryChangeListener { 1214 1215 private final Context mContext; 1216 private final SummaryLoader mSummaryLoader; 1217 1218 @VisibleForTesting 1219 WifiSummaryUpdater mSummaryHelper; 1220 SummaryProvider(Context context, SummaryLoader summaryLoader)1221 public SummaryProvider(Context context, SummaryLoader summaryLoader) { 1222 mContext = context; 1223 mSummaryLoader = summaryLoader; 1224 mSummaryHelper = new WifiSummaryUpdater(mContext, this); 1225 } 1226 1227 1228 @Override setListening(boolean listening)1229 public void setListening(boolean listening) { 1230 mSummaryHelper.register(listening); 1231 } 1232 1233 @Override onSummaryChanged(String summary)1234 public void onSummaryChanged(String summary) { 1235 mSummaryLoader.setSummary(this, summary); 1236 } 1237 } 1238 1239 public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY 1240 = new SummaryLoader.SummaryProviderFactory() { 1241 @Override 1242 public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity, 1243 SummaryLoader summaryLoader) { 1244 return new SummaryProvider(activity, summaryLoader); 1245 } 1246 }; 1247 } 1248