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