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