From 135361c5630590b76d3539f11aaeb49b48b10855 Mon Sep 17 00:00:00 2001 From: Margaux Clerc Date: Mon, 23 Mar 2015 17:41:10 +0100 Subject: [PATCH 1/4] 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(); + } +} From 6ed49c1f3297b782bfabfe6a4f40a571616903bd Mon Sep 17 00:00:00 2001 From: Margaux Clerc Date: Thu, 26 Mar 2015 14:20:37 +0100 Subject: [PATCH 2/4] Update linphone --- .../src/org/linphone/tester/WrapperTester.java | 7 +++++++ submodules/linphone | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/liblinphone_tester/src/org/linphone/tester/WrapperTester.java b/liblinphone_tester/src/org/linphone/tester/WrapperTester.java index 6a999a41c..e9ea4b869 100644 --- a/liblinphone_tester/src/org/linphone/tester/WrapperTester.java +++ b/liblinphone_tester/src/org/linphone/tester/WrapperTester.java @@ -3,6 +3,7 @@ package org.linphone.tester; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCoreFactory; import org.linphone.core.LinphoneCoreListenerBase; +import org.linphone.core.LinphoneFriend; import org.linphone.core.PayloadType; import android.test.AndroidTestCase; @@ -63,6 +64,12 @@ public class WrapperTester extends AndroidTestCase { audioCodecs = mCore.getAudioCodecs(); Assert.assertEquals(audioCodecs.length, audioCodecsNb - 1); } + + //Test LinphoneFriend ref key + String key = "12"; + LinphoneFriend friend = LinphoneCoreFactory.instance().createLinphoneFriend("sip:lala@test.linphone.org"); + friend.setRefKey(key); + Assert.assertEquals(friend.getRefKey(),key); } @Override diff --git a/submodules/linphone b/submodules/linphone index c03389c14..04abb5760 160000 --- a/submodules/linphone +++ b/submodules/linphone @@ -1 +1 @@ -Subproject commit c03389c14cd847cf5d0f4c58318b78c827d9826c +Subproject commit 04abb57606e7f322a10e60288291889843feaa70 From bd0c2df61ebb72682dd157ed8f2ce5ac58e09458 Mon Sep 17 00:00:00 2001 From: Margaux Clerc Date: Mon, 13 Apr 2015 19:06:28 +0200 Subject: [PATCH 3/4] Add linphone friend management --- src/org/linphone/ChatListFragment.java | 17 +- src/org/linphone/Contact.java | 22 +- src/org/linphone/ContactFragment.java | 10 +- src/org/linphone/ContactHelper.java | 297 ----------- src/org/linphone/ContactsFragment.java | 24 +- src/org/linphone/ContactsManager.java | 464 ++++++++++++++++++ src/org/linphone/DialerFragment.java | 2 +- src/org/linphone/EditContactFragment.java | 97 ++-- src/org/linphone/HistoryDetailFragment.java | 12 +- src/org/linphone/HistoryFragment.java | 15 +- src/org/linphone/HistorySimpleFragment.java | 19 +- src/org/linphone/InCallActivity.java | 18 +- src/org/linphone/IncomingCallActivity.java | 6 +- src/org/linphone/LinphoneActivity.java | 350 +------------ src/org/linphone/LinphoneManager.java | 8 +- src/org/linphone/LinphoneUtils.java | 21 +- .../linphone/compatibility/ApiFivePlus.java | 54 +- .../linphone/compatibility/ApiNinePlus.java | 55 ++- .../linphone/compatibility/Compatibility.java | 36 +- 19 files changed, 709 insertions(+), 818 deletions(-) delete mode 100644 src/org/linphone/ContactHelper.java create mode 100644 src/org/linphone/ContactsManager.java diff --git a/src/org/linphone/ChatListFragment.java b/src/org/linphone/ChatListFragment.java index 4db0fe188..9b9dc6116 100644 --- a/src/org/linphone/ChatListFragment.java +++ b/src/org/linphone/ChatListFragment.java @@ -356,7 +356,7 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte Log.e("Chat view cannot parse address",e); return view; } - LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, view.getContext().getContentResolver()); + Contact lContact = ContactsManager.getInstance().findContactWithAddress(address); String message = ""; if (useNativeAPI) { @@ -393,14 +393,13 @@ public class ChatListFragment extends Fragment implements OnClickListener, OnIte TextView sipUri = (TextView) view.findViewById(R.id.sipUri); sipUri.setSelected(true); // For animation - - if (getResources().getBoolean(R.bool.only_display_username_if_unknown) && address.getDisplayName() != null && LinphoneUtils.isSipAddress(address.getDisplayName())) { - address.setDisplayName(LinphoneUtils.getUsernameFromAddress(address.getDisplayName())); - } else if (getResources().getBoolean(R.bool.only_display_username_if_unknown) && LinphoneUtils.isSipAddress(contact)) { - contact = LinphoneUtils.getUsernameFromAddress(contact); - } - - sipUri.setText(address.getDisplayName() == null ? contact : address.getDisplayName()); + + if (getResources().getBoolean(R.bool.only_display_username_if_unknown)) { + sipUri.setText(lContact == null ? address.getUserName() : lContact.getName()); + } else { + sipUri.setText(lContact == null ? address.asStringUriOnly() : lContact.getName()); + } + if (isDraft) { view.findViewById(R.id.draft).setVisibility(View.VISIBLE); } diff --git a/src/org/linphone/Contact.java b/src/org/linphone/Contact.java index 764ed95cc..4352bc21d 100644 --- a/src/org/linphone/Contact.java +++ b/src/org/linphone/Contact.java @@ -39,13 +39,14 @@ public class Contact implements Serializable { private transient Uri photoUri; private transient Bitmap photo; private List numbersOrAddresses; - private LinphoneFriend friend; + private boolean hasFriends; public Contact(String id, String name) { super(); this.id = id; this.name = name; this.photoUri = null; + this.hasFriends = false; } public Contact(String id, String name, Uri photo) { @@ -54,6 +55,7 @@ public class Contact implements Serializable { this.name = name; this.photoUri = photo; this.photo = null; + this.hasFriends = false; } public Contact(String id, String name, Uri photo, Bitmap picture) { @@ -62,14 +64,12 @@ public class Contact implements Serializable { this.name = name; this.photoUri = photo; this.photo = picture; + this.hasFriends = false; } - - public void setFriend(LinphoneFriend friend) { - this.friend = friend; - } - - public LinphoneFriend getFriend() { - return friend; + + + public boolean hasFriends() { + return hasFriends; } public String getID() { @@ -96,6 +96,12 @@ public class Contact implements Serializable { public void refresh(ContentResolver cr) { this.numbersOrAddresses = Compatibility.extractContactNumbersAndAddresses(id, cr); + for(LinphoneFriend friend : LinphoneManager.getLc().getFriendList()) { + if (friend.getRefKey().equals(id)) { + hasFriends = true; + this.numbersOrAddresses.add(friend.getAddress().asStringUriOnly()); + } + } this.name = Compatibility.refreshContactName(cr, id); } } diff --git a/src/org/linphone/ContactFragment.java b/src/org/linphone/ContactFragment.java index cfaacea61..c6dc5e025 100644 --- a/src/org/linphone/ContactFragment.java +++ b/src/org/linphone/ContactFragment.java @@ -120,7 +120,8 @@ public class ContactFragment extends Fragment implements OnClickListener { } TextView contactName = (TextView) view.findViewById(R.id.contactName); - contactName.setText(contact.getName()); + contactName.setText(contact.getName()); + TableLayout controls = (TableLayout) view.findViewById(R.id.controls); controls.removeAllViews(); @@ -170,7 +171,7 @@ public class ContactFragment extends Fragment implements OnClickListener { friend.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - if (LinphoneActivity.instance().newFriend(contact, finalNumberOrAddress)) { + if (ContactsManager.getInstance().createNewFriend(contact, finalNumberOrAddress)) { displayContact(ContactFragment.this.inflater, ContactFragment.this.view); } } @@ -180,7 +181,7 @@ public class ContactFragment extends Fragment implements OnClickListener { friend.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - if (LinphoneActivity.instance().removeFriend(contact, finalNumberOrAddress)) { + if (ContactsManager.getInstance().removeFriend(finalNumberOrAddress)) { displayContact(ContactFragment.this.inflater, ContactFragment.this.view); } } @@ -228,7 +229,7 @@ public class ContactFragment extends Fragment implements OnClickListener { alertDialog.setPositiveButton(getString(R.string.button_ok),new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { deleteExistingContact(); - LinphoneActivity.instance().removeContactFromLists(contact); + ContactsManager.getInstance().removeContactFromLists(getActivity().getContentResolver(),contact); LinphoneActivity.instance().displayContacts(false); } }); @@ -249,6 +250,7 @@ public class ContactFragment extends Fragment implements OnClickListener { try { getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); + ContactsManager.getInstance().removeAllFriends(contact); } catch (Exception e) { Log.w(e.getMessage() + ":" + e.getStackTrace()); } diff --git a/src/org/linphone/ContactHelper.java b/src/org/linphone/ContactHelper.java deleted file mode 100644 index e6df72e82..000000000 --- a/src/org/linphone/ContactHelper.java +++ /dev/null @@ -1,297 +0,0 @@ -/* -ContactHelper.java -Copyright (C) 2011 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. -*/ -package org.linphone; - -import org.linphone.core.LinphoneAddress; -import org.linphone.core.LinphoneCore; -import org.linphone.core.LinphoneProxyConfig; -import org.linphone.mediastream.Version; - -import android.annotation.SuppressLint; -import android.content.ContentResolver; -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; - -public final class ContactHelper { - private String username; - private String domain; - private ContentResolver resolver; - - private Uri foundPhotoUri; - public Uri getUri() { - return foundPhotoUri; - } - - private String displayName; - public String getDisplayName() { - return displayName; - } - - private LinphoneAddress address; - public ContactHelper(LinphoneAddress address, ContentResolver resolver) { - username = address.getUserName(); - domain = address.getDomain(); - this.resolver = resolver; - this.address = address; - } - - public boolean query() { - boolean succeeded = queryContact(); - if (succeeded && !TextUtils.isEmpty(displayName)) { - address.setDisplayName(displayName); - } - return succeeded; - } - - public static Intent prepareEditContactIntent(int id) { - Intent intent = new Intent(Intent.ACTION_EDIT, Contacts.CONTENT_URI); - Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id); - intent.setData(contactUri); - - return intent; - } - - public static boolean testPhotoUri(Cursor c) { - if (c == null) return false; - if (!c.moveToNext()) { - return false; - } - byte[] data = c.getBlob(0); - if (data == null) { - // TODO: simplify all this stuff - // which is here only to check that the - // photoUri really points to some data. - // Not retrieving the data now would be better. - return false; - } - return true; - } - - public static boolean testPhotoUriAndCloseCursor(Cursor c) { - boolean valid = testPhotoUri(c); - if (c != null) c.close(); - return valid; - } - - public static boolean testPhotoUri(ContentResolver resolver, Uri photoUriToTest, String photoCol) { - 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)); - - long id = c.getLong(c.getColumnIndex(idCol)); - Uri contactUri = ContentUris.withAppendedId(android.provider.ContactsContract.Contacts.CONTENT_URI, id); - Uri photoUri = Uri.withAppendedPath(contactUri, android.provider.ContactsContract.Contacts.Photo.CONTENT_DIRECTORY); - - if (photoUri != null) { - String[] projection = { android.provider.ContactsContract.CommonDataKinds.Photo.PHOTO }; - Cursor photoCursor = resolver.query(photoUri, projection, null, null, null); - - boolean isPhotoValid = testPhotoUriAndCloseCursor(photoCursor); - if (isPhotoValid) { - foundPhotoUri = photoUri; - } - } - } - - private boolean checkSIPQueryResult(Cursor c, String columnSip) { - boolean contactFound = false; - - if (c != null) { - while (!contactFound && c.moveToNext()) { - String contact = c.getString(c.getColumnIndex(columnSip)); - if (contact.equals(username + "@" + domain) || contact.equals(username)) { - contactFound = true; - checkPhotosUris(resolver, c, android.provider.ContactsContract.Data.CONTACT_ID, android.provider.ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME); - } else { - String normalizedUsername = null; - LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); - if (lc != null) { - LinphoneProxyConfig lpc = lc.getDefaultProxyConfig(); - if (lpc != null) { - if (contact.contains("@")) { - normalizedUsername = lpc.normalizePhoneNumber(contact.split("@")[0]); - } else { - normalizedUsername = lpc.normalizePhoneNumber(contact); - } - } - } - if (normalizedUsername != null && normalizedUsername.equals(username)) { - contactFound = true; - checkPhotosUris(resolver, c, android.provider.ContactsContract.Data.CONTACT_ID, android.provider.ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME); - } - } - } - c.close(); - } - - return contactFound; - } - - private boolean checkPhoneQueryResult(Cursor c, String columnPhone) { - boolean contactFound = false; - - if (c != null) { - while (!contactFound && c.moveToNext()) { - String contact = c.getString(c.getColumnIndex(columnPhone)); - if (contact.equals(username)) { - contactFound = true; - checkPhotosUris(resolver, c, android.provider.ContactsContract.PhoneLookup._ID, android.provider.ContactsContract.PhoneLookup.DISPLAY_NAME); - } else { - String normalizedUsername = null; - LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); - if (lc != null) { - LinphoneProxyConfig lpc = lc.getDefaultProxyConfig(); - if (lpc != null) { - normalizedUsername = lpc.normalizePhoneNumber(contact); - } - } - if (normalizedUsername != null && normalizedUsername.equals(username)) { - contactFound = true; - checkPhotosUris(resolver, c, android.provider.ContactsContract.PhoneLookup._ID, android.provider.ContactsContract.PhoneLookup.DISPLAY_NAME); - } - } - } - c.close(); - } - - 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 selection = new StringBuilder() - .append(android.provider.ContactsContract.Data.MIMETYPE) - .append(" = '") - .append(android.provider.ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE) - .append("' AND lower(") - .append(android.provider.ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL) - .append(") = 'sip'").toString(); - - Cursor c = resolver.query(uri, projection, selection, null, null); - contactFound = checkSIPQueryResult(c, android.provider.ContactsContract.CommonDataKinds.Im.DATA); - if (contactFound) { - return true; - } - - if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) { - selection = new StringBuilder() - .append(android.provider.ContactsContract.Data.MIMETYPE) - .append(" = '") - .append(android.provider.ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE) - .append("'") - .toString(); - c = resolver.query(uri, projection, selection, null, null); - contactFound = checkSIPQueryResult(c, android.provider.ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS); - if (contactFound) { - c.close(); - return true; - } - } - - Uri lookupUri = Uri.withAppendedPath(android.provider.ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(username)); - projection = new String[] { - android.provider.ContactsContract.PhoneLookup._ID, - android.provider.ContactsContract.PhoneLookup.NUMBER, - 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 418702d80..aa886801a 100644 --- a/src/org/linphone/ContactsFragment.java +++ b/src/org/linphone/ContactsFragment.java @@ -22,6 +22,7 @@ import java.util.List; import org.linphone.compatibility.Compatibility; import org.linphone.core.LinphoneFriend; import org.linphone.core.PresenceActivityType; +import org.linphone.mediastream.Log; import android.annotation.SuppressLint; import android.database.Cursor; @@ -169,7 +170,6 @@ public class ContactsFragment extends Fragment implements OnClickListener, OnIte changeContactsAdapter(); return; } - changeContactsToggle(); if (searchCursor != null) { @@ -177,11 +177,11 @@ public class ContactsFragment extends Fragment implements OnClickListener, OnIte } if (onlyDisplayLinphoneContacts) { - searchCursor = Compatibility.getSIPContactsCursor(getActivity().getContentResolver(), search); + searchCursor = Compatibility.getSIPContactsCursor(getActivity().getContentResolver(), search, ContactsManager.getInstance().getContactsId()); indexer = new AlphabetIndexer(searchCursor, Compatibility.getCursorDisplayNameColumnIndex(searchCursor), " ABCDEFGHIJKLMNOPQRSTUVWXYZ"); contactsList.setAdapter(new ContactsListAdapter(null, searchCursor)); } else { - searchCursor = Compatibility.getContactsCursor(getActivity().getContentResolver(), search); + searchCursor = Compatibility.getContactsCursor(getActivity().getContentResolver(), search, ContactsManager.getInstance().getContactsId()); indexer = new AlphabetIndexer(searchCursor, Compatibility.getCursorDisplayNameColumnIndex(searchCursor), " ABCDEFGHIJKLMNOPQRSTUVWXYZ"); contactsList.setAdapter(new ContactsListAdapter(null, searchCursor)); } @@ -194,8 +194,8 @@ public class ContactsFragment extends Fragment implements OnClickListener, OnIte searchCursor.close(); } - Cursor allContactsCursor = LinphoneActivity.instance().getAllContactsCursor(); - Cursor sipContactsCursor = LinphoneActivity.instance().getSIPContactsCursor(); + Cursor allContactsCursor = ContactsManager.getInstance().getAllContactsCursor(); + Cursor sipContactsCursor = ContactsManager.getInstance().getSIPContactsCursor(); noSipContact.setVisibility(View.GONE); noContact.setVisibility(View.GONE); @@ -207,7 +207,7 @@ public class ContactsFragment extends Fragment implements OnClickListener, OnIte contactsList.setVisibility(View.GONE); } else { indexer = new AlphabetIndexer(sipContactsCursor, Compatibility.getCursorDisplayNameColumnIndex(sipContactsCursor), " ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - contactsList.setAdapter(new ContactsListAdapter(LinphoneActivity.instance().getSIPContacts(), sipContactsCursor)); + contactsList.setAdapter(new ContactsListAdapter(ContactsManager.getInstance().getSIPContacts(), sipContactsCursor)); } } else { if (allContactsCursor.getCount() == 0) { @@ -215,10 +215,10 @@ public class ContactsFragment extends Fragment implements OnClickListener, OnIte contactsList.setVisibility(View.GONE); } else { indexer = new AlphabetIndexer(allContactsCursor, Compatibility.getCursorDisplayNameColumnIndex(allContactsCursor), " ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - contactsList.setAdapter(new ContactsListAdapter(LinphoneActivity.instance().getAllContacts(), allContactsCursor)); + contactsList.setAdapter(new ContactsListAdapter(ContactsManager.getInstance().getAllContacts(), allContactsCursor)); } } - LinphoneActivity.instance().setLinphoneContactsPrefered(onlyDisplayLinphoneContacts); + ContactsManager.getInstance().setLinphoneContactsPrefered(onlyDisplayLinphoneContacts); } private void changeContactsToggle() { @@ -255,7 +255,7 @@ public class ContactsFragment extends Fragment implements OnClickListener, OnIte if (LinphoneActivity.isInstanciated()) { LinphoneActivity.instance().selectMenu(FragmentsAvailable.CONTACTS); - onlyDisplayLinphoneContacts = LinphoneActivity.instance().isLinphoneContactsPrefered(); + onlyDisplayLinphoneContacts = ContactsManager.getInstance().isLinphoneContactsPrefered(); if (getResources().getBoolean(R.bool.show_statusbar_only_on_dialer)) { LinphoneActivity.instance().hideStatusBar(); @@ -350,10 +350,10 @@ public class ContactsFragment extends Fragment implements OnClickListener, OnIte } ImageView friendStatus = (ImageView) view.findViewById(R.id.friendStatus); - LinphoneFriend friend = contact.getFriend(); - if (!LinphoneActivity.instance().isContactPresenceDisabled() && friend != null) { + LinphoneFriend[] friends = LinphoneManager.getLc().getFriendList(); + if (!ContactsManager.getInstance().isContactPresenceDisabled() && friends != null) { friendStatus.setVisibility(View.VISIBLE); - PresenceActivityType presenceActivity = friend.getPresenceModel().getActivity().getType(); + PresenceActivityType presenceActivity = friends[0].getPresenceModel().getActivity().getType(); if (presenceActivity == PresenceActivityType.Online) { friendStatus.setImageResource(R.drawable.led_connected); } else if (presenceActivity == PresenceActivityType.Busy) { diff --git a/src/org/linphone/ContactsManager.java b/src/org/linphone/ContactsManager.java new file mode 100644 index 000000000..8033dc8df --- /dev/null +++ b/src/org/linphone/ContactsManager.java @@ -0,0 +1,464 @@ +package org.linphone; +/* +CallManager.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.accounts.AccountManager; +import android.content.ContentProviderOperation; +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.provider.ContactsContract; +import org.linphone.compatibility.Compatibility; +import org.linphone.core.LinphoneAddress; +import org.linphone.core.LinphoneCoreException; +import org.linphone.core.LinphoneCoreFactory; +import org.linphone.core.LinphoneFriend; +import org.linphone.mediastream.Log; + +import java.util.ArrayList; +import java.util.List; + +public class ContactsManager { + private static ContactsManager instance; + private List contactList, sipContactList; + private Cursor contactCursor, sipContactCursor, friendContactCursor; + private Account mAccount; + private boolean preferLinphoneContacts = false, isContactPresenceDisabled = true; + private ContentResolver contentResolver; + private Context context; + + private ContactsManager() {} + + public static final synchronized ContactsManager getInstance() { + if (instance == null) instance = new ContactsManager(); + return instance; + } + + public List getAllContacts() { + return contactList; + } + + public List getSIPContacts() { + return sipContactList; + } + + public Cursor getAllContactsCursor() { + return contactCursor; + } + + public Cursor getSIPContactsCursor() { + if (sipContactCursor.getCount() > 0) + return sipContactCursor; + else + return friendContactCursor; + } + + public void setLinphoneContactsPrefered(boolean isPrefered) { + preferLinphoneContacts = isPrefered; + } + + public boolean isLinphoneContactsPrefered() { + return preferLinphoneContacts; + } + + public boolean isContactPresenceDisabled() { + return isContactPresenceDisabled; + } + + public void initializeSyncAccount(Context context, ContentResolver contentResolver) { + this.context = context; + this.contentResolver = contentResolver; + Account newAccount = new Account(context.getString(R.string.sync_account_name), context.getString(R.string.sync_account_type)); + AccountManager accountManager = (AccountManager) context.getSystemService(context.ACCOUNT_SERVICE); + accountManager.addAccountExplicitly(newAccount, null, null); + mAccount = newAccount; + } + + public String getDisplayName(String firstName, String lastName) { + String displayName = null; + if (firstName.length() > 0 && lastName.length() > 0) + displayName = firstName + " " + lastName; + else if (firstName.length() > 0) + displayName = firstName; + else if (lastName.length() > 0) + displayName = lastName.toString(); + return displayName; + } + + public void createNewContact(ArrayList ops, String firstName, String lastName){ + int contactID = 0; + + ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.AGGREGATION_MODE, ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null) + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null) + .build() + ); + + if (getDisplayName(firstName, lastName) != null) { + 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) + .withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, lastName) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, getDisplayName(firstName,lastName)) + .build() + ); + } + } + + public void updateExistingContact(ArrayList ops, Contact contact, String firstName, String lastName) { + if (getDisplayName(firstName, lastName) != null) { + String select = ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'" ; + String[] args = new String[] { String.valueOf(contact.getID()) }; + + 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) + .withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, lastName) + .build() + ); + } + } + +//Manage Linphone Friend if we cannot use Sip address + public boolean createNewFriend(Contact contact, String sipUri) { + if (!sipUri.startsWith("sip:")) { + sipUri = "sip:" + sipUri; + } + + LinphoneFriend friend = LinphoneCoreFactory.instance().createLinphoneFriend(sipUri); + friend.edit(); + friend.setRefKey(contact.getID()); + friend.done(); + try { + LinphoneManager.getLc().addFriend(friend); + return true; + } catch (LinphoneCoreException e) { + e.printStackTrace(); + return false; + } + + } + + public void updateFriend(String oldSipUri, String newSipUri) { + if (!newSipUri.startsWith("sip:")) { + newSipUri = "sip:" + newSipUri; + } + + if (!oldSipUri.startsWith("sip:")) { + oldSipUri = "sip:" + oldSipUri; + } + + LinphoneFriend friend = LinphoneManager.getLc().findFriendByAddress(oldSipUri); + if(friend != null) { + friend.edit(); + try { + friend.setAddress(LinphoneCoreFactory.instance().createLinphoneAddress(newSipUri)); + } catch (LinphoneCoreException e) { + e.printStackTrace(); + } + friend.done(); + } + } + + public boolean removeFriend(String sipUri) { + if (!sipUri.startsWith("sip:")) { + sipUri = "sip:" + sipUri; + } + + LinphoneFriend friend = LinphoneManager.getLc().findFriendByAddress(sipUri); + if (friend != null) { + LinphoneManager.getLc().removeFriend(friend); + return true; + } + return false; + } + + public void removeAllFriends(Contact contact) { + for(LinphoneFriend friend : LinphoneManager.getLc().getFriendList()){ + if(friend.getRefKey().equals(contact.getID())) { + LinphoneManager.getLc().removeFriend(friend); + } + } + } + + public Contact findContactWithDisplayName(String displayName) { + String[] projection = { ContactsContract.Data.CONTACT_ID, ContactsContract.Data.DISPLAY_NAME }; + String selection = new StringBuilder() + .append(ContactsContract.Data.DISPLAY_NAME) + .append(" = ?").toString(); + + Cursor c = contentResolver.query(ContactsContract.Data.CONTENT_URI,projection,selection, + new String[]{displayName}, null); + if (c != null) { + if (c.moveToFirst()) { + Contact contact = Compatibility.getContact(contentResolver,c,c.getPosition()); + c.close(); + + if(contact != null) { + return contact; + } else { + return null; + } + } + c.close(); + } + return null; + } + + public List getContactsId(){ + List ids = new ArrayList(); + if(LinphoneManager.getLc().getFriendList() == null) return null; + for(LinphoneFriend friend : LinphoneManager.getLc().getFriendList()) { + if(!ids.contains(friend.getRefKey())){ + ids.add(friend.getRefKey()); + } + } + + return ids; + } +//End linphone Friend + + public Contact findContactWithAddress(LinphoneAddress address){ + for(Contact contact : contactList){ + if(contact.getNumbersOrAddresses().contains(address.asStringUriOnly()) || contact.getNumbersOrAddresses().contains(address.getUserName())){ + return contact; + } + } + + return null; + } + + public void removeContactFromLists(ContentResolver contentResolver, Contact contact) { + for (Contact c : contactList) { + if (c != null && c.getID().equals(contact.getID())) { + contactList.remove(c); + contactCursor = Compatibility.getContactsCursor(contentResolver,getContactsId()); + break; + } + } + + for (Contact c : sipContactList) { + if (c != null && c.getID().equals(contact.getID())) { + sipContactList.remove(c); + sipContactCursor = Compatibility.getSIPContactsCursor(contentResolver,getContactsId()); + break; + } + } + } + + public boolean isContactHasAddress(Contact contact, String address){ + if(contact != null) { + contact.refresh(contentResolver); + if (contact.getNumbersOrAddresses().contains("sip:" + address)) { + return true; + } else { + return false; + } + } + return false; + } + + //Migrate old IM contacts into SIP addresses or linphoneFriends + public void migrateContacts() { + Cursor oldContacts = Compatibility.getImContactsCursor(contentResolver); + ArrayList ops = new ArrayList(); + + if(oldContacts != null){ + for (int i = 0; i < oldContacts.getCount(); i++) { + Contact contact = Compatibility.getContact(contentResolver, oldContacts, i); + for (String address : Compatibility.extractContactImAddresses(contact.getID(), contentResolver)) { + + if (LinphoneUtils.isSipAddress(address)) { + if (address.startsWith("sip:")) { + address = address.substring(4); + } + Compatibility.addSipAddressToContact(context, ops, address, findRawContactID(contentResolver,contact.getID())); + try { + contentResolver.applyBatch(ContactsContract.AUTHORITY, ops); + } catch (Exception e) { + e.printStackTrace(); + } + + ops.clear(); + + if(isContactHasAddress(contact,address)){ + Compatibility.deleteImAddressFromContact(ops, address, contact.getID()); + try { + contentResolver.applyBatch(ContactsContract.AUTHORITY, ops); + } catch (Exception e) { + e.printStackTrace(); + } + + ops.clear(); + } else { + if (!address.startsWith("sip:")) { + address = "sip:" + address; + } + + createNewFriend(contact, address); + + contact.refresh(contentResolver); + + if (address.startsWith("sip:")) { + address = address.substring(4); + } + Compatibility.deleteImAddressFromContact(ops, address, contact.getID()); + try { + contentResolver.applyBatch(ContactsContract.AUTHORITY, ops); + } catch (Exception e) { + e.printStackTrace(); + + } + ops.clear(); + } + } + } + ops.clear(); + contact.refresh(contentResolver); + } + } + } + + public synchronized void prepareContactsInBackground() { + if (contactCursor != null) { + contactCursor.close(); + } + if (sipContactCursor != null) { + sipContactCursor.close(); + } + + contactCursor = Compatibility.getContactsCursor(contentResolver, getContactsId()); + sipContactCursor = Compatibility.getSIPContactsCursor(contentResolver, getContactsId()); + + Thread sipContactsHandler = new Thread(new Runnable() { + @Override + public void run() { + if(sipContactCursor.getCount() > 0) { + for (int i = 0; i < sipContactCursor.getCount(); i++) { + Contact contact = Compatibility.getContact(contentResolver, sipContactCursor, i); + if (contact == null) + continue; + + contact.refresh(contentResolver); + //Add tag to Linphone contact if it not existed + if(!isContactHasLinphoneTag(contact,contentResolver)){ + Compatibility.createLinphoneContactTag(context,contentResolver,contact, + findRawContactID(contentResolver, String.valueOf(contact.getID()))); + } + + sipContactList.add(contact); + } + } + if (contactCursor != null) { + for (int i = 0; i < contactCursor.getCount(); i++) { + Contact contact = Compatibility.getContact(contentResolver, contactCursor, i); + + if (contact == null) + continue; + + for (Contact c : sipContactList) { + if (c != null && c.getID().equals(contact.getID())) { + contact = c; + break; + } + } + contactList.add(contact); + } + } + } + }); + + contactList = new ArrayList(); + sipContactList = new ArrayList(); + + sipContactsHandler.start(); + } + + 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; + } + + 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 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; + } + +} diff --git a/src/org/linphone/DialerFragment.java b/src/org/linphone/DialerFragment.java index 166ffe482..07ed122c8 100644 --- a/src/org/linphone/DialerFragment.java +++ b/src/org/linphone/DialerFragment.java @@ -199,7 +199,7 @@ public class DialerFragment extends Fragment { mAddress.setText(intent.getData().getSchemeSpecificPart()); } else { Uri contactUri = intent.getData(); - String address = ContactHelper.queryAddressOrNumber(getActivity().getContentResolver(),contactUri); + String address = ContactsManager.getInstance().queryAddressOrNumber(getActivity().getContentResolver(),contactUri); if(address != null) { mAddress.setText(address); } else { diff --git a/src/org/linphone/EditContactFragment.java b/src/org/linphone/EditContactFragment.java index 1ca6aa8cc..0889b3b2a 100644 --- a/src/org/linphone/EditContactFragment.java +++ b/src/org/linphone/EditContactFragment.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.List; import org.linphone.compatibility.Compatibility; import org.linphone.mediastream.Version; +import org.linphone.mediastream.Log; import org.linphone.ui.AvatarWithShadow; import android.annotation.SuppressLint; import android.content.ContentProviderOperation; @@ -41,6 +42,7 @@ public class EditContactFragment extends Fragment { private ArrayList ops; private int firstSipAddressIndex = -1; private String newSipOrNumberToAdd; + private ContactsManager contactsManager; public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { this.inflater = inflater; @@ -58,6 +60,8 @@ public class EditContactFragment extends Fragment { isNewContact = true; } } + + contactsManager = ContactsManager.getInstance(); view = inflater.inflate(R.layout.edit_contact, container, false); @@ -87,9 +91,9 @@ public class EditContactFragment extends Fragment { return; } } - createNewContact(); + contactsManager.createNewContact(ops, firstName.getText().toString(), lastName.getText().toString()); } else { - updateExistingContact(); + contactsManager.updateExistingContact(ops, contact, firstName.getText().toString(), lastName.getText().toString()); } for (NewOrUpdatedNumberOrAddress numberOrAddress : numbersAndAddresses) { @@ -98,7 +102,8 @@ public class EditContactFragment extends Fragment { try { getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); - LinphoneActivity.instance().prepareContactsInBackground(); + checkSipAddress(); + contactsManager.prepareContactsInBackground(); } catch (Exception e) { e.printStackTrace(); } @@ -355,53 +360,6 @@ public class EditContactFragment extends Fragment { } } - private void createNewContact() { - contactID = 0; - - 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_NAME, null) - .build() - ); - - if (getDisplayName() != null) { - 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()) - .withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, lastName.getText().toString()) - .build() - ); - } - } - - private void updateExistingContact() { - if (getDisplayName() != null) { - 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) - .withSelection(select, args) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, firstName.getText().toString()) - .withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, lastName.getText().toString()) - .build() - ); - } - } - - private String getDisplayName() { - String displayName = null; - if (firstName.getText().length() > 0 && lastName.getText().length() > 0) - displayName = firstName.getText().toString() + " " + lastName.getText().toString(); - else if (firstName.getText().length() > 0) - displayName = firstName.getText().toString(); - else if (lastName.getText().length() > 0) - displayName = lastName.getText().toString(); - return displayName; - } - private String findRawContactID(String contactID) { Cursor c = getActivity().getContentResolver().query(RawContacts.CONTENT_URI, new String[]{RawContacts._ID},RawContacts.CONTACT_ID + "=?", @@ -466,6 +424,37 @@ public class EditContactFragment extends Fragment { } return null; } + + private boolean checkSipAddress(){ + boolean check = true; + for (NewOrUpdatedNumberOrAddress numberOrAddress : numbersAndAddresses) { + if(numberOrAddress.newNumberOrAddress != null){ + if(numberOrAddress.isSipAddress) { + if(isNewContact){ + Contact c = ContactsManager.getInstance().findContactWithDisplayName(ContactsManager.getInstance().getDisplayName(firstName.getText().toString(), lastName.getText().toString())); + if (!ContactsManager.getInstance().isContactHasAddress(c, numberOrAddress.newNumberOrAddress)) { + if (c != null) { + ContactsManager.getInstance().createNewFriend(c, numberOrAddress.newNumberOrAddress); + } + } + } else { + if (!ContactsManager.getInstance().isContactHasAddress(contact, numberOrAddress.newNumberOrAddress)) { + check = false; + if (numberOrAddress.oldNumberOrAddress == null) { + ContactsManager.getInstance().createNewFriend(contact, numberOrAddress.newNumberOrAddress); + } else { + if(contact.hasFriends()) + ContactsManager.getInstance().updateFriend(numberOrAddress.oldNumberOrAddress, numberOrAddress.newNumberOrAddress); + } + + } + } + } + } + } + + return check; + } class NewOrUpdatedNumberOrAddress { private String oldNumberOrAddress; @@ -515,7 +504,11 @@ public class EditContactFragment extends Fragment { public void delete() { if (isSipAddress) { - Compatibility.deleteSipAddressFromContact(ops, oldNumberOrAddress, String.valueOf(contactID)); + if(contact.hasFriends()) { + ContactsManager.getInstance().removeFriend(oldNumberOrAddress); + } else { + Compatibility.deleteSipAddressFromContact(ops, oldNumberOrAddress, String.valueOf(contactID)); + } Compatibility.deleteLinphoneContactTag(ops, oldNumberOrAddress, findRawLinphoneContactID(String.valueOf(contactID))); } else { String select = ContactsContract.Data.CONTACT_ID + "=? AND " @@ -559,9 +552,9 @@ public class EditContactFragment extends Fragment { newNumberOrAddress = newNumberOrAddress.substring(4); if(!newNumberOrAddress.contains("@")) newNumberOrAddress = newNumberOrAddress + "@" + getResources().getString(R.string.default_domain); + 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) diff --git a/src/org/linphone/HistoryDetailFragment.java b/src/org/linphone/HistoryDetailFragment.java index e82c67093..7d0eae6d8 100644 --- a/src/org/linphone/HistoryDetailFragment.java +++ b/src/org/linphone/HistoryDetailFragment.java @@ -108,16 +108,16 @@ public class HistoryDetailFragment extends Fragment implements OnClickListener { time.setText(callTime == null ? "" : callTime); date.setText(timestampToHumanDate(callDate)); - + LinphoneAddress lAddress; try { lAddress = LinphoneCoreFactory.instance().createLinphoneAddress(sipUri); - Uri pictureUri = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(lAddress, view.getContext().getContentResolver()); - if(pictureUri != null) - LinphoneUtils.setImagePictureFromUri(view.getContext(), contactPicture.getView(), Uri.parse(pictureUri.toString()), R.drawable.unknown_small); - String displayName = lAddress.getDisplayName(); - if (displayName != null) { + Contact contact = ContactsManager.getInstance().findContactWithAddress(lAddress); + if (contact != null) { + LinphoneUtils.setImagePictureFromUri(view.getContext(), contactPicture.getView(),contact.getPhotoUri(), R.drawable.unknown_small); view.findViewById(R.id.addContactRow).setVisibility(View.GONE); + } else { + LinphoneUtils.setImagePictureFromUri(view.getContext(), contactPicture.getView(),null ,R.drawable.unknown_small); } } catch (LinphoneCoreException e) { e.printStackTrace(); diff --git a/src/org/linphone/HistoryFragment.java b/src/org/linphone/HistoryFragment.java index e6f6c1066..6f7c2ee97 100644 --- a/src/org/linphone/HistoryFragment.java +++ b/src/org/linphone/HistoryFragment.java @@ -178,15 +178,16 @@ public class HistoryFragment extends Fragment implements OnClickListener, OnChil address = log.getTo(); } - LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, getActivity().getContentResolver()); - displayName = address.getDisplayName(); + Contact contact = ContactsManager.getInstance().findContactWithAddress(address); String sipUri = address.asStringUriOnly(); - if (displayName == null) { + if (contact == null) { if (getResources().getBoolean(R.bool.only_display_username_if_unknown) && LinphoneUtils.isSipAddress(sipUri)) { - displayName = LinphoneUtils.getUsernameFromAddress(sipUri); + displayName = address.getUserName(); } else { displayName = sipUri; } + } else { + displayName = contact.getName(); } return displayName; @@ -389,8 +390,7 @@ public class HistoryFragment extends Fragment implements OnClickListener, OnChil address = log.getTo(); callDirection.setImageBitmap(outgoingCall); } - - LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, view.getContext().getContentResolver()); + String sipUri = address.asStringUriOnly(); dateAndTime.setText(log.getStartDate() + " " + log.getCallDuration()); view.setTag(sipUri); @@ -455,8 +455,7 @@ public class HistoryFragment extends Fragment implements OnClickListener, OnChil } else { address = log.getTo(); } - - LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, view.getContext().getContentResolver()); + String displayName = getCorrespondentDisplayName(log); String sipUri = address.asStringUriOnly(); contact.setText(displayName + " (" + getChildrenCount(groupPosition) + ")"); diff --git a/src/org/linphone/HistorySimpleFragment.java b/src/org/linphone/HistorySimpleFragment.java index 0827efd1d..266a84d78 100644 --- a/src/org/linphone/HistorySimpleFragment.java +++ b/src/org/linphone/HistorySimpleFragment.java @@ -391,22 +391,25 @@ public class HistorySimpleFragment extends Fragment implements OnClickListener, address = log.getTo(); callDirection.setImageBitmap(outgoingCall); } - - LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, view.getContext().getContentResolver()); - String displayName = address.getDisplayName(); - String sipUri = address.asStringUriOnly(); + + Contact c = ContactsManager.getInstance().findContactWithAddress(address); + String displayName = null; + final String sipUri = address.asStringUriOnly(); + if(c != null){ + displayName = c.getName(); + } if (displayName == null) { if (getResources().getBoolean(R.bool.only_display_username_if_unknown) && LinphoneUtils.isSipAddress(sipUri)) { - contact.setText(LinphoneUtils.getUsernameFromAddress(sipUri)); + contact.setText(address.getUserName()); } else { contact.setText(sipUri); } } else { if (getResources().getBoolean(R.bool.only_display_username_if_unknown) && LinphoneUtils.isSipAddress(address.getDisplayName())) { - contact.setText(LinphoneUtils.getUsernameFromAddress(address.getDisplayName())); - } else { contact.setText(displayName); + } else { + contact.setText(sipUri); } } view.setTag(sipUri); @@ -421,7 +424,7 @@ public class HistorySimpleFragment extends Fragment implements OnClickListener, @Override public void onClick(View v) { if (LinphoneActivity.isInstanciated()) { - LinphoneActivity.instance().displayHistoryDetail(address.asStringUriOnly(), log); + LinphoneActivity.instance().displayHistoryDetail(sipUri, log); } } }); diff --git a/src/org/linphone/InCallActivity.java b/src/org/linphone/InCallActivity.java index a4be92910..052cfd18c 100644 --- a/src/org/linphone/InCallActivity.java +++ b/src/org/linphone/InCallActivity.java @@ -1299,8 +1299,12 @@ public class InCallActivity extends FragmentActivity implements OnClickListener // Image Row LinearLayout imageView = (LinearLayout) inflater.inflate(R.layout.active_call_image_row, container, false); - Uri pictureUri = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(lAddress, imageView.getContext().getContentResolver()); - displayOrHideContactPicture(imageView, pictureUri, false); + Contact contact = ContactsManager.getInstance().findContactWithAddress(lAddress); + if(contact != null) { + displayOrHideContactPicture(imageView, contact.getPhotoUri(), false); + } else { + displayOrHideContactPicture(imageView, null, false); + } callsList.addView(imageView); callView.setTag(imageView); @@ -1321,18 +1325,16 @@ public class InCallActivity extends FragmentActivity implements OnClickListener private void setContactName(LinearLayout callView, LinphoneAddress lAddress, String sipUri, Resources resources) { TextView contact = (TextView) callView.findViewById(R.id.contactNameOrNumber); - - LinphoneUtils.findUriPictureOfContactAndSetDisplayName(lAddress, callView.getContext().getContentResolver()); - String displayName = lAddress.getDisplayName(); - if (displayName == null) { + Contact lContact = ContactsManager.getInstance().findContactWithAddress(lAddress); + if (lContact == null) { if (resources.getBoolean(R.bool.only_display_username_if_unknown) && LinphoneUtils.isSipAddress(sipUri)) { - contact.setText(LinphoneUtils.getUsernameFromAddress(sipUri)); + contact.setText(lAddress.getUserName()); } else { contact.setText(sipUri); } } else { - contact.setText(displayName); + contact.setText(lContact.getName()); } } diff --git a/src/org/linphone/IncomingCallActivity.java b/src/org/linphone/IncomingCallActivity.java index a3fc3de46..fa04b0ad9 100644 --- a/src/org/linphone/IncomingCallActivity.java +++ b/src/org/linphone/IncomingCallActivity.java @@ -124,11 +124,11 @@ public class IncomingCallActivity extends Activity implements LinphoneSliderTrig } LinphoneAddress address = mCall.getRemoteAddress(); // May be greatly sped up using a drawable cache - Uri uri = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, getContentResolver()); - LinphoneUtils.setImagePictureFromUri(this, mPictureView.getView(), uri, R.drawable.unknown_small); + Contact contact = ContactsManager.getInstance().findContactWithAddress(address); + LinphoneUtils.setImagePictureFromUri(this, mPictureView.getView(), contact != null ? contact.getPhotoUri() : null, R.drawable.unknown_small); // To be done after findUriPictureOfContactAndSetDisplayName called - mNameView.setText(address.getDisplayName()); + mNameView.setText(contact != null ? contact.getName() : ""); if (getResources().getBoolean(R.bool.only_display_username_if_unknown)) { mNumberView.setText(address.getUserName()); } else { diff --git a/src/org/linphone/LinphoneActivity.java b/src/org/linphone/LinphoneActivity.java index 594885511..3bf32df7c 100644 --- a/src/org/linphone/LinphoneActivity.java +++ b/src/org/linphone/LinphoneActivity.java @@ -42,29 +42,23 @@ import org.linphone.core.LinphoneCore.RegistrationState; import org.linphone.core.LinphoneCoreException; import org.linphone.core.LinphoneCoreFactory; import org.linphone.core.LinphoneCoreListenerBase; -import org.linphone.core.LinphoneFriend; import org.linphone.core.LinphoneProxyConfig; import org.linphone.mediastream.Log; 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; -import android.database.Cursor; +import android.database.ContentObserver; 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; import android.support.v4.app.FragmentActivity; @@ -81,7 +75,6 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.view.animation.AnimationUtils; import android.widget.AdapterView; -import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; @@ -110,12 +103,9 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene private List fragmentsHistory; private Fragment dialerFragment, messageListFragment, friendStatusListenerFragment; private SavedState dialerSavedState; - private boolean preferLinphoneContacts = false, isAnimationDisabled = false, isContactPresenceDisabled = true; - private List contactList, sipContactList; - private Cursor contactCursor, sipContactCursor; + private boolean isAnimationDisabled = false, preferLinphoneContacts = false; private OrientationEventListener mOrientationHelper; private LinphoneCoreListenerBase mListener; - private Account mAccount; static final boolean isInstanciated() { return instance != null; @@ -159,11 +149,12 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene } } + ContactsManager.getInstance().initializeSyncAccount(getApplicationContext(), getContentResolver()); - /*if(!LinphonePreferences.instance().isContactsMigrationDone()){ - migrateContacts(this); - LinphonePreferences.instance().contactsMigrationDone(); - }*/ + if(!LinphonePreferences.instance().isContactsMigrationDone()){ + //ContactsManager.getInstance().migrateContacts(); + //LinphonePreferences.instance().contactsMigrationDone(); + } setContentView(R.layout.main); instance = this; @@ -181,8 +172,6 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene } } - mAccount = initializeSyncAccount(this); - mListener = new LinphoneCoreListenerBase(){ @Override public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) { @@ -262,12 +251,6 @@ 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); @@ -421,17 +404,12 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene private void updateAnimationsState() { isAnimationDisabled = getResources().getBoolean(R.bool.disable_animations) || !LinphonePreferences.instance().areAnimationsEnabled(); - isContactPresenceDisabled = !getResources().getBoolean(R.bool.enable_linphone_friends); } public boolean isAnimationDisabled() { return isAnimationDisabled; } - public boolean isContactPresenceDisabled() { - return isContactPresenceDisabled; - } - private void changeFragment(Fragment newFragment, FragmentsAvailable newFragmentType, boolean withoutAnimation) { if (statusFragment != null) { statusFragment.closeStatusBar(); @@ -538,10 +516,10 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene Log.e("Cannot display history details",e); return; } - Uri uri = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(lAddress, getContentResolver()); + Contact c = ContactsManager.getInstance().findContactWithAddress(lAddress); - String displayName = lAddress.getDisplayName(); - String pictureUri = uri == null ? null : uri.toString(); + String displayName = c != null ? c.getName() : null; + String pictureUri = c != null && c.getPhotoUri() != null ? c.getPhotoUri().toString() : null; String status; if (log.getDirection() == CallDirection.Outgoing) { @@ -631,9 +609,9 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene Log.e("Cannot display chat",e); return; } - Uri uri = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(lAddress, getContentResolver()); - String displayName = lAddress.getDisplayName(); - String pictureUri = uri == null ? null : uri.toString(); + Contact contact = ContactsManager.getInstance().findContactWithAddress(lAddress); + String displayName = contact != null ? contact.getName() : null; + String pictureUri = contact != null && contact.getPhotoUri() != null ? contact.getPhotoUri().toString() : null; if (currentFragment == FragmentsAvailable.CHATLIST || currentFragment == FragmentsAvailable.CHAT) { if (isTablet()) { @@ -650,16 +628,16 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene } changeCurrentFragment(FragmentsAvailable.CHAT, extras); } - } else { - Intent intent = new Intent(this, ChatActivity.class); - intent.putExtra("SipUri", sipUri); - if (lAddress.getDisplayName() != null) { - intent.putExtra("DisplayName", displayName); - intent.putExtra("PictureUri", pictureUri); - } - startOrientationSensor(); - startActivityForResult(intent, CHAT_ACTIVITY); } + } else { + Intent intent = new Intent(this, ChatActivity.class); + intent.putExtra("SipUri", sipUri); + if (contact != null) { + intent.putExtra("DisplayName", contact.getName()); + intent.putExtra("PictureUri", contact.getPhotoUri()); + } + startOrientationSensor(); + startActivityForResult(intent, CHAT_ACTIVITY); } LinphoneService.instance().resetMessageNotifCount(); @@ -927,7 +905,6 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene } private int mAlwaysChangingPhoneAngle = -1; - private AcceptNewFriendDialog acceptNewFriendDialog; private class LocalOrientationEventListener extends OrientationEventListener { public LocalOrientationEventListener(Context context) { @@ -966,251 +943,6 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene } } - public void showPreferenceErrorDialog(String message) { - - } - - public List getAllContacts() { - return contactList; - } - - public List getSIPContacts() { - return sipContactList; - } - - public Cursor getAllContactsCursor() { - return contactCursor; - } - - public Cursor getSIPContactsCursor() { - return sipContactCursor; - } - - public void setLinphoneContactsPrefered(boolean isPrefered) { - preferLinphoneContacts = isPrefered; - } - - public boolean isLinphoneContactsPrefered() { - return preferLinphoneContacts; - } - - public void onNewSubscriptionRequestReceived(LinphoneFriend friend, - String sipUri) { - if (isContactPresenceDisabled) { - return; - } - - sipUri = sipUri.replace("<", "").replace(">", ""); - if (LinphonePreferences.instance().shouldAutomaticallyAcceptFriendsRequests()) { - Contact contact = findContactWithSipAddress(sipUri); - if (contact != null) { - friend.enableSubscribes(true); - try { - LinphoneManager.getLc().addFriend(friend); - contact.setFriend(friend); - } catch (LinphoneCoreException e) { - e.printStackTrace(); - } - } - } else { - Contact contact = findContactWithSipAddress(sipUri); - if (contact != null) { - FragmentManager fm = getSupportFragmentManager(); - acceptNewFriendDialog = new AcceptNewFriendDialog(contact, sipUri); - acceptNewFriendDialog.show(fm, "New Friend Request Dialog"); - } - } - } - - private Contact findContactWithSipAddress(String sipUri) { - if (!sipUri.startsWith("sip:")) { - sipUri = "sip:" + sipUri; - } - - for (Contact contact : sipContactList) { - for (String addr : contact.getNumbersOrAddresses()) { - if (addr.equals(sipUri)) { - return contact; - } - } - } - return null; - } - - public void onNotifyPresenceReceived(LinphoneFriend friend) { - if (!isContactPresenceDisabled && currentFragment == FragmentsAvailable.CONTACTS && friendStatusListenerFragment != null) { - ((ContactsFragment) friendStatusListenerFragment).invalidate(); - } - } - - public boolean newFriend(Contact contact, String sipUri) { - LinphoneFriend friend = LinphoneCoreFactory.instance().createLinphoneFriend(sipUri); - friend.enableSubscribes(true); - friend.setIncSubscribePolicy(LinphoneFriend.SubscribePolicy.SPAccept); - try { - LinphoneManager.getLc().addFriend(friend); - contact.setFriend(friend); - return true; - } catch (LinphoneCoreException e) { - e.printStackTrace(); - } - return false; - } - - private void acceptNewFriend(Contact contact, String sipUri, boolean accepted) { - acceptNewFriendDialog.dismissAllowingStateLoss(); - if (accepted) { - newFriend(contact, sipUri); - } - } - - public boolean removeFriend(Contact contact, String sipUri) { - LinphoneFriend friend = LinphoneManager.getLc().findFriendByAddress(sipUri); - if (friend != null) { - friend.enableSubscribes(false); - LinphoneManager.getLc().removeFriend(friend); - contact.setFriend(null); - return true; - } - return false; - } - - private void searchFriendAndAddToContact(Contact contact) { - if (contact == null || contact.getNumbersOrAddresses() == null) { - return; - } - - for (String sipUri : contact.getNumbersOrAddresses()) { - if (LinphoneUtils.isSipAddress(sipUri)) { - LinphoneFriend friend = LinphoneManager.getLc().findFriendByAddress(sipUri); - if (friend != null) { - friend.enableSubscribes(true); - friend.setIncSubscribePolicy(LinphoneFriend.SubscribePolicy.SPAccept); - contact.setFriend(friend); - break; - } - } - } - } - - public void removeContactFromLists(Contact contact) { - for (Contact c : contactList) { - if (c != null && c.getID().equals(contact.getID())) { - contactList.remove(c); - contactCursor = Compatibility.getContactsCursor(getContentResolver()); - break; - } - } - - for (Contact c : sipContactList) { - if (c != null && c.getID().equals(contact.getID())) { - sipContactList.remove(c); - sipContactCursor = Compatibility.getSIPContactsCursor(getContentResolver()); - break; - } - } - } - - 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(); - } - if (sipContactCursor != null) { - sipContactCursor.close(); - } - - contactCursor = Compatibility.getContactsCursor(getContentResolver()); - sipContactCursor = Compatibility.getSIPContactsCursor(getContentResolver()); - - Thread sipContactsHandler = new Thread(new Runnable() { - @Override - public void run() { - 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()); - //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) { - 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(); - sipContactList = new ArrayList(); - - sipContactsHandler.start(); - } - private void initInCallMenuLayout(boolean callTransfer) { selectMenu(FragmentsAvailable.DIALER); if (dialerFragment != null) { @@ -1323,7 +1055,7 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene startService(new Intent(ACTION_MAIN).setClass(this, LinphoneService.class)); } - prepareContactsInBackground(); + ContactsManager.getInstance().prepareContactsInBackground(); updateMissedChatCount(); @@ -1467,42 +1199,6 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene } return super.onKeyDown(keyCode, event); } - - @SuppressLint("ValidFragment") - class AcceptNewFriendDialog extends DialogFragment { - private Contact contact; - private String sipUri; - - public AcceptNewFriendDialog(Contact c, String a) { - contact = c; - sipUri = a; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.new_friend_request_dialog, container); - - getDialog().setTitle(R.string.linphone_friend_new_request_title); - - Button yes = (Button) view.findViewById(R.id.yes); - yes.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - acceptNewFriend(contact, sipUri, true); - } - }); - - Button no = (Button) view.findViewById(R.id.no); - no.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - acceptNewFriend(contact, sipUri, false); - } - }); - - return view; - } - } } interface ContactPicked { diff --git a/src/org/linphone/LinphoneManager.java b/src/org/linphone/LinphoneManager.java index 3baa6ff0b..bf82c470e 100644 --- a/src/org/linphone/LinphoneManager.java +++ b/src/org/linphone/LinphoneManager.java @@ -715,9 +715,13 @@ public class LinphoneManager implements LinphoneCoreListener { } try { - LinphoneUtils.findUriPictureOfContactAndSetDisplayName(from, mServiceContext.getContentResolver()); + Contact contact = ContactsManager.getInstance().findContactWithAddress(from); if (!mServiceContext.getResources().getBoolean(R.bool.disable_chat__message_notification)) { - LinphoneService.instance().displayMessageNotification(from.asStringUriOnly(), from.getDisplayName(), textMessage); + if(contact != null) { + LinphoneService.instance().displayMessageNotification(from.asStringUriOnly(), contact.getName(), textMessage); + } else { + LinphoneService.instance().displayMessageNotification(from.asStringUriOnly(), from.getUserName(), textMessage); + } } } catch (Exception e) { } } diff --git a/src/org/linphone/LinphoneUtils.java b/src/org/linphone/LinphoneUtils.java index c82eb5d8c..177d11534 100644 --- a/src/org/linphone/LinphoneUtils.java +++ b/src/org/linphone/LinphoneUtils.java @@ -97,14 +97,14 @@ public final class LinphoneUtils { public static boolean isStrictSipAddress(String numberOrAddress) { return isSipAddress(numberOrAddress) && numberOrAddress.startsWith("sip:"); } - + public static String getUsernameFromAddress(String address) { if (address.contains("sip:")) address = address.replace("sip:", ""); - + if (address.contains("@")) address = address.split("@")[0]; - + return address; } @@ -137,20 +137,9 @@ public final class LinphoneUtils { } - /** - * @param contact sip uri - * @return url/uri of the resource - */ -// public static Uri findUriPictureOfContactAndSetDisplayName(LinphoneAddress address, ContentResolver resolver) { -// return Compatibility.findUriPictureOfContactAndSetDisplayName(address, resolver); -// } - - public static Uri findUriPictureOfContactAndSetDisplayName(LinphoneAddress address, ContentResolver resolver) { - ContactHelper helper = new ContactHelper(address, resolver); - helper.query(); - return helper.getUri(); - } + + public static Bitmap downloadBitmap(Uri uri) { URL url; InputStream is = null; diff --git a/src/org/linphone/compatibility/ApiFivePlus.java b/src/org/linphone/compatibility/ApiFivePlus.java index 18a77a4c9..d538428eb 100644 --- a/src/org/linphone/compatibility/ApiFivePlus.java +++ b/src/org/linphone/compatibility/ApiFivePlus.java @@ -33,6 +33,7 @@ import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.text.ClipboardManager; +import android.text.TextUtils; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; @@ -132,38 +133,49 @@ public class ApiFivePlus { return list; } - public static Cursor getContactsCursor(ContentResolver cr) { + public static Cursor getContactsCursor(ContentResolver cr, List ids) { String req = Data.MIMETYPE + " = '" + CommonDataKinds.Phone.CONTENT_ITEM_TYPE - + "' AND " + CommonDataKinds.Phone.NUMBER + " IS NOT NULL"; - - req += " OR (" + Contacts.Data.MIMETYPE + " = '" + CommonDataKinds.Im.CONTENT_ITEM_TYPE - + "' AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip')"; + + "' AND " + CommonDataKinds.Phone.NUMBER + " IS NOT NULL"; + + req += " OR (" + Contacts.Data.MIMETYPE + " = '" + CommonDataKinds.Im.CONTENT_ITEM_TYPE + + "' AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip')"; + + if(ids != null){ + String s = TextUtils.join(",", ids); + req += " OR (" + Data.CONTACT_ID + " IN (" + s + "))"; + } + + return getGeneralContactCursor(cr, req, true); + } + + public static Cursor getSIPContactsCursor(ContentResolver cr, List ids) { + String req = null; + req = Contacts.Data.MIMETYPE + " = '" + CommonDataKinds.Im.CONTENT_ITEM_TYPE + + "' AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip'"; + + if(ids != null){ + String s = TextUtils.join(",", ids); + req += " OR (" + Data.CONTACT_ID + " IN (" + s + "))"; + } return getGeneralContactCursor(cr, req, true); } - public static Cursor getSIPContactsCursor(ContentResolver cr) { - String req = null; - req = Contacts.Data.MIMETYPE + " = '" + CommonDataKinds.Im.CONTENT_ITEM_TYPE - + "' AND lower(" + CommonDataKinds.Im.CUSTOM_PROTOCOL + ") = 'sip'"; - - return 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 + "'"; - + 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 + "'"; + return getGeneralContactCursor(cr, req, false); } public static Cursor getGeneralContactCursor(ContentResolver cr, String select, boolean shouldGroupBy) { - String[] projection = new String[] { Data.CONTACT_ID, Data.DISPLAY_NAME }; - - String query = Data.DISPLAY_NAME + " IS NOT NULL AND (" + select + ")"; + String query; + + query = Data.DISPLAY_NAME + " IS NOT NULL AND (" + select + ")"; + Cursor cursor = cr.query(Data.CONTENT_URI, projection, query, null, " lower(" + Data.DISPLAY_NAME + ") COLLATE UNICODE ASC"); if (!shouldGroupBy || cursor == null) { @@ -206,7 +218,7 @@ public class ApiFivePlus { String name = getContactDisplayName(cursor); Uri photo = getContactPictureUri(id); InputStream input = getContactPictureInputStream(cr, id); - + Contact contact; if (input == null) { contact = new Contact(id, name); diff --git a/src/org/linphone/compatibility/ApiNinePlus.java b/src/org/linphone/compatibility/ApiNinePlus.java index a52373ebe..1a586b42a 100644 --- a/src/org/linphone/compatibility/ApiNinePlus.java +++ b/src/org/linphone/compatibility/ApiNinePlus.java @@ -19,6 +19,7 @@ import android.provider.ContactsContract.CommonDataKinds; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Data; +import android.text.TextUtils; /* ApiNinePlus.java @@ -126,28 +127,41 @@ public class ApiNinePlus { } c.close(); } - + return list; } - public static Cursor getContactsCursor(ContentResolver cr, String search) { - String req = Data.MIMETYPE + " = '" + CommonDataKinds.Phone.CONTENT_ITEM_TYPE - + "' AND " + CommonDataKinds.Phone.NUMBER + " IS NOT NULL OR (" - + Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE - + "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL)"; - + public static Cursor getContactsCursor(ContentResolver cr, String search, List ids) { + String req; + if(ids != null && ids.size() > 0) { + req = "(" + Data.MIMETYPE + " = '" + CommonDataKinds.Phone.CONTENT_ITEM_TYPE + + "' AND " + CommonDataKinds.Phone.NUMBER + " IS NOT NULL " + + " OR (" + Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + + "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL)" + + " OR (" + Data.CONTACT_ID + " IN (" + TextUtils.join(" , ", ids) + ")))"; + } else { + req = "(" + Data.MIMETYPE + " = '" + CommonDataKinds.Phone.CONTENT_ITEM_TYPE + + "' AND " + CommonDataKinds.Phone.NUMBER + " IS NOT NULL " + + " OR (" + 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 + "%'"; } - + return ApiFivePlus.getGeneralContactCursor(cr, req, true); } - public static Cursor getSIPContactsCursor(ContentResolver cr, String search) { - String req = null; - req = Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE - + "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL"; - + public static Cursor getSIPContactsCursor(ContentResolver cr, String search, List ids) { + + String req = "(" + Data.MIMETYPE + " = '" + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + + "' AND " + ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS + " IS NOT NULL) "; + + if(ids != null && ids.size() > 0) { + req += " OR (" + Data.CONTACT_ID + " IN (" + TextUtils.join(" , ", ids) + "))"; + } + if (search != null) { req += " AND " + Data.DISPLAY_NAME + " LIKE '%" + search + "%'"; } @@ -162,6 +176,7 @@ public class ApiNinePlus { return ApiFivePlus.getGeneralContactCursor(cr, req, false); } + public static Uri findUriPictureOfContactAndSetDisplayName(LinphoneAddress address, ContentResolver cr) { String username = address.getUserName(); String domain = address.getDomain(); @@ -205,8 +220,12 @@ public class ApiNinePlus { public static void deleteLinphoneContactTag(ArrayList ops , String oldAddress, String rawContactId){ if(rawContactId != null) { + String select = ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + + ContactsContract.Data.DATA1 + "= ?"; + String[] args = new String[]{rawContactId, oldAddress}; + ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) - .withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.DATA1 + "=? ", new String[]{rawContactId, oldAddress}) + .withSelection(select, args) .build()); } } @@ -223,10 +242,10 @@ public class ApiNinePlus { ); 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() + .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(); diff --git a/src/org/linphone/compatibility/Compatibility.java b/src/org/linphone/compatibility/Compatibility.java index 69fa40e80..c5e6500e3 100644 --- a/src/org/linphone/compatibility/Compatibility.java +++ b/src/org/linphone/compatibility/Compatibility.java @@ -84,40 +84,40 @@ public class Compatibility { return ApiFivePlus.extractContactNumbersAndAddresses(id, cr); } - public static Cursor getContactsCursor(ContentResolver cr) { + public static Cursor getContactsCursor(ContentResolver cr, List contactsId) { if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) { - return ApiNinePlus.getContactsCursor(cr, null); + return ApiNinePlus.getContactsCursor(cr, null, contactsId); } else { - return ApiFivePlus.getContactsCursor(cr); + return ApiFivePlus.getContactsCursor(cr, contactsId); } } - public static Cursor getContactsCursor(ContentResolver cr, String search) { + public static Cursor getContactsCursor(ContentResolver cr, String search, List contactsId) { if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) { - return ApiNinePlus.getContactsCursor(cr, search); + return ApiNinePlus.getContactsCursor(cr, search, contactsId); } else { - return ApiFivePlus.getContactsCursor(cr); + return ApiFivePlus.getContactsCursor(cr, contactsId); + } + } + + public static Cursor getSIPContactsCursor(ContentResolver cr, List contactsId) { + if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) { + return ApiNinePlus.getSIPContactsCursor(cr, null, contactsId); + } else { + return ApiFivePlus.getSIPContactsCursor(cr, contactsId); } } - public static Cursor getSIPContactsCursor(ContentResolver cr) { + public static Cursor getSIPContactsCursor(ContentResolver cr, String search, List contactsId) { if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) { - return ApiNinePlus.getSIPContactsCursor(cr, null); + return ApiNinePlus.getSIPContactsCursor(cr, search, contactsId); } else { - return ApiFivePlus.getSIPContactsCursor(cr); - } - } - - public static Cursor getSIPContactsCursor(ContentResolver cr, String search) { - if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) { - return ApiNinePlus.getSIPContactsCursor(cr, search); - } else { - return ApiFivePlus.getSIPContactsCursor(cr); + return ApiFivePlus.getSIPContactsCursor(cr, contactsId); } } public static Cursor getImContactsCursor(ContentResolver cr) { - return ApiFivePlus.getSIPContactsCursor(cr); + return ApiFivePlus.getSIPContactsCursor(cr,null); } public static int getCursorDisplayNameColumnIndex(Cursor cursor) { From 3706b6ebe22f82e3be7fd9ec69ba45cf31f8ae62 Mon Sep 17 00:00:00 2001 From: Margaux Clerc Date: Tue, 14 Apr 2015 16:40:10 +0200 Subject: [PATCH 4/4] Fix problems in contacts and remove linphone tag --- src/org/linphone/ContactsManager.java | 165 ++++++++++++------ src/org/linphone/EditContactFragment.java | 80 ++------- src/org/linphone/LinphoneActivity.java | 4 +- src/org/linphone/LinphoneService.java | 22 ++- .../linphone/compatibility/Compatibility.java | 6 +- submodules/linphone | 2 +- 6 files changed, 152 insertions(+), 127 deletions(-) diff --git a/src/org/linphone/ContactsManager.java b/src/org/linphone/ContactsManager.java index 8033dc8df..6e5ecbbdc 100644 --- a/src/org/linphone/ContactsManager.java +++ b/src/org/linphone/ContactsManager.java @@ -39,7 +39,7 @@ import java.util.List; public class ContactsManager { private static ContactsManager instance; private List contactList, sipContactList; - private Cursor contactCursor, sipContactCursor, friendContactCursor; + private Cursor contactCursor, sipContactCursor; private Account mAccount; private boolean preferLinphoneContacts = false, isContactPresenceDisabled = true; private ContentResolver contentResolver; @@ -65,10 +65,7 @@ public class ContactsManager { } public Cursor getSIPContactsCursor() { - if (sipContactCursor.getCount() > 0) - return sipContactCursor; - else - return friendContactCursor; + return sipContactCursor; } public void setLinphoneContactsPrefered(boolean isPrefered) { @@ -103,6 +100,7 @@ public class ContactsManager { return displayName; } + //Contacts public void createNewContact(ArrayList ops, String firstName, String lastName){ int contactID = 0; @@ -147,17 +145,20 @@ public class ContactsManager { } LinphoneFriend friend = LinphoneCoreFactory.instance().createLinphoneFriend(sipUri); - friend.edit(); - friend.setRefKey(contact.getID()); - friend.done(); - try { - LinphoneManager.getLc().addFriend(friend); - return true; - } catch (LinphoneCoreException e) { - e.printStackTrace(); + if(friend != null) { + friend.edit(); + friend.setRefKey(contact.getID()); + friend.done(); + try { + LinphoneManager.getLc().addFriend(friend); + return true; + } catch (LinphoneCoreException e) { + e.printStackTrace(); + return false; + } + } else { return false; } - } public void updateFriend(String oldSipUri, String newSipUri) { @@ -239,13 +240,42 @@ public class ContactsManager { } //End linphone Friend + public boolean removeContactTagIsNeeded(Contact contact){ + contact.refresh(contentResolver); + boolean onlyNumbers = true; + for(String address: contact.getNumbersOrAddresses()){ + if(LinphoneUtils.isSipAddress(address)){ + onlyNumbers = false; + } + } + + return onlyNumbers; + } + + public void removeLinphoneContactTag(Contact contact){ + ArrayList ops = new ArrayList(); + String select = ContactsContract.RawContacts._ID + " = ?"; + String[] args = new String[] { findRawLinphoneContactID(contact.getID()) }; + + + ops.add(ContentProviderOperation.newDelete(ContactsContract.RawContacts.CONTENT_URI) + .withSelection(select, args) + .build() + ); + + try { + contentResolver.applyBatch(ContactsContract.AUTHORITY, ops); + } catch (Exception e) { + Log.w(e.getMessage() + ":" + e.getStackTrace()); + } + } + public Contact findContactWithAddress(LinphoneAddress address){ for(Contact contact : contactList){ if(contact.getNumbersOrAddresses().contains(address.asStringUriOnly()) || contact.getNumbersOrAddresses().contains(address.getUserName())){ return contact; } } - return null; } @@ -270,7 +300,7 @@ public class ContactsManager { public boolean isContactHasAddress(Contact contact, String address){ if(contact != null) { contact.refresh(contentResolver); - if (contact.getNumbersOrAddresses().contains("sip:" + address)) { + if (contact.getNumbersOrAddresses().contains("sip:" + address)) { return true; } else { return false; @@ -279,6 +309,41 @@ public class ContactsManager { return false; } + public 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; + } + + public String findRawLinphoneContactID(String contactID) { + String result = null; + String[] projection = { ContactsContract.RawContacts._ID }; + + String selection = ContactsContract.RawContacts.CONTACT_ID + "=? AND " + + ContactsContract.RawContacts.ACCOUNT_TYPE + "=? "; + + Cursor c = contentResolver.query(ContactsContract.RawContacts.CONTENT_URI, projection, + selection, new String[]{contactID, "org.linphone"}, null); + if (c != null) { + if (c.moveToFirst()) { + result = c.getString(c.getColumnIndex(ContactsContract.RawContacts._ID)); + } + } + c.close(); + return result; + } + //Migrate old IM contacts into SIP addresses or linphoneFriends public void migrateContacts() { Cursor oldContacts = Compatibility.getImContactsCursor(contentResolver); @@ -288,12 +353,13 @@ public class ContactsManager { for (int i = 0; i < oldContacts.getCount(); i++) { Contact contact = Compatibility.getContact(contentResolver, oldContacts, i); for (String address : Compatibility.extractContactImAddresses(contact.getID(), contentResolver)) { - if (LinphoneUtils.isSipAddress(address)) { if (address.startsWith("sip:")) { address = address.substring(4); } - Compatibility.addSipAddressToContact(context, ops, address, findRawContactID(contentResolver,contact.getID())); + + //Add new sip address + Compatibility.addSipAddressToContact(context, ops, address, findRawContactID(contentResolver, contact.getID())); try { contentResolver.applyBatch(ContactsContract.AUTHORITY, ops); } catch (Exception e) { @@ -302,42 +368,39 @@ public class ContactsManager { ops.clear(); - if(isContactHasAddress(contact,address)){ + contact.refresh(contentResolver); + + //If address sip is correctly add, remove the im address + if(contact.getNumbersOrAddresses().contains(address)){ Compatibility.deleteImAddressFromContact(ops, address, contact.getID()); try { contentResolver.applyBatch(ContactsContract.AUTHORITY, ops); } catch (Exception e) { e.printStackTrace(); } - ops.clear(); } else { - if (!address.startsWith("sip:")) { - address = "sip:" + address; + //Add linphone friend instead + if(createNewFriend(contact, address)) { + contact.refresh(contentResolver); + + //Remove IM address + Compatibility.deleteImAddressFromContact(ops, address, contact.getID()); + try { + contentResolver.applyBatch(ContactsContract.AUTHORITY, ops); + } catch (Exception e) { + e.printStackTrace(); + + } } - - createNewFriend(contact, address); - - contact.refresh(contentResolver); - - if (address.startsWith("sip:")) { - address = address.substring(4); - } - Compatibility.deleteImAddressFromContact(ops, address, contact.getID()); - try { - contentResolver.applyBatch(ContactsContract.AUTHORITY, ops); - } catch (Exception e) { - e.printStackTrace(); - - } - ops.clear(); } } + ops.clear(); } - ops.clear(); - contact.refresh(contentResolver); } + oldContacts.close(); } + } public synchronized void prepareContactsInBackground() { @@ -377,6 +440,11 @@ public class ContactsManager { if (contact == null) continue; + //Remove linphone contact tag if the contact has no sip address + if(removeContactTagIsNeeded(contact) && isContactHasLinphoneTag(contact,contentResolver)){ + removeLinphoneContactTag(contact); + } + for (Contact c : sipContactList) { if (c != null && c.getID().equals(contact.getID())) { contact = c; @@ -444,21 +512,4 @@ public class ContactsManager { return false; } - public 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; - } - } diff --git a/src/org/linphone/EditContactFragment.java b/src/org/linphone/EditContactFragment.java index 0889b3b2a..edb2bb77e 100644 --- a/src/org/linphone/EditContactFragment.java +++ b/src/org/linphone/EditContactFragment.java @@ -5,7 +5,6 @@ import java.util.ArrayList; import java.util.List; import org.linphone.compatibility.Compatibility; import org.linphone.mediastream.Version; -import org.linphone.mediastream.Log; import org.linphone.ui.AvatarWithShadow; import android.annotation.SuppressLint; import android.content.ContentProviderOperation; @@ -14,7 +13,6 @@ import android.database.Cursor; import android.graphics.BitmapFactory; import android.os.Bundle; import android.provider.ContactsContract; -import android.provider.ContactsContract.RawContacts; import android.support.v4.app.Fragment; import android.text.Editable; import android.text.InputType; @@ -102,7 +100,7 @@ public class EditContactFragment extends Fragment { try { getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); - checkSipAddress(); + addLinphoneFriendIfNeeded(); contactsManager.prepareContactsInBackground(); } catch (Exception e) { e.printStackTrace(); @@ -360,39 +358,6 @@ 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); - if (c != null) { - String result = null; - if (c.moveToFirst()) { - result = c.getString(c.getColumnIndex(RawContacts._ID)); - } - c.close(); - return result; - } - 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, new String[]{ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME}, @@ -425,35 +390,24 @@ public class EditContactFragment extends Fragment { return null; } - private boolean checkSipAddress(){ - boolean check = true; + private void addLinphoneFriendIfNeeded(){ for (NewOrUpdatedNumberOrAddress numberOrAddress : numbersAndAddresses) { - if(numberOrAddress.newNumberOrAddress != null){ - if(numberOrAddress.isSipAddress) { - if(isNewContact){ - Contact c = ContactsManager.getInstance().findContactWithDisplayName(ContactsManager.getInstance().getDisplayName(firstName.getText().toString(), lastName.getText().toString())); - if (!ContactsManager.getInstance().isContactHasAddress(c, numberOrAddress.newNumberOrAddress)) { - if (c != null) { - ContactsManager.getInstance().createNewFriend(c, numberOrAddress.newNumberOrAddress); - } - } + if(numberOrAddress.newNumberOrAddress != null && numberOrAddress.isSipAddress && !contactsManager.isContactHasAddress(contact, numberOrAddress.newNumberOrAddress)) { + if(isNewContact){ + Contact c = contactsManager.findContactWithDisplayName(ContactsManager.getInstance().getDisplayName(firstName.getText().toString(), lastName.getText().toString())); + if (c != null) { + contactsManager.createNewFriend(c, numberOrAddress.newNumberOrAddress); + } + } else { + if (numberOrAddress.oldNumberOrAddress == null) { + contactsManager.createNewFriend(contact, numberOrAddress.newNumberOrAddress); } else { - if (!ContactsManager.getInstance().isContactHasAddress(contact, numberOrAddress.newNumberOrAddress)) { - check = false; - if (numberOrAddress.oldNumberOrAddress == null) { - ContactsManager.getInstance().createNewFriend(contact, numberOrAddress.newNumberOrAddress); - } else { - if(contact.hasFriends()) - ContactsManager.getInstance().updateFriend(numberOrAddress.oldNumberOrAddress, numberOrAddress.newNumberOrAddress); - } - - } + if(contact.hasFriends()) + contactsManager.updateFriend(numberOrAddress.oldNumberOrAddress, numberOrAddress.newNumberOrAddress); } } } } - - return check; } class NewOrUpdatedNumberOrAddress { @@ -509,7 +463,7 @@ public class EditContactFragment extends Fragment { } else { Compatibility.deleteSipAddressFromContact(ops, oldNumberOrAddress, String.valueOf(contactID)); } - Compatibility.deleteLinphoneContactTag(ops, oldNumberOrAddress, findRawLinphoneContactID(String.valueOf(contactID))); + Compatibility.deleteLinphoneContactTag(ops, oldNumberOrAddress, contactsManager.findRawLinphoneContactID(String.valueOf(contactID))); } else { String select = ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "' AND " @@ -546,7 +500,7 @@ public class EditContactFragment extends Fragment { ); } } else { - String rawContactId = findRawContactID(String.valueOf(contactID)); + String rawContactId = contactsManager.findRawContactID(getActivity().getContentResolver(),String.valueOf(contactID)); if (isSipAddress) { if (newNumberOrAddress.startsWith("sip:")) newNumberOrAddress = newNumberOrAddress.substring(4); @@ -554,7 +508,7 @@ public class EditContactFragment extends Fragment { newNumberOrAddress = newNumberOrAddress + "@" + getResources().getString(R.string.default_domain); Compatibility.addSipAddressToContact(getActivity(), ops, newNumberOrAddress, rawContactId); - Compatibility.addLinphoneContactTag(getActivity(), ops, newNumberOrAddress, findRawLinphoneContactID(String.valueOf(contactID))); + Compatibility.addLinphoneContactTag(getActivity(), ops, newNumberOrAddress, contactsManager.findRawLinphoneContactID(String.valueOf(contactID))); } else { ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId) @@ -579,7 +533,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))); + Compatibility.updateLinphoneContactTag(getActivity(), ops, newNumberOrAddress, oldNumberOrAddress, contactsManager.findRawLinphoneContactID(String.valueOf(contactID))); } else { String select = ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "' AND " diff --git a/src/org/linphone/LinphoneActivity.java b/src/org/linphone/LinphoneActivity.java index 3bf32df7c..271e435b7 100644 --- a/src/org/linphone/LinphoneActivity.java +++ b/src/org/linphone/LinphoneActivity.java @@ -152,8 +152,8 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene ContactsManager.getInstance().initializeSyncAccount(getApplicationContext(), getContentResolver()); if(!LinphonePreferences.instance().isContactsMigrationDone()){ - //ContactsManager.getInstance().migrateContacts(); - //LinphonePreferences.instance().contactsMigrationDone(); + ContactsManager.getInstance().migrateContacts(); + LinphonePreferences.instance().contactsMigrationDone(); } setContentView(R.layout.main); diff --git a/src/org/linphone/LinphoneService.java b/src/org/linphone/LinphoneService.java index 034867c38..f88dae86d 100644 --- a/src/org/linphone/LinphoneService.java +++ b/src/org/linphone/LinphoneService.java @@ -45,6 +45,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; +import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; @@ -52,6 +53,7 @@ import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.SystemClock; +import android.provider.ContactsContract; import android.provider.MediaStore; /** @@ -235,10 +237,12 @@ public final class LinphoneService extends Service { mStartForeground = getClass().getMethod("startForeground", mStartFgSign); mStopForeground = getClass().getMethod("stopForeground", mStopFgSign); } catch (NoSuchMethodException e) { - Log.e(e, "Couldn't find startGoreground or stopForeground"); + Log.e(e, "Couldn't find startForeground or stopForeground"); } } + this.getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, mObserver); + startForegroundCompat(NOTIF_ID, mNotif); if (!mTestDelayElapsed) { @@ -259,6 +263,15 @@ public final class LinphoneService extends Service { , mkeepAlivePendingIntent); } + private ContentObserver mObserver = new ContentObserver(new Handler()) { + + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + } + + }; + private enum IncallIconState {INCALL, PAUSE, VIDEO, IDLE} private IncallIconState mCurrentIncallIconState = IncallIconState.IDLE; private synchronized void setIncallIcon(IncallIconState state) { @@ -299,7 +312,8 @@ public final class LinphoneService extends Service { LinphoneAddress address = LinphoneCoreFactory.instance().createLinphoneAddress(userName,domain,null); address.setDisplayName(displayName); - Uri pictureUri = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, getContentResolver()); + Contact contact = ContactsManager.getInstance().findContactWithAddress(address); + Uri pictureUri = contact != null ? contact.getPhotoUri() : null; Bitmap bm = null; try { bm = MediaStore.Images.Media.getBitmap(getContentResolver(), pictureUri); @@ -376,7 +390,8 @@ public final class LinphoneService extends Service { Uri pictureUri; try { - pictureUri = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(LinphoneCoreFactory.instance().createLinphoneAddress(fromSipUri), getContentResolver()); + Contact contact = ContactsManager.getInstance().findContactWithAddress(LinphoneCoreFactory.instance().createLinphoneAddress(fromSipUri)); + pictureUri = contact.getPhotoUri(); } catch (LinphoneCoreException e1) { Log.e("Cannot parse from address",e1); pictureUri=null; @@ -548,6 +563,7 @@ public final class LinphoneService extends Service { mNM.cancel(MESSAGE_NOTIF_ID); ((AlarmManager) this.getSystemService(Context.ALARM_SERVICE)).cancel(mkeepAlivePendingIntent); + getContentResolver().unregisterContentObserver(mObserver); super.onDestroy(); } diff --git a/src/org/linphone/compatibility/Compatibility.java b/src/org/linphone/compatibility/Compatibility.java index c5e6500e3..226ee1f9e 100644 --- a/src/org/linphone/compatibility/Compatibility.java +++ b/src/org/linphone/compatibility/Compatibility.java @@ -81,7 +81,11 @@ public class Compatibility { } public static List extractContactImAddresses(String id, ContentResolver cr) { - return ApiFivePlus.extractContactNumbersAndAddresses(id, cr); + if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) { + return ApiFivePlus.extractContactNumbersAndAddresses(id, cr); + } else { + return null; + } } public static Cursor getContactsCursor(ContentResolver cr, List contactsId) { diff --git a/submodules/linphone b/submodules/linphone index 04abb5760..ef7677a88 160000 --- a/submodules/linphone +++ b/submodules/linphone @@ -1 +1 @@ -Subproject commit 04abb57606e7f322a10e60288291889843feaa70 +Subproject commit ef7677a88d7aaef4168f884eeaca85af2994ecd9