WifiSettings.java revision eb48882e18ba9b1241a4d79c0cd45d6994b6dec6
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 } 196 197 @Override 198 protected void onDestroy() { 199 super.onDestroy(); 200 201 if (mDialog != null) { 202 mDialog.dismiss(); 203 } 204 } 205 206 @Override 207 public boolean onCreateOptionsMenu(Menu menu) { 208 super.onCreateOptionsMenu(menu); 209 210 menu.add(0, MENU_ID_SCAN, 0, R.string.scan_wifi) 211 .setIcon(R.drawable.ic_menu_scan_network); 212 213 menu.add(0, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced) 214 .setIcon(android.R.drawable.ic_menu_manage); 215 216 return true; 217 } 218 219 @Override 220 public boolean onOptionsItemSelected(MenuItem item) { 221 super.onOptionsItemSelected(item); 222 223 switch (item.getItemId()) { 224 225 case MENU_ID_SCAN: 226 mWifiLayer.attemptScan(); 227 return true; 228 229 case MENU_ID_ADVANCED: 230 Intent intent = new Intent(this, AdvancedSettings.class); 231 startActivity(intent); 232 return true; 233 234 default: 235 return false; 236 } 237 } 238 239 @Override 240 protected void onSaveInstanceState(Bundle outState) { 241 super.onSaveInstanceState(outState); 242 243 if (mDialog != null) { 244 Bundle dialogBundle = mDialog.onSaveInstanceState(); 245 outState.putBundle(INSTANCE_KEY_DIALOG_BUNDLE, dialogBundle); 246 } 247 } 248 249 @Override 250 protected void onRestoreInstanceState(Bundle state) { 251 super.onRestoreInstanceState(state); 252 253 Bundle dialogBundle = state.getBundle(INSTANCE_KEY_DIALOG_BUNDLE); 254 if (dialogBundle != null) { 255 mDialog = new AccessPointDialog(this, mWifiLayer); 256 mDialog.onRestoreInstanceState(dialogBundle); 257 showDialog(mDialog); 258 } 259 } 260 261 /** 262 * {@inheritDoc} 263 */ 264 public void onDismiss(DialogInterface dialog) { 265 if (dialog == mDialog) { 266 mDialog = null; 267 mResumeMode = -1; 268 } 269 } 270 271 @Override 272 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 273 super.onCreateContextMenu(menu, v, menuInfo); 274 275 AccessPointState state = getStateFromMenuInfo(menuInfo); 276 if (state == null) { 277 return; 278 } 279 menu.setHeaderTitle(state.ssid); 280 281 if (state.isConnectable()) { 282 menu.add(0, CONTEXT_MENU_ID_CONNECT, 0, R.string.wifi_context_menu_connect); 283 } 284 285 if (state.isForgetable()) { 286 menu.add(0, CONTEXT_MENU_ID_FORGET, 1, R.string.wifi_context_menu_forget); 287 288 if (state.hasPassword()) { 289 menu.add(0, CONTEXT_MENU_ID_CHANGE_PASSWORD, 2, 290 R.string.wifi_context_menu_change_password); 291 } 292 } 293 } 294 295 @Override 296 public boolean onContextItemSelected(MenuItem item) { 297 298 AccessPointState state = getStateFromMenuInfo(item.getMenuInfo()); 299 if (state == null) { 300 return false; 301 } 302 303 switch (item.getItemId()) { 304 305 case CONTEXT_MENU_ID_CONNECT: 306 connectToNetwork(state); 307 return true; 308 309 case CONTEXT_MENU_ID_FORGET: 310 mWifiLayer.forgetNetwork(state); 311 return true; 312 313 case CONTEXT_MENU_ID_CHANGE_PASSWORD: 314 showAccessPointDialog(state, AccessPointDialog.MODE_CONFIGURE); 315 return true; 316 317 default: 318 return false; 319 } 320 } 321 322 /** 323 * Decides what needs to happen to connect to a particular access point. If 324 * it is secured and doesn't already have a password, it will bring up a 325 * password box. Otherwise it will just connect. 326 */ 327 private void connectToNetwork(AccessPointState state) { 328 if (state.hasSecurity() && !state.hasPassword()) { 329 showAccessPointDialog(state, AccessPointDialog.MODE_INFO); 330 } else { 331 mWifiLayer.connectToNetwork(state); 332 } 333 } 334 335 private AccessPointState getStateFromMenuInfo(ContextMenuInfo menuInfo) { 336 if ((menuInfo == null) || !(menuInfo instanceof AdapterContextMenuInfo)) { 337 return null; 338 } 339 340 AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo; 341 Preference pref = (Preference) getPreferenceScreen().getRootAdapter().getItem( 342 adapterMenuInfo.position); 343 if (pref == null || !(pref instanceof AccessPointPreference)) { 344 return null; 345 } 346 347 return ((AccessPointPreference) pref).getAccessPointState(); 348 } 349 350 //============================ 351 // Preference callbacks 352 //============================ 353 354 @Override 355 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 356 super.onPreferenceTreeClick(preferenceScreen, preference); 357 358 if (preference == mAddOtherNetwork) { 359 showAddOtherNetworkDialog(); 360 } else if (preference == mOpenNetworkNotificationsEnabled) { 361 Settings.Secure.putInt(getContentResolver(), 362 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 363 mOpenNetworkNotificationsEnabled.isChecked() ? 1 : 0); 364 } else if (preference instanceof AccessPointPreference) { 365 AccessPointState state = ((AccessPointPreference) preference).getAccessPointState(); 366 showAccessPointDialog(state, AccessPointDialog.MODE_INFO); 367 } 368 369 return false; 370 } 371 372 //============================ 373 // Wifi-related 374 //============================ 375 376 public WifiLayer getWifiLayer() { 377 return mWifiLayer; 378 } 379 380 private void showAddOtherNetworkDialog() { 381 AccessPointDialog dialog = new AccessPointDialog(this, mWifiLayer); 382 dialog.setState(new AccessPointState(this)); 383 dialog.setMode(AccessPointDialog.MODE_CONFIGURE); 384 dialog.setTitle(R.string.wifi_add_other_network); 385 dialog.setAutoSecurityAllowed(false); 386 mResumeMode = AccessPointDialog.MODE_CONFIGURE; 387 showDialog(dialog); 388 } 389 390 public void showAccessPointDialog(AccessPointState state, int mode) { 391 if (state.isEnterprise() && mKeyStore.test() != KeyStore.NO_ERROR) { 392 Credentials.getInstance().unlock(this); 393 mResumeState = state; 394 mResumeMode = mode; 395 return; 396 } 397 AccessPointDialog dialog = new AccessPointDialog(this, mWifiLayer); 398 dialog.setMode(mode); 399 dialog.setState(state); 400 showDialog(dialog); 401 } 402 403 private void showDialog(Dialog dialog) { 404 // Have only one dialog open at a time 405 if (mDialog != null) { 406 mDialog.dismiss(); 407 } 408 409 mDialog = dialog; 410 if (dialog != null) { 411 dialog.setOnDismissListener(this); 412 dialog.show(); 413 } 414 } 415 416 //============================ 417 // Wifi callbacks 418 //============================ 419 420 public void onError(int messageResId) { 421 Toast.makeText(this, messageResId, Toast.LENGTH_LONG).show(); 422 } 423 424 public void onScanningStatusChanged(boolean started) { 425 mApCategory.setProgress(started); 426 } 427 428 public void onAccessPointSetChanged(AccessPointState ap, boolean added) { 429 430 AccessPointPreference pref = mAps.get(ap); 431 432 if (WifiLayer.LOGV) { 433 Log.v(TAG, "onAccessPointSetChanged with " + ap + " and " 434 + (added ? "added" : "removed") + ", found pref " + pref); 435 } 436 437 if (added) { 438 439 if (pref == null) { 440 pref = new AccessPointPreference(this, ap); 441 mAps.put(ap, pref); 442 } else { 443 pref.setEnabled(true); 444 } 445 446 mApCategory.addPreference(pref); 447 448 } else { 449 450 mAps.remove(ap); 451 452 if (pref != null) { 453 mApCategory.removePreference(pref); 454 } 455 456 } 457 } 458 459 public void onAccessPointsStateChanged(boolean enabled) { 460 if (enabled) { 461 mApCategory.setEnabled(true); 462 } else { 463 mApCategory.removeAll(); 464 mAps.clear(); 465 } 466 467 mAddOtherNetwork.setEnabled(enabled); 468 } 469 470 public void onRetryPassword(AccessPointState ap) { 471 472 if ((mDialog != null) && mDialog.isShowing()) { 473 // If we're already showing a dialog, ignore this request 474 return; 475 } 476 477 showAccessPointDialog(ap, AccessPointDialog.MODE_RETRY_PASSWORD); 478 } 479 480} 481