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