WifiSettings.java revision f2a9717b93e39d7e15f90e84f8b4de24da11463e
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;
20
21import android.app.ActionBar;
22import android.app.Activity;
23import android.app.AlertDialog;
24import android.app.Dialog;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.DialogInterface;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.pm.PackageManager;
31import android.content.res.Resources;
32import android.net.ConnectivityManager;
33import android.net.NetworkInfo;
34import android.net.NetworkInfo.DetailedState;
35import android.net.wifi.ScanResult;
36import android.net.wifi.SupplicantState;
37import android.net.wifi.WifiConfiguration;
38import android.net.wifi.WifiInfo;
39import android.net.wifi.WifiManager;
40import android.net.wifi.WpsInfo;
41import android.os.Bundle;
42import android.os.Handler;
43import android.os.Message;
44import android.preference.Preference;
45import android.preference.PreferenceActivity;
46import android.preference.PreferenceScreen;
47import android.telephony.TelephonyManager;
48import android.util.AttributeSet;
49import android.util.Log;
50import android.view.ContextMenu;
51import android.view.ContextMenu.ContextMenuInfo;
52import android.view.Gravity;
53import android.view.LayoutInflater;
54import android.view.Menu;
55import android.view.MenuInflater;
56import android.view.MenuItem;
57import android.view.View;
58import android.view.View.OnClickListener;
59import android.view.ViewGroup;
60import android.widget.AdapterView.AdapterContextMenuInfo;
61import android.widget.Button;
62import android.widget.ImageButton;
63import android.widget.PopupMenu;
64import android.widget.PopupMenu.OnMenuItemClickListener;
65import android.widget.RelativeLayout;
66import android.widget.Switch;
67import android.widget.TextView;
68import android.widget.Toast;
69
70import com.android.settings.R;
71import com.android.settings.SettingsPreferenceFragment;
72import com.android.settings.wifi.p2p.WifiP2pSettings;
73
74import java.util.ArrayList;
75import java.util.Collection;
76import java.util.Collections;
77import java.util.HashMap;
78import java.util.List;
79import java.util.concurrent.atomic.AtomicBoolean;
80
81/**
82 * Two types of UI are provided here.
83 *
84 * The first is for "usual Settings", appearing as any other Setup fragment.
85 *
86 * The second is for Setup Wizard, with a simplified interface that hides the action bar
87 * and menus.
88 */
89public class WifiSettings extends SettingsPreferenceFragment
90        implements DialogInterface.OnClickListener  {
91    private static final String TAG = "WifiSettings";
92    private static final int MENU_ID_WPS_PBC = Menu.FIRST;
93    private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1;
94    private static final int MENU_ID_P2P = Menu.FIRST + 2;
95    private static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 3;
96    private static final int MENU_ID_ADVANCED = Menu.FIRST + 4;
97    private static final int MENU_ID_SCAN = Menu.FIRST + 5;
98    private static final int MENU_ID_CONNECT = Menu.FIRST + 6;
99    private static final int MENU_ID_FORGET = Menu.FIRST + 7;
100    private static final int MENU_ID_MODIFY = Menu.FIRST + 8;
101
102    private static final int WIFI_DIALOG_ID = 1;
103    private static final int WPS_PBC_DIALOG_ID = 2;
104    private static final int WPS_PIN_DIALOG_ID = 3;
105    private static final int WIFI_SKIPPED_DIALOG_ID = 4;
106    private static final int WIFI_AND_MOBILE_SKIPPED_DIALOG_ID = 5;
107
108    // Combo scans can take 5-6s to complete - set to 10s.
109    private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;
110
111    // Instance state keys
112    private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode";
113    private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
114
115    // Activity result when pressing the Skip button
116    private static final int RESULT_SKIP = Activity.RESULT_FIRST_USER;
117
118    private final IntentFilter mFilter;
119    private final BroadcastReceiver mReceiver;
120    private final Scanner mScanner;
121
122    private WifiManager mWifiManager;
123    private WifiManager.ActionListener mConnectListener;
124    private WifiManager.ActionListener mSaveListener;
125    private WifiManager.ActionListener mForgetListener;
126    private boolean mP2pSupported;
127
128
129    private WifiEnabler mWifiEnabler;
130    // An access point being editted is stored here.
131    private AccessPoint mSelectedAccessPoint;
132
133    private DetailedState mLastState;
134    private WifiInfo mLastInfo;
135
136    private AtomicBoolean mConnected = new AtomicBoolean(false);
137
138    private WifiDialog mDialog;
139
140    private TextView mEmptyView;
141
142    /* Used in Wifi Setup context */
143
144    // this boolean extra specifies whether to disable the Next button when not connected
145    private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
146
147    // this boolean extra specifies whether to auto finish when connection is established
148    private static final String EXTRA_AUTO_FINISH_ON_CONNECT = "wifi_auto_finish_on_connect";
149
150    // this boolean extra shows a custom button that we can control
151    protected static final String EXTRA_SHOW_CUSTOM_BUTTON = "wifi_show_custom_button";
152
153    // show a text regarding data charges when wifi connection is required during setup wizard
154    protected static final String EXTRA_SHOW_WIFI_REQUIRED_INFO = "wifi_show_wifi_required_info";
155
156    // this boolean extra is set if we are being invoked by the Setup Wizard
157    private static final String EXTRA_IS_FIRST_RUN = "firstRun";
158
159    // should Next button only be enabled when we have a connection?
160    private boolean mEnableNextOnConnection;
161
162    // should activity finish once we have a connection?
163    private boolean mAutoFinishOnConnection;
164
165    // Save the dialog details
166    private boolean mDlgEdit;
167    private AccessPoint mDlgAccessPoint;
168    private Bundle mAccessPointSavedState;
169
170    // the action bar uses a different set of controls for Setup Wizard
171    private boolean mSetupWizardMode;
172
173    /* End of "used in Wifi Setup context" */
174
175    public WifiSettings() {
176        mFilter = new IntentFilter();
177        mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
178        mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
179        mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
180        mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
181        mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
182        mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
183        mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
184        mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
185
186        mReceiver = new BroadcastReceiver() {
187            @Override
188            public void onReceive(Context context, Intent intent) {
189                handleEvent(context, intent);
190            }
191        };
192
193        mScanner = new Scanner();
194    }
195
196    @Override
197    public void onCreate(Bundle icicle) {
198        // Set this flag early, as it's needed by getHelpResource(), which is called by super
199        mSetupWizardMode = getActivity().getIntent().getBooleanExtra(EXTRA_IS_FIRST_RUN, false);
200
201        super.onCreate(icicle);
202    }
203
204    @Override
205    public View onCreateView(final LayoutInflater inflater, ViewGroup container,
206            Bundle savedInstanceState) {
207        if (mSetupWizardMode) {
208            View view = inflater.inflate(R.layout.setup_preference, container, false);
209            View other = view.findViewById(R.id.other_network);
210            other.setOnClickListener(new OnClickListener() {
211                @Override
212                public void onClick(View v) {
213                    if (mWifiManager.isWifiEnabled()) {
214                        onAddNetworkPressed();
215                    }
216                }
217            });
218            final ImageButton b = (ImageButton) view.findViewById(R.id.more);
219            if (b != null) {
220                b.setOnClickListener(new OnClickListener() {
221                    @Override
222                    public void onClick(View v) {
223                        if (mWifiManager.isWifiEnabled()) {
224                            PopupMenu pm = new PopupMenu(inflater.getContext(), b);
225                            pm.inflate(R.menu.wifi_setup);
226                            pm.setOnMenuItemClickListener(new OnMenuItemClickListener() {
227                                @Override
228                                public boolean onMenuItemClick(MenuItem item) {
229                                    if (R.id.wifi_wps == item.getItemId()) {
230                                        showDialog(WPS_PBC_DIALOG_ID);
231                                        return true;
232                                    }
233                                    return false;
234                                }
235                            });
236                            pm.show();
237                        }
238                    }
239                });
240            }
241
242            Intent intent = getActivity().getIntent();
243            if (intent.getBooleanExtra(EXTRA_SHOW_CUSTOM_BUTTON, false)) {
244                view.findViewById(R.id.button_bar).setVisibility(View.VISIBLE);
245                view.findViewById(R.id.back_button).setVisibility(View.INVISIBLE);
246                view.findViewById(R.id.skip_button).setVisibility(View.INVISIBLE);
247                view.findViewById(R.id.next_button).setVisibility(View.INVISIBLE);
248
249                Button customButton = (Button) view.findViewById(R.id.custom_button);
250                customButton.setVisibility(View.VISIBLE);
251                customButton.setOnClickListener(new OnClickListener() {
252                    @Override
253                    public void onClick(View v) {
254                        if (isPhone() && !hasSimProblem()) {
255                            showDialog(WIFI_SKIPPED_DIALOG_ID);
256                        } else {
257                            showDialog(WIFI_AND_MOBILE_SKIPPED_DIALOG_ID);
258                        }
259                    }
260                });
261            }
262
263            if (intent.getBooleanExtra(EXTRA_SHOW_WIFI_REQUIRED_INFO, false)) {
264                view.findViewById(R.id.wifi_required_info).setVisibility(View.VISIBLE);
265            }
266
267            return view;
268        } else {
269            return super.onCreateView(inflater, container, savedInstanceState);
270        }
271    }
272
273    @Override
274    public void onActivityCreated(Bundle savedInstanceState) {
275        super.onActivityCreated(savedInstanceState);
276
277        mP2pSupported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT);
278        mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
279
280        mConnectListener = new WifiManager.ActionListener() {
281                                   @Override
282                                   public void onSuccess() {
283                                   }
284                                   @Override
285                                   public void onFailure(int reason) {
286                                       Activity activity = getActivity();
287                                       if (activity != null) {
288                                           Toast.makeText(activity,
289                                                R.string.wifi_failed_connect_message,
290                                                Toast.LENGTH_SHORT).show();
291                                       }
292                                   }
293                               };
294
295        mSaveListener = new WifiManager.ActionListener() {
296                                @Override
297                                public void onSuccess() {
298                                }
299                                @Override
300                                public void onFailure(int reason) {
301                                    Activity activity = getActivity();
302                                    if (activity != null) {
303                                        Toast.makeText(activity,
304                                            R.string.wifi_failed_save_message,
305                                            Toast.LENGTH_SHORT).show();
306                                    }
307                                }
308                            };
309
310        mForgetListener = new WifiManager.ActionListener() {
311                                   @Override
312                                   public void onSuccess() {
313                                   }
314                                   @Override
315                                   public void onFailure(int reason) {
316                                       Activity activity = getActivity();
317                                       if (activity != null) {
318                                           Toast.makeText(activity,
319                                               R.string.wifi_failed_forget_message,
320                                               Toast.LENGTH_SHORT).show();
321                                       }
322                                   }
323                               };
324
325        if (savedInstanceState != null
326                && savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
327            mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE);
328            mAccessPointSavedState = savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
329        }
330
331        final Activity activity = getActivity();
332        final Intent intent = activity.getIntent();
333
334        // first if we're supposed to finish once we have a connection
335        mAutoFinishOnConnection = intent.getBooleanExtra(EXTRA_AUTO_FINISH_ON_CONNECT, false);
336
337        if (mAutoFinishOnConnection) {
338            // Hide the next button
339            if (hasNextButton()) {
340                getNextButton().setVisibility(View.GONE);
341            }
342
343            final ConnectivityManager connectivity = (ConnectivityManager)
344                    activity.getSystemService(Context.CONNECTIVITY_SERVICE);
345            if (connectivity != null
346                    && connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected()) {
347                activity.setResult(Activity.RESULT_OK);
348                activity.finish();
349                return;
350            }
351        }
352
353        // if we're supposed to enable/disable the Next button based on our current connection
354        // state, start it off in the right state
355        mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
356
357        if (mEnableNextOnConnection) {
358            if (hasNextButton()) {
359                final ConnectivityManager connectivity = (ConnectivityManager)
360                        activity.getSystemService(Context.CONNECTIVITY_SERVICE);
361                if (connectivity != null) {
362                    NetworkInfo info = connectivity.getNetworkInfo(
363                            ConnectivityManager.TYPE_WIFI);
364                    changeNextButtonState(info.isConnected());
365                }
366            }
367        }
368
369        addPreferencesFromResource(R.xml.wifi_settings);
370
371        if (mSetupWizardMode) {
372            getView().setSystemUiVisibility(
373//                    View.STATUS_BAR_DISABLE_BACK |
374                    View.STATUS_BAR_DISABLE_HOME |
375                    View.STATUS_BAR_DISABLE_RECENT |
376                    View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS |
377                    View.STATUS_BAR_DISABLE_CLOCK);
378        }
379
380        // On/off switch is hidden for Setup Wizard
381        if (!mSetupWizardMode) {
382            Switch actionBarSwitch = new Switch(activity);
383
384            if (activity instanceof PreferenceActivity) {
385                PreferenceActivity preferenceActivity = (PreferenceActivity) activity;
386                if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) {
387                    final int padding = activity.getResources().getDimensionPixelSize(
388                            R.dimen.action_bar_switch_padding);
389                    actionBarSwitch.setPaddingRelative(0, 0, padding, 0);
390                    activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
391                            ActionBar.DISPLAY_SHOW_CUSTOM);
392                    activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams(
393                            ActionBar.LayoutParams.WRAP_CONTENT,
394                            ActionBar.LayoutParams.WRAP_CONTENT,
395                            Gravity.CENTER_VERTICAL | Gravity.END));
396                }
397            }
398
399            mWifiEnabler = new WifiEnabler(activity, actionBarSwitch);
400        }
401
402        mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
403        getListView().setEmptyView(mEmptyView);
404
405        if (!mSetupWizardMode) {
406            registerForContextMenu(getListView());
407        }
408        setHasOptionsMenu(true);
409    }
410
411    @Override
412    public void onResume() {
413        super.onResume();
414        if (mWifiEnabler != null) {
415            mWifiEnabler.resume();
416        }
417
418        getActivity().registerReceiver(mReceiver, mFilter);
419        updateAccessPoints();
420    }
421
422    @Override
423    public void onPause() {
424        super.onPause();
425        if (mWifiEnabler != null) {
426            mWifiEnabler.pause();
427        }
428        getActivity().unregisterReceiver(mReceiver);
429        mScanner.pause();
430    }
431
432    @Override
433    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
434        final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
435        if (mSetupWizardMode) {
436            // FIXME: add setIcon() when graphics are available
437            menu.add(Menu.NONE, MENU_ID_WPS_PBC, 0, R.string.wifi_menu_wps_pbc)
438                    .setIcon(R.drawable.ic_wps)
439                    .setEnabled(wifiIsEnabled)
440                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
441            menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
442                    .setEnabled(wifiIsEnabled)
443                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
444        } else {
445            menu.add(Menu.NONE, MENU_ID_WPS_PBC, 0, R.string.wifi_menu_wps_pbc)
446                    .setIcon(R.drawable.ic_wps)
447                    .setEnabled(wifiIsEnabled)
448                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
449            menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
450                    .setIcon(R.drawable.ic_menu_add)
451                    .setEnabled(wifiIsEnabled)
452                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
453            menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan)
454                    //.setIcon(R.drawable.ic_menu_scan_network)
455                    .setEnabled(wifiIsEnabled)
456                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
457            menu.add(Menu.NONE, MENU_ID_WPS_PIN, 0, R.string.wifi_menu_wps_pin)
458                    .setEnabled(wifiIsEnabled)
459                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
460            if (mP2pSupported) {
461                menu.add(Menu.NONE, MENU_ID_P2P, 0, R.string.wifi_menu_p2p)
462                        .setEnabled(wifiIsEnabled)
463                        .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
464            }
465            menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
466                    //.setIcon(android.R.drawable.ic_menu_manage)
467                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
468        }
469        super.onCreateOptionsMenu(menu, inflater);
470    }
471
472    @Override
473    public void onSaveInstanceState(Bundle outState) {
474        super.onSaveInstanceState(outState);
475
476        // If the dialog is showing, save its state.
477        if (mDialog != null && mDialog.isShowing()) {
478            outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit);
479            if (mDlgAccessPoint != null) {
480                mAccessPointSavedState = new Bundle();
481                mDlgAccessPoint.saveWifiState(mAccessPointSavedState);
482                outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
483            }
484        }
485    }
486
487    @Override
488    public boolean onOptionsItemSelected(MenuItem item) {
489        switch (item.getItemId()) {
490            case MENU_ID_WPS_PBC:
491                showDialog(WPS_PBC_DIALOG_ID);
492                return true;
493            case MENU_ID_P2P:
494                if (getActivity() instanceof PreferenceActivity) {
495                    ((PreferenceActivity) getActivity()).startPreferencePanel(
496                            WifiP2pSettings.class.getCanonicalName(),
497                            null,
498                            R.string.wifi_p2p_settings_title, null,
499                            this, 0);
500                } else {
501                    startFragment(this, WifiP2pSettings.class.getCanonicalName(), -1, null);
502                }
503                return true;
504            case MENU_ID_WPS_PIN:
505                showDialog(WPS_PIN_DIALOG_ID);
506                return true;
507            case MENU_ID_SCAN:
508                if (mWifiManager.isWifiEnabled()) {
509                    mScanner.forceScan();
510                }
511                return true;
512            case MENU_ID_ADD_NETWORK:
513                if (mWifiManager.isWifiEnabled()) {
514                    onAddNetworkPressed();
515                }
516                return true;
517            case MENU_ID_ADVANCED:
518                if (getActivity() instanceof PreferenceActivity) {
519                    ((PreferenceActivity) getActivity()).startPreferencePanel(
520                            AdvancedWifiSettings.class.getCanonicalName(),
521                            null,
522                            R.string.wifi_advanced_titlebar, null,
523                            this, 0);
524                } else {
525                    startFragment(this, AdvancedWifiSettings.class.getCanonicalName(), -1, null);
526                }
527                return true;
528        }
529        return super.onOptionsItemSelected(item);
530    }
531
532    @Override
533    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
534        if (info instanceof AdapterContextMenuInfo) {
535            Preference preference = (Preference) getListView().getItemAtPosition(
536                    ((AdapterContextMenuInfo) info).position);
537
538            if (preference instanceof AccessPoint) {
539                mSelectedAccessPoint = (AccessPoint) preference;
540                menu.setHeaderTitle(mSelectedAccessPoint.ssid);
541                if (mSelectedAccessPoint.getLevel() != -1
542                        && mSelectedAccessPoint.getState() == null) {
543                    menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect);
544                }
545                if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
546                    menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget);
547                    menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify);
548                }
549            }
550        }
551    }
552
553    @Override
554    public boolean onContextItemSelected(MenuItem item) {
555        if (mSelectedAccessPoint == null) {
556            return super.onContextItemSelected(item);
557        }
558        switch (item.getItemId()) {
559            case MENU_ID_CONNECT: {
560                if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
561                    mWifiManager.connect(mSelectedAccessPoint.networkId,
562                            mConnectListener);
563                } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) {
564                    /** Bypass dialog for unsecured networks */
565                    mSelectedAccessPoint.generateOpenNetworkConfig();
566                    mWifiManager.connect(mSelectedAccessPoint.getConfig(),
567                            mConnectListener);
568                } else {
569                    showDialog(mSelectedAccessPoint, true);
570                }
571                return true;
572            }
573            case MENU_ID_FORGET: {
574                mWifiManager.forget(mSelectedAccessPoint.networkId, mForgetListener);
575                return true;
576            }
577            case MENU_ID_MODIFY: {
578                showDialog(mSelectedAccessPoint, true);
579                return true;
580            }
581        }
582        return super.onContextItemSelected(item);
583    }
584
585    @Override
586    public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
587        if (preference instanceof AccessPoint) {
588            mSelectedAccessPoint = (AccessPoint) preference;
589            /** Bypass dialog for unsecured, unsaved networks */
590            if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE &&
591                    mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
592                mSelectedAccessPoint.generateOpenNetworkConfig();
593                mWifiManager.connect(mSelectedAccessPoint.getConfig(), mConnectListener);
594            } else {
595                showDialog(mSelectedAccessPoint, false);
596            }
597        } else {
598            return super.onPreferenceTreeClick(screen, preference);
599        }
600        return true;
601    }
602
603    private void showDialog(AccessPoint accessPoint, boolean edit) {
604        if (mDialog != null) {
605            removeDialog(WIFI_DIALOG_ID);
606            mDialog = null;
607        }
608
609        // Save the access point and edit mode
610        mDlgAccessPoint = accessPoint;
611        mDlgEdit = edit;
612
613        showDialog(WIFI_DIALOG_ID);
614    }
615
616    @Override
617    public Dialog onCreateDialog(int dialogId) {
618        switch (dialogId) {
619            case WIFI_DIALOG_ID:
620                AccessPoint ap = mDlgAccessPoint; // For manual launch
621                if (ap == null) { // For re-launch from saved state
622                    if (mAccessPointSavedState != null) {
623                        ap = new AccessPoint(getActivity(), mAccessPointSavedState);
624                        // For repeated orientation changes
625                        mDlgAccessPoint = ap;
626                    }
627                }
628                // If it's still null, fine, it's for Add Network
629                mSelectedAccessPoint = ap;
630                mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit);
631                return mDialog;
632            case WPS_PBC_DIALOG_ID:
633                return new WpsDialog(getActivity(), WpsInfo.PBC);
634            case WPS_PIN_DIALOG_ID:
635                return new WpsDialog(getActivity(), WpsInfo.DISPLAY);
636            case WIFI_SKIPPED_DIALOG_ID:
637                return new AlertDialog.Builder(getActivity())
638                            .setMessage(R.string.wifi_skipped_message)
639                            .setCancelable(false)
640                            .setNegativeButton(R.string.wifi_skip_anyway,
641                                    new DialogInterface.OnClickListener() {
642                                @Override
643                                public void onClick(DialogInterface dialog, int id) {
644                                    getActivity().setResult(RESULT_SKIP);
645                                    getActivity().finish();
646                                }
647                            })
648                            .setPositiveButton(R.string.wifi_dont_skip,
649                                    new DialogInterface.OnClickListener() {
650                                @Override
651                                public void onClick(DialogInterface dialog, int id) {
652                                }
653                            })
654                            .create();
655            case WIFI_AND_MOBILE_SKIPPED_DIALOG_ID:
656                return new AlertDialog.Builder(getActivity())
657                            .setMessage(R.string.wifi_and_mobile_skipped_message)
658                            .setCancelable(false)
659                            .setNegativeButton(R.string.wifi_skip_anyway,
660                                    new DialogInterface.OnClickListener() {
661                                @Override
662                                public void onClick(DialogInterface dialog, int id) {
663                                    getActivity().setResult(RESULT_SKIP);
664                                    getActivity().finish();
665                                }
666                            })
667                            .setPositiveButton(R.string.wifi_dont_skip,
668                                    new DialogInterface.OnClickListener() {
669                                @Override
670                                public void onClick(DialogInterface dialog, int id) {
671                                }
672                            })
673                            .create();
674
675        }
676        return super.onCreateDialog(dialogId);
677    }
678
679    private boolean isPhone() {
680        final TelephonyManager telephonyManager = (TelephonyManager)this.getSystemService(
681                Context.TELEPHONY_SERVICE);
682        return telephonyManager != null
683                && telephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
684    }
685
686    /**
687    * Return true if there's any SIM related impediment to connectivity.
688    * Treats Unknown as OK. (Only returns true if we're sure of a SIM problem.)
689    */
690   protected boolean hasSimProblem() {
691       final TelephonyManager telephonyManager = (TelephonyManager)this.getSystemService(
692               Context.TELEPHONY_SERVICE);
693       return telephonyManager != null
694               && telephonyManager.getCurrentPhoneType() == TelephonyManager.PHONE_TYPE_GSM
695               && telephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY
696               && telephonyManager.getSimState() != TelephonyManager.SIM_STATE_UNKNOWN;
697   }
698
699    /**
700     * Shows the latest access points available with supplimental information like
701     * the strength of network and the security for it.
702     */
703    private void updateAccessPoints() {
704        // Safeguard from some delayed event handling
705        if (getActivity() == null) return;
706
707        final int wifiState = mWifiManager.getWifiState();
708
709        switch (wifiState) {
710            case WifiManager.WIFI_STATE_ENABLED:
711                // AccessPoints are automatically sorted with TreeSet.
712                final Collection<AccessPoint> accessPoints = constructAccessPoints();
713                getPreferenceScreen().removeAll();
714                if(accessPoints.size() == 0) {
715                    addMessagePreference(R.string.wifi_empty_list_wifi_on);
716                }
717                for (AccessPoint accessPoint : accessPoints) {
718                    getPreferenceScreen().addPreference(accessPoint);
719                }
720                break;
721
722            case WifiManager.WIFI_STATE_ENABLING:
723                getPreferenceScreen().removeAll();
724                break;
725
726            case WifiManager.WIFI_STATE_DISABLING:
727                addMessagePreference(R.string.wifi_stopping);
728                break;
729
730            case WifiManager.WIFI_STATE_DISABLED:
731                addMessagePreference(R.string.wifi_empty_list_wifi_off);
732                break;
733        }
734    }
735
736    private void addMessagePreference(int messageId) {
737        if (mEmptyView != null) mEmptyView.setText(messageId);
738        getPreferenceScreen().removeAll();
739    }
740
741    /** Returns sorted list of access points */
742    private List<AccessPoint> constructAccessPoints() {
743        ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
744        /** Lookup table to more quickly update AccessPoints by only considering objects with the
745         * correct SSID.  Maps SSID -> List of AccessPoints with the given SSID.  */
746        Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>();
747
748        final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
749        if (configs != null) {
750            for (WifiConfiguration config : configs) {
751                AccessPoint accessPoint = new AccessPoint(getActivity(), config);
752                accessPoint.update(mLastInfo, mLastState);
753                accessPoints.add(accessPoint);
754                apMap.put(accessPoint.ssid, accessPoint);
755            }
756        }
757
758        final List<ScanResult> results = mWifiManager.getScanResults();
759        if (results != null) {
760            for (ScanResult result : results) {
761                // Ignore hidden and ad-hoc networks.
762                if (result.SSID == null || result.SSID.length() == 0 ||
763                        result.capabilities.contains("[IBSS]")) {
764                    continue;
765                }
766
767                boolean found = false;
768                for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
769                    if (accessPoint.update(result))
770                        found = true;
771                }
772                if (!found) {
773                    AccessPoint accessPoint = new AccessPoint(getActivity(), result);
774                    accessPoints.add(accessPoint);
775                    apMap.put(accessPoint.ssid, accessPoint);
776                }
777            }
778        }
779
780        // Pre-sort accessPoints to speed preference insertion
781        Collections.sort(accessPoints);
782        return accessPoints;
783    }
784
785    /** A restricted multimap for use in constructAccessPoints */
786    private class Multimap<K,V> {
787        private HashMap<K,List<V>> store = new HashMap<K,List<V>>();
788        /** retrieve a non-null list of values with key K */
789        List<V> getAll(K key) {
790            List<V> values = store.get(key);
791            return values != null ? values : Collections.<V>emptyList();
792        }
793
794        void put(K key, V val) {
795            List<V> curVals = store.get(key);
796            if (curVals == null) {
797                curVals = new ArrayList<V>(3);
798                store.put(key, curVals);
799            }
800            curVals.add(val);
801        }
802    }
803
804    private void handleEvent(Context context, Intent intent) {
805        String action = intent.getAction();
806        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
807            updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
808                    WifiManager.WIFI_STATE_UNKNOWN));
809        } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||
810                WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||
811                WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
812                updateAccessPoints();
813        } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
814            //Ignore supplicant state changes when network is connected
815            //TODO: we should deprecate SUPPLICANT_STATE_CHANGED_ACTION and
816            //introduce a broadcast that combines the supplicant and network
817            //network state change events so the apps dont have to worry about
818            //ignoring supplicant state change when network is connected
819            //to get more fine grained information.
820            SupplicantState state = (SupplicantState) intent.getParcelableExtra(
821                    WifiManager.EXTRA_NEW_STATE);
822            if (!mConnected.get() && SupplicantState.isHandshakeState(state)) {
823                updateConnectionState(WifiInfo.getDetailedStateOf(state));
824             } else {
825                 // During a connect, we may have the supplicant
826                 // state change affect the detailed network state.
827                 // Make sure a lost connection is updated as well.
828                 updateConnectionState(null);
829             }
830        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
831            NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
832                    WifiManager.EXTRA_NETWORK_INFO);
833            mConnected.set(info.isConnected());
834            changeNextButtonState(info.isConnected());
835            updateAccessPoints();
836            updateConnectionState(info.getDetailedState());
837            if (mAutoFinishOnConnection && info.isConnected()) {
838                Activity activity = getActivity();
839                if (activity != null) {
840                    activity.setResult(Activity.RESULT_OK);
841                    activity.finish();
842                }
843                return;
844            }
845        } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
846            updateConnectionState(null);
847        }
848    }
849
850    private void updateConnectionState(DetailedState state) {
851        /* sticky broadcasts can call this when wifi is disabled */
852        if (!mWifiManager.isWifiEnabled()) {
853            mScanner.pause();
854            return;
855        }
856
857        if (state == DetailedState.OBTAINING_IPADDR) {
858            mScanner.pause();
859        } else {
860            mScanner.resume();
861        }
862
863        mLastInfo = mWifiManager.getConnectionInfo();
864        if (state != null) {
865            mLastState = state;
866        }
867
868        for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) {
869            // Maybe there's a WifiConfigPreference
870            Preference preference = getPreferenceScreen().getPreference(i);
871            if (preference instanceof AccessPoint) {
872                final AccessPoint accessPoint = (AccessPoint) preference;
873                accessPoint.update(mLastInfo, mLastState);
874            }
875        }
876    }
877
878    private void updateWifiState(int state) {
879        Activity activity = getActivity();
880        if (activity != null) {
881            activity.invalidateOptionsMenu();
882        }
883
884        switch (state) {
885            case WifiManager.WIFI_STATE_ENABLED:
886                mScanner.resume();
887                return; // not break, to avoid the call to pause() below
888
889            case WifiManager.WIFI_STATE_ENABLING:
890                addMessagePreference(R.string.wifi_starting);
891                break;
892
893            case WifiManager.WIFI_STATE_DISABLED:
894                addMessagePreference(R.string.wifi_empty_list_wifi_off);
895                break;
896        }
897
898        mLastInfo = null;
899        mLastState = null;
900        mScanner.pause();
901    }
902
903    private class Scanner extends Handler {
904        private int mRetry = 0;
905
906        void resume() {
907            if (!hasMessages(0)) {
908                sendEmptyMessage(0);
909            }
910        }
911
912        void forceScan() {
913            removeMessages(0);
914            sendEmptyMessage(0);
915        }
916
917        void pause() {
918            mRetry = 0;
919            removeMessages(0);
920        }
921
922        @Override
923        public void handleMessage(Message message) {
924            if (mWifiManager.startScan()) {
925                mRetry = 0;
926            } else if (++mRetry >= 3) {
927                mRetry = 0;
928                Activity activity = getActivity();
929                if (activity != null) {
930                    Toast.makeText(activity, R.string.wifi_fail_to_scan,
931                            Toast.LENGTH_LONG).show();
932                }
933                return;
934            }
935            sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);
936        }
937    }
938
939    /**
940     * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
941     * Wifi setup screens, not in usual wifi settings screen.
942     *
943     * @param connected true when the device is connected to a wifi network.
944     */
945    private void changeNextButtonState(boolean connected) {
946        if (mEnableNextOnConnection && hasNextButton()) {
947            getNextButton().setEnabled(connected);
948        }
949    }
950
951    @Override
952    public void onClick(DialogInterface dialogInterface, int button) {
953        if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
954            forget();
955        } else if (button == WifiDialog.BUTTON_SUBMIT) {
956            if (mDialog != null) {
957                submit(mDialog.getController());
958            }
959        }
960    }
961
962    /* package */ void submit(WifiConfigController configController) {
963
964        final WifiConfiguration config = configController.getConfig();
965
966        if (config == null) {
967            if (mSelectedAccessPoint != null
968                    && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
969                mWifiManager.connect(mSelectedAccessPoint.networkId,
970                        mConnectListener);
971            }
972        } else if (config.networkId != INVALID_NETWORK_ID) {
973            if (mSelectedAccessPoint != null) {
974                mWifiManager.save(config, mSaveListener);
975            }
976        } else {
977            if (configController.isEdit()) {
978                mWifiManager.save(config, mSaveListener);
979            } else {
980                mWifiManager.connect(config, mConnectListener);
981            }
982        }
983
984        if (mWifiManager.isWifiEnabled()) {
985            mScanner.resume();
986        }
987        updateAccessPoints();
988    }
989
990    /* package */ void forget() {
991        if (mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
992            // Should not happen, but a monkey seems to triger it
993            Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig());
994            return;
995        }
996
997        mWifiManager.forget(mSelectedAccessPoint.networkId, mForgetListener);
998
999        if (mWifiManager.isWifiEnabled()) {
1000            mScanner.resume();
1001        }
1002        updateAccessPoints();
1003
1004        // We need to rename/replace "Next" button in wifi setup context.
1005        changeNextButtonState(false);
1006    }
1007
1008    /**
1009     * Refreshes acccess points and ask Wifi module to scan networks again.
1010     */
1011    /* package */ void refreshAccessPoints() {
1012        if (mWifiManager.isWifiEnabled()) {
1013            mScanner.resume();
1014        }
1015
1016        getPreferenceScreen().removeAll();
1017    }
1018
1019    /**
1020     * Called when "add network" button is pressed.
1021     */
1022    /* package */ void onAddNetworkPressed() {
1023        // No exact access point is selected.
1024        mSelectedAccessPoint = null;
1025        showDialog(null, true);
1026    }
1027
1028    /* package */ int getAccessPointsCount() {
1029        final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
1030        if (wifiIsEnabled) {
1031            return getPreferenceScreen().getPreferenceCount();
1032        } else {
1033            return 0;
1034        }
1035    }
1036
1037    /**
1038     * Requests wifi module to pause wifi scan. May be ignored when the module is disabled.
1039     */
1040    /* package */ void pauseWifiScan() {
1041        if (mWifiManager.isWifiEnabled()) {
1042            mScanner.pause();
1043        }
1044    }
1045
1046    /**
1047     * Requests wifi module to resume wifi scan. May be ignored when the module is disabled.
1048     */
1049    /* package */ void resumeWifiScan() {
1050        if (mWifiManager.isWifiEnabled()) {
1051            mScanner.resume();
1052        }
1053    }
1054
1055    @Override
1056    protected int getHelpResource() {
1057        if (mSetupWizardMode) {
1058            return 0;
1059        }
1060        return R.string.help_url_wifi;
1061    }
1062
1063    /**
1064     * Used as the outer frame of all setup wizard pages that need to adjust their margins based
1065     * on the total size of the available display. (e.g. side margins set to 10% of total width.)
1066     */
1067    public static class ProportionalOuterFrame extends RelativeLayout {
1068        public ProportionalOuterFrame(Context context) {
1069            super(context);
1070        }
1071        public ProportionalOuterFrame(Context context, AttributeSet attrs) {
1072            super(context, attrs);
1073        }
1074        public ProportionalOuterFrame(Context context, AttributeSet attrs, int defStyle) {
1075            super(context, attrs, defStyle);
1076        }
1077
1078        /**
1079         * Set our margins and title area height proportionally to the available display size
1080         */
1081        @Override
1082        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1083            int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
1084            int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
1085            final Resources resources = getContext().getResources();
1086            float titleHeight = resources.getFraction(R.dimen.setup_title_height, 1, 1);
1087            float sideMargin = resources.getFraction(R.dimen.setup_border_width, 1, 1);
1088            int bottom = resources.getDimensionPixelSize(R.dimen.setup_margin_bottom);
1089            setPaddingRelative(
1090                    (int) (parentWidth * sideMargin),
1091                    0,
1092                    (int) (parentWidth * sideMargin),
1093                    bottom);
1094            View title = findViewById(R.id.title_area);
1095            if (title != null) {
1096                title.setMinimumHeight((int) (parentHeight * titleHeight));
1097            }
1098            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
1099        }
1100    }
1101
1102}
1103