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");