Re-add sip contact address

Add linphone tag in native contact app
Add message to avoid delete contact too easily
Migrate old contact with im to sipAddress
This commit is contained in:
Margaux Clerc 2015-03-23 17:41:10 +01:00
parent 885743b8fe
commit 135361c563
25 changed files with 711 additions and 167 deletions

View file

@ -37,6 +37,9 @@
<uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true"/> <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true"/>
@ -45,6 +48,7 @@
<activity android:name="org.linphone.LinphoneLauncherActivity" <activity android:name="org.linphone.LinphoneLauncherActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:windowSoftInputMode="adjustPan|stateHidden" android:windowSoftInputMode="adjustPan|stateHidden"
android:exported="true"
android:theme="@style/NoTitle"> android:theme="@style/NoTitle">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -56,7 +60,7 @@
<action android:name="android.intent.action.CALL_PRIVILEGED" /> <action android:name="android.intent.action.CALL_PRIVILEGED" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" /> <data android:scheme="tel" />
<data android:scheme="sip" /> <data android:scheme="sip" /> />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
@ -64,14 +68,16 @@
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sip" /> <data android:scheme="sip" />
<data android:scheme="imto" /> <data android:scheme="imto" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<data android:scheme="sip" /> <data android:mimeType="vnd.android.cursor.item/org.linphone.profile" />
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="org.linphone.LinphoneActivity" <activity android:name="org.linphone.LinphoneActivity"
@ -81,7 +87,6 @@
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="org.linphone.IncomingCallActivity" <activity android:name="org.linphone.IncomingCallActivity"
@ -154,6 +159,26 @@
android:label="@string/service_name" android:label="@string/service_name"
android:stopWithTask="false"/> android:stopWithTask="false"/>
<service
android:name="org.linphone.sync.SyncService"
android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data android:name="android.content.SyncAdapter" android:resource="@xml/syncadapter" />
<meta-data android:name="android.provider.CONTACTS_STRUCTURE" android:resource="@xml/contacts" />
</service>
<service
android:name="org.linphone.sync.AuthenticationService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
<receiver android:name="org.linphone.NetworkManager"> <receiver android:name="org.linphone.NetworkManager">
<intent-filter><action android:name="android.net.conn.CONNECTIVITY_CHANGE"></action></intent-filter> <intent-filter><action android:name="android.net.conn.CONNECTIVITY_CHANGE"></action></intent-filter>
</receiver> </receiver>

View file

@ -242,6 +242,19 @@
android:visibility="gone" android:visibility="gone"
android:layout_alignParentRight="true" /> android:layout_alignParentRight="true" />
<TextView
android:id="@+id/voicemailCount"
android:textColor="@android:color/white"
android:layout_alignParentRight="true"
android:textSize="18sp"
android:paddingLeft="5dp"
android:paddingRight="10dp"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:visibility="gone" />
<TextView <TextView
android:id="@+id/exit" android:id="@+id/exit"
android:text="@string/menu_exit" android:text="@string/menu_exit"

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="history_date_format">d MMM</string> <string name="history_date_format">d MMM</string>
<string name="history_detail_date_format">EEEE MMM d HH:mm</string> <string name="history_detail_date_format">EEEE MMM d HH:mm</string>
<string name="messages_date_format">HH:mm d MMM</string> <string name="messages_date_format">HH:mm d MMM</string>
@ -10,6 +9,9 @@
<string name="app_name">Linphone</string> <string name="app_name">Linphone</string>
<string name="service_name">Linphone Service</string> <string name="service_name">Linphone Service</string>
<string name="addressbook_label">Linphone</string> <string name="addressbook_label">Linphone</string>
<string name="sync_account_type">org.linphone</string>
<string name="sync_account_name">linphone contacts</string>
<string name="sync_mimetype">vnd.android.cursor.item/org.linphone.profile</string>
<string name="notification_title">Linphone</string> <string name="notification_title">Linphone</string>
<string name="wait_dialog_text">Starting up</string> <string name="wait_dialog_text">Starting up</string>
<string name="notification_registered">Registered to %s </string> <string name="notification_registered">Registered to %s </string>
@ -23,7 +25,6 @@
<string name="setup_login_generic">I already have a SIP account</string> <string name="setup_login_generic">I already have a SIP account</string>
<string name="setup_linphone_account_hint">Enter your linphone.org username and password</string> <string name="setup_linphone_account_hint">Enter your linphone.org username and password</string>
<string name="setup_general_account_hint">Enter your SIP account username, password and domain</string> <string name="setup_general_account_hint">Enter your SIP account username, password and domain</string>
<string name="setup_username_hint">username</string> <string name="setup_username_hint">username</string>
<string name="tunnel_host"></string> <string name="tunnel_host"></string>

View file

@ -353,6 +353,7 @@
<string name="pref_image_sharing_server_title">Sharing server</string> <string name="pref_image_sharing_server_title">Sharing server</string>
<string name="pref_remote_provisioning_title">Remote provisioning</string> <string name="pref_remote_provisioning_title">Remote provisioning</string>
<string name="delete_contact">Delete</string> <string name="delete_contact">Delete</string>
<string name="delete_contact_dialog">This contact will be deleted.</string>
<string name="sip_address">SIP address</string> <string name="sip_address">SIP address</string>
<string name="phone_number">Phone number</string> <string name="phone_number">Phone number</string>
<string name="contact_first_name">First name</string> <string name="contact_first_name">First name</string>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="org.linphone"
android:icon="@drawable/logo_linphone_57x57"
android:smallIcon="@drawable/logo_linphone_57x57"
android:label="@string/app_name"/>

13
res/xml/contacts.xml Normal file
View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<ContactsSource
xmlns:android="http://schemas.android.com/apk/res/android">
<ContactsDataKind
android:mimeType="vnd.android.cursor.item/org.linphone.profile"
android:icon="@drawable/logo_linphone_57x57"
android:summaryColumn="data2"
android:detailColumn="data3"
android:detailSocialSummary="true"/>
</ContactsSource>

7
res/xml/syncadapter.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.android.contacts"
android:accountType="org.linphone"
android:supportsUploading="false"
android:userVisible="false"/>

View file

