From 135361c5630590b76d3539f11aaeb49b48b10855 Mon Sep 17 00:00:00 2001 From: Margaux Clerc Date: Mon, 23 Mar 2015 17:41:10 +0100 Subject: [PATCH] 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 --- AndroidManifest.xml | 41 +++- res/layout-small/status.xml | 13 ++ res/values/custom.xml | 13 +- res/values/strings.xml | 1 + res/xml/authenticator.xml | 8 + res/xml/contacts.xml | 13 ++ res/xml/syncadapter.xml | 7 + src/org/linphone/Contact.java | 12 +- src/org/linphone/ContactFragment.java | 18 +- src/org/linphone/ContactHelper.java | 75 ++++++- src/org/linphone/ContactsFragment.java | 2 +- src/org/linphone/DialerFragment.java | 10 +- src/org/linphone/EditContactFragment.java | 64 ++++-- src/org/linphone/LinphoneActivity.java | 123 ++++++++--- src/org/linphone/LinphonePreferences.java | 8 + src/org/linphone/PreferencesMigrator.java | 6 +- src/org/linphone/StatusFragment.java | 4 +- .../linphone/compatibility/ApiElevenPlus.java | 13 +- .../linphone/compatibility/ApiFivePlus.java | 4 +- .../linphone/compatibility/ApiNinePlus.java | 195 +++++++++++------- .../linphone/compatibility/Compatibility.java | 39 +++- .../linphone/sync/AuthenticationService.java | 39 ++++ src/org/linphone/sync/Authenticator.java | 85 ++++++++ src/org/linphone/sync/SyncAdapter.java | 40 ++++ src/org/linphone/sync/SyncService.java | 45 ++++ 25 files changed, 711 insertions(+), 167 deletions(-) create mode 100644 res/xml/authenticator.xml create mode 100644 res/xml/contacts.xml create mode 100644 res/xml/syncadapter.xml create mode 100644 src/org/linphone/sync/AuthenticationService.java create mode 100644 src/org/linphone/sync/Authenticator.java create mode 100755 src/org/linphone/sync/SyncAdapter.java create mode 100755 src/org/linphone/sync/SyncService.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 53d5f3619..84c05cc03 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -37,7 +37,10 @@ - + + + + @@ -45,6 +48,7 @@ @@ -56,7 +60,7 @@ - + /> @@ -64,14 +68,16 @@ + - + + - - - + + + + + + + + + + + + + + + + + @@ -182,8 +207,8 @@ - - diff --git a/res/layout-small/status.xml b/res/layout-small/status.xml index 13203f9d0..c79638cbb 100644 --- a/res/layout-small/status.xml +++ b/res/layout-small/status.xml @@ -241,6 +241,19 @@ android:adjustViewBounds="true" android:visibility="gone" android:layout_alignParentRight="true" /> + + - d MMM EEEE MMM d HH:mm HH:mm d MMM HH:mm linphone-mms-%s.jpg - + Linphone Linphone Service Linphone + org.linphone + linphone contacts + vnd.android.cursor.item/org.linphone.profile Linphone Starting up - Registered to %s + Registered to %s Fails to register to %s Linphone %s SIP (rfc 3261) compatible phone under GNU Public License V2 http://www.linphone.org\n\nInstructions\nhttp://www.linphone.org/m/help\n\n© 2011 Belledonne Communications - + This assistant will help you to use a SIP account for your calls. Create an account on linphone.org I already have a linphone.org account I already have a SIP account Enter your linphone.org username and password Enter your SIP account username, password and domain - - username + username diff --git a/res/values/strings.xml b/res/values/strings.xml index 79551c89c..863a82ac8 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -353,6 +353,7 @@ Sharing server Remote provisioning Delete + This contact will be deleted. SIP address Phone number First name diff --git a/res/xml/authenticator.xml b/res/xml/authenticator.xml new file mode 100644 index 000000000..885948b88 --- /dev/null +++ b/res/xml/authenticator.xml @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/res/xml/contacts.xml b/res/xml/contacts.xml new file mode 100644 index 000000000..f05076c82 --- /dev/null +++ b/res/xml/contacts.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/res/xml/syncadapter.xml b/res/xml/syncadapter.xml new file mode 100644 index 000000000..0b7795d77 --- /dev/null +++ b/res/xml/syncadapter.xml @@ -0,0 +1,7 @@ + + + diff --git a/src/org/linphone/Contact.java b/src/org/linphone/Contact.java index 14d434808..764ed95cc 100644 --- a/src/org/linphone/Contact.java +++ b/src/org/linphone/Contact.java @@ -38,7 +38,7 @@ public class Contact implements Serializable { private String name; private transient Uri photoUri; private transient Bitmap photo; - private List numerosOrAddresses; + private List numbersOrAddresses; private LinphoneFriend friend; public Contact(String id, String name) { @@ -88,14 +88,14 @@ public class Contact implements Serializable { return photo; } - public List getNumerosOrAddresses() { - if (numerosOrAddresses == null) - numerosOrAddresses = new ArrayList(); - return numerosOrAddresses; + public List getNumbersOrAddresses() { + if (numbersOrAddresses == null) + numbersOrAddresses = new ArrayList(); + return numbersOrAddresses; } public void refresh(ContentResolver cr) { - this.numerosOrAddresses = Compatibility.extractContactNumbersAndAddresses(id, cr); + this.numbersOrAddresses = Compatibility.extractContactNumbersAndAddresses(id, cr); this.name = Compatibility.refreshContactName(cr, id); } } diff --git a/src/org/linphone/ContactFragment.java b/src/org/linphone/ContactFragment.java index 8de8ef76a..cfaacea61 100644 --- a/src/org/linphone/ContactFragment.java +++ b/src/org/linphone/ContactFragment.java @@ -27,7 +27,9 @@ import org.linphone.mediastream.Log; import org.linphone.ui.AvatarWithShadow; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.content.ContentProviderOperation; +import android.content.DialogInterface; import android.graphics.BitmapFactory; import android.os.Bundle; import android.provider.ContactsContract; @@ -122,7 +124,7 @@ public class ContactFragment extends Fragment implements OnClickListener { TableLayout controls = (TableLayout) view.findViewById(R.id.controls); controls.removeAllViews(); - for (String numberOrAddress : contact.getNumerosOrAddresses()) { + for (String numberOrAddress : contact.getNumbersOrAddresses()) { View v = inflater.inflate(R.layout.contact_control_row, null); String displayednumberOrAddress = numberOrAddress; @@ -221,9 +223,17 @@ public class ContactFragment extends Fragment implements OnClickListener { if (id == R.id.editContact) { LinphoneActivity.instance().editContact(contact); } else if (id == R.id.deleteContact) { - deleteExistingContact(); - LinphoneActivity.instance().removeContactFromLists(contact); - LinphoneActivity.instance().displayContacts(false); + 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(); + LinphoneActivity.instance().removeContactFromLists(contact); + LinphoneActivity.instance().displayContacts(false); + } + }); + alertDialog.setNegativeButton(getString(R.string.button_cancel),null); + alertDialog.show(); } } diff --git a/src/org/linphone/ContactHelper.java b/src/org/linphone/ContactHelper.java index eb33caa98..e6df72e82 100644 --- a/src/org/linphone/ContactHelper.java +++ b/src/org/linphone/ContactHelper.java @@ -29,6 +29,7 @@ import android.content.ContentUris; import android.content.Intent; import android.database.Cursor; import android.net.Uri; +import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; import android.text.TextUtils; @@ -97,6 +98,35 @@ public final class ContactHelper { Cursor cursor = resolver.query(photoUriToTest, new String[]{photoCol}, null, null, null); 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) { displayName = c.getString(c.getColumnIndex(nameCol)); @@ -179,13 +209,51 @@ public final class ContactHelper { 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") private final boolean queryContact() { boolean contactFound = false; 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() .append(android.provider.ContactsContract.Data.MIMETYPE) @@ -211,6 +279,7 @@ public final class ContactHelper { c = resolver.query(uri, projection, selection, null, null); contactFound = checkSIPQueryResult(c, android.provider.ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS); if (contactFound) { + c.close(); return true; } } @@ -222,7 +291,7 @@ public final class ContactHelper { android.provider.ContactsContract.PhoneLookup.DISPLAY_NAME }; c = resolver.query(lookupUri, projection, null, null, null); contactFound = checkPhoneQueryResult(c, android.provider.ContactsContract.PhoneLookup.NUMBER); - + c.close(); return contactFound; } } \ No newline at end of file diff --git a/src/org/linphone/ContactsFragment.java b/src/org/linphone/ContactsFragment.java index a013fceb7..418702d80 100644 --- a/src/org/linphone/ContactsFragment.java +++ b/src/org/linphone/ContactsFragment.java @@ -127,7 +127,7 @@ public class ContactsFragment extends Fragment implements OnClickListener, OnIte searchContacts(searchField.getText().toString()); } }); - + return view; } diff --git a/src/org/linphone/DialerFragment.java b/src/org/linphone/DialerFragment.java index 279ee6c61..166ffe482 100644 --- a/src/org/linphone/DialerFragment.java +++ b/src/org/linphone/DialerFragment.java @@ -198,8 +198,14 @@ public class DialerFragment extends Fragment { } else if (scheme.startsWith("call") || scheme.startsWith("sip")) { mAddress.setText(intent.getData().getSchemeSpecificPart()); } else { - Log.e("Unknown scheme: ",scheme); - mAddress.setText(intent.getData().getSchemeSpecificPart()); + 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.clearDisplayedName(); diff --git a/src/org/linphone/EditContactFragment.java b/src/org/linphone/EditContactFragment.java index 1eb37daa9..1ca6aa8cc 100644 --- a/src/org/linphone/EditContactFragment.java +++ b/src/org/linphone/EditContactFragment.java @@ -41,7 +41,7 @@ public class EditContactFragment extends Fragment { private ArrayList ops; private int firstSipAddressIndex = -1; private String newSipOrNumberToAdd; - + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { this.inflater = inflater; @@ -97,12 +97,12 @@ public class EditContactFragment extends Fragment { } try { - getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); - LinphoneActivity.instance().prepareContactsInBackground(); + getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); + LinphoneActivity.instance().prepareContactsInBackground(); } catch (Exception e) { e.printStackTrace(); } - + getFragmentManager().popBackStackImmediate(); } }); @@ -156,7 +156,7 @@ public class EditContactFragment extends Fragment { public void afterTextChanged(Editable s) { } }); - + if (!isNewContact) { String fn = findContactFirstName(String.valueOf(contactID)); String ln = findContactLastName(String.valueOf(contactID)); @@ -201,7 +201,7 @@ public class EditContactFragment extends Fragment { numbersAndAddresses = new ArrayList(); if (contact != null) { - for (String numberOrAddress : contact.getNumerosOrAddresses()) { + for (String numberOrAddress : contact.getNumbersOrAddresses()) { View view = displayNumberOrAddress(controls, numberOrAddress); if (view != null) controls.addView(view); @@ -288,6 +288,7 @@ public class EditContactFragment extends Fragment { nounoa.delete(); numbersAndAddresses.remove(nounoa); view.setVisibility(View.GONE); + } }); return view; @@ -358,11 +359,14 @@ public class EditContactFragment extends Fragment { contactID = 0; ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) - .withValue(RawContacts.ACCOUNT_TYPE, null) - .withValue(RawContacts.ACCOUNT_NAME, null).build()); - + .withValue(ContactsContract.RawContacts.AGGREGATION_MODE, ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT) + .withValue(RawContacts.ACCOUNT_TYPE, null) + .withValue(RawContacts.ACCOUNT_NAME, null) + .build() + ); + if (getDisplayName() != null) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, contactID) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, firstName.getText().toString()) @@ -377,7 +381,7 @@ public class EditContactFragment extends Fragment { String select = ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'" ; String[] args = new String[] { String.valueOf(contactID) }; - ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection(select, args) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, firstName.getText().toString()) @@ -386,7 +390,7 @@ public class EditContactFragment extends Fragment { ); } } - + private String getDisplayName() { String displayName = null; if (firstName.getText().length() > 0 && lastName.getText().length() > 0) @@ -400,9 +404,8 @@ public class EditContactFragment extends Fragment { private String findRawContactID(String contactID) { Cursor c = getActivity().getContentResolver().query(RawContacts.CONTENT_URI, - new String[]{RawContacts._ID}, - RawContacts.CONTACT_ID + "=?", - new String[]{contactID}, null); + new String[]{RawContacts._ID},RawContacts.CONTACT_ID + "=?", + new String[]{contactID}, null); if (c != null) { String result = null; if (c.moveToFirst()) { @@ -413,6 +416,24 @@ public class EditContactFragment extends Fragment { } 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) { Cursor c = getActivity().getContentResolver().query(ContactsContract.Data.CONTENT_URI, @@ -495,6 +516,7 @@ public class EditContactFragment extends Fragment { public void delete() { if (isSipAddress) { Compatibility.deleteSipAddressFromContact(ops, oldNumberOrAddress, String.valueOf(contactID)); + Compatibility.deleteLinphoneContactTag(ops, oldNumberOrAddress, findRawLinphoneContactID(String.valueOf(contactID))); } else { String select = ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "' AND " @@ -521,24 +543,25 @@ public class EditContactFragment extends Fragment { newNumberOrAddress = newNumberOrAddress + "@" + getResources().getString(R.string.default_domain); Compatibility.addSipAddressToContact(getActivity(), ops, newNumberOrAddress); } else { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, newNumberOrAddress) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM) - .withValue(ContactsContract.CommonDataKinds.Phone.LABEL, getString(R.string.addressbook_label)) + .withValue(ContactsContract.CommonDataKinds.Phone.LABEL, getString(R.string.addressbook_label)) .build() ); } } else { String rawContactId = findRawContactID(String.valueOf(contactID)); - if (isSipAddress) { if (newNumberOrAddress.startsWith("sip:")) newNumberOrAddress = newNumberOrAddress.substring(4); if(!newNumberOrAddress.contains("@")) 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 { ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId) @@ -563,6 +586,7 @@ public class EditContactFragment extends Fragment { if(!newNumberOrAddress.contains("@")) newNumberOrAddress = newNumberOrAddress + "@" + getResources().getString(R.string.default_domain); Compatibility.updateSipAddressForContact(ops, oldNumberOrAddress, newNumberOrAddress, String.valueOf(contactID)); + Compatibility.updateLinphoneContactTag(getActivity(), ops, newNumberOrAddress, oldNumberOrAddress, findRawLinphoneContactID(String.valueOf(contactID))); } else { String select = ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "' AND " @@ -578,4 +602,4 @@ public class EditContactFragment extends Fragment { } } } -} +} \ No newline at end of file diff --git a/src/org/linphone/LinphoneActivity.java b/src/org/linphone/LinphoneActivity.java index 9f91481ea..594885511 100644 --- a/src/org/linphone/LinphoneActivity.java +++ b/src/org/linphone/LinphoneActivity.java @@ -49,9 +49,13 @@ import org.linphone.setup.RemoteProvisioningLoginActivity; import org.linphone.setup.SetupActivity; import org.linphone.ui.AddressText; +import android.accounts.Account; +import android.accounts.AccountManager; import android.annotation.SuppressLint; import android.app.Activity; import android.content.ComponentName; +import android.content.ContentProviderOperation; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -59,6 +63,7 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; +import android.provider.ContactsContract; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.Fragment.SavedState; @@ -110,6 +115,7 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene private Cursor contactCursor, sipContactCursor; private OrientationEventListener mOrientationHelper; private LinphoneCoreListenerBase mListener; + private Account mAccount; static final boolean isInstanciated() { 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); instance = this; fragmentsHistory = new ArrayList(); @@ -169,6 +181,8 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene } } + mAccount = initializeSyncAccount(this); + mListener = new LinphoneCoreListenerBase(){ @Override public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) { @@ -248,6 +262,13 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene 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() { menu = (LinearLayout) findViewById(R.id.menu); mark = (LinearLayout) findViewById(R.id.mark); @@ -1007,7 +1028,7 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene } for (Contact contact : sipContactList) { - for (String addr : contact.getNumerosOrAddresses()) { + for (String addr : contact.getNumbersOrAddresses()) { if (addr.equals(sipUri)) { return contact; } @@ -1055,11 +1076,11 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene } private void searchFriendAndAddToContact(Contact contact) { - if (contact == null || contact.getNumerosOrAddresses() == null) { + if (contact == null || contact.getNumbersOrAddresses() == null) { return; } - for (String sipUri : contact.getNumerosOrAddresses()) { + for (String sipUri : contact.getNumbersOrAddresses()) { if (LinphoneUtils.isSipAddress(sipUri)) { LinphoneFriend friend = LinphoneManager.getLc().findFriendByAddress(sipUri); 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 ops = new ArrayList(); + + 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() { if (contactCursor != null) { contactCursor.close(); @@ -1104,35 +1165,44 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene Thread sipContactsHandler = new Thread(new Runnable() { @Override public void run() { - if(sipContactCursor != null) { - for (int i = 0; i < sipContactCursor.getCount(); i++) { - Contact contact = Compatibility.getContact(getContentResolver(), sipContactCursor, i); - if (contact == null) - continue; + if(sipContactCursor != null) { + Log.w(sipContactCursor.getCount()); + for (int i = 0; i < sipContactCursor.getCount(); i++) { + Contact contact = Compatibility.getContact(getContentResolver(), sipContactCursor, i); + if (contact == null) + continue; - contact.refresh(getContentResolver()); - if (!isContactPresenceDisabled) { - searchFriendAndAddToContact(contact); - } - sipContactList.add(contact); + 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(contactCursor != null) { - for (int i = 0; i < contactCursor.getCount(); i++) { - Contact contact = Compatibility.getContact(getContentResolver(), contactCursor, i); - if (contact == null) - continue; - for (Contact c : sipContactList) { - if (c != null && c.getID().equals(contact.getID())) { - contact = c; - break; - } - } - contactList.add(contact); + if (!isContactPresenceDisabled) { + searchFriendAndAddToContact(contact); } + sipContactList.add(contact); } } + if(contactCursor != null){ + for (int i = 0; i < contactCursor.getCount(); i++) { + Contact contact = Compatibility.getContact(getContentResolver(), contactCursor, i); + + if (contact == null) + continue; + + for (Contact c : sipContactList) { + if (c != null && c.getID().equals(contact.getID())) { + contact = c; + break; + } + } + contactList.add(contact); + } + } + getContentResolver().requestSync(mAccount, ContactsContract.AUTHORITY, new Bundle()); + } }); contactList = new ArrayList(); @@ -1195,6 +1265,7 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene extras.putSerializable("Contact", contact); changeCurrentFragment(FragmentsAvailable.EDIT_CONTACT, extras); } + } public void editContact(Contact contact, String sipAddress) diff --git a/src/org/linphone/LinphonePreferences.java b/src/org/linphone/LinphonePreferences.java index 17fed12c1..bdb89344a 100644 --- a/src/org/linphone/LinphonePreferences.java +++ b/src/org/linphone/LinphonePreferences.java @@ -1143,4 +1143,12 @@ public class LinphonePreferences { public void setCodecBitrateLimit(int 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); + } } diff --git a/src/org/linphone/PreferencesMigrator.java b/src/org/linphone/PreferencesMigrator.java index 85d4c0f4b..f6cae93c4 100644 --- a/src/org/linphone/PreferencesMigrator.java +++ b/src/org/linphone/PreferencesMigrator.java @@ -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. */ -import org.linphone.LinphoneManager; import org.linphone.LinphonePreferences.AccountBuilder; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCoreException; @@ -28,6 +27,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.res.Resources; + import android.preference.PreferenceManager; /** @@ -120,7 +120,7 @@ public class PreferencesMigrator { doAccountMigration(i, i == getPrefInt(R.string.pref_default_account_key, 0)); } } - + private void doAccountMigration(int index, boolean isDefaultAccount) { String key = index == 0 ? "" : String.valueOf(index); @@ -163,7 +163,7 @@ public class PreferencesMigrator { } } } - + private void deleteAllOldPreferences() { Editor editor = mOldPrefs.edit(); editor.clear(); diff --git a/src/org/linphone/StatusFragment.java b/src/org/linphone/StatusFragment.java index 7a3862829..e6f997b2c 100644 --- a/src/org/linphone/StatusFragment.java +++ b/src/org/linphone/StatusFragment.java @@ -134,7 +134,6 @@ public class StatusFragment extends Fragment { }); } // setMiniLedsForEachAccount(); - populateSliderContent(); populateSliderContent(); sliderContentAccounts.invalidate(); } catch (IllegalStateException ise) {} @@ -163,11 +162,10 @@ public class StatusFragment extends Fragment { } }; - + LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); if (lc != null) { lc.addListener(mListener); - LinphoneProxyConfig lpc = lc.getDefaultProxyConfig(); if (lpc != null) { mListener.registrationState(lc, lpc, lpc.getState(), null); diff --git a/src/org/linphone/compatibility/ApiElevenPlus.java b/src/org/linphone/compatibility/ApiElevenPlus.java index d252c3985..1fc9b8409 100644 --- a/src/org/linphone/compatibility/ApiElevenPlus.java +++ b/src/org/linphone/compatibility/ApiElevenPlus.java @@ -18,7 +18,7 @@ import android.graphics.Bitmap; import android.media.AudioManager; import android.net.Uri; 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.Intents.Insert; @@ -144,9 +144,8 @@ public class ApiElevenPlus { ArrayList data = new ArrayList(); ContentValues sipAddressRow = new ContentValues(); - sipAddressRow.put(Contacts.Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); - sipAddressRow.put(Im.DATA, sipUri); - sipAddressRow.put(Im.CUSTOM_PROTOCOL,"Sip"); + sipAddressRow.put(Contacts.Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE); + sipAddressRow.put(SipAddress.SIP_ADDRESS, sipUri); data.add(sipAddressRow); intent.putParcelableArrayListExtra(Insert.DATA, data); @@ -160,10 +159,8 @@ public class ApiElevenPlus { ArrayList data = new ArrayList(); ContentValues sipAddressRow = new ContentValues(); - sipAddressRow.put(Contacts.Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); - sipAddressRow.put(Im.DATA, sipUri); - sipAddressRow.put(Im.CUSTOM_PROTOCOL,"Sip"); - data.add(sipAddressRow); + sipAddressRow.put(Contacts.Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE); + sipAddressRow.put(SipAddress.SIP_ADDRESS, sipUri); data.add(sipAddressRow); intent.putParcelableArrayListExtra(Insert.DATA, data); diff --git a/src/org/linphone/compatibility/ApiFivePlus.java b/src/org/linphone/compatibility/ApiFivePlus.java index 02a86c282..18a77a4c9 100644 --- a/src/org/linphone/compatibility/ApiFivePlus.java +++ b/src/org/linphone/compatibility/ApiFivePlus.java @@ -111,7 +111,7 @@ public class ApiFivePlus { c.close(); } - // SIP addresses + // IM addresses String selection = new StringBuilder() .append(Data.CONTACT_ID).append(" = ? AND ") .append(Data.MIMETYPE).append(" = '") @@ -246,7 +246,7 @@ public class ApiFivePlus { Cursor cursor = getSIPContactCursor(cr, sipUri); Contact contact = getContact(cr, cursor, 0); - if (contact != null && contact.getNumerosOrAddresses().contains(sipUri)) { + if (contact != null && contact.getNumbersOrAddresses().contains(sipUri)) { address.setDisplayName(contact.getName()); cursor.close(); return contact.getPhotoUri(); diff --git a/src/org/linphone/compatibility/ApiNinePlus.java b/src/org/linphone/compatibility/ApiNinePlus.java index cd83f47ea..a52373ebe 100644 --- a/src/org/linphone/compatibility/ApiNinePlus.java +++ b/src/org/linphone/compatibility/ApiNinePlus.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import org.linphone.Contact; +import org.linphone.LinphoneUtils; import org.linphone.R; import org.linphone.core.LinphoneAddress; @@ -42,52 +43,48 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @TargetApi(9) public class ApiNinePlus { - + public static void addSipAddressToContact(Context context, ArrayList ops, String sipAddress) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Im.DATA, sipAddress) - .withValue(ContactsContract.CommonDataKinds.Im.TYPE, ContactsContract.CommonDataKinds.Im.TYPE_CUSTOM) - .withValue(ContactsContract.CommonDataKinds.Im.PROTOCOL, ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM) - .withValue(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL,"Sip") - .withValue(ContactsContract.CommonDataKinds.Im.LABEL, context.getString(R.string.addressbook_label)) - .build() - ); + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.SipAddress.DATA, sipAddress) + .withValue(CommonDataKinds.SipAddress.TYPE, CommonDataKinds.SipAddress.TYPE_CUSTOM) + .withValue(CommonDataKinds.SipAddress.LABEL, context.getString(R.string.addressbook_label)) + .build() + ); } public static void addSipAddressToContact(Context context, ArrayList ops, String sipAddress, String rawContactID) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactID) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Im.DATA, sipAddress) - .withValue(ContactsContract.CommonDataKinds.Im.TYPE, ContactsContract.CommonDataKinds.Im.TYPE_CUSTOM) - .withValue(ContactsContract.CommonDataKinds.Im.PROTOCOL, ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM) - .withValue(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL,"Sip") - .withValue(ContactsContract.CommonDataKinds.Im.LABEL, context.getString(R.string.addressbook_label)) + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactID) + .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.SipAddress.DATA, sipAddress) + .withValue(CommonDataKinds.SipAddress.TYPE, CommonDataKinds.SipAddress.TYPE_CUSTOM) + .withValue(CommonDataKinds.SipAddress.LABEL, context.getString(R.string.addressbook_label)) .build() ); } - public static void updateSipAddressForContact(ArrayList ops, String oldIm, String newIm, String contactID) { - String select = ContactsContract.Data.CONTACT_ID + "=? AND " - + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE + "' AND " - + ContactsContract.CommonDataKinds.Im.DATA + "=? AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip'"; - String[] args = new String[] { String.valueOf(contactID), oldIm }; + public static void updateSipAddressForContact(ArrayList ops, String oldSipAddress, String newSipAddress, String contactID) { + String select = ContactsContract.Data.CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "' AND " + + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + "=?"; + String[] args = new String[] { String.valueOf(contactID), oldSipAddress }; ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) - .withSelection(select, args) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Im.DATA, newIm) + .withSelection(select, args) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS, newSipAddress) .build() ); } - public static void deleteSipAddressFromContact(ArrayList ops, String oldIm, String contactID) { - String select = ContactsContract.Data.CONTACT_ID + "=? AND " - + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE + "' AND " - + ContactsContract.CommonDataKinds.Im.DATA + "=? AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip'"; - String[] args = new String[] { String.valueOf(contactID), oldIm }; + public static void deleteSipAddressFromContact(ArrayList ops, String oldSipAddress, String contactID) { + String select = ContactsContract.Data.CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "' AND " + + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + "=? "; + String[] args = new String[] { String.valueOf(contactID), oldSipAddress }; ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) .withSelection(select, args) @@ -99,7 +96,7 @@ public class ApiNinePlus { List list = new ArrayList(); Uri uri = Data.CONTENT_URI; - String[] projection = {ContactsContract.CommonDataKinds.Im.DATA}; + String[] projection; // Phone Numbers Cursor c = cr.query(Phone.CONTENT_URI, new String[] { Phone.NUMBER }, Phone.CONTACT_ID + " = " + id, null, null); @@ -110,28 +107,7 @@ public class ApiNinePlus { } 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 String selection2 = new StringBuilder() .append(Data.CONTACT_ID) @@ -155,13 +131,10 @@ public class ApiNinePlus { } 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 (" - + Data.MIMETYPE + " = '" + CommonDataKinds.Im.CONTENT_ITEM_TYPE - + "' AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip'" - + " 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))"; + + Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + + "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL)"; if (search != null) { req += " AND " + Data.DISPLAY_NAME + " LIKE '%" + search + "%'"; @@ -172,11 +145,8 @@ public class ApiNinePlus { public static Cursor getSIPContactsCursor(ContentResolver cr, String search) { String req = null; - req = "(" + Data.MIMETYPE + " = '" + CommonDataKinds.Im.CONTENT_ITEM_TYPE - + "' AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip'" - + " 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))"; + req = Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + + "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL"; if (search != null) { req += " AND " + Data.DISPLAY_NAME + " LIKE '%" + search + "%'"; @@ -184,18 +154,14 @@ public class ApiNinePlus { return ApiFivePlus.getGeneralContactCursor(cr, req, true); } - + private static Cursor getSIPContactCursor(ContentResolver cr, String id) { String req = null; - req = "(" + Contacts.Data.MIMETYPE + " = '" + CommonDataKinds.Im.CONTENT_ITEM_TYPE - + " AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip' AND " - + 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 + "'"; + req = Contacts.Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + + "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " LIKE '" + id + "'"; return ApiFivePlus.getGeneralContactCursor(cr, req, false); } - public static Uri findUriPictureOfContactAndSetDisplayName(LinphoneAddress address, ContentResolver cr) { String username = address.getUserName(); String domain = address.getDomain(); @@ -203,7 +169,7 @@ public class ApiNinePlus { Cursor cursor = getSIPContactCursor(cr, sipUri); 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()); cursor.close(); return contact.getPhotoUri(); @@ -212,4 +178,85 @@ public class ApiNinePlus { cursor.close(); return null; } + + //Linphone Contacts Tag + public static void addLinphoneContactTag(Context context, ArrayList 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 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 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 ops = new ArrayList(); + + 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 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(); + } + } + } } diff --git a/src/org/linphone/compatibility/Compatibility.java b/src/org/linphone/compatibility/Compatibility.java index 21ea8d89d..69fa40e80 100644 --- a/src/org/linphone/compatibility/Compatibility.java +++ b/src/org/linphone/compatibility/Compatibility.java @@ -79,6 +79,10 @@ public class Compatibility { return ApiFivePlus.extractContactNumbersAndAddresses(id, cr); } } + + public static List extractContactImAddresses(String id, ContentResolver cr) { + return ApiFivePlus.extractContactNumbersAndAddresses(id, cr); + } public static Cursor getContactsCursor(ContentResolver cr) { if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) { @@ -111,6 +115,10 @@ public class Compatibility { return ApiFivePlus.getSIPContactsCursor(cr); } } + + public static Cursor getImContactsCursor(ContentResolver cr) { + return ApiFivePlus.getSIPContactsCursor(cr); + } public static int getCursorDisplayNameColumnIndex(Cursor cursor) { if (Version.sdkAboveOrEqual(Version.API05_ECLAIR_20)) { @@ -261,7 +269,36 @@ public class Compatibility { ApiFivePlus.deleteSipAddressFromContact(ops, oldSipAddress, contactID); } } - + + public static void deleteImAddressFromContact(ArrayList ops, String oldSipAddress, String contactID) { + ApiFivePlus.deleteSipAddressFromContact(ops, oldSipAddress, contactID); + } + + //Linphone Contacts Tag + public static void addLinphoneContactTag(Context context, ArrayList 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 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 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) { if (Version.sdkAboveOrEqual(Version.API16_JELLY_BEAN_41)) { diff --git a/src/org/linphone/sync/AuthenticationService.java b/src/org/linphone/sync/AuthenticationService.java new file mode 100644 index 000000000..aec317e82 --- /dev/null +++ b/src/org/linphone/sync/AuthenticationService.java @@ -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(); + } +} \ No newline at end of file diff --git a/src/org/linphone/sync/Authenticator.java b/src/org/linphone/sync/Authenticator.java new file mode 100644 index 000000000..30f3e8065 --- /dev/null +++ b/src/org/linphone/sync/Authenticator.java @@ -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(); + } +} \ No newline at end of file diff --git a/src/org/linphone/sync/SyncAdapter.java b/src/org/linphone/sync/SyncAdapter.java new file mode 100755 index 000000000..620adc861 --- /dev/null +++ b/src/org/linphone/sync/SyncAdapter.java @@ -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) { + } +} + diff --git a/src/org/linphone/sync/SyncService.java b/src/org/linphone/sync/SyncService.java new file mode 100755 index 000000000..fc9ee6638 --- /dev/null +++ b/src/org/linphone/sync/SyncService.java @@ -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(); + } +}