1 /* 2 * Copyright (C) 2006 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; 18 19 import android.app.AlertDialog; 20 import android.content.ContentUris; 21 import android.content.ContentValues; 22 import android.content.Intent; 23 import android.content.SharedPreferences; 24 import android.content.res.Resources; 25 import android.database.Cursor; 26 import android.net.Uri; 27 import android.os.Bundle; 28 import android.os.SystemProperties; 29 import android.preference.EditTextPreference; 30 import android.preference.ListPreference; 31 import android.preference.Preference; 32 import android.preference.PreferenceActivity; 33 import android.provider.Telephony; 34 import android.util.Log; 35 import android.view.KeyEvent; 36 import android.view.Menu; 37 import android.view.MenuItem; 38 39 import com.android.internal.telephony.TelephonyProperties; 40 41 42 public class ApnEditor extends PreferenceActivity 43 implements SharedPreferences.OnSharedPreferenceChangeListener, 44 Preference.OnPreferenceChangeListener { 45 46 private final static String TAG = ApnEditor.class.getSimpleName(); 47 48 private final static String SAVED_POS = "pos"; 49 private final static String KEY_AUTH_TYPE = "auth_type"; 50 51 private static final int MENU_DELETE = Menu.FIRST; 52 private static final int MENU_SAVE = Menu.FIRST + 1; 53 private static final int MENU_CANCEL = Menu.FIRST + 2; 54 55 private static String sNotSet; 56 private EditTextPreference mName; 57 private EditTextPreference mApn; 58 private EditTextPreference mProxy; 59 private EditTextPreference mPort; 60 private EditTextPreference mUser; 61 private EditTextPreference mServer; 62 private EditTextPreference mPassword; 63 private EditTextPreference mMmsc; 64 private EditTextPreference mMcc; 65 private EditTextPreference mMnc; 66 private EditTextPreference mMmsProxy; 67 private EditTextPreference mMmsPort; 68 private ListPreference mAuthType; 69 private EditTextPreference mApnType; 70 71 private String mCurMnc; 72 private String mCurMcc; 73 74 private Uri mUri; 75 private Cursor mCursor; 76 private boolean mNewApn; 77 private boolean mFirstTime; 78 private Resources mRes; 79 80 /** 81 * Standard projection for the interesting columns of a normal note. 82 */ 83 private static final String[] sProjection = new String[] { 84 Telephony.Carriers._ID, // 0 85 Telephony.Carriers.NAME, // 1 86 Telephony.Carriers.APN, // 2 87 Telephony.Carriers.PROXY, // 3 88 Telephony.Carriers.PORT, // 4 89 Telephony.Carriers.USER, // 5 90 Telephony.Carriers.SERVER, // 6 91 Telephony.Carriers.PASSWORD, // 7 92 Telephony.Carriers.MMSC, // 8 93 Telephony.Carriers.MCC, // 9 94 Telephony.Carriers.MNC, // 10 95 Telephony.Carriers.NUMERIC, // 11 96 Telephony.Carriers.MMSPROXY,// 12 97 Telephony.Carriers.MMSPORT, // 13 98 Telephony.Carriers.AUTH_TYPE, // 14 99 Telephony.Carriers.TYPE, // 15 100 }; 101 102 private static final int ID_INDEX = 0; 103 private static final int NAME_INDEX = 1; 104 private static final int APN_INDEX = 2; 105 private static final int PROXY_INDEX = 3; 106 private static final int PORT_INDEX = 4; 107 private static final int USER_INDEX = 5; 108 private static final int SERVER_INDEX = 6; 109 private static final int PASSWORD_INDEX = 7; 110 private static final int MMSC_INDEX = 8; 111 private static final int MCC_INDEX = 9; 112 private static final int MNC_INDEX = 10; 113 private static final int MMSPROXY_INDEX = 12; 114 private static final int MMSPORT_INDEX = 13; 115 private static final int AUTH_TYPE_INDEX = 14; 116 private static final int TYPE_INDEX = 15; 117 118 119 @Override 120 protected void onCreate(Bundle icicle) { 121 super.onCreate(icicle); 122 123 addPreferencesFromResource(R.xml.apn_editor); 124 125 sNotSet = getResources().getString(R.string.apn_not_set); 126 mName = (EditTextPreference) findPreference("apn_name"); 127 mApn = (EditTextPreference) findPreference("apn_apn"); 128 mProxy = (EditTextPreference) findPreference("apn_http_proxy"); 129 mPort = (EditTextPreference) findPreference("apn_http_port"); 130 mUser = (EditTextPreference) findPreference("apn_user"); 131 mServer = (EditTextPreference) findPreference("apn_server"); 132 mPassword = (EditTextPreference) findPreference("apn_password"); 133 mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy"); 134 mMmsPort = (EditTextPreference) findPreference("apn_mms_port"); 135 mMmsc = (EditTextPreference) findPreference("apn_mmsc"); 136 mMcc = (EditTextPreference) findPreference("apn_mcc"); 137 mMnc = (EditTextPreference) findPreference("apn_mnc"); 138 mApnType = (EditTextPreference) findPreference("apn_type"); 139 140 mAuthType = (ListPreference) findPreference("auth_type"); 141 mAuthType.setOnPreferenceChangeListener(this); 142 143 mRes = getResources(); 144 145 final Intent intent = getIntent(); 146 final String action = intent.getAction(); 147 148 mFirstTime = icicle == null; 149 150 if (action.equals(Intent.ACTION_EDIT)) { 151 mUri = intent.getData(); 152 } else if (action.equals(Intent.ACTION_INSERT)) { 153 if (mFirstTime || icicle.getInt(SAVED_POS) == 0) { 154 mUri = getContentResolver().insert(intent.getData(), new ContentValues()); 155 } else { 156 mUri = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, 157 icicle.getInt(SAVED_POS)); 158 } 159 mNewApn = true; 160 // If we were unable to create a new note, then just finish 161 // this activity. A RESULT_CANCELED will be sent back to the 162 // original activity if they requested a result. 163 if (mUri == null) { 164 Log.w(TAG, "Failed to insert new telephony provider into " 165 + getIntent().getData()); 166 finish(); 167 return; 168 } 169 170 // The new entry was created, so assume all will end well and 171 // set the result to be returned. 172 setResult(RESULT_OK, (new Intent()).setAction(mUri.toString())); 173 174 } else { 175 finish(); 176 return; 177 } 178 179 mCursor = managedQuery(mUri, sProjection, null, null); 180 mCursor.moveToFirst(); 181 182 fillUi(); 183 } 184 185 @Override 186 public void onResume() { 187 super.onResume(); 188 getPreferenceScreen().getSharedPreferences() 189 .registerOnSharedPreferenceChangeListener(this); 190 } 191 192 @Override 193 public void onPause() { 194 getPreferenceScreen().getSharedPreferences() 195 .unregisterOnSharedPreferenceChangeListener(this); 196 super.onPause(); 197 } 198 199 private void fillUi() { 200 if (mFirstTime) { 201 mFirstTime = false; 202 // Fill in all the values from the db in both text editor and summary 203 mName.setText(mCursor.getString(NAME_INDEX)); 204 mApn.setText(mCursor.getString(APN_INDEX)); 205 mProxy.setText(mCursor.getString(PROXY_INDEX)); 206 mPort.setText(mCursor.getString(PORT_INDEX)); 207 mUser.setText(mCursor.getString(USER_INDEX)); 208 mServer.setText(mCursor.getString(SERVER_INDEX)); 209 mPassword.setText(mCursor.getString(PASSWORD_INDEX)); 210 mMmsProxy.setText(mCursor.getString(MMSPROXY_INDEX)); 211 mMmsPort.setText(mCursor.getString(MMSPORT_INDEX)); 212 mMmsc.setText(mCursor.getString(MMSC_INDEX)); 213 mMcc.setText(mCursor.getString(MCC_INDEX)); 214 mMnc.setText(mCursor.getString(MNC_INDEX)); 215 mApnType.setText(mCursor.getString(TYPE_INDEX)); 216 if (mNewApn) { 217 String numeric = 218 SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC); 219 // MCC is first 3 chars and then in 2 - 3 chars of MNC 220 if (numeric != null && numeric.length() > 4) { 221 // Country code 222 String mcc = numeric.substring(0, 3); 223 // Network code 224 String mnc = numeric.substring(3); 225 // Auto populate MNC and MCC for new entries, based on what SIM reports 226 mMcc.setText(mcc); 227 mMnc.setText(mnc); 228 mCurMnc = mnc; 229 mCurMcc = mcc; 230 } 231 } 232 int authVal = mCursor.getInt(AUTH_TYPE_INDEX); 233 if (authVal != -1) { 234 mAuthType.setValueIndex(authVal); 235 } 236 237 } 238 239 mName.setSummary(checkNull(mName.getText())); 240 mApn.setSummary(checkNull(mApn.getText())); 241 mProxy.setSummary(checkNull(mProxy.getText())); 242 mPort.setSummary(checkNull(mPort.getText())); 243 mUser.setSummary(checkNull(mUser.getText())); 244 mServer.setSummary(checkNull(mServer.getText())); 245 mPassword.setSummary(starify(mPassword.getText())); 246 mMmsProxy.setSummary(checkNull(mMmsProxy.getText())); 247 mMmsPort.setSummary(checkNull(mMmsPort.getText())); 248 mMmsc.setSummary(checkNull(mMmsc.getText())); 249 mMcc.setSummary(checkNull(mMcc.getText())); 250 mMnc.setSummary(checkNull(mMnc.getText())); 251 mApnType.setSummary(checkNull(mApnType.getText())); 252 253 String authVal = mAuthType.getValue(); 254 if (authVal != null) { 255 int authValIndex = Integer.parseInt(authVal); 256 mAuthType.setValueIndex(authValIndex); 257 258 String []values = mRes.getStringArray(R.array.apn_auth_entries); 259 mAuthType.setSummary(values[authValIndex]); 260 } else { 261 mAuthType.setSummary(sNotSet); 262 } 263 } 264 265 public boolean onPreferenceChange(Preference preference, Object newValue) { 266 String key = preference.getKey(); 267 if (KEY_AUTH_TYPE.equals(key)) { 268 try { 269 int index = Integer.parseInt((String) newValue); 270 mAuthType.setValueIndex(index); 271 272 String []values = mRes.getStringArray(R.array.apn_auth_entries); 273 mAuthType.setSummary(values[index]); 274 } catch (NumberFormatException e) { 275 return false; 276 } 277 } 278 return true; 279 } 280 281 @Override 282 public boolean onCreateOptionsMenu(Menu menu) { 283 super.onCreateOptionsMenu(menu); 284 // If it's a new APN, then cancel will delete the new entry in onPause 285 if (!mNewApn) { 286 menu.add(0, MENU_DELETE, 0, R.string.menu_delete) 287 .setIcon(android.R.drawable.ic_menu_delete); 288 } 289 menu.add(0, MENU_SAVE, 0, R.string.menu_save) 290 .setIcon(android.R.drawable.ic_menu_save); 291 menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel) 292 .setIcon(android.R.drawable.ic_menu_close_clear_cancel); 293 return true; 294 } 295 296 @Override 297 public boolean onOptionsItemSelected(MenuItem item) { 298 switch (item.getItemId()) { 299 case MENU_DELETE: 300 deleteApn(); 301 return true; 302 case MENU_SAVE: 303 if (validateAndSave(false)) { 304 finish(); 305 } 306 return true; 307 case MENU_CANCEL: 308 if (mNewApn) { 309 getContentResolver().delete(mUri, null, null); 310 } 311 finish(); 312 return true; 313 } 314 return super.onOptionsItemSelected(item); 315 } 316 317 @Override 318 public boolean onKeyDown(int keyCode, KeyEvent event) { 319 switch (keyCode) { 320 case KeyEvent.KEYCODE_BACK: { 321 if (validateAndSave(false)) { 322 finish(); 323 } 324 return true; 325 } 326 } 327 return super.onKeyDown(keyCode, event); 328 } 329 330 @Override 331 protected void onSaveInstanceState(Bundle icicle) { 332 super.onSaveInstanceState(icicle); 333 if (validateAndSave(true)) { 334 icicle.putInt(SAVED_POS, mCursor.getInt(ID_INDEX)); 335 } 336 } 337 338 /** 339 * Check the key fields' validity and save if valid. 340 * @param force save even if the fields are not valid, if the app is 341 * being suspended 342 * @return true if the data was saved 343 */ 344 private boolean validateAndSave(boolean force) { 345 String name = checkNotSet(mName.getText()); 346 String apn = checkNotSet(mApn.getText()); 347 String mcc = checkNotSet(mMcc.getText()); 348 String mnc = checkNotSet(mMnc.getText()); 349 350 String errorMsg = null; 351 if (name.length() < 1) { 352 errorMsg = mRes.getString(R.string.error_name_empty); 353 } else if (apn.length() < 1) { 354 errorMsg = mRes.getString(R.string.error_apn_empty); 355 } else if (mcc.length() != 3) { 356 errorMsg = mRes.getString(R.string.error_mcc_not3); 357 } else if ((mnc.length() & 0xFFFE) != 2) { 358 errorMsg = mRes.getString(R.string.error_mnc_not23); 359 } 360 361 if (errorMsg != null && !force) { 362 showErrorMessage(errorMsg); 363 return false; 364 } 365 366 if (!mCursor.moveToFirst()) { 367 Log.w(TAG, 368 "Could not go to the first row in the Cursor when saving data."); 369 return false; 370 } 371 372 // If it's a new APN and a name or apn haven't been entered, then erase the entry 373 if (force && mNewApn && name.length() < 1 && apn.length() < 1) { 374 getContentResolver().delete(mUri, null, null); 375 return false; 376 } 377 378 ContentValues values = new ContentValues(); 379 380 // Add a dummy name "Untitled", if the user exits the screen without adding a name but 381 // entered other information worth keeping. 382 values.put(Telephony.Carriers.NAME, 383 name.length() < 1 ? getResources().getString(R.string.untitled_apn) : name); 384 values.put(Telephony.Carriers.APN, apn); 385 values.put(Telephony.Carriers.PROXY, checkNotSet(mProxy.getText())); 386 values.put(Telephony.Carriers.PORT, checkNotSet(mPort.getText())); 387 values.put(Telephony.Carriers.MMSPROXY, checkNotSet(mMmsProxy.getText())); 388 values.put(Telephony.Carriers.MMSPORT, checkNotSet(mMmsPort.getText())); 389 values.put(Telephony.Carriers.USER, checkNotSet(mUser.getText())); 390 values.put(Telephony.Carriers.SERVER, checkNotSet(mServer.getText())); 391 values.put(Telephony.Carriers.PASSWORD, checkNotSet(mPassword.getText())); 392 values.put(Telephony.Carriers.MMSC, checkNotSet(mMmsc.getText())); 393 394 String authVal = mAuthType.getValue(); 395 if (authVal != null) { 396 values.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(authVal)); 397 } 398 399 values.put(Telephony.Carriers.TYPE, checkNotSet(mApnType.getText())); 400 401 values.put(Telephony.Carriers.MCC, mcc); 402 values.put(Telephony.Carriers.MNC, mnc); 403 404 values.put(Telephony.Carriers.NUMERIC, mcc + mnc); 405 406 if (mCurMnc != null && mCurMcc != null) { 407 if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) { 408 values.put(Telephony.Carriers.CURRENT, 1); 409 } 410 } 411 412 getContentResolver().update(mUri, values, null, null); 413 414 return true; 415 } 416 417 private void showErrorMessage(String message) { 418 new AlertDialog.Builder(this) 419 .setTitle(R.string.error_title) 420 .setMessage(message) 421 .setPositiveButton(android.R.string.ok, null) 422 .show(); 423 } 424 425 private void deleteApn() { 426 getContentResolver().delete(mUri, null, null); 427 finish(); 428 } 429 430 private String starify(String value) { 431 if (value == null || value.length() == 0) { 432 return sNotSet; 433 } else { 434 char[] password = new char[value.length()]; 435 for (int i = 0; i < password.length; i++) { 436 password[i] = '*'; 437 } 438 return new String(password); 439 } 440 } 441 442 private String checkNull(String value) { 443 if (value == null || value.length() == 0) { 444 return sNotSet; 445 } else { 446 return value; 447 } 448 } 449 450 private String checkNotSet(String value) { 451 if (value == null || value.equals(sNotSet)) { 452 return ""; 453 } else { 454 return value; 455 } 456 } 457 458 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 459 Preference pref = findPreference(key); 460 if (pref != null) { 461 if (pref.equals(mPassword)){ 462 pref.setSummary(starify(sharedPreferences.getString(key, ""))); 463 } else { 464 pref.setSummary(checkNull(sharedPreferences.getString(key, ""))); 465 } 466 } 467 } 468 } 469