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