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