WifiSettings.java revision e304b9f4f7f5bf07522fcc05615d172abd6c52a3
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("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 280 menu.setHeaderTitle(state.getHumanReadableSsid()); 281 282 if (state.isConnectable()) { 283 menu.add(0, CONTEXT_MENU_ID_CONNECT, 0, R.string.wifi_context_menu_connect); 284 } 285 286 if (state.isForgetable()) { 287 menu.add(0, CONTEXT_MENU_ID_FORGET, 1, R.string.wifi_context_menu_forget); 288 289 if (state.hasPassword()) { 290 menu.add(0, CONTEXT_MENU_ID_CHANGE_PASSWORD, 2, 291 R.string.wifi_context_menu_change_password); 292 } 293 } 294 } 295 296 @Override 297 public boolean onContextItemSelected(MenuItem item) { 298 299 AccessPointState state = getStateFromMenuInfo(item.getMenuInfo()); 300 if (state == null) { 301 return false; 302 } 303 304 switch (item.getItemId()) { 305 306 case CONTEXT_MENU_ID_CONNECT: 307 connectToNetwork(state); 308 return true; 309 310 case CONTEXT_MENU_ID_FORGET: 311 mWifiLayer.forgetNetwork(state); 312 return true; 313 314 case CONTEXT_MENU_ID_CHANGE_PASSWORD: 315 showAccessPointDialog(state, AccessPointDialog.MODE_CONFIGURE); 316 return true; 317 318 default: 319 return false; 320 } 321 } 322 323 /** 324 * Decides what needs to happen to connect to a particular access point. If 325 * it is secured and doesn't already have a password, it will bring up a 326 * password box. Otherwise it will just connect. 327 */ 328 private void connectToNetwork(AccessPointState state) { 329 if (state.hasSecurity() && !state.hasPassword()) { 330 showAccessPointDialog(state, AccessPointDialog.MODE_INFO); 331 } else { 332 mWifiLayer.connectToNetwork(state); 333 } 334 } 335 336 private AccessPointState getStateFromMenuInfo(ContextMenuInfo menuInfo) { 337 if ((menuInfo == null) || !(menuInfo instanceof AdapterContextMenuInfo)) { 338 return null; 339 } 340 341 AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo; 342 Preference pref = (Preference) getPreferenceScreen().getRootAdapter().getItem( 343 adapterMenuInfo.position); 344 if (pref == null || !(pref instanceof AccessPointPreference)) { 345 return null; 346 } 347 348 return ((AccessPointPreference) pref).getAccessPointState(); 349 } 350 351 //============================ 352 // Preference callbacks 353 //============================ 354 355 @Override 356 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 357 super.onPreferenceTreeClick(preferenceScreen, preference); 358 359 if (preference == mAddOtherNetwork) { 360 showAddOtherNetworkDialog(); 361 } else if (preference == mOpenNetworkNotificationsEnabled) { 362 Settings.Secure.putInt(getContentResolver(), 363 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 364 mOpenNetworkNotificationsEnabled.isChecked() ? 1 : 0); 365 } else if (preference instanceof AccessPointPreference) { 366 AccessPointState state = ((AccessPointPreference) preference).getAccessPointState(); 367 showAccessPointDialog(state, AccessPointDialog.MODE_INFO); 368 } 369 370 return false; 371 } 372 373 //============================ 374 // Wifi-related 375 //============================ 376 377 public WifiLayer getWifiLayer() { 378 return mWifiLayer; 379 } 380 381 private void showAddOtherNetworkDialog() { 382 AccessPointDialog dialog = new AccessPointDialog(this, mWifiLayer); 383 dialog.setState(new AccessPointState(this)); 384 dialog.setMode(AccessPointDialog.MODE_CONFIGURE); 385 dialog.setTitle(R.string.wifi_add_other_network); 386 dialog.setAutoSecurityAllowed(false); 387 mResumeMode = AccessPointDialog.MODE_CONFIGURE; 388 showDialog(dialog); 389 } 390 391 public void showAccessPointDialog(AccessPointState state, int mode) { 392 if (state.isEnterprise() && mKeyStore.test() != KeyStore.NO_ERROR) { 393 Credentials.getInstance().unlock(this); 394 mResumeState = state; 395 mResumeMode = mode; 396 return; 397 } 398 AccessPointDialog dialog = new AccessPointDialog(this, mWifiLayer); 399 dialog.setMode(mode); 400 dialog.setState(state); 401 showDialog(dialog); 402 } 403 404 private void showDialog(Dialog dialog) { 405 // Have only one dialog open at a time 406 if (mDialog != null) { 407 mDialog.dismiss(); 408 } 409 410 mDialog = dialog; 411 if (dialog != null) { 412 dialog.setOnDismissListener(this); 413 dialog.show(); 414 } 415 } 416 417 //============================ 418 // Wifi callbacks 419 //============================ 420 421 public void onError(int messageResId) { 422 Toast.makeText(this, messageResId, Toast.LENGTH_LONG).show(); 423 } 424 425 public void onScanningStatusChanged(boolean started) { 426 mApCategory.setProgress(started); 427 } 428 429 public void onAccessPointSetChanged(AccessPointState ap, boolean added) { 430 431 AccessPointPreference pref = mAps.get(ap); 432 433 if (WifiLayer.LOGV) { 434 Log.v(TAG, "onAccessPointSetChanged with " + ap + " and " 435 + (added ? "added" : "removed") + ", found pref " + pref); 436 } 437 438 if (added) { 439 440 if (pref == null) { 441 pref = new AccessPointPreference(this, ap); 442 mAps.put(ap, pref); 443 } else { 444 pref.setEnabled(true); 445 } 446 447 mApCategory.addPreference(pref); 448 449 } else { 450 451 mAps.remove(ap); 452 453 if (pref != null) { 454 mApCategory.removePreference(pref); 455 } 456 457 } 458 } 459 460 public void onAccessPointsStateChanged(boolean enabled) { 461 if (enabled) { 462 mApCategory.setEnabled(true); 463 } else { 464 mApCategory.removeAll(); 465 mAps.clear(); 466 } 467 468 mAddOtherNetwork.setEnabled(enabled); 469 } 470 471 public void onRetryPassword(AccessPointState ap) { 472 473 if ((mDialog != null) && mDialog.isShowing()) { 474 // If we're already showing a dialog, ignore this request 475 return; 476 } 477 478 showAccessPointDialog(ap, AccessPointDialog.MODE_RETRY_PASSWORD); 479 } 480 481} 482