@ -38,7 +38,7 @@ public class Contact implements Serializable {
private String name; private String name;
private transient Uri photoUri; private transient Uri photoUri;
private transient Bitmap photo; private transient Bitmap photo;
private List<String> numerosOrAddresses; private List<String> numbersOrAddresses;
private LinphoneFriend friend; private LinphoneFriend friend;
public Contact(String id, String name) { public Contact(String id, String name) {
@ -88,14 +88,14 @@ public class Contact implements Serializable {
return photo; return photo;
} }
public List<String> getNumerosOrAddresses() { public List<String> getNumbersOrAddresses() {
if (numerosOrAddresses == null) if (numbersOrAddresses == null)
numerosOrAddresses = new ArrayList<String>(); numbersOrAddresses = new ArrayList<String>();
return numerosOrAddresses; return numbersOrAddresses;
} }
public void refresh(ContentResolver cr) { public void refresh(ContentResolver cr) {
this.numerosOrAddresses = Compatibility.extractContactNumbersAndAddresses(id, cr); this.numbersOrAddresses = Compatibility.extractContactNumbersAndAddresses(id, cr);
this.name = Compatibility.refreshContactName(cr, id); this.name = Compatibility.refreshContactName(cr, id);
} }
} }

View file

@ -27,7 +27,9 @@ import org.linphone.mediastream.Log;
import org.linphone.ui.AvatarWithShadow; import org.linphone.ui.AvatarWithShadow;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.ContentProviderOperation; import android.content.ContentProviderOperation;
import android.content.DialogInterface;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.os.Bundle; import android.os.Bundle;
import android.provider.ContactsContract; import android.provider.ContactsContract;
@ -122,7 +124,7 @@ public class ContactFragment extends Fragment implements OnClickListener {
TableLayout controls = (TableLayout) view.findViewById(R.id.controls); TableLayout controls = (TableLayout) view.findViewById(R.id.controls);
controls.removeAllViews(); controls.removeAllViews();
for (String numberOrAddress : contact.getNumerosOrAddresses()) { for (String numberOrAddress : contact.getNumbersOrAddresses()) {
View v = inflater.inflate(R.layout.contact_control_row, null); View v = inflater.inflate(R.layout.contact_control_row, null);
String displayednumberOrAddress = numberOrAddress; String displayednumberOrAddress = numberOrAddress;
@ -221,10 +223,18 @@ public class ContactFragment extends Fragment implements OnClickListener {
if (id == R.id.editContact) { if (id == R.id.editContact) {
LinphoneActivity.instance().editContact(contact); LinphoneActivity.instance().editContact(contact);
} else if (id == R.id.deleteContact) { } else if (id == R.id.deleteContact) {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(getActivity());
alertDialog.setMessage(getString(R.string.delete_contact_dialog));
alertDialog.setPositiveButton(getString(R.string.button_ok),new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
deleteExistingContact(); deleteExistingContact();
LinphoneActivity.instance().removeContactFromLists(contact); LinphoneActivity.instance().removeContactFromLists(contact);
LinphoneActivity.instance().displayContacts(false); LinphoneActivity.instance().displayContacts(false);
} }
});
alertDialog.setNegativeButton(getString(R.string.button_cancel),null);
alertDialog.show();
}
} }
private void deleteExistingContact() { private void deleteExistingContact() {

View file

@ -29,6 +29,7 @@ import android.content.ContentUris;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Contacts;
import android.text.TextUtils; import android.text.TextUtils;
@ -98,6 +99,35 @@ public final class ContactHelper {
return testPhotoUriAndCloseCursor(cursor); return testPhotoUriAndCloseCursor(cursor);
} }
public static String queryAddressOrNumber(ContentResolver resolver, Uri contactUri) {
// Phone Numbers
String[] projection = new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER};
Cursor c = resolver.query(contactUri, projection, null, null, null);
if (c != null) {
while (c.moveToNext()) {
int numberIndex = c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
String number = c.getString(numberIndex);
c.close();
return number;
}
}
// SIP addresses
projection = new String[] {ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS};
c = resolver.query(contactUri, projection, null, null, null);
if (c != null) {
while (c.moveToNext()) {
int numberIndex = c.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS);
String address = c.getString(numberIndex);
c.close();
return address;
}
}
c.close();
return null;
}
private void checkPhotosUris(ContentResolver resolver, Cursor c, String idCol, String nameCol) { private void checkPhotosUris(ContentResolver resolver, Cursor c, String idCol, String nameCol) {
displayName = c.getString(c.getColumnIndex(nameCol)); displayName = c.getString(c.getColumnIndex(nameCol));
@ -180,12 +210,50 @@ public final class ContactHelper {
return contactFound; return contactFound;
} }
static boolean isContactHasLinphoneTag(Contact contact, ContentResolver cr) {
String select = ContactsContract.Data.CONTACT_ID + " = ?";
String[] args = new String[] { contact.getID() };
String[] projection = new String[] {ContactsContract.Data.MIMETYPE };
Cursor cursor = cr.query(ContactsContract.Data.CONTENT_URI, projection, select, args, null);
if (cursor != null) {
while (cursor.moveToNext()) {
if(cursor.getString(cursor.getColumnIndex(ContactsContract.Data.MIMETYPE)).equals("vnd.android.cursor.item/org.linphone.profile")){
cursor.close();
return true;
}
}
}
cursor.close();
return false;
}
public static String findRawContactID(ContentResolver cr, String contactID) {
Cursor c = cr.query(ContactsContract.RawContacts.CONTENT_URI,
new String[]{ContactsContract.RawContacts._ID},
ContactsContract.RawContacts.CONTACT_ID + "=?",
new String[]{contactID}, null);
if (c != null) {
String result = null;
if (c.moveToFirst()) {
result = c.getString(c.getColumnIndex(ContactsContract.RawContacts._ID));
}
c.close();
return result;
}
return null;
}
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
private final boolean queryContact() { private final boolean queryContact() {
boolean contactFound = false; boolean contactFound = false;
Uri uri = android.provider.ContactsContract.Data.CONTENT_URI; Uri uri = android.provider.ContactsContract.Data.CONTENT_URI;
String[] projection = { android.provider.ContactsContract.Data.CONTACT_ID, android.provider.ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, android.provider.ContactsContract.CommonDataKinds.Im.DATA }; String[] projection = { android.provider.ContactsContract.Data.CONTACT_ID, android.provider.ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
android.provider.ContactsContract.CommonDataKinds.Im.DATA };
String selection = new StringBuilder() String selection = new StringBuilder()
.append(android.provider.ContactsContract.Data.MIMETYPE) .append(android.provider.ContactsContract.Data.MIMETYPE)
@ -211,6 +279,7 @@ public final class ContactHelper {
c = resolver.query(uri, projection, selection, null, null); c = resolver.query(uri, projection, selection, null, null);
contactFound = checkSIPQueryResult(c, android.provider.ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS); contactFound = checkSIPQueryResult(c, android.provider.ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS);
if (contactFound) { if (contactFound) {
c.close();
return true; return true;
} }
} }
@ -222,7 +291,7 @@ public final class ContactHelper {
android.provider.ContactsContract.PhoneLookup.DISPLAY_NAME }; android.provider.ContactsContract.PhoneLookup.DISPLAY_NAME };
c = resolver.query(lookupUri, projection, null, null, null); c = resolver.query(lookupUri, projection, null, null, null);
contactFound = checkPhoneQueryResult(c, android.provider.ContactsContract.PhoneLookup.NUMBER); contactFound = checkPhoneQueryResult(c, android.provider.ContactsContract.PhoneLookup.NUMBER);
c.close();
return contactFound; return contactFound;
} }
} }

