summaryrefslogtreecommitdiffstats
path: root/sip/src/com/android/services/telephony/sip/SipEditor.java
diff options
context:
space:
mode:
Diffstat (limited to 'sip/src/com/android/services/telephony/sip/SipEditor.java')
-rw-r--r--sip/src/com/android/services/telephony/sip/SipEditor.java660
1 files changed, 660 insertions, 0 deletions
diff --git a/sip/src/com/android/services/telephony/sip/SipEditor.java b/sip/src/com/android/services/telephony/sip/SipEditor.java
new file mode 100644
index 000000000..a76529686
--- /dev/null
+++ b/sip/src/com/android/services/telephony/sip/SipEditor.java
@@ -0,0 +1,660 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony.sip;
+
+import com.android.internal.telephony.CallManager;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.services.telephony.sip.SipUtil;
+
+import android.app.ActionBar;
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.net.sip.SipManager;
+import android.net.sip.SipProfile;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.preference.CheckBoxPreference;
+import android.preference.EditTextPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.Toast;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * The activity class for editing a new or existing SIP profile.
+ */
+public class SipEditor extends PreferenceActivity
+ implements Preference.OnPreferenceChangeListener {
+ private static final String PREFIX = "[SipEditor] ";
+ private static final boolean VERBOSE = true; /* STOP SHIP if true */
+
+ private static final int MENU_SAVE = Menu.FIRST;
+ private static final int MENU_DISCARD = Menu.FIRST + 1;
+ private static final int MENU_REMOVE = Menu.FIRST + 2;
+
+ private static final String KEY_PROFILE = "profile";
+ private static final String GET_METHOD_PREFIX = "get";
+ private static final char SCRAMBLED = '*';
+ private static final int NA = 0;
+
+ private PrimaryAccountSelector mPrimaryAccountSelector;
+ private AdvancedSettings mAdvancedSettings;
+ private SipSharedPreferences mSharedPreferences;
+ private boolean mDisplayNameSet;
+ private boolean mHomeButtonClicked;
+ private boolean mUpdateRequired;
+ private boolean mUpdatedFieldIsEmpty;
+
+ private SipManager mSipManager;
+ private SipProfileDb mProfileDb;
+ private SipProfile mOldProfile;
+ private CallManager mCallManager;
+ private Button mRemoveButton;
+
+ enum PreferenceKey {
+ Username(R.string.username, 0, R.string.default_preference_summary),
+ Password(R.string.password, 0, R.string.default_preference_summary),
+ DomainAddress(R.string.domain_address, 0, R.string.default_preference_summary),
+ DisplayName(R.string.display_name, 0, R.string.display_name_summary),
+ ProxyAddress(R.string.proxy_address, 0, R.string.optional_summary),
+ Port(R.string.port, R.string.default_port, R.string.default_port),
+ Transport(R.string.transport, R.string.default_transport, NA),
+ SendKeepAlive(R.string.send_keepalive, R.string.sip_system_decide, NA),
+ AuthUserName(R.string.auth_username, 0, R.string.optional_summary);
+
+ final int text;
+ final int initValue;
+ final int defaultSummary;
+ Preference preference;
+
+ /**
+ * @param key The key name of the preference.
+ * @param initValue The initial value of the preference.
+ * @param defaultSummary The default summary value of the preference
+ * when the preference value is empty.
+ */
+ PreferenceKey(int text, int initValue, int defaultSummary) {
+ this.text = text;
+ this.initValue = initValue;
+ this.defaultSummary = defaultSummary;
+ }
+
+ String getValue() {
+ if (preference instanceof EditTextPreference) {
+ return ((EditTextPreference) preference).getText();
+ } else if (preference instanceof ListPreference) {
+ return ((ListPreference) preference).getValue();
+ }
+ throw new RuntimeException("getValue() for the preference " + this);
+ }
+
+ void setValue(String value) {
+ if (preference instanceof EditTextPreference) {
+ String oldValue = getValue();
+ ((EditTextPreference) preference).setText(value);
+ if (this != Password) {
+ if (VERBOSE) {
+ log(this + ": setValue() " + value + ": " + oldValue + " --> " +
+ getValue());
+ }
+ }
+ } else if (preference instanceof ListPreference) {
+ ((ListPreference) preference).setValue(value);
+ }
+
+ if (TextUtils.isEmpty(value)) {
+ preference.setSummary(defaultSummary);
+ } else if (this == Password) {
+ preference.setSummary(scramble(value));
+ } else if ((this == DisplayName)
+ && value.equals(getDefaultDisplayName())) {
+ preference.setSummary(defaultSummary);
+ } else {
+ preference.setSummary(value);
+ }
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mHomeButtonClicked = false;
+ if (mCallManager.getState() != PhoneConstants.State.IDLE) {
+ mAdvancedSettings.show();
+ getPreferenceScreen().setEnabled(false);
+ if (mRemoveButton != null) mRemoveButton.setEnabled(false);
+ } else {
+ getPreferenceScreen().setEnabled(true);
+ if (mRemoveButton != null) mRemoveButton.setEnabled(true);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ if (VERBOSE) log("onCreate, start profile editor");
+ super.onCreate(savedInstanceState);
+
+ mSipManager = SipManager.newInstance(this);
+ mSharedPreferences = new SipSharedPreferences(this);
+ mProfileDb = new SipProfileDb(this);
+ mCallManager = CallManager.getInstance();
+
+ setContentView(R.layout.sip_settings_ui);
+ addPreferencesFromResource(R.xml.sip_edit);
+
+ SipProfile p = mOldProfile = (SipProfile) ((savedInstanceState == null)
+ ? getIntent().getParcelableExtra(SipSettings.KEY_SIP_PROFILE)
+ : savedInstanceState.getParcelable(KEY_PROFILE));
+
+ PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen();
+ for (int i = 0, n = screen.getPreferenceCount(); i < n; i++) {
+ setupPreference(screen.getPreference(i));
+ }
+
+ if (p == null) {
+ screen.setTitle(R.string.sip_edit_new_title);
+ }
+
+ mAdvancedSettings = new AdvancedSettings();
+ mPrimaryAccountSelector = new PrimaryAccountSelector(p);
+
+ loadPreferencesFromProfile(p);
+ }
+
+ @Override
+ public void onPause() {
+ if (VERBOSE) log("onPause, finishing: " + isFinishing());
+ if (!isFinishing()) {
+ mHomeButtonClicked = true;
+ validateAndSetResult();
+ }
+ super.onPause();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ menu.add(0, MENU_DISCARD, 0, R.string.sip_menu_discard)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ menu.add(0, MENU_SAVE, 0, R.string.sip_menu_save)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ menu.add(0, MENU_REMOVE, 0, R.string.remove_sip_account)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ MenuItem removeMenu = menu.findItem(MENU_REMOVE);
+ removeMenu.setVisible(mOldProfile != null);
+ menu.findItem(MENU_SAVE).setEnabled(mUpdateRequired);
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case MENU_SAVE:
+ validateAndSetResult();
+ return true;
+
+ case MENU_DISCARD:
+ finish();
+ return true;
+
+ case MENU_REMOVE: {
+ setRemovedProfileAndFinish();
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK:
+ validateAndSetResult();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ private void saveAndRegisterProfile(SipProfile p) throws IOException {
+ if (p == null) return;
+ mProfileDb.saveProfile(p);
+ if (p.getAutoRegistration() || mSharedPreferences.isPrimaryAccount(p.getUriString())) {
+ try {
+ mSipManager.open(p, SipUtil.createIncomingCallPendingIntent(this), null);
+ } catch (Exception e) {
+ log("saveAndRegisterProfile, register failed for profile: " + p.getUriString() +
+ ", exception: " + e);
+ }
+ }
+ }
+
+ private void deleteAndUnregisterProfile(SipProfile p) {
+ if (p == null) return;
+ mProfileDb.deleteProfile(p);
+ unregisterProfile(p.getUriString());
+ }
+
+ private void unregisterProfile(String uri) {
+ try {
+ mSipManager.close(uri);
+ } catch (Exception e) {
+ log("unregisterProfile, unregister failed for profile: " + uri + ", exception: " + e);
+ }
+ }
+
+ private void setRemovedProfileAndFinish() {
+ Intent intent = new Intent(this, SipSettings.class);
+ setResult(RESULT_FIRST_USER, intent);
+ Toast.makeText(this, R.string.removing_account, Toast.LENGTH_SHORT)
+ .show();
+ replaceProfile(mOldProfile, null);
+ // do finish() in replaceProfile() in a background thread
+ }
+
+ private void showAlert(Throwable e) {
+ String msg = e.getMessage();
+ if (TextUtils.isEmpty(msg)) msg = e.toString();
+ showAlert(msg);
+ }
+
+ private void showAlert(final String message) {
+ if (mHomeButtonClicked) {
+ if (VERBOSE) log("Home button clicked, don't show dialog: " + message);
+ return;
+ }
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ new AlertDialog.Builder(SipEditor.this)
+ .setTitle(android.R.string.dialog_alert_title)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setMessage(message)
+ .setPositiveButton(R.string.alert_dialog_ok, null)
+ .show();
+ }
+ });
+ }
+
+ private boolean isEditTextEmpty(PreferenceKey key) {
+ EditTextPreference pref = (EditTextPreference) key.preference;
+ return TextUtils.isEmpty(pref.getText())
+ || pref.getSummary().equals(getString(key.defaultSummary));
+ }
+
+ private void validateAndSetResult() {
+ boolean allEmpty = true;
+ CharSequence firstEmptyFieldTitle = null;
+ for (PreferenceKey key : PreferenceKey.values()) {
+ Preference p = key.preference;
+ if (p instanceof EditTextPreference) {
+ EditTextPreference pref = (EditTextPreference) p;
+ boolean fieldEmpty = isEditTextEmpty(key);
+ if (allEmpty && !fieldEmpty) allEmpty = false;
+
+ // use default value if display name is empty
+ if (fieldEmpty) {
+ switch (key) {
+ case DisplayName:
+ pref.setText(getDefaultDisplayName());
+ break;
+ case AuthUserName:
+ case ProxyAddress:
+ // optional; do nothing
+ break;
+ case Port:
+ pref.setText(getString(R.string.default_port));
+ break;
+ default:
+ if (firstEmptyFieldTitle == null) {
+ firstEmptyFieldTitle = pref.getTitle();
+ }
+ }
+ } else if (key == PreferenceKey.Port) {
+ int port = Integer.parseInt(PreferenceKey.Port.getValue());
+ if ((port < 1000) || (port > 65534)) {
+ showAlert(getString(R.string.not_a_valid_port));
+ return;
+ }
+ }
+ }
+ }
+
+ if (allEmpty || !mUpdateRequired) {
+ finish();
+ return;
+ } else if (firstEmptyFieldTitle != null) {
+ showAlert(getString(R.string.empty_alert, firstEmptyFieldTitle));
+ return;
+ }
+ try {
+ SipProfile profile = createSipProfile();
+ Intent intent = new Intent(this, SipSettings.class);
+ intent.putExtra(SipSettings.KEY_SIP_PROFILE, (Parcelable) profile);
+ setResult(RESULT_OK, intent);
+ Toast.makeText(this, R.string.saving_account, Toast.LENGTH_SHORT).show();
+
+ replaceProfile(mOldProfile, profile);
+ // do finish() in replaceProfile() in a background thread
+ } catch (Exception e) {
+ log("validateAndSetResult, can not create new SipProfile, exception: " + e);
+ showAlert(e);
+ }
+ }
+
+ private void unregisterOldPrimaryAccount() {
+ String primaryAccountUri = mSharedPreferences.getPrimaryAccount();
+ if (VERBOSE) log("unregisterOldPrimaryAccount, old primary: " + primaryAccountUri);
+ if ((primaryAccountUri != null) && !mSharedPreferences.isReceivingCallsEnabled()) {
+ if (VERBOSE) log("unregisterOldPrimaryAccount, calling unregister");
+ unregisterProfile(primaryAccountUri);
+ }
+ }
+
+ private void replaceProfile(final SipProfile oldProfile, final SipProfile newProfile) {
+ // Replace profile in a background thread as it takes time to access the
+ // storage; do finish() once everything goes fine.
+ // newProfile may be null if the old profile is to be deleted rather
+ // than being modified.
+ new Thread(new Runnable() {
+ public void run() {
+ try {
+ // if new profile is primary, unregister the old primary account
+ if ((newProfile != null) && mPrimaryAccountSelector.isSelected()) {
+ unregisterOldPrimaryAccount();
+ }
+
+ mPrimaryAccountSelector.commit(newProfile);
+ deleteAndUnregisterProfile(oldProfile);
+ saveAndRegisterProfile(newProfile);
+ finish();
+ } catch (Exception e) {
+ log("replaceProfile, can not save/register new SipProfile, exception: " + e);
+ showAlert(e);
+ }
+ }
+ }, "SipEditor").start();
+ }
+
+ private String getProfileName() {
+ return PreferenceKey.Username.getValue() + "@"
+ + PreferenceKey.DomainAddress.getValue();
+ }
+
+ private SipProfile createSipProfile() throws Exception {
+ return new SipProfile.Builder(
+ PreferenceKey.Username.getValue(),
+ PreferenceKey.DomainAddress.getValue())
+ .setProfileName(getProfileName())
+ .setPassword(PreferenceKey.Password.getValue())
+ .setOutboundProxy(PreferenceKey.ProxyAddress.getValue())
+ .setProtocol(PreferenceKey.Transport.getValue())
+ .setDisplayName(PreferenceKey.DisplayName.getValue())
+ .setPort(Integer.parseInt(PreferenceKey.Port.getValue()))
+ .setSendKeepAlive(isAlwaysSendKeepAlive())
+ .setAutoRegistration(
+ mSharedPreferences.isReceivingCallsEnabled())
+ .setAuthUserName(PreferenceKey.AuthUserName.getValue())
+ .build();
+ }
+
+ public boolean onPreferenceChange(Preference pref, Object newValue) {
+ if (!mUpdateRequired) {
+ mUpdateRequired = true;
+ if (mOldProfile != null) {
+ unregisterProfile(mOldProfile.getUriString());
+ }
+ }
+ if (pref instanceof CheckBoxPreference) {
+ invalidateOptionsMenu();
+ return true;
+ }
+ String value = (newValue == null) ? "" : newValue.toString();
+ if (TextUtils.isEmpty(value)) {
+ pref.setSummary(getPreferenceKey(pref).defaultSummary);
+ } else if (pref == PreferenceKey.Password.preference) {
+ pref.setSummary(scramble(value));
+ } else {
+ pref.setSummary(value);
+ }
+
+ if (pref == PreferenceKey.DisplayName.preference) {
+ ((EditTextPreference) pref).setText(value);
+ checkIfDisplayNameSet();
+ }
+
+ // SAVE menu should be enabled once the user modified some preference.
+ invalidateOptionsMenu();
+ return true;
+ }
+
+ private PreferenceKey getPreferenceKey(Preference pref) {
+ for (PreferenceKey key : PreferenceKey.values()) {
+ if (key.preference == pref) return key;
+ }
+ throw new RuntimeException("not possible to reach here");
+ }
+
+ private void loadPreferencesFromProfile(SipProfile p) {
+ if (p != null) {
+ if (VERBOSE) log("loadPreferencesFromProfile, existing profile: " + p.getProfileName());
+ try {
+ Class profileClass = SipProfile.class;
+ for (PreferenceKey key : PreferenceKey.values()) {
+ Method meth = profileClass.getMethod(GET_METHOD_PREFIX
+ + getString(key.text), (Class[])null);
+ if (key == PreferenceKey.SendKeepAlive) {
+ boolean value = ((Boolean) meth.invoke(p, (Object[]) null)).booleanValue();
+ key.setValue(getString(value
+ ? R.string.sip_always_send_keepalive
+ : R.string.sip_system_decide));
+ } else {
+ Object value = meth.invoke(p, (Object[])null);
+ key.setValue((value == null) ? "" : value.toString());
+ }
+ }
+ checkIfDisplayNameSet();
+ } catch (Exception e) {
+ log("loadPreferencesFromProfile, can not load pref from profile, exception: " + e);
+ }
+ } else {
+ if (VERBOSE) log("loadPreferencesFromProfile, edit a new profile");
+ for (PreferenceKey key : PreferenceKey.values()) {
+ key.preference.setOnPreferenceChangeListener(this);
+
+ // FIXME: android:defaultValue in preference xml file doesn't
+ // work. Even if we setValue() for each preference in the case
+ // of (p != null), the dialog still shows android:defaultValue,
+ // not the value set by setValue(). This happens if
+ // android:defaultValue is not empty. Is it a bug?
+ if (key.initValue != 0) {
+ key.setValue(getString(key.initValue));
+ }
+ }
+ mDisplayNameSet = false;
+ }
+ }
+
+ private boolean isAlwaysSendKeepAlive() {
+ ListPreference pref = (ListPreference) PreferenceKey.SendKeepAlive.preference;
+ return getString(R.string.sip_always_send_keepalive).equals(pref.getValue());
+ }
+
+ private void setCheckBox(PreferenceKey key, boolean checked) {
+ CheckBoxPreference pref = (CheckBoxPreference) key.preference;
+ pref.setChecked(checked);
+ }
+
+ private void setupPreference(Preference pref) {
+ pref.setOnPreferenceChangeListener(this);
+ for (PreferenceKey key : PreferenceKey.values()) {
+ String name = getString(key.text);
+ if (name.equals(pref.getKey())) {
+ key.preference = pref;
+ return;
+ }
+ }
+ }
+
+ private void checkIfDisplayNameSet() {
+ String displayName = PreferenceKey.DisplayName.getValue();
+ mDisplayNameSet = !TextUtils.isEmpty(displayName)
+ && !displayName.equals(getDefaultDisplayName());
+ if (VERBOSE) log("checkIfDisplayNameSet, displayName set: " + mDisplayNameSet);
+ if (mDisplayNameSet) {
+ PreferenceKey.DisplayName.preference.setSummary(displayName);
+ } else {
+ PreferenceKey.DisplayName.setValue("");
+ }
+ }
+
+ private static String getDefaultDisplayName() {
+ return PreferenceKey.Username.getValue();
+ }
+
+ private static String scramble(String s) {
+ char[] cc = new char[s.length()];
+ Arrays.fill(cc, SCRAMBLED);
+ return new String(cc);
+ }
+
+ // only takes care of the primary account setting in SipSharedSettings
+ private class PrimaryAccountSelector {
+ private CheckBoxPreference mCheckbox;
+ private final boolean mWasPrimaryAccount;
+
+ // @param profile profile to be edited; null if adding new profile
+ PrimaryAccountSelector(SipProfile profile) {
+ mCheckbox = (CheckBoxPreference) getPreferenceScreen()
+ .findPreference(getString(R.string.set_primary));
+ boolean noPrimaryAccountSet = !mSharedPreferences.hasPrimaryAccount();
+ boolean editNewProfile = (profile == null);
+ mWasPrimaryAccount = !editNewProfile && mSharedPreferences.isPrimaryAccount(
+ profile.getUriString());
+
+ if (VERBOSE) {
+ log(" noPrimaryAccountSet: " + noPrimaryAccountSet);
+ log(" editNewProfile: " + editNewProfile);
+ log(" mWasPrimaryAccount: " + mWasPrimaryAccount);
+ }
+
+ mCheckbox.setChecked(mWasPrimaryAccount || (editNewProfile && noPrimaryAccountSet));
+ }
+
+ boolean isSelected() {
+ return mCheckbox.isChecked();
+ }
+
+ // profile is null if the user removes it
+ void commit(SipProfile profile) {
+ if ((profile != null) && mCheckbox.isChecked()) {
+ mSharedPreferences.setPrimaryAccount(profile.getUriString());
+ } else if (mWasPrimaryAccount) {
+ mSharedPreferences.unsetPrimaryAccount();
+ }
+ if (VERBOSE) {
+ log("PrimaryAccountSelector.commit, new primary account: " +
+ mSharedPreferences.getPrimaryAccount());
+ }
+ }
+ }
+
+ private class AdvancedSettings implements Preference.OnPreferenceClickListener {
+ private Preference mAdvancedSettingsTrigger;
+ private Preference[] mPreferences;
+ private boolean mShowing = false;
+
+ AdvancedSettings() {
+ mAdvancedSettingsTrigger = getPreferenceScreen().findPreference(
+ getString(R.string.advanced_settings));
+ mAdvancedSettingsTrigger.setOnPreferenceClickListener(this);
+
+ loadAdvancedPreferences();
+ }
+
+ private void loadAdvancedPreferences() {
+ PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen();
+
+ addPreferencesFromResource(R.xml.sip_advanced_edit);
+ PreferenceGroup group = (PreferenceGroup) screen.findPreference(
+ getString(R.string.advanced_settings_container));
+ screen.removePreference(group);
+
+ mPreferences = new Preference[group.getPreferenceCount()];
+ int order = screen.getPreferenceCount();
+ for (int i = 0, n = mPreferences.length; i < n; i++) {
+ Preference pref = group.getPreference(i);
+ pref.setOrder(order++);
+ setupPreference(pref);
+ mPreferences[i] = pref;
+ }
+ }
+
+ void show() {
+ mShowing = true;
+ mAdvancedSettingsTrigger.setSummary(R.string.advanced_settings_hide);
+ PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen();
+ for (Preference pref : mPreferences) {
+ screen.addPreference(pref);
+ if (VERBOSE) {
+ log("AdvancedSettings.show, pref: " + pref.getKey() + ", order: " +
+ pref.getOrder());
+ }
+ }
+ }
+
+ private void hide() {
+ mShowing = false;
+ mAdvancedSettingsTrigger.setSummary(R.string.advanced_settings_show);
+ PreferenceGroup screen = (PreferenceGroup) getPreferenceScreen();
+ for (Preference pref : mPreferences) {
+ screen.removePreference(pref);
+ }
+ }
+
+ public boolean onPreferenceClick(Preference preference) {
+ if (VERBOSE) log("AdvancedSettings.onPreferenceClick");
+ if (!mShowing) {
+ show();
+ } else {
+ hide();
+ }
+ return true;
+ }
+ }
+
+ private static void log(String msg) {
+ Log.d(SipUtil.LOG_TAG, PREFIX + msg);
+ }
+}