First checkin of sip setting.

Change-Id: Ie59799f769aba9af81413aa02cc443bf2be9d8dc
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 06fadf0..350eb6e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,5 +1,5 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.sip.demo">
+        package="com.android.settings.sip">
     <uses-permission android:name="android.permission.INTERNET"></uses-permission>
 	<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"></uses-permission>
 	<uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission>
@@ -17,8 +17,8 @@
 	<uses-permission android:name="android.permission.VIBRATE" ></uses-permission>
 	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" ></uses-permission>
 
-    <application android:label="Sip Joy 2">
-        <activity android:name=".SipMain"
+    <application android:label="Sip Joy 3">
+        <activity android:name="com.android.sip.demo.SipMain"
                   android:launchMode="singleTop"
                   android:configChanges="orientation|keyboardHidden">
             <intent-filter>
@@ -28,8 +28,21 @@
             </intent-filter>
         </activity>
 
+        <activity android:label="Sip Settings"
+                  android:name=".SipSettings"
+                  android:launchMode="singleTop"
+                  android:configChanges="orientation|keyboardHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".SipEditor"
+                android:configChanges="orientation|keyboardHidden">
+        </activity>
+
         <receiver
-            android:name=".IncomingCallReceiver">
+            android:name="com.android.sip.demo.IncomingCallReceiver">
             <intent-filter>
                 <action android:name="com.android.sip.demo.SipMain" />
             </intent-filter>
