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 (mSelectedAccessPoint.getSecurity() 499 == AccessPoint.SECURITY_OWE_TRANSITION)) { 500 /** Bypass dialog for unsecured networks */ 501 mSelectedAccessPoint.generateOpenNetworkConfig(); 502 connect(mSelectedAccessPoint.getConfig(), isSavedNetwork); 503 } else { 504 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT); 505 } 506 return true; 507 } 508 case MENU_ID_FORGET: { 509 forget(); 510 return true; 511 } 512 case MENU_ID_MODIFY: { 513 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_MODIFY); 514 return true; 515 } 516 } 517 return super.onContextItemSelected(item); 518 } 519 520 @Override onPreferenceTreeClick(Preference preference)521 public boolean onPreferenceTreeClick(Preference preference) { 522 // If the preference has a fragment set, open that 523 if (preference.getFragment() != null) { 524 preference.setOnPreferenceClickListener(null); 525 return super.onPreferenceTreeClick(preference); 526 } 527 528 if (preference instanceof LongPressAccessPointPreference) { 529 mSelectedAccessPoint = ((LongPressAccessPointPreference) preference).getAccessPoint(); 530 if (mSelectedAccessPoint == null) { 531 return false; 532 } 533 if (mSelectedAccessPoint.isActive()) { 534 return super.onPreferenceTreeClick(preference); 535 } 536 /** 537 * Bypass dialog and connect to unsecured networks, or previously connected saved 538 * networks, or Passpoint provided networks. 539 */ 540 switch (WifiUtils.getConnectingType(mSelectedAccessPoint)) { 541 case WifiUtils.CONNECT_TYPE_OSU_PROVISION: 542 mSelectedAccessPoint.startOsuProvisioning(mConnectListener); 543 mClickedConnect = true; 544 break; 545 546 case WifiUtils.CONNECT_TYPE_OPEN_NETWORK: 547 mSelectedAccessPoint.generateOpenNetworkConfig(); 548 connect(mSelectedAccessPoint.getConfig(), mSelectedAccessPoint.isSaved()); 549 break; 550 551 case WifiUtils.CONNECT_TYPE_SAVED_NETWORK: 552 connect(mSelectedAccessPoint.getConfig(), true /* isSavedNetwork */); 553 break; 554 555 default: 556 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT); 557 break; 558 } 559 } else if (preference == mAddWifiNetworkPreference) { 560 onAddNetworkPressed(); 561 } else { 562 return super.onPreferenceTreeClick(preference); 563 } 564 return true; 565 } 566 showDialog(AccessPoint accessPoint, int dialogMode)567 private void showDialog(AccessPoint accessPoint, int dialogMode) { 568 if (accessPoint != null) { 569 WifiConfiguration config = accessPoint.getConfig(); 570 if (WifiUtils.isNetworkLockedDown(getActivity(), config) && accessPoint.isActive()) { 571 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), 572 RestrictedLockUtilsInternal.getDeviceOwner(getActivity())); 573 return; 574 } 575 } 576 577 if (mDialog != null) { 578 removeDialog(WIFI_DIALOG_ID); 579 mDialog = null; 580 } 581 582 // Save the access point and edit mode 583 mDlgAccessPoint = accessPoint; 584 mDialogMode = dialogMode; 585 586 showDialog(WIFI_DIALOG_ID); 587 } 588 589 @Override onCreateDialog(int dialogId)590 public Dialog onCreateDialog(int dialogId) { 591 switch (dialogId) { 592 case WIFI_DIALOG_ID: 593 // modify network 594 if (mDlgAccessPoint == null && mAccessPointSavedState != null) { 595 // restore AP from save state 596 mDlgAccessPoint = new AccessPoint(getActivity(), mAccessPointSavedState); 597 // Reset the saved access point data 598 mAccessPointSavedState = null; 599 } 600 mDialog = WifiDialog 601 .createModal(getActivity(), this, mDlgAccessPoint, mDialogMode); 602 mSelectedAccessPoint = mDlgAccessPoint; 603 return mDialog; 604 } 605 return super.onCreateDialog(dialogId); 606 } 607 608 @Override onDialogShowing()609 public void onDialogShowing() { 610 super.onDialogShowing(); 611 setOnDismissListener(this); 612 } 613 614 @Override onDismiss(DialogInterface dialog)615 public void onDismiss(DialogInterface dialog) { 616 // We don't keep any dialog object when dialog was dismissed. 617 mDialog = null; 618 } 619 620 @Override getDialogMetricsCategory(int dialogId)621 public int getDialogMetricsCategory(int dialogId) { 622 switch (dialogId) { 623 case WIFI_DIALOG_ID: 624 return SettingsEnums.DIALOG_WIFI_AP_EDIT; 625 default: 626 return 0; 627 } 628 } 629 630 /** 631 * Called to indicate the list of AccessPoints has been updated and 632 * getAccessPoints should be called to get the latest information. 633 */ 634 @Override onAccessPointsChanged()635 public void onAccessPointsChanged() { 636 Log.d(TAG, "onAccessPointsChanged (WifiTracker) callback initiated"); 637 updateAccessPointsDelayed(); 638 } 639 640 /** 641 * Updates access points from {@link WifiManager#getScanResults()}. Adds a delay to have 642 * progress bar displayed before starting to modify APs. 643 */ updateAccessPointsDelayed()644 private void updateAccessPointsDelayed() { 645 // Safeguard from some delayed event handling 646 if (getActivity() != null && !mIsRestricted && mWifiManager.isWifiEnabled()) { 647 final View view = getView(); 648 final Handler handler = view.getHandler(); 649 if (handler != null && handler.hasCallbacks(mUpdateAccessPointsRunnable)) { 650 return; 651 } 652 setProgressBarVisible(true); 653 view.postDelayed(mUpdateAccessPointsRunnable, 300 /* delay milliseconds */); 654 } 655 } 656 657 /** Called when the state of Wifi has changed. */ 658 @Override onWifiStateChanged(int state)659 public void onWifiStateChanged(int state) { 660 if (mIsRestricted) { 661 return; 662 } 663 664 final int wifiState = mWifiManager.getWifiState(); 665 switch (wifiState) { 666 case WifiManager.WIFI_STATE_ENABLED: 667 updateAccessPointPreferences(); 668 break; 669 670 case WifiManager.WIFI_STATE_ENABLING: 671 removeConnectedAccessPointPreference(); 672 removeAccessPointPreference(); 673 addMessagePreference(R.string.wifi_starting); 674 setProgressBarVisible(true); 675 break; 676 677 case WifiManager.WIFI_STATE_DISABLING: 678 removeConnectedAccessPointPreference(); 679 removeAccessPointPreference(); 680 addMessagePreference(R.string.wifi_stopping); 681 break; 682 683 case WifiManager.WIFI_STATE_DISABLED: 684 setOffMessage(); 685 setAdditionalSettingsSummaries(); 686 setProgressBarVisible(false); 687 break; 688 } 689 } 690 691 /** 692 * Called when the connection state of wifi has changed. 693 */ 694 @Override onConnectedChanged()695 public void onConnectedChanged() { 696 changeNextButtonState(mWifiTracker.isConnected()); 697 } 698 699 /** Helper method to return whether an AccessPoint is disabled due to a wrong password */ isDisabledByWrongPassword(AccessPoint accessPoint)700 private static boolean isDisabledByWrongPassword(AccessPoint accessPoint) { 701 WifiConfiguration config = accessPoint.getConfig(); 702 if (config == null) { 703 return false; 704 } 705 WifiConfiguration.NetworkSelectionStatus networkStatus = 706 config.getNetworkSelectionStatus(); 707 if (networkStatus == null || networkStatus.isNetworkEnabled()) { 708 return false; 709 } 710 int reason = networkStatus.getNetworkSelectionDisableReason(); 711 return WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD == reason; 712 } 713 updateAccessPointPreferences()714 private void updateAccessPointPreferences() { 715 // in case state has changed 716 if (!mWifiManager.isWifiEnabled()) { 717 return; 718 } 719 // AccessPoints are sorted by the WifiTracker 720 final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints(); 721 if (isVerboseLoggingEnabled()) { 722 Log.i(TAG, "updateAccessPoints called for: " + accessPoints); 723 } 724 725 boolean hasAvailableAccessPoints = false; 726 mStatusMessagePreference.setVisible(false); 727 mConnectedAccessPointPreferenceCategory.setVisible(true); 728 mAccessPointsPreferenceCategory.setVisible(true); 729 730 cacheRemoveAllPrefs(mAccessPointsPreferenceCategory); 731 732 int index = 733 configureConnectedAccessPointPreferenceCategory(accessPoints) ? 1 : 0; 734 int numAccessPoints = accessPoints.size(); 735 for (; index < numAccessPoints; index++) { 736 AccessPoint accessPoint = accessPoints.get(index); 737 // Ignore access points that are out of range. 738 if (accessPoint.isReachable()) { 739 String key = accessPoint.getKey(); 740 hasAvailableAccessPoints = true; 741 LongPressAccessPointPreference pref = 742 (LongPressAccessPointPreference) getCachedPreference(key); 743 if (pref != null) { 744 pref.setOrder(index); 745 continue; 746 } 747 LongPressAccessPointPreference preference = 748 createLongPressAccessPointPreference(accessPoint); 749 preference.setKey(key); 750 preference.setOrder(index); 751 if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr()) 752 && (accessPoint.getSecurity() != AccessPoint.SECURITY_NONE && 753 accessPoint.getSecurity() != AccessPoint.SECURITY_OWE && 754 accessPoint.getSecurity() != AccessPoint.SECURITY_OWE_TRANSITION)) { 755 if (!accessPoint.isSaved() || isDisabledByWrongPassword(accessPoint)) { 756 onPreferenceTreeClick(preference); 757 mOpenSsid = null; 758 } 759 } 760 mAccessPointsPreferenceCategory.addPreference(preference); 761 accessPoint.setListener(WifiSettings.this); 762 preference.refresh(); 763 } 764 } 765 removeCachedPrefs(mAccessPointsPreferenceCategory); 766 mAddWifiNetworkPreference.setOrder(index); 767 mAccessPointsPreferenceCategory.addPreference(mAddWifiNetworkPreference); 768 setAdditionalSettingsSummaries(); 769 770 if (!hasAvailableAccessPoints) { 771 setProgressBarVisible(true); 772 Preference pref = new Preference(getPrefContext()); 773 pref.setSelectable(false); 774 pref.setSummary(R.string.wifi_empty_list_wifi_on); 775 pref.setOrder(index++); 776 pref.setKey(PREF_KEY_EMPTY_WIFI_LIST); 777 mAccessPointsPreferenceCategory.addPreference(pref); 778 } else { 779 // Continuing showing progress bar for an additional delay to overlap with animation 780 getView().postDelayed(mHideProgressBarRunnable, 1700 /* delay millis */); 781 } 782 } 783 784 @NonNull createLongPressAccessPointPreference( AccessPoint accessPoint)785 private LongPressAccessPointPreference createLongPressAccessPointPreference( 786 AccessPoint accessPoint) { 787 return new LongPressAccessPointPreference(accessPoint, getPrefContext(), mUserBadgeCache, 788 false /* forSavedNetworks */, R.drawable.ic_wifi_signal_0, this); 789 } 790 791 @NonNull 792 @VisibleForTesting createConnectedAccessPointPreference( AccessPoint accessPoint, Context context)793 ConnectedAccessPointPreference createConnectedAccessPointPreference( 794 AccessPoint accessPoint, Context context) { 795 return new ConnectedAccessPointPreference(accessPoint, context, mUserBadgeCache, 796 R.drawable.ic_wifi_signal_0, false /* forSavedNetworks */, this); 797 } 798 799 /** 800 * Configure the ConnectedAccessPointPreferenceCategory and return true if the Category was 801 * shown. 802 */ configureConnectedAccessPointPreferenceCategory( List<AccessPoint> accessPoints)803 private boolean configureConnectedAccessPointPreferenceCategory( 804 List<AccessPoint> accessPoints) { 805 if (accessPoints.size() == 0) { 806 removeConnectedAccessPointPreference(); 807 return false; 808 } 809 810 AccessPoint connectedAp = accessPoints.get(0); 811 if (!connectedAp.isActive()) { 812 removeConnectedAccessPointPreference(); 813 return false; 814 } 815 816 // Is the preference category empty? 817 if (mConnectedAccessPointPreferenceCategory.getPreferenceCount() == 0) { 818 addConnectedAccessPointPreference(connectedAp); 819 return true; 820 } 821 822 // Is the previous currently connected SSID different from the new one? 823 ConnectedAccessPointPreference preference = 824 (ConnectedAccessPointPreference) 825 (mConnectedAccessPointPreferenceCategory.getPreference(0)); 826 // The AccessPoints need to be the same reference to ensure that updates are reflected 827 // in the UI. 828 if (preference.getAccessPoint() != connectedAp) { 829 removeConnectedAccessPointPreference(); 830 addConnectedAccessPointPreference(connectedAp); 831 return true; 832 } 833 834 // Else same AP is connected, simply refresh the connected access point preference 835 // (first and only access point in this category). 836 preference.refresh(); 837 // Update any potential changes to the connected network and ensure that the callback is 838 // registered after an onStop lifecycle event. 839 registerCaptivePortalNetworkCallback(getCurrentWifiNetwork(), preference); 840 return true; 841 } 842 843 /** 844 * Creates a Preference for the given {@link AccessPoint} and adds it to the 845 * {@link #mConnectedAccessPointPreferenceCategory}. 846 */ addConnectedAccessPointPreference(AccessPoint connectedAp)847 private void addConnectedAccessPointPreference(AccessPoint connectedAp) { 848 final ConnectedAccessPointPreference pref = 849 createConnectedAccessPointPreference(connectedAp, getPrefContext()); 850 registerCaptivePortalNetworkCallback(getCurrentWifiNetwork(), pref); 851 852 // Launch details page or captive portal on click. 853 pref.setOnPreferenceClickListener( 854 preference -> { 855 pref.getAccessPoint().saveWifiState(pref.getExtras()); 856 if (mCaptivePortalNetworkCallback != null 857 && mCaptivePortalNetworkCallback.isCaptivePortal()) { 858 mConnectivityManager.startCaptivePortalApp( 859 mCaptivePortalNetworkCallback.getNetwork()); 860 } else { 861 launchNetworkDetailsFragment(pref); 862 } 863 return true; 864 }); 865 866 pref.setOnGearClickListener( 867 preference -> { 868 pref.getAccessPoint().saveWifiState(pref.getExtras()); 869 launchNetworkDetailsFragment(pref); 870 }); 871 872 pref.refresh(); 873 874 mConnectedAccessPointPreferenceCategory.addPreference(pref); 875 mConnectedAccessPointPreferenceCategory.setVisible(true); 876 if (mClickedConnect) { 877 mClickedConnect = false; 878 scrollToPreference(mConnectedAccessPointPreferenceCategory); 879 } 880 } 881 registerCaptivePortalNetworkCallback( Network wifiNetwork, ConnectedAccessPointPreference pref)882 private void registerCaptivePortalNetworkCallback( 883 Network wifiNetwork, ConnectedAccessPointPreference pref) { 884 if (wifiNetwork == null || pref == null) { 885 Log.w(TAG, "Network or Preference were null when registering callback."); 886 return; 887 } 888 889 if (mCaptivePortalNetworkCallback != null 890 && mCaptivePortalNetworkCallback.isSameNetworkAndPreference(wifiNetwork, pref)) { 891 return; 892 } 893 894 unregisterCaptivePortalNetworkCallback(); 895 896 mCaptivePortalNetworkCallback = new CaptivePortalNetworkCallback(wifiNetwork, pref); 897 mConnectivityManager.registerNetworkCallback( 898 new NetworkRequest.Builder() 899 .clearCapabilities() 900 .addTransportType(TRANSPORT_WIFI) 901 .build(), 902 mCaptivePortalNetworkCallback, 903 new Handler(Looper.getMainLooper())); 904 } 905 unregisterCaptivePortalNetworkCallback()906 private void unregisterCaptivePortalNetworkCallback() { 907 if (mCaptivePortalNetworkCallback != null) { 908 try { 909 mConnectivityManager.unregisterNetworkCallback(mCaptivePortalNetworkCallback); 910 } catch (RuntimeException e) { 911 Log.e(TAG, "Unregistering CaptivePortalNetworkCallback failed.", e); 912 } 913 mCaptivePortalNetworkCallback = null; 914 } 915 } 916 launchAddNetworkFragment()917 private void launchAddNetworkFragment() { 918 new SubSettingLauncher(getContext()) 919 .setTitleRes(R.string.wifi_add_network) 920 .setDestination(AddNetworkFragment.class.getName()) 921 .setSourceMetricsCategory(getMetricsCategory()) 922 .setResultListener(this, ADD_NETWORK_REQUEST) 923 .launch(); 924 } 925 launchNetworkDetailsFragment(ConnectedAccessPointPreference pref)926 private void launchNetworkDetailsFragment(ConnectedAccessPointPreference pref) { 927 final AccessPoint accessPoint = pref.getAccessPoint(); 928 final Context context = getContext(); 929 final CharSequence title = 930 FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_DATAUSAGE_HEADER) 931 ? accessPoint.getTitle() 932 : context.getText(R.string.pref_title_network_details); 933 934 new SubSettingLauncher(getContext()) 935 .setTitleText(title) 936 .setDestination(WifiNetworkDetailsFragment.class.getName()) 937 .setArguments(pref.getExtras()) 938 .setSourceMetricsCategory(getMetricsCategory()) 939 .launch(); 940 } 941 getCurrentWifiNetwork()942 private Network getCurrentWifiNetwork() { 943 return mWifiManager != null ? mWifiManager.getCurrentNetwork() : null; 944 } 945 946 /** Removes all preferences and hide the {@link #mConnectedAccessPointPreferenceCategory}. */ removeConnectedAccessPointPreference()947 private void removeConnectedAccessPointPreference() { 948 mConnectedAccessPointPreferenceCategory.removeAll(); 949 mConnectedAccessPointPreferenceCategory.setVisible(false); 950 unregisterCaptivePortalNetworkCallback(); 951 } 952 removeAccessPointPreference()953 private void removeAccessPointPreference() { 954 mAccessPointsPreferenceCategory.removeAll(); 955 mAccessPointsPreferenceCategory.setVisible(false); 956 } 957 958 @VisibleForTesting setAdditionalSettingsSummaries()959 void setAdditionalSettingsSummaries() { 960 mConfigureWifiSettingsPreference.setSummary(getString( 961 isWifiWakeupEnabled() 962 ? R.string.wifi_configure_settings_preference_summary_wakeup_on 963 : R.string.wifi_configure_settings_preference_summary_wakeup_off)); 964 965 final List<AccessPoint> savedNetworks = 966 WifiSavedConfigUtils.getAllConfigs(getContext(), mWifiManager); 967 final int numSavedNetworks = (savedNetworks != null) ? savedNetworks.size() : 0; 968 mSavedNetworksPreference.setVisible(numSavedNetworks > 0); 969 if (numSavedNetworks > 0) { 970 mSavedNetworksPreference.setSummary( 971 getSavedNetworkSettingsSummaryText(savedNetworks, numSavedNetworks)); 972 } 973 } 974 getSavedNetworkSettingsSummaryText( List<AccessPoint> savedNetworks, int numSavedNetworks)975 private String getSavedNetworkSettingsSummaryText( 976 List<AccessPoint> savedNetworks, int numSavedNetworks) { 977 int numSavedPasspointNetworks = 0; 978 for (AccessPoint savedNetwork : savedNetworks) { 979 if (savedNetwork.isPasspointConfig() || savedNetwork.isPasspoint()) { 980 numSavedPasspointNetworks++; 981 } 982 } 983 final int numSavedNormalNetworks = numSavedNetworks - numSavedPasspointNetworks; 984 985 if (numSavedNetworks == numSavedNormalNetworks) { 986 return getResources().getQuantityString(R.plurals.wifi_saved_access_points_summary, 987 numSavedNormalNetworks, numSavedNormalNetworks); 988 } else if (numSavedNetworks == numSavedPasspointNetworks) { 989 return getResources().getQuantityString( 990 R.plurals.wifi_saved_passpoint_access_points_summary, 991 numSavedPasspointNetworks, numSavedPasspointNetworks); 992 } else { 993 return getResources().getQuantityString(R.plurals.wifi_saved_all_access_points_summary, 994 numSavedNetworks, numSavedNetworks); 995 } 996 } 997 isWifiWakeupEnabled()998 private boolean isWifiWakeupEnabled() { 999 final Context context = getContext(); 1000 final PowerManager powerManager = context.getSystemService(PowerManager.class); 1001 final ContentResolver contentResolver = context.getContentResolver(); 1002 return Settings.Global.getInt(contentResolver, 1003 Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1 1004 && Settings.Global.getInt(contentResolver, 1005 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1 1006 && Settings.Global.getInt(contentResolver, 1007 Settings.Global.AIRPLANE_MODE_ON, 0) == 0 1008 && !powerManager.isPowerSaveMode(); 1009 } 1010 setOffMessage()1011 private void setOffMessage() { 1012 final CharSequence title = getText(R.string.wifi_empty_list_wifi_off); 1013 // Don't use WifiManager.isScanAlwaysAvailable() to check the Wi-Fi scanning mode. Instead, 1014 // read the system settings directly. Because when the device is in Airplane mode, even if 1015 // Wi-Fi scanning mode is on, WifiManager.isScanAlwaysAvailable() still returns "off". 1016 final boolean wifiScanningMode = Settings.Global.getInt(getActivity().getContentResolver(), 1017 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1; 1018 final CharSequence description = wifiScanningMode ? getText(R.string.wifi_scan_notify_text) 1019 : getText(R.string.wifi_scan_notify_text_scanning_off); 1020 final LinkifyUtils.OnClickListener clickListener = 1021 () -> new SubSettingLauncher(getContext()) 1022 .setDestination(ScanningSettings.class.getName()) 1023 .setTitleRes(R.string.location_scanning_screen_title) 1024 .setSourceMetricsCategory(getMetricsCategory()) 1025 .launch(); 1026 mStatusMessagePreference.setText(title, description, clickListener); 1027 removeConnectedAccessPointPreference(); 1028 removeAccessPointPreference(); 1029 mStatusMessagePreference.setVisible(true); 1030 } 1031 addMessagePreference(int messageId)1032 private void addMessagePreference(int messageId) { 1033 mStatusMessagePreference.setTitle(messageId); 1034 mStatusMessagePreference.setVisible(true); 1035 1036 } 1037 setProgressBarVisible(boolean visible)1038 protected void setProgressBarVisible(boolean visible) { 1039 if (mProgressHeader != null) { 1040 mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE); 1041 } 1042 } 1043 1044 /** 1045 * Renames/replaces "Next" button when appropriate. "Next" button usually exists in 1046 * Wifi setup screens, not in usual wifi settings screen. 1047 * 1048 * @param enabled true when the device is connected to a wifi network. 1049 */ changeNextButtonState(boolean enabled)1050 private void changeNextButtonState(boolean enabled) { 1051 if (mEnableNextOnConnection && hasNextButton()) { 1052 getNextButton().setEnabled(enabled); 1053 } 1054 } 1055 1056 @Override onForget(WifiDialog dialog)1057 public void onForget(WifiDialog dialog) { 1058 forget(); 1059 } 1060 1061 @Override onSubmit(WifiDialog dialog)1062 public void onSubmit(WifiDialog dialog) { 1063 if (mDialog != null) { 1064 submit(mDialog.getController()); 1065 } 1066 } 1067 1068 @Override onScan(WifiDialog dialog, String ssid)1069 public void onScan(WifiDialog dialog, String ssid) { 1070 // Launch QR code scanner to join a network. 1071 startActivityForResult(WifiDppUtils.getEnrolleeQrCodeScannerIntent(ssid), 1072 REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER); 1073 } 1074 submit(WifiConfigController configController)1075 /* package */ void submit(WifiConfigController configController) { 1076 1077 final WifiConfiguration config = configController.getConfig(); 1078 1079 if (config == null) { 1080 if (mSelectedAccessPoint != null 1081 && mSelectedAccessPoint.isSaved()) { 1082 connect(mSelectedAccessPoint.getConfig(), true /* isSavedNetwork */); 1083 } 1084 } else if (configController.getMode() == WifiConfigUiBase.MODE_MODIFY) { 1085 mWifiManager.save(config, mSaveListener); 1086 } else { 1087 mWifiManager.save(config, mSaveListener); 1088 if (mSelectedAccessPoint != null) { // Not an "Add network" 1089 connect(config, false /* isSavedNetwork */); 1090 } 1091 } 1092 1093 mWifiTracker.resumeScanning(); 1094 } 1095 forget()1096 /* package */ void forget() { 1097 mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_WIFI_FORGET); 1098 if (!mSelectedAccessPoint.isSaved()) { 1099 if (mSelectedAccessPoint.getNetworkInfo() != null && 1100 mSelectedAccessPoint.getNetworkInfo().getState() != State.DISCONNECTED) { 1101 // Network is active but has no network ID - must be ephemeral. 1102 mWifiManager.disableEphemeralNetwork( 1103 AccessPoint.convertToQuotedString(mSelectedAccessPoint.getSsidStr())); 1104 } else { 1105 // Should not happen, but a monkey seems to trigger it 1106 Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig()); 1107 return; 1108 } 1109 } else if (mSelectedAccessPoint.getConfig().isPasspoint()) { 1110 try { 1111 mWifiManager.removePasspointConfiguration(mSelectedAccessPoint.getConfig().FQDN); 1112 } catch (IllegalArgumentException e) { 1113 Log.e(TAG, "Failed to remove Passpoint configuration with error: " + e); 1114 return; 1115 } 1116 } else { 1117 mWifiManager.forget(mSelectedAccessPoint.getConfig().networkId, mForgetListener); 1118 } 1119 1120 mWifiTracker.resumeScanning(); 1121 1122 // We need to rename/replace "Next" button in wifi setup context. 1123 changeNextButtonState(false); 1124 } 1125 connect(final WifiConfiguration config, boolean isSavedNetwork)1126 protected void connect(final WifiConfiguration config, boolean isSavedNetwork) { 1127 // Log subtype if configuration is a saved network. 1128 mMetricsFeatureProvider.action(getContext(), SettingsEnums.ACTION_WIFI_CONNECT, 1129 isSavedNetwork); 1130 mWifiManager.connect(config, mConnectListener); 1131 mClickedConnect = true; 1132 } 1133 connect(final int networkId, boolean isSavedNetwork)1134 protected void connect(final int networkId, boolean isSavedNetwork) { 1135 // Log subtype if configuration is a saved network. 1136 mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_WIFI_CONNECT, 1137 isSavedNetwork); 1138 mWifiManager.connect(networkId, mConnectListener); 1139 } 1140 1141 @VisibleForTesting handleAddNetworkRequest(int result, Intent data)1142 void handleAddNetworkRequest(int result, Intent data) { 1143 if (result == Activity.RESULT_OK) { 1144 handleAddNetworkSubmitEvent(data); 1145 } 1146 mWifiTracker.resumeScanning(); 1147 } 1148 handleAddNetworkSubmitEvent(Intent data)1149 private void handleAddNetworkSubmitEvent(Intent data) { 1150 final WifiConfiguration wifiConfiguration = data.getParcelableExtra( 1151 AddNetworkFragment.WIFI_CONFIG_KEY); 1152 if (wifiConfiguration != null) { 1153 mWifiManager.save(wifiConfiguration, mSaveListener); 1154 } 1155 } 1156 1157 /** 1158 * Called when "add network" button is pressed. 1159 */ onAddNetworkPressed()1160 private void onAddNetworkPressed() { 1161 // No exact access point is selected. 1162 mSelectedAccessPoint = null; 1163 launchAddNetworkFragment(); 1164 } 1165 1166 @Override getHelpResource()1167 public int getHelpResource() { 1168 return R.string.help_url_wifi; 1169 } 1170 1171 @Override onAccessPointChanged(final AccessPoint accessPoint)1172 public void onAccessPointChanged(final AccessPoint accessPoint) { 1173 Log.d(TAG, "onAccessPointChanged (singular) callback initiated"); 1174 View view = getView(); 1175 if (view != null) { 1176 view.post(new Runnable() { 1177 @Override 1178 public void run() { 1179 Object tag = accessPoint.getTag(); 1180 if (tag != null) { 1181 ((AccessPointPreference) tag).refresh(); 1182 } 1183 } 1184 }); 1185 } 1186 } 1187 1188 @Override onLevelChanged(AccessPoint accessPoint)1189 public void onLevelChanged(AccessPoint accessPoint) { 1190 ((AccessPointPreference) accessPoint.getTag()).onLevelChanged(); 1191 } 1192 1193 public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 1194 new BaseSearchIndexProvider() { 1195 @Override 1196 public List<SearchIndexableRaw> getRawDataToIndex(Context context, 1197 boolean enabled) { 1198 final List<SearchIndexableRaw> result = new ArrayList<>(); 1199 final Resources res = context.getResources(); 1200 1201 // Add fragment title if we are showing this fragment 1202 if (res.getBoolean(R.bool.config_show_wifi_settings)) { 1203 SearchIndexableRaw data = new SearchIndexableRaw(context); 1204 data.title = res.getString(R.string.wifi_settings); 1205 data.screenTitle = res.getString(R.string.wifi_settings); 1206 data.keywords = res.getString(R.string.keywords_wifi); 1207 data.key = DATA_KEY_REFERENCE; 1208 result.add(data); 1209 } 1210 1211 return result; 1212 } 1213 }; 1214 1215 private static class SummaryProvider 1216 implements SummaryLoader.SummaryProvider, OnSummaryChangeListener { 1217 1218 private final Context mContext; 1219 private final SummaryLoader mSummaryLoader; 1220 1221 @VisibleForTesting 1222 WifiSummaryUpdater mSummaryHelper; 1223 SummaryProvider(Context context, SummaryLoader summaryLoader)1224 public SummaryProvider(Context context, SummaryLoader summaryLoader) { 1225 mContext = context; 1226 mSummaryLoader = summaryLoader; 1227 mSummaryHelper = new WifiSummaryUpdater(mContext, this); 1228 } 1229 1230 1231 @Override setListening(boolean listening)1232 public void setListening(boolean listening) { 1233 mSummaryHelper.register(listening); 1234 } 1235 1236 @Override onSummaryChanged(String summary)1237 public void onSummaryChanged(String summary) { 1238 mSummaryLoader.setSummary(this, summary); 1239 } 1240 } 1241 1242 public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY 1243 = new SummaryLoader.SummaryProviderFactory() { 1244 @Override 1245 public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity, 1246 SummaryLoader summaryLoader) { 1247 return new SummaryProvider(activity, summaryLoader); 1248 } 1249 }; 1250 } 1251