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