diff --git a/res/values/array.xml b/res/values/array.xml
new file mode 100644
index 0000000..2ba321d
--- /dev/null
+++ b/res/values/array.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <string-array name="transport_types">
+        <item>UDP</item>
+        <item>TCP</item>
+    </string-array>
+</resources>
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 0000000..93da80c
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name">Sip Account Settings</string>
+    <string name="sip_settings_activity_title">Sip Settings Activity</string>
+    <string name="add_sip_account">Add SIP account</string>
+    <string name="sip_account_list">SIP accounts</string>
+
+    <string name="sip_menu_save">Save</string>
+    <string name="sip_menu_discard">Discard</string>
+
+    <string name="alert_dialog_ok">OK</string>
+
+
+    <string name="sip_edit_title">Edit your SIP account</string>
+
+    <string name="profile_name_title">Name</string>
+    <string name="profile_name_summary">Not set</string>
+    <string name="domain_address_title">Domain</string>
+    <string name="domain_address_summary"></string>
+    <string name="username_title">Username</string>
+    <string name="username_summary">Enter the username</string>
+    <string name="password_title">Password</string>
+    <string name="password_summary">Click to enter password</string>
+    <string name="display_name_title">Display name</string>
+    <string name="display_name_summary">Your display name</string>
+    <string name="proxy_address_title">Outbound Proxy address</string>
+    <string name="proxy_address_summary"></string>
+    <string name="port_title">Port Number</string>
+    <string name="port_summary">5060</string>
+    <string name="transport_title">Transport Type</string>
+    <string name="transport_summary">TCP</string>
+
+    <string name="sip_menu_register">Register the account</string>
+    <string name="sip_menu_unregister">Unregister the account</string>
+    <string name="sip_menu_edit">Edit the account</string>
+    <string name="sip_menu_delete">Delete the account</string>
+
+    <string name="empty_alert"> cannot be empty</string>
+
+    <!-- Do not translate the following strings. Used for the preference keys -->
+    <string name="profile_name">ProfileName</string>
+    <string name="domain_address">ServerAddress</string>
+    <string name="username">UserName</string>
+    <string name="password">Password</string>
+    <string name="display_name">DisplayName</string>
+    <string name="proxy_address">ProxyAddress</string>
+    <string name="port">Port</string>
+    <string name="transport">Protocol</string>
+
+</resources>
diff --git a/res/xml/sip_edit.xml b/res/xml/sip_edit.xml
new file mode 100644
index 0000000..6bfacb3
--- /dev/null
+++ b/res/xml/sip_edit.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        android:title="@string/sip_edit_title"
+        android:persistent="false">
+
+    <EditTextPreference
+        android:key="@string/profile_name"
+        android:title="@string/profile_name_title"
+        android:defaultValue="@string/profile_name_summary"
+        android:summary="@string/profile_name_summary"
+        android:dialogTitle="@string/profile_name_title"
+        android:singleLine="true"/>
+
+    <EditTextPreference
+        android:key="@string/domain_address"
+        android:title="@string/domain_address_title"
+        android:defaultValue="@string/domain_address_summary"
+        android:summary="@string/domain_address_summary"
+        android:dialogTitle="@string/domain_address_title"
+        android:singleLine="true"/>
+
+    <EditTextPreference
+        android:key="@string/username"
+        android:title="@string/username_title"
+        android:summary="@string/username_summary"
+        android:dialogTitle="@string/username_title"
+        android:singleLine="true"/>
+
+    <EditTextPreference
+        android:key="@string/password"
+        android:title="@string/password_title"
+        android:summary="@string/password_summary"
+        android:dialogTitle="@string/password_title"
+        android:password="true"
+        android:singleLine="true"/>
+
+    <EditTextPreference
+        android:key="@string/display_name"
+        android:title="@string/display_name_title"
+        android:defaultValue="@string/display_name_summary"
+        android:summary="@string/display_name_summary"
+        android:dialogTitle="@string/display_name_title"
+        android:singleLine="true"/>
+
+    <EditTextPreference
+        android:key="@string/proxy_address"
+        android:title="@string/proxy_address_title"
+        android:defaultValue="@string/proxy_address_summary"
+        android:summary="@string/proxy_address_summary"
+        android:dialogTitle="@string/proxy_address_title"
+        android:singleLine="true"/>
+
+    <EditTextPreference
+        android:key="@string/port"
+        android:title="@string/port_title"
+        android:defaultValue="@string/port_summary"
+        android:summary="@string/port_summary"
+        android:dialogTitle="@string/port_title"
+        android:singleLine="true"/>
+
+    <ListPreference
+        android:key="@string/transport"
+        android:title="@string/transport_title"
+        android:entries="@array/transport_types"
+        android:entryValues="@array/transport_types"
+        android:summary="@string/transport_summary"
+        android:persistent="false"
+        android:dialogTitle="@string/transport_title"/>
+
+</PreferenceScreen>
diff --git a/res/xml/sip_setting.xml b/res/xml/sip_setting.xml
new file mode 100644
index 0000000..39b40ef
--- /dev/null
+++ b/res/xml/sip_setting.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        android:title="@string/sip_settings_activity_title">
+
+    <PreferenceScreen android:key="add_sip_account"
+        android:title="@string/add_sip_account">
+    </PreferenceScreen>
+
+    <PreferenceCategory android:key="sip_account_list" android:title="@string/sip_account_list">
+    </PreferenceCategory>
+</PreferenceScreen>
diff --git a/src/com/android/settings/sip/SipEditor.java b/src/com/android/settings/sip/SipEditor.java
new file mode 100644
index 0000000..6f3c5ee
--- /dev/null
+++ b/src/com/android/settings/sip/SipEditor.java
@@ -0,0 +1,275 @@
+/*
+ * 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.settings.sip;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.sip.SipProfile;
+import android.os.Bundle;
+import android.os.Parcel;
+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 java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * The activity class for editing a new or existing SIP profile.
+ */
+public class SipEditor extends PreferenceActivity
+        implements Preference.OnPreferenceChangeListener {
+    private static final int MENU_SAVE = Menu.FIRST;
+    private static final int MENU_DISCARD = Menu.FIRST + 1;
+
+    private static final String TAG = SipEditor.class.getSimpleName();
+    private static final String KEY_PROFILE = "profile";
+    private static final String NOT_SET = "<No Set>";
+    private static final String SCRAMBLED = "****";
+    private static final String EMPTY = "";
+    private static final String DEFAULT_SIP_PORT = "5060";
+    private static final String DEFAULT_PROTOCOL = "UDP";
+    private static final String GET_METHOD_PREFIX = "get";
+
+    private boolean mAddingProfile;
+
+    enum PreferenceKey {
+        ProfileName(R.string.profile_name, 0, EMPTY),
+        DomainAddress(R.string.domain_address, 1, EMPTY),
+        Username(R.string.username, 2, EMPTY),
+        Password(R.string.password, 3, EMPTY),
+        DisplayName(R.string.display_name, 4, EMPTY),
+        ProxyAddress(R.string.proxy_address, 5, EMPTY),
+        Port(R.string.port, 6, DEFAULT_SIP_PORT),
+        Transport(R.string.transport, 7, DEFAULT_PROTOCOL);
+
+        /**
+         * @param key The key name of the preference.
+         * @param index The index of the preference in the view.
+         * @param defaultValue The default value of the preference.
+         */
+        PreferenceKey(int text, int index, String defaultValue) {
+            this.text = text;
+            this.index = index;
+            this.defaultValue = defaultValue;
+        }
+
+        public final int text;
+        public final int index;
+        public final String defaultValue;
+    }
+
+    private Preference[] mPreferences =
+            new Preference[PreferenceKey.values().length];
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log.v(TAG, "start profile editor");
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.sip_edit);
+        SipProfile p = (SipProfile) ((savedInstanceState == null)
+                ? getIntent().getParcelableExtra(SipSettings.KEY_SIP_PROFILE)
+                : savedInstanceState.getParcelable(KEY_PROFILE));
+
+        for (PreferenceKey key : PreferenceKey.values()) {
+            mPreferences[key.index] =
+                    setupPreference(getResources().getString(key.text));
+        }
+        loadPreferencesFromProfile(p);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        super.onCreateOptionsMenu(menu);
+        menu.add(0, MENU_SAVE, 0, R.string.sip_menu_save)
+                .setIcon(android.R.drawable.ic_menu_save);
+        menu.add(0, MENU_DISCARD, 0, R.string.sip_menu_discard)
+                .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case MENU_SAVE:
+                if (validateAndSetResult()) {
+                    finish();
+                }
+                return true;
+
+            case MENU_DISCARD:
+                finish();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_BACK:
+                if (validateAndSetResult()) finish();
+                return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    private void showAlert(String message) {
+        new AlertDialog.Builder(this)
+                .setTitle(android.R.string.dialog_alert_title)
+                .setIcon(android.R.drawable.ic_dialog_alert)
+                .setMessage(message)
+                .setPositiveButton(R.string.alert_dialog_ok,
+                        new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int w) {
+                            }
+                        })
+                .show();
+    }
+
+    private boolean validateAndSetResult() {
+        for(Preference pref : mPreferences)  {
+            String value;
+            if (pref instanceof ListPreference) {
+                value = ((ListPreference)pref).getValue();
+            } else {
+                value = ((EditTextPreference)pref).getText();
+            }
+            if (TextUtils.isEmpty(value) &&
+                    (pref != mPreferences[PreferenceKey.ProxyAddress.index])) {
+                showAlert(pref.getTitle() +
+                        getResources().getString(R.string.empty_alert));
+                return false;
+            }
+        }
+        try {
+            Intent intent = new Intent(this, SipSettings.class);
+            intent.putExtra(SipSettings.KEY_SIP_PROFILE,
+                    (Parcelable) createSipProfile());
+            setResult(RESULT_OK, intent);
+            return true;
+        } catch (Exception e) {
+            showAlert(e.getMessage());
+            return false;
+        }
+    }
+
+    private SipProfile createSipProfile() {
+        try {
+            return new SipProfile.Builder(
+                    getValue(PreferenceKey.Username),
+                    getValue(PreferenceKey.DomainAddress))
+                    .setProfileName(getValue(PreferenceKey.ProfileName))
+                    .setPassword(getValue(PreferenceKey.Password))
+                    .setOutboundProxy(getValue(PreferenceKey.ProxyAddress))
+                    .setProtocol(getValue(PreferenceKey.Transport))
+                    .setDisplayName(getValue(PreferenceKey.DisplayName))
+                    .setPort(Integer.parseInt(getValue(PreferenceKey.Port)))
+                    .build();
+        } catch (Exception e) {
+            Log.e(TAG, "Can not create new SipProfile : " + e.getMessage());
+            return null;
+        }
+    }
+
+    public boolean onPreferenceChange(Preference pref, Object newValue) {
+        String value = (String) newValue;
+        if (value == null) value = EMPTY;
+        if (pref != mPreferences[PreferenceKey.Password.index]) {
+            pref.setSummary(value);
+        } else {
+            pref.setSummary(SCRAMBLED);
+        }
+        return true;
+    }
+
+    private void loadPreferencesFromProfile(SipProfile p) {
+        if (p != null) {
+            Log.v(TAG, "Edit the existing profile : " + p.getProfileName());
+            try {
+                Class profileClass =
+                        Class.forName("android.net.sip.SipProfile");
+                for (PreferenceKey key : PreferenceKey.values()) {
+                    Method meth = profileClass.getMethod(GET_METHOD_PREFIX +
+                            getResources().getString(key.text), (Class[])null);
+                    if (key == PreferenceKey.Port) {
+                        setValue(key,
+                                String.valueOf(meth.invoke(p, (Object[])null)));
+                    } else {
+                        setValue(key, (String) meth.invoke(p, (Object[])null));
+                    }
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Can not load pref from profile:" + e.getMessage());
+            }
+        } else {
+            Log.v(TAG, "Edit a new profile");
+            for (PreferenceKey key : PreferenceKey.values()) {
+                Preference pref = mPreferences[key.index];
+                pref.setOnPreferenceChangeListener(this);
+                if (pref instanceof EditTextPreference) {
+                    ((EditTextPreference)pref).setText(key.defaultValue);
+                } else {
+                    ((ListPreference)pref).setValue(key.defaultValue);
+                }
+                pref.setSummary(EMPTY.equals(key.defaultValue)
+                        ? NOT_SET : key.defaultValue);
+            }
+        }
+    }
+
+    private String getValue(PreferenceKey key) {
+        Preference pref = mPreferences[key.index];
+        if (pref instanceof EditTextPreference) {
+            return ((EditTextPreference)pref).getText();
+        }
+        return ((ListPreference)pref).getValue();
+    }
+
+    private void setValue(PreferenceKey key, String value) {
+        Preference pref = mPreferences[key.index];
+        if (pref instanceof EditTextPreference) {
+            ((EditTextPreference)pref).setText(value);
+        } else {
+            ((ListPreference)pref).setValue(value);
+        }
+        if (TextUtils.isEmpty(value)) {
+            value = NOT_SET;
+        } else {
+            if (key == PreferenceKey.Password) value = SCRAMBLED;
+        }
+        pref.setSummary(value);
+    }
+
+    private Preference setupPreference(String key) {
+        Preference pref = getPreferenceScreen().findPreference(key);
+        pref.setOnPreferenceChangeListener(this);
+        return pref;
+    }
+}
diff --git a/src/com/android/settings/sip/SipSettings.java b/src/com/android/settings/sip/SipSettings.java
new file mode 100644
index 0000000..181c5c4
--- /dev/null
+++ b/src/com/android/settings/sip/SipSettings.java
@@ -0,0 +1,375 @@
+/*
+ * 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.settings.sip;
+
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.sip.ISipService;
+import android.net.sip.ISipSession;
+import android.net.sip.ISipSessionListener;
+import android.net.sip.SipProfile;
+import android.net.sip.SipManager;
+import android.net.sip.SipSessionAdapter;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceScreen;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+
+public class SipSettings extends PreferenceActivity {
+    static final String KEY_SIP_PROFILE = "sip_profile";
+
+    private static final String PREF_ADD_SIP = "add_sip_account";
+    private static final String PREF_SIP_LIST = "sip_account_list";
+    private static final String PROFILE_OBJ_FILE = ".pobj";
+    private static final String TAG = "SipSettings";
+    private static final String REGISTERED = "REGISTERED";
+    private static final String UNREGISTERED = "UNREGISTERED";
+
+    private static final String INCOMING_CALL_ACTION =
+            "com.android.sip.demo.SipMain";
+
+    private static final int REQUEST_ADD_OR_EDIT_SIP_PROFILE = 1;
+
+    private static final int CONTEXT_MENU_REGISTER_ID = ContextMenu.FIRST;
+    private static final int CONTEXT_MENU_UNREGISTER_ID = ContextMenu.FIRST + 1;
+    private static final int CONTEXT_MENU_EDIT_ID = ContextMenu.FIRST + 2;
+    private static final int CONTEXT_MENU_DELETE_ID = ContextMenu.FIRST + 3;
+    private static final int EXPIRY_TIME = 600;
+
+    private String mProfilesDirectory;
+
+    private SipProfile mProfile;
+    private ISipService mSipService;
+
+    private PreferenceCategory mSipListContainer;
+    private Map<String, SipPreference> mSipPreferenceMap;
+    private List<SipProfile> mSipProfileList;
+
+    private class SipPreference extends Preference {
+        SipProfile mProfile;
+        SipPreference(Context c, SipProfile p) {
+            super(c);
+            setProfile(p);
+        }
+
+        void setProfile(SipProfile p) {
+            mProfile = p;
+            setTitle(p.getProfileName());
+            setSummary(p.getUserName() + "@" + p.getServerAddress());
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.sip_setting);
+        mProfilesDirectory = getFilesDir().getAbsolutePath() + "/profiles/";
+        mSipListContainer = (PreferenceCategory) findPreference(PREF_SIP_LIST);
+
+        PreferenceScreen mAddSip = (PreferenceScreen) findPreference(PREF_ADD_SIP);
+        mAddSip.setOnPreferenceClickListener(
+                new OnPreferenceClickListener() {
+                    public boolean onPreferenceClick(Preference preference) {
+                        startSipEditor(null);
+                        return true;
+                    }
+                });
+
+        // for long-press gesture on a profile preference
+        registerForContextMenu(getListView());
+        retrieveSipListFromStorage();
+
+        new Thread(new Runnable() {
+            public void run() {
+                // SipService must be obtained from non-main thread
+                mSipService = SipManager.getSipService(SipSettings.this);
+                Log.v(TAG, "got SipService: " + mSipService);
+            }
+        }).start();
+    }
+
+    private void retrieveSipListFromStorage() {
+
+        mSipPreferenceMap = new LinkedHashMap<String, SipPreference>();
+        mSipProfileList = Collections.synchronizedList(
+                new ArrayList<SipProfile>());
+        mSipListContainer.removeAll();
+
+        File root = new File(mProfilesDirectory);
+        String[] dirs = root.list();
+        if (dirs == null) return;
+        for (String dir : dirs) {
+            File f = new File(new File(root, dir), PROFILE_OBJ_FILE);
+            if (!f.exists()) continue;
+            try {
+                SipProfile p = deserialize(f);
+                if (p == null) continue;
+                if (!dir.equals(p.getProfileName())) continue;
+
+                mSipProfileList.add(p);
+            } catch (IOException e) {
+                Log.e(TAG, "retrieveVpnListFromStorage()", e);
+            }
+        }
+        Collections.sort(mSipProfileList, new Comparator<SipProfile>() {
+            public int compare(SipProfile p1, SipProfile p2) {
+                return p1.getProfileName().compareTo(p2.getProfileName());
+            }
+
+            public boolean equals(SipProfile p) {
+                // not used
+                return false;
+            }
+        });
+        for (SipProfile p : mSipProfileList) {
+            Preference pref = addPreferenceFor(p, true);
+        }
+    }
+
+    private SipPreference addPreferenceFor(SipProfile p, boolean addToContainer)
+            {
+        SipPreference pref = new SipPreference(this, p);
+        mSipPreferenceMap.put(p.getProfileName(), pref);
+        if (addToContainer) mSipListContainer.addPreference(pref);
+
+        pref.setOnPreferenceClickListener(
+                new Preference.OnPreferenceClickListener() {
+                    public boolean onPreferenceClick(Preference pref) {
+                        startSipEditor(((SipPreference) pref).mProfile);
+                        return true;
+                    }
+                });
+        return pref;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterForContextMenu(getListView());
+    }
+
+    private SipProfile getProfile(int position) {
+        return ((position >= 0) ? mSipProfileList.get(position) : null);
+    }
+
+    private int getProfilePositionFrom(AdapterContextMenuInfo menuInfo) {
+        return menuInfo.position - mSipListContainer.getOrder() - 1;
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View v,
+            ContextMenuInfo menuInfo) {
+        super.onCreateContextMenu(menu, v, menuInfo);
+
+        SipProfile p = getProfile(getProfilePositionFrom(
+                    (AdapterContextMenuInfo) menuInfo));
+        if (p != null) {
+            menu.setHeaderTitle(p.getProfileName());
+
+            menu.add(0, CONTEXT_MENU_REGISTER_ID, 0, R.string.sip_menu_register);
+            menu.add(0, CONTEXT_MENU_UNREGISTER_ID, 0, R.string.sip_menu_unregister);
+            menu.add(0, CONTEXT_MENU_EDIT_ID, 0, R.string.sip_menu_edit);
+            menu.add(0, CONTEXT_MENU_DELETE_ID, 0, R.string.sip_menu_delete);
+        }
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        SipProfile p = getProfile(getProfilePositionFrom(
+                (AdapterContextMenuInfo) item.getMenuInfo()));
+
+        switch(item.getItemId()) {
+        case CONTEXT_MENU_REGISTER_ID:
+            if (p != null) {
+                try {
+                    openToReceiveCalls(p);
+                    ISipSession session = mSipService.createSession(p,
+                            createSessionAdapter());
+                    session.register(EXPIRY_TIME);
+                } catch (Exception e) {
+                    Log.e(TAG, "register failed:" + e.toString());
+                }
+            }
+            return true;
+        case CONTEXT_MENU_UNREGISTER_ID:
+            if (p != null) {
+                try {
+                    ISipSession session = mSipService.createSession(p,
+                            createSessionAdapter());
+                    session.unregister();
+                } catch (Exception e) {
+                    Log.e(TAG, "unregister failed:" + e);
+                }
+            }
+            return true;
+
+        case CONTEXT_MENU_EDIT_ID:
+            startSipEditor(p);
+            return true;
+
+        case CONTEXT_MENU_DELETE_ID:
+            deleteProfile(p);
+            return true;
+        }
+
+        return super.onContextItemSelected(item);
+    }
+
+    // TODO: Use the Util class in settings.vpn instead
+    private void deleteProfile(String name) {
+        deleteProfile(new File(name));
+    }
+
+    private void deleteProfile(File file) {
+        if (file.isDirectory()) {
+            for (File child : file.listFiles()) deleteProfile(child);
+        }
+        file.delete();
+    }
+
+    private void deleteProfile(SipProfile p) {
+        mSipProfileList.remove(p);
+        SipPreference pref = mSipPreferenceMap.remove(p.getProfileName());
+        mSipListContainer.removePreference(pref);
+        deleteProfile(mProfilesDirectory + p.getProfileName());
+    }
+
+    private void saveProfileToStorage(SipProfile p) throws IOException {
+        if (mProfile != null) deleteProfile(mProfile);
+        File f = new File(mProfilesDirectory + p.getProfileName());
+        if (!f.exists()) f.mkdirs();
+        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
+                new File(f, PROFILE_OBJ_FILE)));
+        oos.writeObject(p);
+        oos.close();
+        mSipProfileList.add(p);
+        addPreferenceFor(p, true);
+    }
+
+    @Override
+    protected void onActivityResult(final int requestCode, final int resultCode,
+            final Intent intent) {
+        Log.v(TAG, "onActivityResult");
+        if (resultCode != RESULT_OK) return;
+        SipProfile profile = intent.getParcelableExtra(KEY_SIP_PROFILE);
+        try {
+            saveProfileToStorage(profile);
+        } catch (IOException e) {
+            Log.v(TAG, "Can not save the profile : " + e.getMessage());
+        }
+        Log.v(TAG, "New Profile Name is " + profile.getProfileName());
+    }
+
+    private SipProfile deserialize(File profileObjectFile) throws IOException {
+        try {
+            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
+                    profileObjectFile));
+            SipProfile p = (SipProfile) ois.readObject();
+            ois.close();
+            return p;
+        } catch (ClassNotFoundException e) {
+            Log.d(TAG, "deserialize a profile", e);
+            return null;
+        }
+    }
+
+    private void startSipEditor(final SipProfile profile) {
+        mProfile = profile;
+        Intent intent = new Intent(this, SipEditor.class);
+        intent.putExtra(KEY_SIP_PROFILE, (Parcelable) profile);
+        startActivityForResult(intent, REQUEST_ADD_OR_EDIT_SIP_PROFILE);
+    }
+
+    private void setSessionSummary(final ISipSession session, final String message) {
+        runOnUiThread(new Runnable() {
+            public void run() {
+                try {
+                    SipPreference pref =
+                            mSipPreferenceMap.get(session.getLocalProfile().getProfileName());
+                    if (pref != null) {
+                        pref.setSummary(message);
+                        if (REGISTERED.equals(message)) {
+                            Log.v(TAG, "========= REGISTERED!!!");
+                        }
+                    }
+                } catch (Exception e) {
+                    Log.e(TAG, "setSessionSummary failed:" + e);
+                }
+            }
+        });
+    }
+
+    private ISipSessionListener createSessionAdapter() {
+        return new SipSessionAdapter() {
+            @Override
+            public void onRegistrationDone(ISipSession session, int duration) {
+                Log.v(TAG, "=========1 REGISTERED!!!");
+                setSessionSummary(session, (duration < 0) ? UNREGISTERED : REGISTERED);
+            }
+
+            @Override
+            public void onRegistrationFailed(ISipSession session,
+                    String className, String message) {
+                setSessionSummary(session, "Registration error: " + message);
+            }
+
+            @Override
+            public void onRegistrationTimeout(ISipSession session) {
+                setSessionSummary(session, "Registration timed out");
+            }
+        };
+    }
+
+    private synchronized void openToReceiveCalls(SipProfile p) {
+        try {
+            mSipService.openToReceiveCalls(p, INCOMING_CALL_ACTION);
+        } catch (Exception e) {
+            Log.e(TAG, "Can not openToReceiveCalls:" + e);
+        }
+    }
+}
diff --git a/src/com/android/sip/demo/SipMain.java b/src/com/android/sip/demo/SipMain.java
index 7d7122d..c5f893a 100644
--- a/src/com/android/sip/demo/SipMain.java
+++ b/src/com/android/sip/demo/SipMain.java
@@ -89,7 +89,7 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        addPreferencesFromResource(R.xml.dev_pref);
+        addPreferencesFromResource(com.android.settings.sip.R.xml.dev_pref);
 
         mCallStatus = getPreferenceScreen().findPreference("call_status");
         mPeerUri = setupEditTextPreference("peer");