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