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