WifiSettings.java revision aef794a2894e996fafa17097744a2bab75eb680d
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
17package com.android.settings.wifi;
18
19import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
20import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
21
22import android.app.Activity;
23import android.app.Dialog;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.Intent;
27import android.content.res.Resources;
28import android.content.res.TypedArray;
29import android.graphics.drawable.Drawable;
30import android.net.ConnectivityManager;
31import android.net.NetworkInfo;
32import android.net.NetworkInfo.State;
33import android.net.wifi.WifiConfiguration;
34import android.net.wifi.WifiManager;
35import android.net.wifi.WpsInfo;
36import android.nfc.NfcAdapter;
37import android.os.Bundle;
38import android.preference.Preference;
39import android.preference.PreferenceScreen;
40import android.text.Spannable;
41import android.text.style.TextAppearanceSpan;
42import android.util.Log;
43import android.view.ContextMenu;
44import android.view.ContextMenu.ContextMenuInfo;
45import android.view.Gravity;
46import android.view.Menu;
47import android.view.MenuInflater;
48import android.view.MenuItem;
49import android.view.View;
50import android.widget.AdapterView.AdapterContextMenuInfo;
51import android.widget.TextView;
52import android.widget.TextView.BufferType;
53import android.widget.Toast;
54
55import com.android.internal.logging.MetricsLogger;
56import com.android.settings.LinkifyUtils;
57import com.android.settings.R;
58import com.android.settings.RestrictedSettingsFragment;
59import com.android.settings.SettingsActivity;
60import com.android.settings.location.ScanningSettings;
61import com.android.settings.search.BaseSearchIndexProvider;
62import com.android.settings.search.Indexable;
63import com.android.settings.search.SearchIndexableRaw;
64import com.android.settingslib.wifi.AccessPoint;
65import com.android.settingslib.wifi.AccessPoint.AccessPointListener;
66import com.android.settingslib.wifi.WifiTracker;
67
68import java.util.ArrayList;
69import java.util.Collection;
70import java.util.List;
71
72/**
73 * Two types of UI are provided here.
74 *
75 * The first is for "usual Settings", appearing as any other Setup fragment.
76 *
77 * The second is for Setup Wizard, with a simplified interface that hides the action bar
78 * and menus.
79 */
80public class WifiSettings extends RestrictedSettingsFragment
81        implements DialogInterface.OnClickListener, Indexable, WifiTracker.WifiListener,
82        AccessPointListener {
83
84    private static final String TAG = "WifiSettings";
85
86    /* package */ static final int MENU_ID_WPS_PBC = Menu.FIRST;
87    private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1;
88    private static final int MENU_ID_SAVED_NETWORK = Menu.FIRST + 2;
89    /* package */ static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 3;
90    private static final int MENU_ID_ADVANCED = Menu.FIRST + 4;
91    private static final int MENU_ID_SCAN = Menu.FIRST + 5;
92    private static final int MENU_ID_CONNECT = Menu.FIRST + 6;
93    private static final int MENU_ID_FORGET = Menu.FIRST + 7;
94    private static final int MENU_ID_MODIFY = Menu.FIRST + 8;
95    private static final int MENU_ID_WRITE_NFC = Menu.FIRST + 9;
96
97    public static final int WIFI_DIALOG_ID = 1;
98    /* package */ static final int WPS_PBC_DIALOG_ID = 2;
99    private static final int WPS_PIN_DIALOG_ID = 3;
100    private static final int WRITE_NFC_DIALOG_ID = 6;
101
102    // Instance state keys
103    private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode";
104    private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
105    private static final String SAVED_WIFI_NFC_DIALOG_STATE = "wifi_nfc_dlg_state";
106
107    private static boolean savedNetworksExist;
108
109    protected WifiManager mWifiManager;
110    private WifiManager.ActionListener mConnectListener;
111    private WifiManager.ActionListener mSaveListener;
112    private WifiManager.ActionListener mForgetListener;
113
114    private WifiEnabler mWifiEnabler;
115    // An access point being editted is stored here.
116    private AccessPoint mSelectedAccessPoint;
117
118    private WifiDialog mDialog;
119    private WriteWifiConfigToNfcDialog mWifiToNfcDialog;
120
121    private TextView mEmptyView;
122
123    // this boolean extra specifies whether to disable the Next button when not connected. Used by
124    // account creation outside of setup wizard.
125    private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
126    // This string extra specifies a network to open the connect dialog on, so the user can enter
127    // network credentials.  This is used by quick settings for secured networks.
128    private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid";
129
130    // should Next button only be enabled when we have a connection?
131    private boolean mEnableNextOnConnection;
132
133    // Save the dialog details
134    private boolean mDlgEdit;
135    private AccessPoint mDlgAccessPoint;
136    private Bundle mAccessPointSavedState;
137    private Bundle mWifiNfcDialogSavedState;
138
139    private WifiTracker mWifiTracker;
140
141    /* End of "used in Wifi Setup context" */
142
143    public WifiSettings() {
144        super(DISALLOW_CONFIG_WIFI);
145    }
146
147    @Override
148    public void onActivityCreated(Bundle savedInstanceState) {
149        super.onActivityCreated(savedInstanceState);
150
151        mWifiTracker = new WifiTracker(getActivity(), this, true, true);
152        mWifiManager = mWifiTracker.getManager();
153
154        mConnectListener = new WifiManager.ActionListener() {
155                                   @Override
156                                   public void onSuccess() {
157                                   }
158                                   @Override
159                                   public void onFailure(int reason) {
160                                       Activity activity = getActivity();
161                                       if (activity != null) {
162                                           Toast.makeText(activity,
163                                                R.string.wifi_failed_connect_message,
164                                                Toast.LENGTH_SHORT).show();
165                                       }
166                                   }
167                               };
168
169        mSaveListener = new WifiManager.ActionListener() {
170                                @Override
171                                public void onSuccess() {
172                                }
173                                @Override
174                                public void onFailure(int reason) {
175                                    Activity activity = getActivity();
176                                    if (activity != null) {
177                                        Toast.makeText(activity,
178                                            R.string.wifi_failed_save_message,
179                                            Toast.LENGTH_SHORT).show();
180                                    }
181                                }
182                            };
183
184        mForgetListener = new WifiManager.ActionListener() {
185                                   @Override
186                                   public void onSuccess() {
187                                   }
188                                   @Override
189                                   public void onFailure(int reason) {
190                                       Activity activity = getActivity();
191                                       if (activity != null) {
192                                           Toast.makeText(activity,
193                                               R.string.wifi_failed_forget_message,
194                                               Toast.LENGTH_SHORT).show();
195                                       }
196                                   }
197                               };
198
199        if (savedInstanceState != null) {
200            mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE);
201            if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
202                mAccessPointSavedState =
203                    savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
204            }
205
206            if (savedInstanceState.containsKey(SAVED_WIFI_NFC_DIALOG_STATE)) {
207                mWifiNfcDialogSavedState =
208                    savedInstanceState.getBundle(SAVED_WIFI_NFC_DIALOG_STATE);
209            }
210        }
211
212        // if we're supposed to enable/disable the Next button based on our current connection
213        // state, start it off in the right state
214        Intent intent = getActivity().getIntent();
215        mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
216
217        if (mEnableNextOnConnection) {
218            if (hasNextButton()) {
219                final ConnectivityManager connectivity = (ConnectivityManager)
220                        getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
221                if (connectivity != null) {
222                    NetworkInfo info = connectivity.getNetworkInfo(
223                            ConnectivityManager.TYPE_WIFI);
224                    changeNextButtonState(info.isConnected());
225                }
226            }
227        }
228
229        addPreferencesFromResource(R.xml.wifi_settings);
230
231        mEmptyView = initEmptyView();
232        registerForContextMenu(getListView());
233        setHasOptionsMenu(true);
234
235        if (intent.hasExtra(EXTRA_START_CONNECT_SSID)) {
236            String ssid = intent.getStringExtra(EXTRA_START_CONNECT_SSID);
237            onAccessPointsChanged();
238            PreferenceScreen preferenceScreen = getPreferenceScreen();
239            for (int i = 0; i < preferenceScreen.getPreferenceCount(); i++) {
240                Preference preference = preferenceScreen.getPreference(i);
241                if (preference instanceof AccessPointPreference) {
242                    AccessPoint accessPoint = ((AccessPointPreference) preference).getAccessPoint();
243                    if (ssid.equals(accessPoint.getSsid()) && !accessPoint.isSaved()
244                            && accessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
245                        onPreferenceTreeClick(preferenceScreen, preference);
246                        break;
247                    }
248                }
249            }
250        }
251    }
252
253    @Override
254    public void onDestroyView() {
255        super.onDestroyView();
256
257        if (mWifiEnabler != null) {
258            mWifiEnabler.teardownSwitchBar();
259        }
260    }
261
262    @Override
263    public void onStart() {
264        super.onStart();
265
266        // On/off switch is hidden for Setup Wizard (returns null)
267        mWifiEnabler = createWifiEnabler();
268    }
269
270    /**
271     * @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard)
272     */
273    /* package */ WifiEnabler createWifiEnabler() {
274        final SettingsActivity activity = (SettingsActivity) getActivity();
275        return new WifiEnabler(activity, activity.getSwitchBar());
276    }
277
278    @Override
279    public void onResume() {
280        final Activity activity = getActivity();
281        super.onResume();
282        if (mWifiEnabler != null) {
283            mWifiEnabler.resume(activity);
284        }
285
286        mWifiTracker.startTracking();
287    }
288
289    @Override
290    public void onPause() {
291        super.onPause();
292        if (mWifiEnabler != null) {
293            mWifiEnabler.pause();
294        }
295
296        mWifiTracker.stopTracking();
297    }
298
299    @Override
300    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
301        // If the user is not allowed to configure wifi, do not show the menu.
302        if (isUiRestricted()) return;
303
304        addOptionsMenuItems(menu);
305        super.onCreateOptionsMenu(menu, inflater);
306    }
307
308    /**
309     * @param menu
310     */
311    void addOptionsMenuItems(Menu menu) {
312        final boolean wifiIsEnabled = mWifiTracker.isWifiEnabled();
313        TypedArray ta = getActivity().getTheme().obtainStyledAttributes(
314                new int[] {R.attr.ic_menu_add, R.attr.ic_wps});
315        menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
316                .setIcon(ta.getDrawable(0))
317                .setEnabled(wifiIsEnabled)
318                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
319        if (savedNetworksExist) {
320            menu.add(Menu.NONE, MENU_ID_SAVED_NETWORK, 0, R.string.wifi_saved_access_points_label)
321                    .setIcon(ta.getDrawable(0))
322                    .setEnabled(wifiIsEnabled)
323                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
324        }
325        menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.menu_stats_refresh)
326               .setEnabled(wifiIsEnabled)
327               .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
328        menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
329                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
330        ta.recycle();
331    }
332
333    @Override
334    protected int getMetricsCategory() {
335        return MetricsLogger.WIFI;
336    }
337
338    @Override
339    public void onSaveInstanceState(Bundle outState) {
340        super.onSaveInstanceState(outState);
341
342        // If the dialog is showing, save its state.
343        if (mDialog != null && mDialog.isShowing()) {
344            outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit);
345            if (mDlgAccessPoint != null) {
346                mAccessPointSavedState = new Bundle();
347                mDlgAccessPoint.saveWifiState(mAccessPointSavedState);
348                outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
349            }
350        }
351
352        if (mWifiToNfcDialog != null && mWifiToNfcDialog.isShowing()) {
353            Bundle savedState = new Bundle();
354            mWifiToNfcDialog.saveState(savedState);
355            outState.putBundle(SAVED_WIFI_NFC_DIALOG_STATE, savedState);
356        }
357    }
358
359    @Override
360    public boolean onOptionsItemSelected(MenuItem item) {
361        // If the user is not allowed to configure wifi, do not handle menu selections.
362        if (isUiRestricted()) return false;
363
364        switch (item.getItemId()) {
365            case MENU_ID_WPS_PBC:
366                showDialog(WPS_PBC_DIALOG_ID);
367                return true;
368                /*
369            case MENU_ID_P2P:
370                if (getActivity() instanceof SettingsActivity) {
371                    ((SettingsActivity) getActivity()).startPreferencePanel(
372                            WifiP2pSettings.class.getCanonicalName(),
373                            null,
374                            R.string.wifi_p2p_settings_title, null,
375                            this, 0);
376                } else {
377                    startFragment(this, WifiP2pSettings.class.getCanonicalName(),
378                            R.string.wifi_p2p_settings_title, -1, null);
379                }
380                return true;
381                */
382            case MENU_ID_WPS_PIN:
383                showDialog(WPS_PIN_DIALOG_ID);
384                return true;
385            case MENU_ID_SCAN:
386                MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_FORCE_SCAN);
387                mWifiTracker.forceScan();
388                return true;
389            case MENU_ID_ADD_NETWORK:
390                if (mWifiTracker.isWifiEnabled()) {
391                    onAddNetworkPressed();
392                }
393                return true;
394            case MENU_ID_SAVED_NETWORK:
395                if (getActivity() instanceof SettingsActivity) {
396                    ((SettingsActivity) getActivity()).startPreferencePanel(
397                            SavedAccessPointsWifiSettings.class.getCanonicalName(), null,
398                            R.string.wifi_saved_access_points_titlebar, null, this, 0);
399                } else {
400                    startFragment(this, SavedAccessPointsWifiSettings.class.getCanonicalName(),
401                            R.string.wifi_saved_access_points_titlebar,
402                            -1 /* Do not request a result */, null);
403                }
404                return true;
405            case MENU_ID_ADVANCED:
406                if (getActivity() instanceof SettingsActivity) {
407                    ((SettingsActivity) getActivity()).startPreferencePanel(
408                            AdvancedWifiSettings.class.getCanonicalName(), null,
409                            R.string.wifi_advanced_titlebar, null, this, 0);
410                } else {
411                    startFragment(this, AdvancedWifiSettings.class.getCanonicalName(),
412                            R.string.wifi_advanced_titlebar, -1 /* Do not request a results */,
413                            null);
414                }
415                return true;
416        }
417        return super.onOptionsItemSelected(item);
418    }
419
420    @Override
421    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
422        if (info instanceof AdapterContextMenuInfo) {
423            Preference preference = (Preference) getListView().getItemAtPosition(
424                    ((AdapterContextMenuInfo) info).position);
425
426            if (preference instanceof AccessPointPreference) {
427                mSelectedAccessPoint = ((AccessPointPreference) preference).getAccessPoint();
428                menu.setHeaderTitle(mSelectedAccessPoint.getSsid());
429                if (mSelectedAccessPoint.isConnectable()) {
430                    menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect);
431                }
432
433                if (mSelectedAccessPoint.isSaved() || mSelectedAccessPoint.isEphemeral()) {
434                    // Allow forgetting a network if either the network is saved or ephemerally
435                    // connected. (In the latter case, "forget" blacklists the network so it won't
436                    // be used again, ephemerally).
437                    menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget);
438                }
439                if (mSelectedAccessPoint.isSaved()) {
440                    menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify);
441                    NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
442                    if (nfcAdapter != null && nfcAdapter.isEnabled() &&
443                            mSelectedAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
444                        // Only allow writing of NFC tags for password-protected networks.
445                        menu.add(Menu.NONE, MENU_ID_WRITE_NFC, 0, R.string.wifi_menu_write_to_nfc);
446                    }
447                }
448            }
449        }
450    }
451
452    @Override
453    public boolean onContextItemSelected(MenuItem item) {
454        if (mSelectedAccessPoint == null) {
455            return super.onContextItemSelected(item);
456        }
457        switch (item.getItemId()) {
458            case MENU_ID_CONNECT: {
459                if (mSelectedAccessPoint.isSaved()) {
460                    connect(mSelectedAccessPoint.getConfig());
461                } else if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE) {
462                    /** Bypass dialog for unsecured networks */
463                    mSelectedAccessPoint.generateOpenNetworkConfig();
464                    connect(mSelectedAccessPoint.getConfig());
465                } else {
466                    showDialog(mSelectedAccessPoint, true);
467                }
468                return true;
469            }
470            case MENU_ID_FORGET: {
471                forget();
472                return true;
473            }
474            case MENU_ID_MODIFY: {
475                showDialog(mSelectedAccessPoint, true);
476                return true;
477            }
478            case MENU_ID_WRITE_NFC:
479                showDialog(WRITE_NFC_DIALOG_ID);
480                return true;
481
482        }
483        return super.onContextItemSelected(item);
484    }
485
486    @Override
487    public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
488        if (preference instanceof AccessPointPreference) {
489            mSelectedAccessPoint = ((AccessPointPreference) preference).getAccessPoint();
490            /** Bypass dialog for unsecured, unsaved, and inactive networks */
491            if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE &&
492                    !mSelectedAccessPoint.isSaved() && !mSelectedAccessPoint.isActive()) {
493                mSelectedAccessPoint.generateOpenNetworkConfig();
494                if (!savedNetworksExist) {
495                    savedNetworksExist = true;
496                    getActivity().invalidateOptionsMenu();
497                }
498                connect(mSelectedAccessPoint.getConfig());
499            } else {
500                showDialog(mSelectedAccessPoint, false);
501            }
502        } else {
503            return super.onPreferenceTreeClick(screen, preference);
504        }
505        return true;
506    }
507
508    private void showDialog(AccessPoint accessPoint, boolean edit) {
509        if (mDialog != null) {
510            removeDialog(WIFI_DIALOG_ID);
511            mDialog = null;
512        }
513
514        // Save the access point and edit mode
515        mDlgAccessPoint = accessPoint;
516        mDlgEdit = edit;
517
518        showDialog(WIFI_DIALOG_ID);
519    }
520
521    @Override
522    public Dialog onCreateDialog(int dialogId) {
523        switch (dialogId) {
524            case WIFI_DIALOG_ID:
525                AccessPoint ap = mDlgAccessPoint; // For manual launch
526                if (ap == null) { // For re-launch from saved state
527                    if (mAccessPointSavedState != null) {
528                        ap = new AccessPoint(getActivity(), mAccessPointSavedState);
529                        // For repeated orientation changes
530                        mDlgAccessPoint = ap;
531                        // Reset the saved access point data
532                        mAccessPointSavedState = null;
533                    }
534                }
535                // If it's null, fine, it's for Add Network
536                mSelectedAccessPoint = ap;
537                mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit);
538                return mDialog;
539            case WPS_PBC_DIALOG_ID:
540                return new WpsDialog(getActivity(), WpsInfo.PBC);
541            case WPS_PIN_DIALOG_ID:
542                return new WpsDialog(getActivity(), WpsInfo.DISPLAY);
543            case WRITE_NFC_DIALOG_ID:
544                if (mSelectedAccessPoint != null) {
545                    mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(
546                            getActivity(), mSelectedAccessPoint.getConfig().networkId,
547                            mSelectedAccessPoint.getSecurity(),
548                            mWifiManager);
549                } else if (mWifiNfcDialogSavedState != null) {
550                    mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(
551                            getActivity(), mWifiNfcDialogSavedState, mWifiManager);
552                }
553
554                return mWifiToNfcDialog;
555        }
556        return super.onCreateDialog(dialogId);
557    }
558
559    /**
560     * Shows the latest access points available with supplemental information like
561     * the strength of network and the security for it.
562     */
563    @Override
564    public void onAccessPointsChanged() {
565        // Safeguard from some delayed event handling
566        if (getActivity() == null) return;
567
568        if (isUiRestricted()) {
569            addMessagePreference(R.string.wifi_empty_list_user_restricted);
570            return;
571        }
572        final int wifiState = mWifiManager.getWifiState();
573
574        switch (wifiState) {
575            case WifiManager.WIFI_STATE_ENABLED:
576                // AccessPoints are automatically sorted with TreeSet.
577                final Collection<AccessPoint> accessPoints =
578                        mWifiTracker.getAccessPoints();
579                getPreferenceScreen().removeAll();
580                if (accessPoints.size() == 0) {
581                    addMessagePreference(R.string.wifi_empty_list_wifi_on);
582                }
583
584                for (AccessPoint accessPoint : accessPoints) {
585                    // Ignore access points that are out of range.
586                    if (accessPoint.getLevel() != -1) {
587                        AccessPointPreference preference = new AccessPointPreference(accessPoint,
588                                getActivity());
589
590                        getPreferenceScreen().addPreference(preference);
591                        accessPoint.setListener(this);
592                    }
593                }
594                break;
595
596            case WifiManager.WIFI_STATE_ENABLING:
597                getPreferenceScreen().removeAll();
598                break;
599
600            case WifiManager.WIFI_STATE_DISABLING:
601                addMessagePreference(R.string.wifi_stopping);
602                break;
603
604            case WifiManager.WIFI_STATE_DISABLED:
605                setOffMessage();
606                break;
607        }
608        // Update "Saved Networks" menu option.
609        if (savedNetworksExist != mWifiTracker.doSavedNetworksExist()) {
610            savedNetworksExist = !savedNetworksExist;
611            getActivity().invalidateOptionsMenu();
612        }
613    }
614
615    protected TextView initEmptyView() {
616        TextView emptyView = (TextView) getActivity().findViewById(android.R.id.empty);
617        emptyView.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
618        getListView().setEmptyView(emptyView);
619        return emptyView;
620    }
621
622    private void setOffMessage() {
623        if (mEmptyView == null) {
624            return;
625        }
626
627        final CharSequence briefText = getText(R.string.wifi_empty_list_wifi_off);
628        if (isUiRestricted()) {
629            // Show only the brief text if the user is not allowed to configure scanning settings.
630            mEmptyView.setText(briefText, BufferType.SPANNABLE);
631        } else {
632            // Append the description of scanning settings with link.
633            final StringBuilder contentBuilder = new StringBuilder();
634            contentBuilder.append(briefText);
635            contentBuilder.append("\n\n");
636            contentBuilder.append(getText(R.string.wifi_scan_notify_text));
637            LinkifyUtils.linkify(mEmptyView, contentBuilder, new LinkifyUtils.OnClickListener() {
638                @Override
639                public void onClick() {
640                    final SettingsActivity activity =
641                            (SettingsActivity) WifiSettings.this.getActivity();
642                    activity.startPreferencePanel(ScanningSettings.class.getName(), null,
643                            R.string.location_scanning_screen_title, null, null, 0);
644                }
645            });
646        }
647        // Embolden and enlarge the brief description anyway.
648        Spannable boldSpan = (Spannable) mEmptyView.getText();
649        boldSpan.setSpan(
650                new TextAppearanceSpan(getActivity(), android.R.style.TextAppearance_Medium), 0,
651                briefText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
652        getPreferenceScreen().removeAll();
653    }
654
655    private void addMessagePreference(int messageId) {
656        if (mEmptyView != null) mEmptyView.setText(messageId);
657        getPreferenceScreen().removeAll();
658    }
659
660    @Override
661    public void onWifiStateChanged(int state) {
662        Activity activity = getActivity();
663        if (activity != null) {
664            activity.invalidateOptionsMenu();
665        }
666
667        switch (state) {
668            case WifiManager.WIFI_STATE_ENABLING:
669                addMessagePreference(R.string.wifi_starting);
670                break;
671
672            case WifiManager.WIFI_STATE_DISABLED:
673                setOffMessage();
674                break;
675        }
676    }
677
678    @Override
679    public void onConnectedChanged() {
680        changeNextButtonState(mWifiTracker.isConnected());
681    }
682
683    /**
684     * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
685     * Wifi setup screens, not in usual wifi settings screen.
686     *
687     * @param enabled true when the device is connected to a wifi network.
688     */
689    private void changeNextButtonState(boolean enabled) {
690        if (mEnableNextOnConnection && hasNextButton()) {
691            getNextButton().setEnabled(enabled);
692        }
693    }
694
695    @Override
696    public void onClick(DialogInterface dialogInterface, int button) {
697        if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
698            forget();
699        } else if (button == WifiDialog.BUTTON_SUBMIT) {
700            if (mDialog != null) {
701                submit(mDialog.getController());
702            }
703        }
704    }
705
706    /* package */ void submit(WifiConfigController configController) {
707
708        final WifiConfiguration config = configController.getConfig();
709
710        if (config == null) {
711            if (mSelectedAccessPoint != null
712                    && mSelectedAccessPoint.isSaved()) {
713                connect(mSelectedAccessPoint.getConfig());
714            }
715        } else if (config.networkId != INVALID_NETWORK_ID) {
716            if (mSelectedAccessPoint != null) {
717                mWifiManager.save(config, mSaveListener);
718            }
719        } else {
720            if (configController.isEdit()) {
721                mWifiManager.save(config, mSaveListener);
722            } else {
723                connect(config);
724            }
725        }
726
727        mWifiTracker.resumeScanning();
728    }
729
730    /* package */ void forget() {
731        MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_FORGET);
732        if (!mSelectedAccessPoint.isSaved()) {
733            if (mSelectedAccessPoint.getNetworkInfo().getState() != State.DISCONNECTED) {
734                // Network is active but has no network ID - must be ephemeral.
735                mWifiManager.disableEphemeralNetwork(
736                        AccessPoint.convertToQuotedString(mSelectedAccessPoint.getSsid()));
737            } else {
738                // Should not happen, but a monkey seems to trigger it
739                Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig());
740                return;
741            }
742        } else {
743            mWifiManager.forget(mSelectedAccessPoint.getConfig().networkId, mForgetListener);
744        }
745
746        mWifiTracker.resumeScanning();
747
748        // We need to rename/replace "Next" button in wifi setup context.
749        changeNextButtonState(false);
750    }
751
752    protected void connect(final WifiConfiguration config) {
753        MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_CONNECT);
754        mWifiManager.connect(config, mConnectListener);
755    }
756
757    protected void connect(final int networkId) {
758        MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_CONNECT);
759        mWifiManager.connect(networkId, mConnectListener);
760    }
761
762    /**
763     * Refreshes acccess points and ask Wifi module to scan networks again.
764     */
765    /* package */ void refreshAccessPoints() {
766        mWifiTracker.resumeScanning();
767
768        getPreferenceScreen().removeAll();
769    }
770
771    /**
772     * Called when "add network" button is pressed.
773     */
774    /* package */ void onAddNetworkPressed() {
775        MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_ADD_NETWORK);
776        // No exact access point is selected.
777        mSelectedAccessPoint = null;
778        showDialog(null, true);
779    }
780
781    /* package */ int getAccessPointsCount() {
782        final boolean wifiIsEnabled = mWifiTracker.isWifiEnabled();
783        if (wifiIsEnabled) {
784            return getPreferenceScreen().getPreferenceCount();
785        } else {
786            return 0;
787        }
788    }
789
790    /**
791     * Requests wifi module to pause wifi scan. May be ignored when the module is disabled.
792     */
793    /* package */ void pauseWifiScan() {
794        mWifiTracker.pauseScanning();
795    }
796
797    /**
798     * Requests wifi module to resume wifi scan. May be ignored when the module is disabled.
799     */
800    /* package */ void resumeWifiScan() {
801        mWifiTracker.resumeScanning();
802    }
803
804    @Override
805    protected int getHelpResource() {
806        return R.string.help_url_wifi;
807    }
808
809    @Override
810    public void onAccessPointChanged(AccessPoint accessPoint) {
811        ((AccessPointPreference) accessPoint.getTag()).refresh();
812    }
813
814    @Override
815    public void onLevelChanged(AccessPoint accessPoint) {
816        ((AccessPointPreference) accessPoint.getTag()).onLevelChanged();
817    }
818
819    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
820        new BaseSearchIndexProvider() {
821            @Override
822            public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
823                final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
824                final Resources res = context.getResources();
825
826                // Add fragment title
827                SearchIndexableRaw data = new SearchIndexableRaw(context);
828                data.title = res.getString(R.string.wifi_settings);
829                data.screenTitle = res.getString(R.string.wifi_settings);
830                data.keywords = res.getString(R.string.keywords_wifi);
831                result.add(data);
832
833                // Add saved Wi-Fi access points
834                final Collection<AccessPoint> accessPoints =
835                        WifiTracker.getCurrentAccessPoints(context, true, false);
836                for (AccessPoint accessPoint : accessPoints) {
837                    data = new SearchIndexableRaw(context);
838                    data.title = accessPoint.getSsid();
839                    data.screenTitle = res.getString(R.string.wifi_settings);
840                    data.enabled = enabled;
841                    result.add(data);
842                }
843
844                return result;
845            }
846        };
847}
848