From 16ae046543463026b40b76c59c61a8a5347f9905 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 7 Dec 2018 14:13:19 +0100 Subject: [PATCH] Split LinphoneContact into two classes, one dedicated to Android contact API --- .../org/linphone/contacts/AndroidContact.java | 495 ++++++++++ .../linphone/contacts/LinphoneContact.java | 918 ++---------------- 2 files changed, 593 insertions(+), 820 deletions(-) create mode 100644 app/src/main/java/org/linphone/contacts/AndroidContact.java diff --git a/app/src/main/java/org/linphone/contacts/AndroidContact.java b/app/src/main/java/org/linphone/contacts/AndroidContact.java new file mode 100644 index 000000000..f8228203f --- /dev/null +++ b/app/src/main/java/org/linphone/contacts/AndroidContact.java @@ -0,0 +1,495 @@ +package org.linphone.contacts; + +/* +AndroidContact.java +Copyright (C) 2018 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.database.Cursor; +import android.net.Uri; +import android.provider.ContactsContract; +import androidx.core.util.Pair; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.linphone.LinphoneManager; +import org.linphone.LinphoneService; +import org.linphone.R; +import org.linphone.mediastream.Log; + +class AndroidContact implements Serializable { + protected String mAndroidId, mAndroidRawId, mAndroidTagId, mAndroidLookupKey; + + private transient ArrayList mChangesToCommit; + + protected AndroidContact() { + mChangesToCommit = new ArrayList<>(); + } + + protected String getAndroidId() { + return mAndroidId; + } + + protected void setAndroidId(String id) { + mAndroidId = id; + } + + protected String getAndroidLookupKey() { + return mAndroidLookupKey; + } + + protected void setAndroidLookupKey(String lookupKey) { + mAndroidLookupKey = lookupKey; + } + + protected Uri getAndroidLookupUri() { + return ContactsContract.Contacts.getLookupUri( + Long.parseLong(getAndroidId()), getAndroidLookupKey()); + } + + protected boolean isAndroidContact() { + return mAndroidId != null; + } + + protected void addChangesToCommit(ContentProviderOperation operation) { + Log.d("[Contact] Added operation " + operation); + mChangesToCommit.add(operation); + } + + protected void saveChangesCommited() { + if (ContactsManager.getInstance().hasContactsAccess() && mChangesToCommit.size() > 0) { + try { + ContentProviderResult[] results = + LinphoneService.instance() + .getContentResolver() + .applyBatch(ContactsContract.AUTHORITY, mChangesToCommit); + if (results != null && results.length > 0 && results[0] != null) { + Log.i("[Contact] Contact created with URI " + results[0].uri); + } + } catch (Exception e) { + Log.e(e); + } finally { + mChangesToCommit.clear(); + } + } + } + + protected void createAndroidContact() { + // TODO + if (LinphoneManager.getInstance() + .getContext() + .getResources() + .getBoolean(R.bool.use_linphone_tag)) { + Log.i("[Contact] Creating contact using linphone account type"); + addChangesToCommit( + ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue( + ContactsContract.RawContacts.ACCOUNT_TYPE, + ContactsManager.getInstance() + .getString(R.string.sync_account_type)) + .withValue( + ContactsContract.RawContacts.ACCOUNT_NAME, + ContactsManager.getInstance() + .getString(R.string.sync_account_name)) + .withValue( + ContactsContract.RawContacts.AGGREGATION_MODE, + ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT) + .build()); + } else { + Log.i("[Contact] Creating contact using default account type"); + addChangesToCommit( + ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null) + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null) + .withValue( + ContactsContract.RawContacts.AGGREGATION_MODE, + ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT) + .build()); + } + } + + protected void deleteAndroidContact() { + ContactsManager.getInstance().delete(getAndroidId()); + } + + protected Uri getContactThumbnailPictureUri() { + Uri person = + ContentUris.withAppendedId( + ContactsContract.Contacts.CONTENT_URI, Long.parseLong(getAndroidId())); + return Uri.withAppendedPath(person, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY); + } + + protected Uri getContactPictureUri() { + Uri person = + ContentUris.withAppendedId( + ContactsContract.Contacts.CONTENT_URI, Long.parseLong(getAndroidId())); + return Uri.withAppendedPath(person, ContactsContract.Contacts.Photo.DISPLAY_PHOTO); + } + + protected void setName(String fn, String ln) { + if ((fn == null || fn.isEmpty()) && (ln == null || ln.isEmpty())) { + Log.e("[Contact] Can't set both first and last name to null or empty"); + return; + } + + if (mAndroidId == null || "0".equals(mAndroidId)) { + Log.i("[Contact] Setting given & family name " + fn + " " + ln + " to new contact."); + // TODO + } else { + Log.i( + "[Contact] Setting given & family name " + + fn + + " " + + ln + + " to existing contact " + + mAndroidId); + // TODO + } + } + + protected void setOrganization(String org) { + if (org == null || org.isEmpty()) { + if (mAndroidId == null) { + Log.e("[Contact] Can't set organization to null or empty for new contact"); + return; + } + } + if (mAndroidId == null || "0".equals(mAndroidId)) { + Log.i("[Contact] Setting organization " + org + " to new contact."); + // TODO + } else { + Log.i("[Contact] Setting organization " + org + " to existing contact " + mAndroidId); + // TODO + } + } + + protected void setPhoto(byte[] photo) { + if (photo == null) { + Log.e("[Contact] Can't set null picture."); + return; + } + + if (mAndroidId == null || "0".equals(mAndroidId)) { + Log.i("[Contact] Setting picture to new contact."); + // TODO + } else { + Log.i("[Contact] Setting picture to existing contact " + mAndroidId); + // TODO + } + } + + protected void removeNumberOrAddress(String noa) { + if (noa == null || noa.isEmpty()) { + Log.e("[Contact] Can't remove null or empty number or address."); + return; + } + + if (mAndroidId == null || "0".equals(mAndroidId)) { + Log.i("[Contact] Removing number or address " + noa + " from new contact."); + // TODO + } else { + Log.i( + "[Contact] Removing number or address " + + noa + + " from existing contact " + + mAndroidId); + // TODO + } + } + + protected void addNumberOrAddress(String value, String oldValueToReplace) { + if (value == null || value.isEmpty()) { + Log.e("[Contact] Can't add null or empty number or address"); + return; + } + + if (oldValueToReplace != null) { + if (mAndroidId != null) { + Log.e("[Contact] Can't update a number or address in non existing contact"); + return; + } + + Log.i( + "[Contact] Updating " + + oldValueToReplace + + " by " + + value + + " in contact " + + mAndroidId); + // TODO + } else { + if (mAndroidId == null || "0".equals(mAndroidId)) { + Log.i("[Contact] Adding number or address " + value + " to new contact."); + // TODO + } else { + Log.i( + "[Contact] Adding number or address " + + value + + " to existing contact " + + mAndroidId); + // TODO + } + } + } + + protected void getAndroidIds() { + mAndroidRawId = findRawContactID(); + if (LinphoneManager.getInstance() + .getContext() + .getResources() + .getBoolean(R.bool.use_linphone_tag)) { + mAndroidTagId = findLinphoneRawContactId(); + } + } + + protected Pair getContactNames() { + Pair names = null; + ContentResolver resolver = LinphoneService.instance().getContentResolver(); + String[] proj = + new String[] { + ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, + ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME + }; + String select = + ContactsContract.Data.CONTACT_ID + + "=? AND " + + ContactsContract.Data.MIMETYPE + + "=?"; + String[] args = + new String[] { + getAndroidId(), + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + }; + Cursor c = resolver.query(ContactsContract.Data.CONTENT_URI, proj, select, args, null); + if (c != null) { + if (c.moveToFirst()) { + String fn = + c.getString( + c.getColumnIndex( + ContactsContract.CommonDataKinds.StructuredName + .GIVEN_NAME)); + String ln = + c.getString( + c.getColumnIndex( + ContactsContract.CommonDataKinds.StructuredName + .FAMILY_NAME)); + names = new Pair<>(fn, ln); + } + c.close(); + } + return names; + } + + protected String getNativeContactOrganization() { + String org = null; + ContentResolver resolver = LinphoneService.instance().getContentResolver(); + String[] proj = new String[] {ContactsContract.CommonDataKinds.Organization.COMPANY}; + String select = + ContactsContract.Data.CONTACT_ID + + "=? AND " + + ContactsContract.Data.MIMETYPE + + "=?"; + String[] args = + new String[] { + getAndroidId(), ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE + }; + Cursor c = resolver.query(ContactsContract.Data.CONTENT_URI, proj, select, args, null); + if (c != null) { + if (c.moveToFirst()) { + org = + c.getString( + c.getColumnIndex( + ContactsContract.CommonDataKinds.Organization.COMPANY)); + } + c.close(); + } + return org; + } + + protected String findRawContactID() { + ContentResolver resolver = LinphoneService.instance().getContentResolver(); + String result = null; + String[] projection = {ContactsContract.RawContacts._ID}; + + String selection = ContactsContract.RawContacts.CONTACT_ID + "=?"; + Cursor c = + resolver.query( + ContactsContract.RawContacts.CONTENT_URI, + projection, + selection, + new String[] {getAndroidId()}, + null); + if (c != null) { + if (c.moveToFirst()) { + result = c.getString(c.getColumnIndex(ContactsContract.RawContacts._ID)); + } + c.close(); + } + return result; + } + + protected List getAddressesAndNumbersForAndroidContact() { + List result = new ArrayList<>(); + ContentResolver resolver = LinphoneService.instance().getContentResolver(); + + String select = + ContactsContract.Data.CONTACT_ID + + " =? AND (" + + ContactsContract.Data.MIMETYPE + + "=? OR " + + ContactsContract.Data.MIMETYPE + + "=? OR " + + ContactsContract.Data.MIMETYPE + + "=?)"; + String[] projection = + new String[] { + "data1", "data4", ContactsContract.Data.MIMETYPE + }; // PHONE_NUMBER == SIP_ADDRESS == "data1"... + Cursor c = + resolver.query( + ContactsContract.Data.CONTENT_URI, + projection, + select, + new String[] { + getAndroidId(), + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE, + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE, + ContactsManager.getInstance().getString(R.string.sync_mimetype) + }, + null); + if (c != null) { + while (c.moveToNext()) { + String mime = c.getString(c.getColumnIndex(ContactsContract.Data.MIMETYPE)); + if (mime != null && mime.length() > 0) { + if (mime.equals(ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE) + || mime.equals( + ContactsManager.getInstance() + .getString(R.string.sync_mimetype))) { + String number = c.getString(c.getColumnIndex("data1")); // SIP_ADDRESS + result.add(new LinphoneNumberOrAddress(number, true)); + } else if (mime.equals( + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) { + String number = c.getString(c.getColumnIndex("data1")); // PHONE_NUMBER + String normalized_number = + c.getString(c.getColumnIndex("data4")); // NORMALIZED_PHONE_NUMBER + result.add(new LinphoneNumberOrAddress(number, normalized_number)); + } + } + } + c.close(); + } + Collections.sort(result); + return result; + } + + protected void createLinphoneTagIfNeeded() { + if (LinphoneManager.getInstance() + .getContext() + .getResources() + .getBoolean(R.bool.use_linphone_tag)) { + if (mAndroidTagId == null && findLinphoneRawContactId() == null) { + createLinphoneContactTag(); + } + } + } + + private String findLinphoneRawContactId() { + ContentResolver resolver = LinphoneService.instance().getContentResolver(); + String result = null; + String[] projection = {ContactsContract.RawContacts._ID}; + + String selection = + ContactsContract.RawContacts.CONTACT_ID + + "=? AND " + + ContactsContract.RawContacts.ACCOUNT_TYPE + + "=?"; + Cursor c = + resolver.query( + ContactsContract.RawContacts.CONTENT_URI, + projection, + selection, + new String[] { + getAndroidId(), + ContactsManager.getInstance().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 void createLinphoneContactTag() { + ArrayList batch = new ArrayList<>(); + + /*batch.add( + ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue( + ContactsContract.RawContacts.ACCOUNT_TYPE, + ContactsManager.getInstance().getString(R.string.sync_account_type)) + .withValue( + ContactsContract.RawContacts.ACCOUNT_NAME, + ContactsManager.getInstance().getString(R.string.sync_account_name)) + .withValue( + ContactsContract.RawContacts.AGGREGATION_MODE, + ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT) + .build()); + + batch.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, + getFullName()) + .build()); + + batch.add( + ContentProviderOperation.newUpdate( + ContactsContract.AggregationExceptions.CONTENT_URI) + .withValue( + ContactsContract.AggregationExceptions.TYPE, + ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER) + .withValue( + ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, + mAndroidRawId) + .withValueBackReference( + ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, 0) + .build());*/ + // TODO + Log.i("[Contact] Creating linphone tag"); + + try { + LinphoneService.instance() + .getContentResolver() + .applyBatch(ContactsContract.AUTHORITY, batch); + mAndroidTagId = findLinphoneRawContactId(); + } catch (Exception e) { + Log.e(e); + } + } +} diff --git a/app/src/main/java/org/linphone/contacts/LinphoneContact.java b/app/src/main/java/org/linphone/contacts/LinphoneContact.java index ba8cfb721..c41f782e3 100644 --- a/app/src/main/java/org/linphone/contacts/LinphoneContact.java +++ b/app/src/main/java/org/linphone/contacts/LinphoneContact.java @@ -19,23 +19,13 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.database.Cursor; import android.net.Uri; -import android.provider.ContactsContract; -import android.provider.ContactsContract.CommonDataKinds; +import androidx.core.util.Pair; import java.io.Serializable; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Locale; import org.linphone.LinphoneManager; -import org.linphone.LinphoneService; -import org.linphone.R; import org.linphone.core.Address; import org.linphone.core.Core; import org.linphone.core.Friend; @@ -43,32 +33,24 @@ import org.linphone.core.FriendList; import org.linphone.core.PresenceBasicStatus; import org.linphone.core.PresenceModel; import org.linphone.core.SubscribePolicy; -import org.linphone.mediastream.Log; -public class LinphoneContact implements Serializable, Comparable { +public class LinphoneContact extends AndroidContact + implements Serializable, Comparable { private static final long serialVersionUID = 9015568163905205244L; private transient Friend mFriend; - private String mFullName, - mFirstName, - mLastName, - mAndroidId, - mAndroidRawId, - mAndroidTagId, - mOrganization, - mAndroidLookupKey; + private String mFullName, mFirstName, mLastName, mOrganization; private transient Uri mPhotoUri, mThumbnailUri; private List mAddresses; - private transient ArrayList mChangesToCommit; private boolean mHasSipAddress; public LinphoneContact() { + super(); mAddresses = new ArrayList<>(); mAndroidId = null; mAndroidLookupKey = null; mThumbnailUri = null; mPhotoUri = null; - mChangesToCommit = new ArrayList<>(); mHasSipAddress = false; } @@ -82,73 +64,6 @@ public class LinphoneContact implements Serializable, Comparable 0) { - try { - ContentProviderResult[] results = - LinphoneService.instance() - .getContentResolver() - .applyBatch(ContactsContract.AUTHORITY, mChangesToCommit); - if (results != null && results.length > 0 && results[0] != null) { - Log.i("[Contact] Contact created with URI " + results[0].uri); - } - } catch (Exception e) { - Log.e(e); - } finally { - mChangesToCommit = new ArrayList<>(); - } - } - - createOrUpdateFriend(); - } - - public void delete() { - if (isAndroidContact()) { - ContactsManager.getInstance().delete(getAndroidId()); - } - if (isFriend()) { - deleteFriend(); - } - } - public void deleteFriend() { Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); if (mFriend != null && lc != null) { @@ -878,16 +375,71 @@ public class LinphoneContact implements Serializable, Comparable(); if (isAndroidContact()) { - getContactNames(); - getNativeContactOrganization(); + Pair names = getContactNames(); + if (names != null) { + mFirstName = names.first; + mLastName = names.second; + } + + mOrganization = getNativeContactOrganization(); getAndroidIds(); + mHasSipAddress = false; for (LinphoneNumberOrAddress noa : getAddressesAndNumbersForAndroidContact()) { addNumberOrAddress(noa); @@ -921,290 +473,16 @@ public class LinphoneContact implements Serializable, Comparable getAddressesAndNumbersForAndroidContact() { - List result = new ArrayList<>(); - ContentResolver resolver = LinphoneService.instance().getContentResolver(); - - String select = - ContactsContract.Data.CONTACT_ID - + " =? AND (" - + ContactsContract.Data.MIMETYPE - + "=? OR " - + ContactsContract.Data.MIMETYPE - + "=? OR " - + ContactsContract.Data.MIMETYPE - + "=?)"; - String[] projection = - new String[] { - "data1", "data4", ContactsContract.Data.MIMETYPE - }; // PHONE_NUMBER == SIP_ADDRESS == "data1"... - Cursor c = - resolver.query( - ContactsContract.Data.CONTENT_URI, - projection, - select, - new String[] { - getAndroidId(), - ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE, - ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE, - ContactsManager.getInstance().getString(R.string.sync_mimetype) - }, - null); - if (c != null) { - while (c.moveToNext()) { - String mime = c.getString(c.getColumnIndex(ContactsContract.Data.MIMETYPE)); - if (mime != null && mime.length() > 0) { - if (mime.equals(ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE) - || mime.equals( - ContactsManager.getInstance() - .getString(R.string.sync_mimetype))) { - String number = c.getString(c.getColumnIndex("data1")); // SIP_ADDRESS - result.add(new LinphoneNumberOrAddress(number, true)); - } else if (mime.equals( - ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) { - String number = c.getString(c.getColumnIndex("data1")); // PHONE_NUMBER - String normalized_number = - c.getString(c.getColumnIndex("data4")); // NORMALIZED_PHONE_NUMBER - result.add(new LinphoneNumberOrAddress(number, normalized_number)); - } - } - } - c.close(); - } - Collections.sort(result); - return result; - } - - private String findLinphoneRawContactId() { - ContentResolver resolver = LinphoneService.instance().getContentResolver(); - String result = null; - String[] projection = {ContactsContract.RawContacts._ID}; - - String selection = - ContactsContract.RawContacts.CONTACT_ID - + "=? AND " - + ContactsContract.RawContacts.ACCOUNT_TYPE - + "=?"; - Cursor c = - resolver.query( - ContactsContract.RawContacts.CONTENT_URI, - projection, - selection, - new String[] { - getAndroidId(), - ContactsManager.getInstance().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; - } - - public void createLinphoneTagIfNeeded() { - if (LinphoneManager.getInstance() - .getContext() - .getResources() - .getBoolean(R.bool.use_linphone_tag)) { - if (mAndroidTagId == null && findLinphoneRawContactId() == null) { - createLinphoneContactTag(); - } - } - } - - private void createLinphoneContactTag() { - ArrayList batch = new ArrayList<>(); - - batch.add( - ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) - .withValue( - ContactsContract.RawContacts.ACCOUNT_TYPE, - ContactsManager.getInstance().getString(R.string.sync_account_type)) - .withValue( - ContactsContract.RawContacts.ACCOUNT_NAME, - ContactsManager.getInstance().getString(R.string.sync_account_name)) - .withValue( - ContactsContract.RawContacts.AGGREGATION_MODE, - ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT) - .build()); - - batch.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, - getFullName()) - .build()); - - batch.add( - ContentProviderOperation.newUpdate( - ContactsContract.AggregationExceptions.CONTENT_URI) - .withValue( - ContactsContract.AggregationExceptions.TYPE, - ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER) - .withValue( - ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, - mAndroidRawId) - .withValueBackReference( - ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, 0) - .build()); - Log.i("[Contact] Creating linphone tag"); - - try { - LinphoneService.instance() - .getContentResolver() - .applyBatch(ContactsContract.AUTHORITY, batch); - mAndroidTagId = findLinphoneRawContactId(); - } catch (Exception e) { - Log.e(e); + if (isFriend()) { + deleteFriend(); } } }