WifiSettings.java revision c9a663abf759fdce0006e88fdc7333cf00515345
1/*
2 * Copyright (C) 2007 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 com.android.settings.ProgressCategory;
20import com.android.settings.R;
21
22import android.app.Dialog;
23import android.content.DialogInterface;
24import android.content.Intent;
25import android.net.wifi.WifiManager;
26import android.os.Bundle;
27import android.preference.Preference;
28import android.preference.PreferenceActivity;
29import android.preference.PreferenceScreen;
30import android.preference.CheckBoxPreference;
31import android.provider.Settings;
32import android.security.Credentials;
33import android.security.KeyStore;
34import android.util.Log;
35import android.view.ContextMenu;
36import android.view.Menu;
37import android.view.MenuItem;
38import android.view.View;
39import android.view.ContextMenu.ContextMenuInfo;
40import android.widget.AdapterView;
41import android.widget.Toast;
42import android.widget.AdapterView.AdapterContextMenuInfo;
43
44import java.util.Set;
45import java.util.WeakHashMap;
46
47/**
48 * Settings screen for WiFi. This will be launched from the main system settings.
49 */
50public class WifiSettings extends PreferenceActivity implements WifiLayer.Callback,
51        DialogInterface.OnDismissListener {
52
53    private static final String TAG = "WifiSettings";
54
55    //============================
56    // Preference/activity member variables
57    //============================
58
59    private static final String INSTANCE_KEY_DIALOG_BUNDLE =
60            "com.android.settings.wifi.WifiSettings:dialogBundle";
61    /*
62     * We don't use Activity's dialog management because AlertDialog isn't fully
63     * able to change many of its features after it's been created, and the
64     * dialog management only creates once.
65     */
66    private Dialog mDialog;
67
68    private static final String KEY_ONLY_ACCESS_POINTS = "only_access_points";
69    private static final String KEY_ADD_OTHER_NETWORK = "add_other_network";
70
71    private static final int CONTEXT_MENU_ID_CONNECT = Menu.FIRST;
72    private static final int CONTEXT_MENU_ID_FORGET = Menu.FIRST + 1;
73    private static final int CONTEXT_MENU_ID_CHANGE_PASSWORD = Menu.FIRST + 2;
74
75    private static final int MENU_ID_SCAN = Menu.FIRST;
76    private static final int MENU_ID_ADVANCED = Menu.FIRST + 1;
77
78    private static final String KEY_WIFI_ENABLED = "wifi_enabled";
79    private static final String KEY_OPEN_NETWORK_NOTIFICATIONS_ENABLED =
80            "open_network_notifications_enabled";
81    private static final String KEY_ACCESS_POINTS = "access_points";
82
83    private ProgressCategory mApCategory;
84    private CheckBoxPreference mWifiEnabled;
85    private WifiEnabler mWifiEnabler;
86    private CheckBoxPreference mOpenNetworkNotificationsEnabled;
87    private Preference mAddOtherNetwork;
88
89    private WeakHashMap<AccessPointState, AccessPointPreference> mAps;
90
91    private KeyStore mKeyStore = KeyStore.getInstance();
92    private AccessPointState mResumeState = null;
93    private int mResumeMode;
94
95    //============================
96    // Wifi member variables
97    //============================
98
99    private WifiLayer mWifiLayer;
100
101    //============================
102    // Activity lifecycle
103    //============================
104
105    public WifiSettings() {
106        mAps = new WeakHashMap<AccessPointState, AccessPointPreference>();
107        mWifiLayer = new WifiLayer(this, this);
108    }
109
110    @Override
111    protected void onCreate(Bundle savedInstanceState) {
112        super.onCreate(savedInstanceState);
113
114        onCreatePreferences();
115        mWifiLayer.onCreate();
116
117        onCreatedWifi();
118        mWifiLayer.onCreatedCallback();
119    }
120
121    private int getPreferenceResource() {
122        if (getIntent().getBooleanExtra(KEY_ONLY_ACCESS_POINTS, false)) {
123            return R.xml.wifi_access_points;
124        } else {
125            return R.xml.wifi_settings;
126        }
127    }
128
129    /**
130     * Shouldn't have any dependency on the wifi layer.
131     */
132    private void onCreatePreferences() {
133        addPreferencesFromResource(getPreferenceResource());
134
135        final PreferenceScreen preferenceScreen = getPreferenceScreen();
136
137        mApCategory = (ProgressCategory) preferenceScreen.findPreference(KEY_ACCESS_POINTS);
138        // We don't want the ordering to be the order preferences are added,
139        // instead we want*:
140        //   1) preferred, visible APs
141        //   2) visible APs
142        //   3) preferred, APs out of range
143        //   * this ordering logic is in AccessPointPreference's compareTo
144        mApCategory.setOrderingAsAdded(false);
145
146        if (!getIntent().getBooleanExtra(KEY_ONLY_ACCESS_POINTS, false)) {
147            mWifiEnabled = (CheckBoxPreference) preferenceScreen.findPreference(KEY_WIFI_ENABLED);
148            mWifiEnabler = new WifiEnabler(this, (WifiManager) getSystemService(WIFI_SERVICE),
149                    mWifiEnabled);
150
151            mOpenNetworkNotificationsEnabled = (CheckBoxPreference) preferenceScreen
152                    .findPreference(KEY_OPEN_NETWORK_NOTIFICATIONS_ENABLED);
153            mOpenNetworkNotificationsEnabled.setChecked(Settings.Secure.getInt(getContentResolver(),
154                    Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0) == 1);
155        }
156
157        mAddOtherNetwork = preferenceScreen.findPreference(KEY_ADD_OTHER_NETWORK);
158
159        registerForContextMenu(getListView());
160    }
161
162    private void onCreatedWifi() {
163    }
164
165    @Override
166    protected void onResume() {
167        super.onResume();
168        mWifiLayer.onResume();
169        if (mWifiEnabler != null) {
170            mWifiEnabler.resume();
171        }
172        // do what we should have after keystore is unlocked.
173        if (mResumeState != null) {
174            if (mKeyStore.test() == KeyStore.NO_ERROR) {
175                showAccessPointDialog(mResumeState, mResumeMode);
176            }
177            mResumeMode = -1;
178            mResumeState = null;
179        } else {
180            if (mResumeMode == AccessPointDialog.MODE_CONFIGURE) {
181                if (mKeyStore.test() == KeyStore.NO_ERROR) {
182                    ((AccessPointDialog) mDialog).enableEnterpriseFields();
183                }
184            }
185        }
186    }
187
188    @Override
189    protected void onPause() {
190        super.onPause();
191        mWifiLayer.onPause();
192        if (mWifiEnabler != null) {
193            mWifiEnabler.pause();
194        }
195        if (mDialog != null) {
196            mDialog.dismiss();
197        }
198    }
199
200    @Override
201    protected void onDestroy() {
202        super.onDestroy();
203
204        if (mDialog != null) {
205            mDialog.dismiss();
206        }
207    }
208
209    @Override
210    public boolean onCreateOptionsMenu(Menu menu) {
211        super.onCreateOptionsMenu(menu);
212
213        menu.add(0, MENU_ID_SCAN, 0, R.string.scan_wifi)
214            .setIcon(R.drawable.ic_menu_scan_network);
215
216        menu.add(0, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
217            .setIcon(android.R.drawable.ic_menu_manage);
218
219        return true;
220    }
221
222    @Override
223    public boolean onOptionsItemSelected(MenuItem item) {
224        super.onOptionsItemSelected(item);
225
226        switch (item.getItemId()) {
227
228            case MENU_ID_SCAN:
229                mWifiLayer.attemptScan();
230                return true;
231
232            case MENU_ID_ADVANCED:
233                Intent intent = new Intent(this, AdvancedSettings.class);
234                startActivity(intent);
235                return true;
236
237            default:
238                return false;
239        }
240    }
241
242    @Override
243    protected void onSaveInstanceState(Bundle outState) {
244        super.onSaveInstanceState(outState);
245
246        if (mDialog != null) {
247            Bundle dialogBundle = mDialog.onSaveInstanceState();
248            outState.putBundle(INSTANCE_KEY_DIALOG_BUNDLE, dialogBundle);
249        }
250    }
251
252    @Override
253    protected void onRestoreInstanceState(Bundle state) {
254        super.onRestoreInstanceState(state);
255
256        Bundle dialogBundle = state.getBundle(INSTANCE_KEY_DIALOG_BUNDLE);
257        if (dialogBundle != null) {
258            mDialog = new AccessPointDialog(this, mWifiLayer);
259            mDialog.onRestoreInstanceState(dialogBundle);
260            showDialog(mDialog);
261        }
262    }
263
264    /**
265     * {@inheritDoc}
266     */
267    public void onDismiss(DialogInterface dialog) {
268        if (dialog == mDialog) {
269            mDialog = null;
270            mResumeMode = -1;
271        }
272    }
273
274    @Override
275    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
276        super.onCreateContextMenu(menu, v, menuInfo);
277
278        AccessPointState state = getStateFromMenuInfo(menuInfo);
279        if (state == null) {
280            return;
281        }
282        menu.setHeaderTitle(state.ssid);
283
284        if (state.isConnectable()) {
285            menu.add(0, CONTEXT_MENU_ID_CONNECT, 0, R.string.wifi_context_menu_connect);
286        }
287
288        if (state.isForgetable()) {
289            menu.add(0, CONTEXT_MENU_ID_FORGET, 1, R.string.wifi_context_menu_forget);
290
291            if (state.hasPassword()) {
292                menu.add(0, CONTEXT_MENU_ID_CHANGE_PASSWORD, 2,
293                        R.string.wifi_context_menu_change_password);
294            }
295        }
296    }
297
298    @Override
299    public boolean onContextItemSelected(MenuItem item) {
300
301        AccessPointState state = getStateFromMenuInfo(item.getMenuInfo());
302        if (state == null) {
303            return false;
304        }
305
306        switch (item.getItemId()) {
307
308            case CONTEXT_MENU_ID_CONNECT:
309                connectToNetwork(state);
310                return true;
311
312            case CONTEXT_MENU_ID_FORGET:
313                mWifiLayer.forgetNetwork(state);
314                return true;
315
316            case CONTEXT_MENU_ID_CHANGE_PASSWORD:
317                showAccessPointDialog(state, AccessPointDialog.MODE_CONFIGURE);
318                return true;
319
320            default:
321                return false;
322        }
323    }
324
325    /**
326     * Decides what needs to happen to connect to a particular access point. If
327     * it is secured and doesn't already have a password, it will bring up a
328     * password box. Otherwise it will just connect.
329     */
330    private void connectToNetwork(AccessPointState state) {
331        if (state.hasSecurity() && !state.hasPassword()) {
332            showAccessPointDialog(state, AccessPointDialog.MODE_INFO);
333        } else {
334            mWifiLayer.connectToNetwork(state);
335        }
336    }
337
338    private AccessPointState getStateFromMenuInfo(ContextMenuInfo menuInfo) {
339        if ((menuInfo == null) || !(menuInfo instanceof AdapterContextMenuInfo)) {
340            return null;
341        }
342
343        AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
344        Preference pref = (Preference) getPreferenceScreen().getRootAdapter().getItem(
345                adapterMenuInfo.position);
346        if (pref == null || !(pref instanceof AccessPointPreference)) {
347            return null;
348        }
349
350        return ((AccessPointPreference) pref).getAccessPointState();
351    }
352
353    //============================
354    // Preference callbacks
355    //============================
356
357    @Override
358    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
359        super.onPreferenceTreeClick(preferenceScreen, preference);
360
361        if (preference == mAddOtherNetwork) {
362            showAddOtherNetworkDialog();
363        } else if (preference == mOpenNetworkNotificationsEnabled) {
364            Settings.Secure.putInt(getContentResolver(),
365                Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
366                mOpenNetworkNotificationsEnabled.isChecked() ? 1 : 0);
367        } else if (preference instanceof AccessPointPreference) {
368            AccessPointState state = ((AccessPointPreference) preference).getAccessPointState();
369            showAccessPointDialog(state, AccessPointDialog.MODE_INFO);
370        }
371
372        return false;
373    }
374
375    //============================
376    // Wifi-related
377    //============================
378
379    public WifiLayer getWifiLayer() {
380        return mWifiLayer;
381    }
382
383    private void showAddOtherNetworkDialog() {
384        AccessPointDialog dialog = new AccessPointDialog(this, mWifiLayer);
385        dialog.setState(new AccessPointState(this));
386        dialog.setMode(AccessPointDialog.MODE_CONFIGURE);
387        dialog.setTitle(R.string.wifi_add_other_network);
388        dialog.setAutoSecurityAllowed(false);
389        mResumeMode = AccessPointDialog.MODE_CONFIGURE;
390        showDialog(dialog);
391    }
392
393    public void showAccessPointDialog(AccessPointState state, int mode) {
394        if (state.isEnterprise() && mKeyStore.test() != KeyStore.NO_ERROR) {
395            Credentials.getInstance().unlock(this);
396            mResumeState = state;
397            mResumeMode = mode;
398            return;
399        }
400        AccessPointDialog dialog = new AccessPointDialog(this, mWifiLayer);
401        dialog.setMode(mode);
402        dialog.setState(state);
403        showDialog(dialog);
404    }
405
406    private void showDialog(Dialog dialog) {
407        // Have only one dialog open at a time
408        if (mDialog != null) {
409            mDialog.dismiss();
410        }
411
412        mDialog = dialog;
413        if (dialog != null) {
414            dialog.setOnDismissListener(this);
415            dialog.show();
416        }
417    }
418
419    //============================
420    // Wifi callbacks
421    //============================
422
423    public void onError(int messageResId) {
424        Toast.makeText(this, messageResId, Toast.LENGTH_LONG).show();
425    }
426
427    public void onScanningStatusChanged(boolean started) {
428        mApCategory.setProgress(started);
429    }
430
431    public void onAccessPointSetChanged(AccessPointState ap, boolean added) {
432
433        AccessPointPreference pref = mAps.get(ap);
434
435        if (WifiLayer.LOGV) {
436            Log.v(TAG, "onAccessPointSetChanged with " + ap + " and "
437                    + (added ? "added" : "removed") + ", found pref " + pref);
438        }
439
440        if (added) {
441
442            if (pref == null) {
443                pref = new AccessPointPreference(this, ap);
444                mAps.put(ap, pref);
445            } else {
446                pref.setEnabled(true);
447            }
448
449            mApCategory.addPreference(pref);
450
451        } else {
452
453            mAps.remove(ap);
454
455            if (pref != null) {
456                mApCategory.removePreference(pref);
457            }
458
459        }
460    }
461
462    public void onAccessPointsStateChanged(boolean enabled) {
463        if (enabled) {
464            mApCategory.setEnabled(true);
465        } else {
466            mApCategory.removeAll();
467            mAps.clear();
468        }
469
470        mAddOtherNetwork.setEnabled(enabled);
471    }
472
473    public void onRetryPassword(AccessPointState ap) {
474
475        if ((mDialog != null) && mDialog.isShowing()) {
476            // If we're already showing a dialog, ignore this request
477            return;
478        }
479
480        showAccessPointDialog(ap, AccessPointDialog.MODE_RETRY_PASSWORD);
481    }
482
483}
484