Home | History | Annotate | Download | only in sip
      1 /*
      2  * Copyright (C) 2010 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.phone.sip;
     18 
     19 import com.android.internal.telephony.CallManager;
     20 import com.android.internal.telephony.Phone;
     21 import com.android.phone.R;
     22 import com.android.phone.SipUtil;
     23 
     24 import android.app.ActionBar;
     25 import android.app.AlertDialog;
     26 import android.content.Intent;
     27 import android.net.sip.SipManager;
     28 import android.net.sip.SipProfile;
     29 import android.os.Bundle;
     30 import android.os.Parcelable;
     31 import android.preference.CheckBoxPreference;
     32 import android.preference.EditTextPreference;
     33 import android.preference.ListPreference;
     34 import android.preference.Preference;
     35 import android.preference.PreferenceActivity;
     36 import android.preference.PreferenceGroup;
     37 import android.text.TextUtils;
     38 import android.util.Log;
     39 import android.view.KeyEvent;
     40 import android.view.Menu;
     41 import android.view.MenuItem;
     42 import android.view.View;
     43 import android.widget.Button;
     44 import android.widget.Toast;
     45 
     46 import java.io.IOException;
     47 import java.lang.reflect.Method;
     48 import java.util.Arrays;
     49 
     50 /**
     51  * The activity class for editing a new or existing SIP profile.
     52  */
     53 public class SipEditor extends PreferenceActivity
     54         implements Preference.OnPreferenceChangeListener {
     55     private static final int MENU_SAVE = Menu.FIRST;
     56     private static final int MENU_DISCARD = Menu.FIRST + 1;
     57     private static final int MENU_REMOVE = Menu.FIRST + 2;
     58 
     59     private static final String TAG = SipEditor.class.getSimpleName();
     60     private static final String KEY_PROFILE = "profile";
     61     private static final String GET_METHOD_PREFIX = "get";
     62     private static final char SCRAMBLED = '*';
     63     private static final int NA = 0;
     64 
     65     private PrimaryAccountSelector mPrimaryAccountSelector;
     66     private AdvancedSettings mAdvancedSettings;
     67     private SipSharedPreferences mSharedPreferences;
     68     private boolean mDisplayNameSet;
     69     private boolean mHomeButtonClicked;
     70     private boolean mUpdateRequired;
     71     private boolean mUpdatedFieldIsEmpty;
     72 
     73     private SipManager mSipManager;
     74     private SipProfileDb mProfileDb;
     75     private SipProfile mOldProfile;
     76     private CallManager mCallManager;
     77     private Button mRemoveButton;
     78 
     79     enum PreferenceKey {
     80         Username(R.string.username, 0, R.string.default_preference_summary),
     81         Password(R.string.password, 0, R.string.default_preference_summary),
     82         DomainAddress(R.string.domain_address, 0, R.string.default_preference_summary),
     83         DisplayName(R.string.display_name, 0, R.string.display_name_summary),
     84         ProxyAddress(R.string.proxy_address, 0, R.string.optional_summary),
     85         Port(R.string.port, R.string.default_port, R.string.default_port),
     86         Transport(R.string.transport, R.string.default_transport, NA),
     87         SendKeepAlive(R.string.send_keepalive, R.string.sip_system_decide, NA),
     88         AuthUserName(R.string.auth_username, 0, R.string.optional_summary);
     89 
     90         final int text;
     91         final int initValue;
     92         final int defaultSummary;
     93         Preference preference;
     94 
     95         /**
     96          * @param key The key name of the preference.
     97          * @param initValue The initial value of the preference.
     98          * @param defaultSummary The default summary value of the preference
     99          *        when the preference value is empty.
    100          */
    101         PreferenceKey(int text, int initValue, int defaultSummary) {
    102             this.text = text;
    103             this.initValue = initValue;
    104             this.defaultSummary = defaultSummary;
    105         }
    106 
    107         String getValue() {
    108             if (preference instanceof EditTextPreference) {
    109                 return ((EditTextPreference) preference).getText();
    110             } else if (preference instanceof ListPreference) {
    111                 return ((ListPreference) preference).getValue();
    112             }
    113             throw new RuntimeException("getValue() for the preference " + this);
    114         }
    115 
    116         void setValue(String value) {
    117             if (preference instanceof EditTextPreference) {
    118                 String oldValue = getValue();
    119                 ((EditTextPreference) preference).setText(value);
    120                 if (this != Password) {
    121                     Log.v(TAG, this + ": setValue() " + value + ": " + oldValue
    122                             + " --> " + getValue());
    123                 }
    124             } else if (preference instanceof ListPreference) {
    125                 ((ListPreference) preference).setValue(value);
    126             }
    127 
    128             if (TextUtils.isEmpty(value)) {
    129                 preference.setSummary(defaultSummary);
    130             } else if (this == Password) {
    131                 preference.setSummary(scramble(value));
    132             } else if ((this == DisplayName)
    133                     && value.equals(getDefaultDisplayName())) {
    134                 preference.setSummary(defaultSummary);
    135             } else {
    136                 preference.setSummary(value);
    137             }
    138         }
    139     }
    140 
    141     @Override
    142     public void onResume() {
    143         super.onResume();
    144         mHomeButtonClicked = false;
    145         if (mCallManager.getState() != Phone.State.IDLE) {
    146             mAdvancedSettings.show();
    147             getPreferenceScreen().setEnabled(false);
    148             if (mRemoveButton != null) mRemoveButton.setEnabled(false);
    149         } else {
    150             getPreferenceScreen().setEnabled(true);
    151             if (mRemoveButton != null) mRemoveButton.setEnabled(true);
    152         }
    153     }
    154 
    155     @Override
    156     public void onCreate(Bundle savedInstanceState) {
    157         Log.v(TAG, "start profile editor");
    158         super.onCreate(savedInstanceState);
    159 
    160         mSipManager = SipManager.newInstance(this);
    161         mSharedPreferences = new SipSharedPreferences(this);
    162         mProfileDb = new SipProfileDb(this);
    163         mCallManager = CallManager.getInstance();
    164 
    165         setContentView(R.layout.sip_settings_ui);
    166         addPreferencesFromResource(R.xml.sip_edit);
    167 
    168         SipProfile p = mOldProfile = (SipProfile) ((savedInstanceState == null)
    169                 ? getIntent().getParcelableExtra(SipSettings.KEY_SIP_PROFILE)
    170                 : savedInstanceState.getParcelable(KEY_PROFILE));
    171 
    172         PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen();
    173         for (int i = 0, n = screen.getPreferenceCount(); i < n; i++) {
    174             setupPreference(screen.getPreference(i));
    175         }
    176 
    177         if (p == null) {
    178             screen.setTitle(R.string.sip_edit_new_title);
    179         }
    180 
    181         mAdvancedSettings = new AdvancedSettings();
    182         mPrimaryAccountSelector = new PrimaryAccountSelector(p);
    183 
    184         loadPreferencesFromProfile(p);
    185     }
    186 
    187     @Override
    188     public void onPause() {
    189         Log.v(TAG, "SipEditor onPause(): finishing? " + isFinishing());
    190         if (!isFinishing()) {
    191             mHomeButtonClicked = true;
    192             validateAndSetResult();
    193         }
    194         super.onPause();
    195     }
    196 
    197     @Override
    198     public boolean onCreateOptionsMenu(Menu menu) {
    199         super.onCreateOptionsMenu(menu);
    200         menu.add(0, MENU_DISCARD, 0, R.string.sip_menu_discard)
    201                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    202         menu.add(0, MENU_SAVE, 0, R.string.sip_menu_save)
    203                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    204         menu.add(0, MENU_REMOVE, 0, R.string.remove_sip_account)
    205                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    206         return true;
    207     }
    208 
    209     @Override
    210     public boolean onPrepareOptionsMenu(Menu menu) {
    211         MenuItem removeMenu = menu.findItem(MENU_REMOVE);
    212         removeMenu.setVisible(mOldProfile != null);
    213         menu.findItem(MENU_SAVE).setEnabled(mUpdateRequired);
    214         return super.onPrepareOptionsMenu(menu);
    215     }
    216 
    217     @Override
    218     public boolean onOptionsItemSelected(MenuItem item) {
    219         switch (item.getItemId()) {
    220             case MENU_SAVE:
    221                 validateAndSetResult();
    222                 return true;
    223 
    224             case MENU_DISCARD:
    225                 finish();
    226                 return true;
    227 
    228             case MENU_REMOVE: {
    229                 setRemovedProfileAndFinish();
    230                 return true;
    231             }
    232         }
    233         return super.onOptionsItemSelected(item);
    234     }
    235 
    236     @Override
    237     public boolean onKeyDown(int keyCode, KeyEvent event) {
    238         switch (keyCode) {
    239             case KeyEvent.KEYCODE_BACK:
    240                 validateAndSetResult();
    241                 return true;
    242         }
    243         return super.onKeyDown(keyCode, event);
    244     }
    245 
    246     private void saveAndRegisterProfile(SipProfile p) throws IOException {
    247         if (p == null) return;
    248         mProfileDb.saveProfile(p);
    249         if (p.getAutoRegistration()
    250                 || mSharedPreferences.isPrimaryAccount(p.getUriString())) {
    251             try {
    252                 mSipManager.open(p, SipUtil.createIncomingCallPendingIntent(),
    253                         null);
    254             } catch (Exception e) {
    255                 Log.e(TAG, "register failed: " + p.getUriString(), e);
    256             }
    257         }
    258     }
    259 
    260     private void deleteAndUnregisterProfile(SipProfile p) {
    261         if (p == null) return;
    262         mProfileDb.deleteProfile(p);
    263         unregisterProfile(p.getUriString());
    264     }
    265 
    266     private void unregisterProfile(String uri) {
    267         try {
    268             mSipManager.close(uri);
    269         } catch (Exception e) {
    270             Log.e(TAG, "unregister failed: " + uri, e);
    271         }
    272     }
    273 
    274     private void setRemovedProfileAndFinish() {
    275         Intent intent = new Intent(this, SipSettings.class);
    276         setResult(RESULT_FIRST_USER, intent);
    277         Toast.makeText(this, R.string.removing_account, Toast.LENGTH_SHORT)
    278                 .show();
    279         replaceProfile(mOldProfile, null);
    280         // do finish() in replaceProfile() in a background thread
    281     }
    282 
    283     private void showAlert(Throwable e) {
    284         String msg = e.getMessage();
    285         if (TextUtils.isEmpty(msg)) msg = e.toString();
    286         showAlert(msg);
    287     }
    288 
    289     private void showAlert(final String message) {
    290         if (mHomeButtonClicked) {
    291             Log.v(TAG, "Home button clicked, don't show dialog: " + message);
    292             return;
    293         }
    294         runOnUiThread(new Runnable() {
    295             @Override
    296             public void run() {
    297                 new AlertDialog.Builder(SipEditor.this)
    298                         .setTitle(android.R.string.dialog_alert_title)
    299                         .setIconAttribute(android.R.attr.alertDialogIcon)
    300                         .setMessage(message)
    301                         .setPositiveButton(R.string.alert_dialog_ok, null)
    302                         .show();
    303             }
    304         });
    305     }
    306 
    307     private boolean isEditTextEmpty(PreferenceKey key) {
    308         EditTextPreference pref = (EditTextPreference) key.preference;
    309         return TextUtils.isEmpty(pref.getText())
    310                 || pref.getSummary().equals(getString(key.defaultSummary));
    311     }
    312 
    313     private void validateAndSetResult() {
    314         boolean allEmpty = true;
    315         CharSequence firstEmptyFieldTitle = null;
    316         for (PreferenceKey key : PreferenceKey.values()) {
    317             Preference p = key.preference;
    318             if (p instanceof EditTextPreference) {
    319                 EditTextPreference pref = (EditTextPreference) p;
    320                 boolean fieldEmpty = isEditTextEmpty(key);
    321                 if (allEmpty && !fieldEmpty) allEmpty = false;
    322 
    323                 // use default value if display name is empty
    324                 if (fieldEmpty) {
    325                     switch (key) {
    326                         case DisplayName:
    327                             pref.setText(getDefaultDisplayName());
    328                             break;
    329                         case AuthUserName:
    330                         case ProxyAddress:
    331                             // optional; do nothing
    332                             break;
    333                         case Port:
    334                             pref.setText(getString(R.string.default_port));
    335                             break;
    336                         default:
    337                             if (firstEmptyFieldTitle == null) {
    338                                 firstEmptyFieldTitle = pref.getTitle();
    339                             }
    340                     }
    341                 } else if (key == PreferenceKey.Port) {
    342                     int port = Integer.parseInt(PreferenceKey.Port.getValue());
    343                     if ((port < 1000) || (port > 65534)) {
    344                         showAlert(getString(R.string.not_a_valid_port));
    345                         return;
    346                     }
    347                 }
    348             }
    349         }
    350 
    351         if (allEmpty || !mUpdateRequired) {
    352             finish();
    353             return;
    354         } else if (firstEmptyFieldTitle != null) {
    355             showAlert(getString(R.string.empty_alert, firstEmptyFieldTitle));
    356             return;
    357         }
    358         try {
    359             SipProfile profile = createSipProfile();
    360             Intent intent = new Intent(this, SipSettings.class);
    361             intent.putExtra(SipSettings.KEY_SIP_PROFILE, (Parcelable) profile);
    362             setResult(RESULT_OK, intent);
    363             Toast.makeText(this, R.string.saving_account, Toast.LENGTH_SHORT)
    364                     .show();
    365 
    366             replaceProfile(mOldProfile, profile);
    367             // do finish() in replaceProfile() in a background thread
    368         } catch (Exception e) {
    369             Log.w(TAG, "Can not create new SipProfile", e);
    370             showAlert(e);
    371         }
    372     }
    373 
    374     private void unregisterOldPrimaryAccount() {
    375         String primaryAccountUri = mSharedPreferences.getPrimaryAccount();
    376         Log.v(TAG, "old primary: " + primaryAccountUri);
    377         if ((primaryAccountUri != null)
    378                 && !mSharedPreferences.isReceivingCallsEnabled()) {
    379             Log.v(TAG, "unregister old primary: " + primaryAccountUri);
    380             unregisterProfile(primaryAccountUri);
    381         }
    382     }
    383 
    384     private void replaceProfile(final SipProfile oldProfile,
    385             final SipProfile newProfile) {
    386         // Replace profile in a background thread as it takes time to access the
    387         // storage; do finish() once everything goes fine.
    388         // newProfile may be null if the old profile is to be deleted rather
    389         // than being modified.
    390         new Thread(new Runnable() {
    391             public void run() {
    392                 try {
    393                     // if new profile is primary, unregister the old primary account
    394                     if ((newProfile != null) && mPrimaryAccountSelector.isSelected()) {
    395                         unregisterOldPrimaryAccount();
    396                     }
    397 
    398                     mPrimaryAccountSelector.commit(newProfile);
    399                     deleteAndUnregisterProfile(oldProfile);
    400                     saveAndRegisterProfile(newProfile);
    401                     finish();
    402                 } catch (Exception e) {
    403                     Log.e(TAG, "Can not save/register new SipProfile", e);
    404                     showAlert(e);
    405                 }
    406             }
    407         }, "SipEditor").start();
    408     }
    409 
    410     private String getProfileName() {
    411         return PreferenceKey.Username.getValue() + "@"
    412                 + PreferenceKey.DomainAddress.getValue();
    413     }
    414 
    415     private SipProfile createSipProfile() throws Exception {
    416             return new SipProfile.Builder(
    417                     PreferenceKey.Username.getValue(),
    418                     PreferenceKey.DomainAddress.getValue())
    419                     .setProfileName(getProfileName())
    420                     .setPassword(PreferenceKey.Password.getValue())
    421                     .setOutboundProxy(PreferenceKey.ProxyAddress.getValue())
    422                     .setProtocol(PreferenceKey.Transport.getValue())
    423                     .setDisplayName(PreferenceKey.DisplayName.getValue())
    424                     .setPort(Integer.parseInt(PreferenceKey.Port.getValue()))
    425                     .setSendKeepAlive(isAlwaysSendKeepAlive())
    426                     .setAutoRegistration(
    427                             mSharedPreferences.isReceivingCallsEnabled())
    428                     .setAuthUserName(PreferenceKey.AuthUserName.getValue())
    429                     .build();
    430     }
    431 
    432     public boolean onPreferenceChange(Preference pref, Object newValue) {
    433         if (!mUpdateRequired) {
    434             mUpdateRequired = true;
    435             if (mOldProfile != null) {
    436                 unregisterProfile(mOldProfile.getUriString());
    437             }
    438         }
    439         if (pref instanceof CheckBoxPreference) {
    440             invalidateOptionsMenu();
    441             return true;
    442         }
    443         String value = (newValue == null) ? "" : newValue.toString();
    444         if (TextUtils.isEmpty(value)) {
    445             pref.setSummary(getPreferenceKey(pref).defaultSummary);
    446         } else if (pref == PreferenceKey.Password.preference) {
    447             pref.setSummary(scramble(value));
    448         } else {
    449             pref.setSummary(value);
    450         }
    451 
    452         if (pref == PreferenceKey.DisplayName.preference) {
    453             ((EditTextPreference) pref).setText(value);
    454             checkIfDisplayNameSet();
    455         }
    456 
    457         // SAVE menu should be enabled once the user modified some preference.
    458         invalidateOptionsMenu();
    459         return true;
    460     }
    461 
    462     private PreferenceKey getPreferenceKey(Preference pref) {
    463         for (PreferenceKey key : PreferenceKey.values()) {
    464             if (key.preference == pref) return key;
    465         }
    466         throw new RuntimeException("not possible to reach here");
    467     }
    468 
    469     private void loadPreferencesFromProfile(SipProfile p) {
    470         if (p != null) {
    471             Log.v(TAG, "Edit the existing profile : " + p.getProfileName());
    472             try {
    473                 Class profileClass = SipProfile.class;
    474                 for (PreferenceKey key : PreferenceKey.values()) {
    475                     Method meth = profileClass.getMethod(GET_METHOD_PREFIX
    476                             + getString(key.text), (Class[])null);
    477                     if (key == PreferenceKey.SendKeepAlive) {
    478                         boolean value = ((Boolean)
    479                                 meth.invoke(p, (Object[]) null)).booleanValue();
    480                         key.setValue(getString(value
    481                                 ? R.string.sip_always_send_keepalive
    482                                 : R.string.sip_system_decide));
    483                     } else {
    484                         Object value = meth.invoke(p, (Object[])null);
    485                         key.setValue((value == null) ? "" : value.toString());
    486                     }
    487                 }
    488                 checkIfDisplayNameSet();
    489             } catch (Exception e) {
    490                 Log.e(TAG, "Can not load pref from profile", e);
    491             }
    492         } else {
    493             Log.v(TAG, "Edit a new profile");
    494             for (PreferenceKey key : PreferenceKey.values()) {
    495                 key.preference.setOnPreferenceChangeListener(this);
    496 
    497                 // FIXME: android:defaultValue in preference xml file doesn't
    498                 // work. Even if we setValue() for each preference in the case
    499                 // of (p != null), the dialog still shows android:defaultValue,
    500                 // not the value set by setValue(). This happens if
    501                 // android:defaultValue is not empty. Is it a bug?
    502                 if (key.initValue != 0) {
    503                     key.setValue(getString(key.initValue));
    504                 }
    505             }
    506             mDisplayNameSet = false;
    507         }
    508     }
    509 
    510     private boolean isAlwaysSendKeepAlive() {
    511         ListPreference pref = (ListPreference)
    512                 PreferenceKey.SendKeepAlive.preference;
    513         return getString(R.string.sip_always_send_keepalive).equals(
    514                 pref.getValue());
    515     }
    516 
    517     private void setCheckBox(PreferenceKey key, boolean checked) {
    518         CheckBoxPreference pref = (CheckBoxPreference) key.preference;
    519         pref.setChecked(checked);
    520     }
    521 
    522     private void setupPreference(Preference pref) {
    523         pref.setOnPreferenceChangeListener(this);
    524         for (PreferenceKey key : PreferenceKey.values()) {
    525             String name = getString(key.text);
    526             if (name.equals(pref.getKey())) {
    527                 key.preference = pref;
    528                 return;
    529             }
    530         }
    531     }
    532 
    533     private void checkIfDisplayNameSet() {
    534         String displayName = PreferenceKey.DisplayName.getValue();
    535         mDisplayNameSet = !TextUtils.isEmpty(displayName)
    536                 && !displayName.equals(getDefaultDisplayName());
    537         Log.d(TAG, "displayName set? " + mDisplayNameSet);
    538         if (mDisplayNameSet) {
    539             PreferenceKey.DisplayName.preference.setSummary(displayName);
    540         } else {
    541             PreferenceKey.DisplayName.setValue("");
    542         }
    543     }
    544 
    545     private static String getDefaultDisplayName() {
    546         return PreferenceKey.Username.getValue();
    547     }
    548 
    549     private static String scramble(String s) {
    550         char[] cc = new char[s.length()];
    551         Arrays.fill(cc, SCRAMBLED);
    552         return new String(cc);
    553     }
    554 
    555     // only takes care of the primary account setting in SipSharedSettings
    556     private class PrimaryAccountSelector {
    557         private CheckBoxPreference mCheckbox;
    558         private final boolean mWasPrimaryAccount;
    559 
    560         // @param profile profile to be edited; null if adding new profile
    561         PrimaryAccountSelector(SipProfile profile) {
    562             mCheckbox = (CheckBoxPreference) getPreferenceScreen()
    563                     .findPreference(getString(R.string.set_primary));
    564             boolean noPrimaryAccountSet =
    565                     !mSharedPreferences.hasPrimaryAccount();
    566             boolean editNewProfile = (profile == null);
    567             mWasPrimaryAccount = !editNewProfile
    568                     && mSharedPreferences.isPrimaryAccount(
    569                             profile.getUriString());
    570 
    571             Log.v(TAG, " noPrimaryAccountSet: " + noPrimaryAccountSet);
    572             Log.v(TAG, " editNewProfile: " + editNewProfile);
    573             Log.v(TAG, " mWasPrimaryAccount: " + mWasPrimaryAccount);
    574 
    575             mCheckbox.setChecked(mWasPrimaryAccount
    576                     || (editNewProfile && noPrimaryAccountSet));
    577         }
    578 
    579         boolean isSelected() {
    580             return mCheckbox.isChecked();
    581         }
    582 
    583         // profile is null if the user removes it
    584         void commit(SipProfile profile) {
    585             if ((profile != null) && mCheckbox.isChecked()) {
    586                 mSharedPreferences.setPrimaryAccount(profile.getUriString());
    587             } else if (mWasPrimaryAccount) {
    588                 mSharedPreferences.unsetPrimaryAccount();
    589             }
    590             Log.d(TAG, " primary account changed to : "
    591                     + mSharedPreferences.getPrimaryAccount());
    592         }
    593     }
    594 
    595     private class AdvancedSettings
    596             implements Preference.OnPreferenceClickListener {
    597         private Preference mAdvancedSettingsTrigger;
    598         private Preference[] mPreferences;
    599         private boolean mShowing = false;
    600 
    601         AdvancedSettings() {
    602             mAdvancedSettingsTrigger = getPreferenceScreen().findPreference(
    603                     getString(R.string.advanced_settings));
    604             mAdvancedSettingsTrigger.setOnPreferenceClickListener(this);
    605 
    606             loadAdvancedPreferences();
    607         }
    608 
    609         private void loadAdvancedPreferences() {
    610             PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen();
    611 
    612             addPreferencesFromResource(R.xml.sip_advanced_edit);
    613             PreferenceGroup group = (PreferenceGroup) screen.findPreference(
    614                     getString(R.string.advanced_settings_container));
    615             screen.removePreference(group);
    616 
    617             mPreferences = new Preference[group.getPreferenceCount()];
    618             int order = screen.getPreferenceCount();
    619             for (int i = 0, n = mPreferences.length; i < n; i++) {
    620                 Preference pref = group.getPreference(i);
    621                 pref.setOrder(order++);
    622                 setupPreference(pref);
    623                 mPreferences[i] = pref;
    624             }
    625         }
    626 
    627         void show() {
    628             mShowing = true;
    629             mAdvancedSettingsTrigger.setSummary(R.string.advanced_settings_hide);
    630             PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen();
    631             for (Preference pref : mPreferences) {
    632                 screen.addPreference(pref);
    633                 Log.v(TAG, "add pref " + pref.getKey() + ": order=" + pref.getOrder());
    634             }
    635         }
    636 
    637         private void hide() {
    638             mShowing = false;
    639             mAdvancedSettingsTrigger.setSummary(R.string.advanced_settings_show);
    640             PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen();
    641             for (Preference pref : mPreferences) {
    642                 screen.removePreference(pref);
    643             }
    644         }
    645 
    646         public boolean onPreferenceClick(Preference preference) {
    647             Log.v(TAG, "optional settings clicked");
    648             if (!mShowing) {
    649                 show();
    650             } else {
    651                 hide();
    652             }
    653             return true;
    654         }
    655     }
    656 }
    657