View file

@ -198,9 +198,15 @@ public class DialerFragment extends Fragment {
} else if (scheme.startsWith("call") || scheme.startsWith("sip")) { } else if (scheme.startsWith("call") || scheme.startsWith("sip")) {
mAddress.setText(intent.getData().getSchemeSpecificPart()); mAddress.setText(intent.getData().getSchemeSpecificPart());
} else { } else {
Log.e("Unknown scheme: ",scheme); Uri contactUri = intent.getData();
String address = ContactHelper.queryAddressOrNumber(getActivity().getContentResolver(),contactUri);
if(address != null) {
mAddress.setText(address);
} else {
Log.e("Unknown scheme: ", scheme);
mAddress.setText(intent.getData().getSchemeSpecificPart()); mAddress.setText(intent.getData().getSchemeSpecificPart());
} }
}
mAddress.clearDisplayedName(); mAddress.clearDisplayedName();
intent.setData(null); intent.setData(null);

View file

@ -201,7 +201,7 @@ public class EditContactFragment extends Fragment {
numbersAndAddresses = new ArrayList<NewOrUpdatedNumberOrAddress>(); numbersAndAddresses = new ArrayList<NewOrUpdatedNumberOrAddress>();
if (contact != null) { if (contact != null) {
for (String numberOrAddress : contact.getNumerosOrAddresses()) { for (String numberOrAddress : contact.getNumbersOrAddresses()) {
View view = displayNumberOrAddress(controls, numberOrAddress); View view = displayNumberOrAddress(controls, numberOrAddress);
if (view != null) if (view != null)
controls.addView(view); controls.addView(view);
@ -288,6 +288,7 @@ public class EditContactFragment extends Fragment {
nounoa.delete(); nounoa.delete();
numbersAndAddresses.remove(nounoa); numbersAndAddresses.remove(nounoa);
view.setVisibility(View.GONE); view.setVisibility(View.GONE);
} }
}); });
return view; return view;
@ -358,8 +359,11 @@ public class EditContactFragment extends Fragment {
contactID = 0; contactID = 0;
ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.AGGREGATION_MODE, ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT)
.withValue(RawContacts.ACCOUNT_TYPE, null) .withValue(RawContacts.ACCOUNT_TYPE, null)
.withValue(RawContacts.ACCOUNT_NAME, null).build()); .withValue(RawContacts.ACCOUNT_NAME, null)
.build()
);
if (getDisplayName() != null) { if (getDisplayName() != null) {
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
@ -400,8 +404,7 @@ public class EditContactFragment extends Fragment {
private String findRawContactID(String contactID) { private String findRawContactID(String contactID) {
Cursor c = getActivity().getContentResolver().query(RawContacts.CONTENT_URI, Cursor c = getActivity().getContentResolver().query(RawContacts.CONTENT_URI,
new String[]{RawContacts._ID}, new String[]{RawContacts._ID},RawContacts.CONTACT_ID + "=?",
RawContacts.CONTACT_ID + "=?",
new String[]{contactID}, null); new String[]{contactID}, null);
if (c != null) { if (c != null) {
String result = null; String result = null;
@ -414,6 +417,24 @@ public class EditContactFragment extends Fragment {
return null; return null;
} }
private String findRawLinphoneContactID(String contactID) {
String result = null;
String[] projection = { RawContacts._ID };
String selection = RawContacts.CONTACT_ID + "=? AND "
+ RawContacts.ACCOUNT_TYPE + "=? ";
Cursor c = getActivity().getContentResolver().query(RawContacts.CONTENT_URI, projection,
selection, new String[]{contactID, getString(R.string.sync_account_type)}, null);
if (c != null) {
if (c.moveToFirst()) {
result = c.getString(c.getColumnIndex(ContactsContract.RawContacts._ID));
}
}
c.close();
return result;
}
private String findContactFirstName(String contactID) { private String findContactFirstName(String contactID) {
Cursor c = getActivity().getContentResolver().query(ContactsContract.Data.CONTENT_URI, Cursor c = getActivity().getContentResolver().query(ContactsContract.Data.CONTENT_URI,
new String[]{ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME}, new String[]{ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME},
@ -495,6 +516,7 @@ public class EditContactFragment extends Fragment {
public void delete() { public void delete() {
if (isSipAddress) { if (isSipAddress) {
Compatibility.deleteSipAddressFromContact(ops, oldNumberOrAddress, String.valueOf(contactID)); Compatibility.deleteSipAddressFromContact(ops, oldNumberOrAddress, String.valueOf(contactID));
Compatibility.deleteLinphoneContactTag(ops, oldNumberOrAddress, findRawLinphoneContactID(String.valueOf(contactID)));
} else { } else {
String select = ContactsContract.Data.CONTACT_ID + "=? AND " String select = ContactsContract.Data.CONTACT_ID + "=? AND "
+ ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "' AND " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "' AND "
@ -532,13 +554,14 @@ public class EditContactFragment extends Fragment {
} }
} else { } else {
String rawContactId = findRawContactID(String.valueOf(contactID)); String rawContactId = findRawContactID(String.valueOf(contactID));
if (isSipAddress) { if (isSipAddress) {
if (newNumberOrAddress.startsWith("sip:")) if (newNumberOrAddress.startsWith("sip:"))
newNumberOrAddress = newNumberOrAddress.substring(4); newNumberOrAddress = newNumberOrAddress.substring(4);
if(!newNumberOrAddress.contains("@")) if(!newNumberOrAddress.contains("@"))
newNumberOrAddress = newNumberOrAddress + "@" + getResources().getString(R.string.default_domain); newNumberOrAddress = newNumberOrAddress + "@" + getResources().getString(R.string.default_domain);
Compatibility.addSipAddressToContact(getActivity(), ops, newNumberOrAddress, rawContactId); Compatibility.addSipAddressToContact(getActivity(), ops, newNumberOrAddress, rawContactId);
Compatibility.addLinphoneContactTag(getActivity(), ops, newNumberOrAddress, findRawLinphoneContactID(String.valueOf(contactID)));
} else { } else {
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId) .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId)
@ -563,6 +586,7 @@ public class EditContactFragment extends Fragment {
if(!newNumberOrAddress.contains("@")) if(!newNumberOrAddress.contains("@"))
newNumberOrAddress = newNumberOrAddress + "@" + getResources().getString(R.string.default_domain); newNumberOrAddress = newNumberOrAddress + "@" + getResources().getString(R.string.default_domain);
Compatibility.updateSipAddressForContact(ops, oldNumberOrAddress, newNumberOrAddress, String.valueOf(contactID)); Compatibility.updateSipAddressForContact(ops, oldNumberOrAddress, newNumberOrAddress, String.valueOf(contactID));
Compatibility.updateLinphoneContactTag(getActivity(), ops, newNumberOrAddress, oldNumberOrAddress, findRawLinphoneContactID(String.valueOf(contactID)));
} else { } else {
String select = ContactsContract.Data.CONTACT_ID + "=? AND " String select = ContactsContract.Data.CONTACT_ID + "=? AND "
+ ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "' AND " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "' AND "

View file

@ -49,9 +49,13 @@ import org.linphone.setup.RemoteProvisioningLoginActivity;
import org.linphone.setup.SetupActivity; import org.linphone.setup.SetupActivity;
import org.linphone.ui.AddressText; import org.linphone.ui.AddressText;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
@ -59,6 +63,7 @@ import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.Fragment.SavedState; import android.support.v4.app.Fragment.SavedState;
@ -110,6 +115,7 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
private Cursor contactCursor, sipContactCursor; private Cursor contactCursor, sipContactCursor;
private OrientationEventListener mOrientationHelper; private OrientationEventListener mOrientationHelper;
private LinphoneCoreListenerBase mListener; private LinphoneCoreListenerBase mListener;
private Account mAccount;
static final boolean isInstanciated() { static final boolean isInstanciated() {
return instance != null; return instance != null;
@ -153,6 +159,12 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
} }
} }
/*if(!LinphonePreferences.instance().isContactsMigrationDone()){
migrateContacts(this);
LinphonePreferences.instance().contactsMigrationDone();
}*/
setContentView(R.layout.main); setContentView(R.layout.main);
instance = this; instance = this;
fragmentsHistory = new ArrayList<FragmentsAvailable>(); fragmentsHistory = new ArrayList<FragmentsAvailable>();
@ -169,6 +181,8 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
} }
} }
mAccount = initializeSyncAccount(this);
mListener = new LinphoneCoreListenerBase(){ mListener = new LinphoneCoreListenerBase(){
@Override @Override
public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) { public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) {
@ -248,6 +262,13 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
updateAnimationsState(); updateAnimationsState();
} }
private Account initializeSyncAccount(Context context) {
Account newAccount = new Account(context.getString(R.string.sync_account_name), context.getString(R.string.sync_account_type));
AccountManager accountManager = (AccountManager) context.getSystemService(ACCOUNT_SERVICE);
accountManager.addAccountExplicitly(newAccount, null, null);
return newAccount;
}
private void initButtons() { private void initButtons() {
menu = (LinearLayout) findViewById(R.id.menu); menu = (LinearLayout) findViewById(R.id.menu);
mark = (LinearLayout) findViewById(R.id.mark); mark = (LinearLayout) findViewById(R.id.mark);
@ -1007,7 +1028,7 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
} }
for (Contact contact : sipContactList) { for (Contact contact : sipContactList) {
for (String addr : contact.getNumerosOrAddresses()) { for (String addr : contact.getNumbersOrAddresses()) {
if (addr.equals(sipUri)) { if (addr.equals(sipUri)) {
return contact; return contact;
} }
@ -1055,11 +1076,11 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
} }
private void searchFriendAndAddToContact(Contact contact) { private void searchFriendAndAddToContact(Contact contact) {
if (contact == null || contact.getNumerosOrAddresses() == null) { if (contact == null || contact.getNumbersOrAddresses() == null) {
return; return;
} }
for (String sipUri : contact.getNumerosOrAddresses()) { for (String sipUri : contact.getNumbersOrAddresses()) {
if (LinphoneUtils.isSipAddress(sipUri)) { if (LinphoneUtils.isSipAddress(sipUri)) {
LinphoneFriend friend = LinphoneManager.getLc().findFriendByAddress(sipUri); LinphoneFriend friend = LinphoneManager.getLc().findFriendByAddress(sipUri);
if (friend != null) { if (friend != null) {
@ -1090,6 +1111,46 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
} }
} }
public void migrateContacts(Context context) {
ContentResolver cr = getContentResolver();
Cursor oldContacts = Compatibility.getImContactsCursor(cr);
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
if(oldContacts != null){
for (int i = 0; i < oldContacts.getCount(); i++) {
Contact c = Compatibility.getContact(cr, oldContacts, i);
for (String address : Compatibility.extractContactImAddresses(c.getID(), cr)) {
if (LinphoneUtils.isSipAddress(address)) {
if (address.startsWith("sip:")) {
address = address.substring(4);
}
Compatibility.addSipAddressToContact(context, ops, address, ContactHelper.findRawContactID(cr,c.getID()));
try {
cr.applyBatch(ContactsContract.AUTHORITY, ops);
} catch (Exception e) {
e.printStackTrace();
}
ops.clear();
c.refresh(cr);
if(c.getNumbersOrAddresses().contains("sip:"+address)){
Compatibility.deleteImAddressFromContact(ops, address, c.getID());
try {
cr.applyBatch(ContactsContract.AUTHORITY, ops);
} catch (Exception e) {
e.printStackTrace();
}
} else {
Log.w("Cannot migrate this contact " + c.getName());
}
}
}
ops.clear();
}
}
}
public synchronized void prepareContactsInBackground() { public synchronized void prepareContactsInBackground() {
if (contactCursor != null) { if (contactCursor != null) {
contactCursor.close(); contactCursor.close();
@ -1105,21 +1166,29 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
@Override @Override
public void run() { public void run() {
if(sipContactCursor != null) { if(sipContactCursor != null) {
Log.w(sipContactCursor.getCount());
for (int i = 0; i < sipContactCursor.getCount(); i++) { for (int i = 0; i < sipContactCursor.getCount(); i++) {
Contact contact = Compatibility.getContact(getContentResolver(), sipContactCursor, i); Contact contact = Compatibility.getContact(getContentResolver(), sipContactCursor, i);
if (contact == null) if (contact == null)
continue; continue;
contact.refresh(getContentResolver()); contact.refresh(getContentResolver());
//Add tag to Linphone contact if it not existed
if(!ContactHelper.isContactHasLinphoneTag(contact,getContentResolver())){
Compatibility.createLinphoneContactTag(getApplicationContext(),getContentResolver(),contact,
ContactHelper.findRawContactID(getContentResolver(),String.valueOf(contact.getID())));
}
if (!isContactPresenceDisabled) { if (!isContactPresenceDisabled) {
searchFriendAndAddToContact(contact); searchFriendAndAddToContact(contact);
} }
sipContactList.add(contact); sipContactList.add(contact);
} }
} }
if(contactCursor != null) { if(contactCursor != null){
for (int i = 0; i < contactCursor.getCount(); i++) { for (int i = 0; i < contactCursor.getCount(); i++) {
Contact contact = Compatibility.getContact(getContentResolver(), contactCursor, i); Contact contact = Compatibility.getContact(getContentResolver(), contactCursor, i);
if (contact == null) if (contact == null)
continue; continue;
@ -1132,6 +1201,7 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
contactList.add(contact); contactList.add(contact);
} }
} }
getContentResolver().requestSync(mAccount, ContactsContract.AUTHORITY, new Bundle());
} }
}); });
@ -1195,6 +1265,7 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
extras.putSerializable("Contact", contact); extras.putSerializable("Contact", contact);
changeCurrentFragment(FragmentsAvailable.EDIT_CONTACT, extras); changeCurrentFragment(FragmentsAvailable.EDIT_CONTACT, extras);
} }
} }
public void editContact(Contact contact, String sipAddress) public void editContact(Contact contact, String sipAddress)

View file

@ -1143,4 +1143,12 @@ public class LinphonePreferences {
public void setCodecBitrateLimit(int bitrate) { public void setCodecBitrateLimit(int bitrate) {
getConfig().setInt("audio", "codec_bitrate_limit", bitrate); getConfig().setInt("audio", "codec_bitrate_limit", bitrate);
} }
public void contactsMigrationDone(){
getConfig().setBool("app", "contacts_migration_done",true);
}
public boolean isContactsMigrationDone(){
return getConfig().getBool("app", "contacts_migration_done",false);
}
} }

View file

@ -19,7 +19,6 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
import org.linphone.LinphoneManager;
import org.linphone.LinphonePreferences.AccountBuilder; import org.linphone.LinphonePreferences.AccountBuilder;
import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCoreException; import org.linphone.core.LinphoneCoreException;
@ -28,6 +27,7 @@ import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.Editor;
import android.content.res.Resources; import android.content.res.Resources;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
/** /**

View file

@ -134,7 +134,6 @@ public class StatusFragment extends Fragment {
}); });
} }
// setMiniLedsForEachAccount(); // setMiniLedsForEachAccount();
populateSliderContent();
populateSliderContent(); populateSliderContent();
sliderContentAccounts.invalidate(); sliderContentAccounts.invalidate();
} catch (IllegalStateException ise) {} } catch (IllegalStateException ise) {}
@ -167,7 +166,6 @@ public class StatusFragment extends Fragment {
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
if (lc != null) { if (lc != null) {
lc.addListener(mListener); lc.addListener(mListener);
LinphoneProxyConfig lpc = lc.getDefaultProxyConfig(); LinphoneProxyConfig lpc = lc.getDefaultProxyConfig();
if (lpc != null) { if (lpc != null) {
mListener.registrationState(lc, lpc, lpc.getState(), null); mListener.registrationState(lc, lpc, lpc.getState(), null);

View file

@ -18,7 +18,7 @@ import android.graphics.Bitmap;
import android.media.AudioManager; import android.media.AudioManager;
import android.net.Uri; import android.net.Uri;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Im; import android.provider.ContactsContract.CommonDataKinds.SipAddress;
import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Intents.Insert; import android.provider.ContactsContract.Intents.Insert;
@ -144,9 +144,8 @@ public class ApiElevenPlus {
ArrayList<ContentValues> data = new ArrayList<ContentValues>(); ArrayList<ContentValues> data = new ArrayList<ContentValues>();
ContentValues sipAddressRow = new ContentValues(); ContentValues sipAddressRow = new ContentValues();
sipAddressRow.put(Contacts.Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); sipAddressRow.put(Contacts.Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
sipAddressRow.put(Im.DATA, sipUri); sipAddressRow.put(SipAddress.SIP_ADDRESS, sipUri);
sipAddressRow.put(Im.CUSTOM_PROTOCOL,"Sip");
data.add(sipAddressRow); data.add(sipAddressRow);
intent.putParcelableArrayListExtra(Insert.DATA, data); intent.putParcelableArrayListExtra(Insert.DATA, data);
@ -160,10 +159,8 @@ public class ApiElevenPlus {
ArrayList<ContentValues> data = new ArrayList<ContentValues>(); ArrayList<ContentValues> data = new ArrayList<ContentValues>();
ContentValues sipAddressRow = new ContentValues(); ContentValues sipAddressRow = new ContentValues();
sipAddressRow.put(Contacts.Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); sipAddressRow.put(Contacts.Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
sipAddressRow.put(Im.DATA, sipUri); sipAddressRow.put(SipAddress.SIP_ADDRESS, sipUri);
sipAddressRow.put(Im.CUSTOM_PROTOCOL,"Sip");
data.add(sipAddressRow);
data.add(sipAddressRow); data.add(sipAddressRow);
intent.putParcelableArrayListExtra(Insert.DATA, data); intent.putParcelableArrayListExtra(Insert.DATA, data);

View file

@ -111,7 +111,7 @@ public class ApiFivePlus {
c.close(); c.close();
} }
// SIP addresses // IM addresses
String selection = new StringBuilder() String selection = new StringBuilder()
.append(Data.CONTACT_ID).append(" = ? AND ") .append(Data.CONTACT_ID).append(" = ? AND ")
.append(Data.MIMETYPE).append(" = '") .append(Data.MIMETYPE).append(" = '")
@ -246,7 +246,7 @@ public class ApiFivePlus {
Cursor cursor = getSIPContactCursor(cr, sipUri); Cursor cursor = getSIPContactCursor(cr, sipUri);
Contact contact = getContact(cr, cursor, 0); Contact contact = getContact(cr, cursor, 0);
if (contact != null && contact.getNumerosOrAddresses().contains(sipUri)) { if (contact != null && contact.getNumbersOrAddresses().contains(sipUri)) {
address.setDisplayName(contact.getName()); address.setDisplayName(contact.getName());
cursor.close(); cursor.close();
return contact.getPhotoUri(); return contact.getPhotoUri();

View file

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.linphone.Contact; import org.linphone.Contact;
import org.linphone.LinphoneUtils;
import org.linphone.R; import org.linphone.R;
import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneAddress;
@ -46,12 +47,10 @@ public class ApiNinePlus {
public static void addSipAddressToContact(Context context, ArrayList<ContentProviderOperation> ops, String sipAddress) { public static void addSipAddressToContact(Context context, ArrayList<ContentProviderOperation> ops, String sipAddress) {
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE) .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Im.DATA, sipAddress) .withValue(ContactsContract.CommonDataKinds.SipAddress.DATA, sipAddress)
.withValue(ContactsContract.CommonDataKinds.Im.TYPE, ContactsContract.CommonDataKinds.Im.TYPE_CUSTOM) .withValue(CommonDataKinds.SipAddress.TYPE, CommonDataKinds.SipAddress.TYPE_CUSTOM)
.withValue(ContactsContract.CommonDataKinds.Im.PROTOCOL, ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM) .withValue(CommonDataKinds.SipAddress.LABEL, context.getString(R.string.addressbook_label))
.withValue(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL,"Sip")
.withValue(ContactsContract.CommonDataKinds.Im.LABEL, context.getString(R.string.addressbook_label))
.build() .build()
); );
} }
@ -59,35 +58,33 @@ public class ApiNinePlus {
public static void addSipAddressToContact(Context context, ArrayList<ContentProviderOperation> ops, String sipAddress, String rawContactID) { public static void addSipAddressToContact(Context context, ArrayList<ContentProviderOperation> ops, String sipAddress, String rawContactID) {
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactID) .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactID)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE) .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Im.DATA, sipAddress) .withValue(ContactsContract.CommonDataKinds.SipAddress.DATA, sipAddress)
.withValue(ContactsContract.CommonDataKinds.Im.TYPE, ContactsContract.CommonDataKinds.Im.TYPE_CUSTOM) .withValue(CommonDataKinds.SipAddress.TYPE, CommonDataKinds.SipAddress.TYPE_CUSTOM)
.withValue(ContactsContract.CommonDataKinds.Im.PROTOCOL, ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM) .withValue(CommonDataKinds.SipAddress.LABEL, context.getString(R.string.addressbook_label))
.withValue(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL,"Sip")
.withValue(ContactsContract.CommonDataKinds.Im.LABEL, context.getString(R.string.addressbook_label))
.build() .build()
); );
} }
public static void updateSipAddressForContact(ArrayList<ContentProviderOperation> ops, String oldIm, String newIm, String contactID) { public static void updateSipAddressForContact(ArrayList<ContentProviderOperation> ops, String oldSipAddress, String newSipAddress, String contactID) {
String select = ContactsContract.Data.CONTACT_ID + "=? AND " String select = ContactsContract.Data.CONTACT_ID + "=? AND "
+ ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE + "' AND " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "' AND "
+ ContactsContract.CommonDataKinds.Im.DATA + "=? AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip'"; + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + "=?";
String[] args = new String[] { String.valueOf(contactID), oldIm }; String[] args = new String[] { String.valueOf(contactID), oldSipAddress };
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(select, args) .withSelection(select, args)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Im.DATA, newIm) .withValue(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS, newSipAddress)
.build() .build()
); );
} }
public static void deleteSipAddressFromContact(ArrayList<ContentProviderOperation> ops, String oldIm, String contactID) { public static void deleteSipAddressFromContact(ArrayList<ContentProviderOperation> ops, String oldSipAddress, String contactID) {
String select = ContactsContract.Data.CONTACT_ID + "=? AND " String select = ContactsContract.Data.CONTACT_ID + "=? AND "
+ ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE + "' AND " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "' AND "
+ ContactsContract.CommonDataKinds.Im.DATA + "=? AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip'"; + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + "=? ";
String[] args = new String[] { String.valueOf(contactID), oldIm }; String[] args = new String[] { String.valueOf(contactID), oldSipAddress };
ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(select, args) .withSelection(select, args)
@ -99,7 +96,7 @@ public class ApiNinePlus {
List<String> list = new ArrayList<String>(); List<String> list = new ArrayList<String>();
Uri uri = Data.CONTENT_URI; Uri uri = Data.CONTENT_URI;
String[] projection = {ContactsContract.CommonDataKinds.Im.DATA}; String[] projection;
// Phone Numbers // Phone Numbers
Cursor c = cr.query(Phone.CONTENT_URI, new String[] { Phone.NUMBER }, Phone.CONTACT_ID + " = " + id, null, null); Cursor c = cr.query(Phone.CONTENT_URI, new String[] { Phone.NUMBER }, Phone.CONTACT_ID + " = " + id, null, null);
@ -111,27 +108,6 @@ public class ApiNinePlus {
c.close(); c.close();
} }
// IM addresses
String selection = new StringBuilder()
.append(Data.CONTACT_ID)
.append(" = ? AND ")
.append(Data.MIMETYPE)
.append(" = '")
.append(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
.append("' AND lower(")
.append(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL)
.append(") = 'sip'")
.toString();
projection = new String[] {ContactsContract.CommonDataKinds.Im.DATA};
c = cr.query(uri, projection, selection, new String[]{id}, null);
if (c != null) {
int nbId = c.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA);
while (c.moveToNext()) {
list.add("sip:" + c.getString(nbId));
}
c.close();
}
// SIP addresses // SIP addresses
String selection2 = new StringBuilder() String selection2 = new StringBuilder()
.append(Data.CONTACT_ID) .append(Data.CONTACT_ID)
@ -155,13 +131,10 @@ public class ApiNinePlus {
} }
public static Cursor getContactsCursor(ContentResolver cr, String search) { public static Cursor getContactsCursor(ContentResolver cr, String search) {
String req = "(" + Data.MIMETYPE + " = '" + CommonDataKinds.Phone.CONTENT_ITEM_TYPE String req = Data.MIMETYPE + " = '" + CommonDataKinds.Phone.CONTENT_ITEM_TYPE
+ "' AND " + CommonDataKinds.Phone.NUMBER + " IS NOT NULL OR (" + "' AND " + CommonDataKinds.Phone.NUMBER + " IS NOT NULL OR ("
+ Data.MIMETYPE + " = '" + CommonDataKinds.Im.CONTENT_ITEM_TYPE + Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
+ "' AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip'" + "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL)";
+ " AND " + ContactsContract.CommonDataKinds.Im.DATA + " IS NOT NULL"
+ ") OR (" + Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
+ "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL))";
if (search != null) { if (search != null) {
req += " AND " + Data.DISPLAY_NAME + " LIKE '%" + search + "%'"; req += " AND " + Data.DISPLAY_NAME + " LIKE '%" + search + "%'";
@ -172,11 +145,8 @@ public class ApiNinePlus {
public static Cursor getSIPContactsCursor(ContentResolver cr, String search) { public static Cursor getSIPContactsCursor(ContentResolver cr, String search) {
String req = null; String req = null;
req = "(" + Data.MIMETYPE + " = '" + CommonDataKinds.Im.CONTENT_ITEM_TYPE req = Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
+ "' AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip'" + "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL";
+ " AND " + ContactsContract.CommonDataKinds.Im.DATA + " IS NOT NULL"
+ " OR (" + Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
+ "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL))";
if (search != null) { if (search != null) {
req += " AND " + Data.DISPLAY_NAME + " LIKE '%" + search + "%'"; req += " AND " + Data.DISPLAY_NAME + " LIKE '%" + search + "%'";
@ -187,15 +157,11 @@ public class ApiNinePlus {
private static Cursor getSIPContactCursor(ContentResolver cr, String id) { private static Cursor getSIPContactCursor(ContentResolver cr, String id) {
String req = null; String req = null;
req = "(" + Contacts.Data.MIMETYPE + " = '" + CommonDataKinds.Im.CONTENT_ITEM_TYPE req = Contacts.Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
+ " AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip' AND " + "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " LIKE '" + id + "'";
+ android.provider.ContactsContract.CommonDataKinds.Im.DATA + " LIKE '" + id + "' "
+ " OR " + Contacts.Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
+ " AND " + android.provider.ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " LIKE '" + id + "'";
return ApiFivePlus.getGeneralContactCursor(cr, req, false); return ApiFivePlus.getGeneralContactCursor(cr, req, false);
} }
public static Uri findUriPictureOfContactAndSetDisplayName(LinphoneAddress address, ContentResolver cr) { public static Uri findUriPictureOfContactAndSetDisplayName(LinphoneAddress address, ContentResolver cr) {
String username = address.getUserName(); String username = address.getUserName();
String domain = address.getDomain(); String domain = address.getDomain();
@ -203,7 +169,7 @@ public class ApiNinePlus {
Cursor cursor = getSIPContactCursor(cr, sipUri); Cursor cursor = getSIPContactCursor(cr, sipUri);
Contact contact = ApiFivePlus.getContact(cr, cursor, 0); Contact contact = ApiFivePlus.getContact(cr, cursor, 0);
if (contact != null && contact.getNumerosOrAddresses().contains(sipUri)) { if (contact != null && contact.getNumbersOrAddresses().contains(sipUri)) {
address.setDisplayName(contact.getName()); address.setDisplayName(contact.getName());
cursor.close(); cursor.close();
return contact.getPhotoUri(); return contact.getPhotoUri();
@ -212,4 +178,85 @@ public class ApiNinePlus {
cursor.close(); cursor.close();
return null; return null;
} }
//Linphone Contacts Tag
public static void addLinphoneContactTag(Context context, ArrayList<ContentProviderOperation> ops, String newAddress, String rawContactId){
if(rawContactId != null) {
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId)
.withValue(ContactsContract.Data.MIMETYPE, context.getString(R.string.sync_mimetype))
.withValue(ContactsContract.Data.DATA1, newAddress)
.withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name))
.withValue(ContactsContract.Data.DATA3, newAddress)
.build()
);
}
}
public static void updateLinphoneContactTag(Context context, ArrayList<ContentProviderOperation> ops, String newAddress, String oldAddress, String rawContactId){
if(rawContactId != null) {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.DATA1 + "=? ", new String[]{rawContactId, oldAddress})
.withValue(ContactsContract.Data.DATA1, newAddress)
.withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name))
.withValue(ContactsContract.Data.DATA3, newAddress)
.build());
}
}
public static void deleteLinphoneContactTag(ArrayList<ContentProviderOperation> ops , String oldAddress, String rawContactId){
if(rawContactId != null) {
ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.DATA1 + "=? ", new String[]{rawContactId, oldAddress})
.build());
}
}
public static void createLinphoneContactTag(Context context, ContentResolver contentResolver, Contact contact, String rawContactId){
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
if (contact != null) {
ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.AGGREGATION_MODE, ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, context.getString(R.string.sync_account_type))
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, context.getString(R.string.sync_account_name))
.build()
);
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, contact.getName())
.build()
);
List<String> numbersOrAddresses = contact.getNumbersOrAddresses();
for (String numberOrAddress : numbersOrAddresses) {
if (LinphoneUtils.isSipAddress(numberOrAddress)) {
if (numberOrAddress.startsWith("sip:")){
numberOrAddress = numberOrAddress.substring(4);
}
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, context.getString(R.string.sync_mimetype))
.withValue(ContactsContract.Data.DATA1, numberOrAddress)
.withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name))
.withValue(ContactsContract.Data.DATA3, numberOrAddress)
.build()
);
}
}
ops.add(ContentProviderOperation.newUpdate(ContactsContract.AggregationExceptions.CONTENT_URI)
.withValue(ContactsContract.AggregationExceptions.TYPE, ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER)
.withValue(ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, rawContactId)
.withValueBackReference(ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, 0).build());
try {
contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
} catch (Exception e) {
e.printStackTrace();
}
}
}
} }

View file

@ -80,6 +80,10 @@ public class Compatibility {
} }
} }
public static List<String> extractContactImAddresses(String id, ContentResolver cr) {
return ApiFivePlus.extractContactNumbersAndAddresses(id, cr);
}
public static Cursor getContactsCursor(ContentResolver cr) { public static Cursor getContactsCursor(ContentResolver cr) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) { if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
return ApiNinePlus.getContactsCursor(cr, null); return ApiNinePlus.getContactsCursor(cr, null);
@ -112,6 +116,10 @@ public class Compatibility {
} }
} }
public static Cursor getImContactsCursor(ContentResolver cr) {
return ApiFivePlus.getSIPContactsCursor(cr);
}
public static int getCursorDisplayNameColumnIndex(Cursor cursor) { public static int getCursorDisplayNameColumnIndex(Cursor cursor) {
if (Version.sdkAboveOrEqual(Version.API05_ECLAIR_20)) { if (Version.sdkAboveOrEqual(Version.API05_ECLAIR_20)) {
return ApiFivePlus.getCursorDisplayNameColumnIndex(cursor); return ApiFivePlus.getCursorDisplayNameColumnIndex(cursor);
@ -262,6 +270,35 @@ public class Compatibility {
} }
} }
public static void deleteImAddressFromContact(ArrayList<ContentProviderOperation> ops, String oldSipAddress, String contactID) {
ApiFivePlus.deleteSipAddressFromContact(ops, oldSipAddress, contactID);
}
//Linphone Contacts Tag
public static void addLinphoneContactTag(Context context, ArrayList<ContentProviderOperation> ops, String newSipAddress, String rawContactId) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
ApiNinePlus.addLinphoneContactTag(context, ops, newSipAddress, rawContactId);
}
}
public static void updateLinphoneContactTag(Context context, ArrayList<ContentProviderOperation> ops, String newSipAddress, String oldSipAddress, String rawContactId) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
ApiNinePlus.updateLinphoneContactTag(context, ops, newSipAddress, oldSipAddress, rawContactId);
}
}
public static void deleteLinphoneContactTag(ArrayList<ContentProviderOperation> ops, String oldSipAddress, String rawContactId) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
ApiNinePlus.deleteLinphoneContactTag(ops, oldSipAddress, rawContactId);
}
}
public static void createLinphoneContactTag(Context context, ContentResolver contentResolver, Contact contact, String rawContactId) {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
ApiNinePlus.createLinphoneContactTag(context, contentResolver, contact, rawContactId);
}
}
//End of Linphone Contact Tag
public static void removeGlobalLayoutListener(ViewTreeObserver viewTreeObserver, OnGlobalLayoutListener keyboardListener) { public static void removeGlobalLayoutListener(ViewTreeObserver viewTreeObserver, OnGlobalLayoutListener keyboardListener) {
if (Version.sdkAboveOrEqual(Version.API16_JELLY_BEAN_41)) { if (Version.sdkAboveOrEqual(Version.API16_JELLY_BEAN_41)) {

View file

@ -0,0 +1,39 @@
package org.linphone.sync;
/*
AuthenticationService.java
Copyright (C) 2015 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class AuthenticationService extends Service {
private Authenticator mAuthenticator;
@Override
public void onCreate() {
mAuthenticator = new Authenticator(this);
}
@Override
public IBinder onBind(Intent intent) {
return mAuthenticator.getIBinder();
}
}

View file

@ -0,0 +1,85 @@
package org.linphone.sync;
/*
Authenticator.java
Copyright (C) 2015 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.*;
import android.content.Context;
import android.os.Bundle;
public class Authenticator extends AbstractAccountAuthenticator {
public Authenticator(Context context) {
super(context);
}
@Override
public Bundle editProperties(
AccountAuthenticatorResponse r, String s) {
throw new UnsupportedOperationException();
}
@Override
public Bundle addAccount(
AccountAuthenticatorResponse r,
String s,
String s2,
String[] strings,
Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public Bundle confirmCredentials(
AccountAuthenticatorResponse r,
Account account,
Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public Bundle getAuthToken(
AccountAuthenticatorResponse r,
Account account,
String s,
Bundle bundle) throws NetworkErrorException {
throw new UnsupportedOperationException();
}
@Override
public String getAuthTokenLabel(String s) {
throw new UnsupportedOperationException();
}
@Override
public Bundle updateCredentials(
AccountAuthenticatorResponse r,
Account account,
String s, Bundle bundle) throws NetworkErrorException {
throw new UnsupportedOperationException();
}
@Override
public Bundle hasFeatures(
AccountAuthenticatorResponse r,
Account account, String[] strings) throws NetworkErrorException {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,40 @@
package org.linphone.sync;
/*
SyncAdapter.java
Copyright (C) 2015 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.SyncResult;
import android.os.Bundle;
public class SyncAdapter extends AbstractThreadedSyncAdapter {
public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
}
}

View file

@ -0,0 +1,45 @@
package org.linphone.sync;
/*
SyncService.java
Copyright (C) 2015 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class SyncService extends Service {
private static SyncAdapter sSyncAdapter = null;
private static final Object sSyncAdapterLock = new Object();
@Override
public void onCreate() {
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
}
}
}
@Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
}