diff --git a/android/src/main/java/com/rt2zz/reactnativecontacts/impl/ContactsManagerImpl.java b/android/src/main/java/com/rt2zz/reactnativecontacts/impl/ContactsManagerImpl.java new file mode 100644 index 00000000..061f46b2 --- /dev/null +++ b/android/src/main/java/com/rt2zz/reactnativecontacts/impl/ContactsManagerImpl.java @@ -0,0 +1,1336 @@ +package com.rt2zz.reactnativecontacts.impl; + +import android.Manifest; +import android.app.Activity; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Bundle; +import android.provider.ContactsContract; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; + +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; +import com.rt2zz.reactnativecontacts.ContactsProvider; + +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +public class ContactsManagerImpl { + + private static final String PERMISSION_DENIED = "denied"; + private static final String PERMISSION_AUTHORIZED = "authorized"; + private static final String PERMISSION_READ_CONTACTS = Manifest.permission.READ_CONTACTS; + private static final int PERMISSION_REQUEST_CODE = 888; + private static final int REQUEST_OPEN_CONTACT_FORM = 52941; + private static final int REQUEST_OPEN_EXISTING_CONTACT = 52942; + + private static Promise updateContactPromise; + private static Promise requestPromise; + + private final ReactApplicationContext reactApplicationContext; + + private Executor executor; + + + public ContactsManagerImpl(ReactApplicationContext reactContext, boolean useSerialExecutor) { + this.reactApplicationContext = reactContext; + this.executor = initializeExecutor(useSerialExecutor); + } + + private Executor initializeExecutor(boolean useSerialExecutor){ + if(useSerialExecutor){ + return Executors.newSingleThreadExecutor(); //AsyncTask.SERIAL_EXECUTOR + } + return Executors.newCachedThreadPool(); + } + + protected Executor getExecutor() { + return executor; + } + + @NonNull + protected ReactApplicationContext getReactApplicationContext() { + return Objects.requireNonNull(reactApplicationContext, "Context not initialized"); + } + + @Nullable + protected final Activity getCurrentActivity() { + return reactApplicationContext.getCurrentActivity(); + } + + /* + * Returns all contactable records on phone + * queries CommonDataKinds.Contactables to get phones and emails + */ + public void getAll(Promise promise) { + getAllContacts(promise); + } + + /** + * Introduced for iOS compatibility. Same as getAll + * + * @param promise promise + */ + public void getAllWithoutPhotos(Promise promise) { + getAllContacts(promise); + } + + /** + * Retrieves contacts. + * Uses raw URI when rawUri is true, makes assets copy + * otherwise. + */ + private void getAllContacts(final Promise promise) { + getExecutor().execute(() -> { + Context context = getReactApplicationContext(); + ContentResolver cr = context.getContentResolver(); + ContactsProvider contactsProvider = new ContactsProvider(cr); + WritableArray contacts = contactsProvider.getContacts(); + promise.resolve(contacts); + }); + } + + public void getCount(final Promise promise) { + getExecutor().execute(() -> { + Context context = getReactApplicationContext(); + ContentResolver cr = context.getContentResolver(); + ContactsProvider contactsProvider = new ContactsProvider(cr); + try { + Integer contacts = contactsProvider.getContactsCount(); + promise.resolve(contacts); + } catch (Exception e) { + promise.reject(e); + } + }); + } + + /** + * Retrieves contacts matching String. + * Uses raw URI when rawUri is true, makes assets copy + * otherwise. + * + * @param searchString String to match + */ + public void getContactsMatchingString(final String searchString, final Promise promise) { + getExecutor().execute(() -> { + Context context = getReactApplicationContext(); + ContentResolver cr = context.getContentResolver(); + ContactsProvider contactsProvider = new ContactsProvider(cr); + WritableArray contacts = contactsProvider.getContactsMatchingString(searchString); + promise.resolve(contacts); + }); + } + + /** + * Retrieves contacts matching a phone number. + * Uses raw URI when rawUri is true, makes assets copy + * otherwise. + * + * @param phoneNumber phone number to match + */ + public void getContactsByPhoneNumber(final String phoneNumber, final Promise promise) { + getExecutor().execute(new Runnable() { + @Override + public void run() { + Context context = getReactApplicationContext(); + ContentResolver cr = context.getContentResolver(); + ContactsProvider contactsProvider = new ContactsProvider(cr); + WritableArray contacts = contactsProvider.getContactsByPhoneNumber(phoneNumber); + promise.resolve(contacts); + } + }); + } + + /** + * Retrieves contacts matching an email address. + * Uses raw URI when rawUri is true, makes assets copy + * otherwise. + * + * @param emailAddress email address to match + */ + public void getContactsByEmailAddress(final String emailAddress, final Promise promise) { + getExecutor().execute(() -> { + Context context = getReactApplicationContext(); + ContentResolver cr = context.getContentResolver(); + ContactsProvider contactsProvider = new ContactsProvider(cr); + WritableArray contacts = contactsProvider.getContactsByEmailAddress(emailAddress); + promise.resolve(contacts); + }); + } + + /** + * Retrieves thumbnailPath for contact, or null if not + * available. + * + * @param contactId contact identifier, recordID + */ + public void getPhotoForId(final String contactId, final Promise promise) { + getExecutor().execute(() -> { + Context context = getReactApplicationContext(); + ContentResolver cr = context.getContentResolver(); + ContactsProvider contactsProvider = new ContactsProvider(cr); + String photoUri = contactsProvider.getPhotoUriFromContactId(contactId); + promise.resolve(photoUri); + }); + } + + /** + * Retrieves contact for contact, or null if not + * available. + * + * @param contactId contact identifier, recordID + */ + public void getContactById(final String contactId, final Promise promise) { + getExecutor().execute(() -> { + Context context = getReactApplicationContext(); + ContentResolver cr = context.getContentResolver(); + ContactsProvider contactsProvider = new ContactsProvider(cr); + WritableMap contact = contactsProvider.getContactById(contactId); + promise.resolve(contact); + }); + } + + + public void writePhotoToPath(final String contactId, final String file, final Promise promise) { + getExecutor().execute(() -> { + Context context = getReactApplicationContext(); + ContentResolver cr = context.getContentResolver(); + + Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.parseLong(contactId)); + InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(cr, uri); + OutputStream outputStream = null; + try { + outputStream = new FileOutputStream(file); + BitmapFactory.decodeStream(inputStream).compress(Bitmap.CompressFormat.PNG, 100, outputStream); + promise.resolve(true); + } catch (FileNotFoundException e) { + promise.reject(e.toString()); + } finally { + try { + if (outputStream != null) { + outputStream.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + + private Bitmap getThumbnailBitmap(String thumbnailPath) { + // Thumbnail from absolute path + Bitmap photo = BitmapFactory.decodeFile(thumbnailPath); + + if (photo == null) { + // Try to find the thumbnail from assets + AssetManager assetManager = getReactApplicationContext().getAssets(); + InputStream inputStream = null; + try { + inputStream = assetManager.open(thumbnailPath); + photo = BitmapFactory.decodeStream(inputStream); + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return photo; + } + + /* + * Start open contact form + */ + public void openContactForm(ReadableMap contact, Promise promise) { + + String givenName = contact.hasKey("givenName") ? contact.getString("givenName") : null; + String middleName = contact.hasKey("middleName") ? contact.getString("middleName") : null; + String displayName = contact.hasKey("displayName") ? contact.getString("displayName") : null; + String familyName = contact.hasKey("familyName") ? contact.getString("familyName") : null; + String prefix = contact.hasKey("prefix") ? contact.getString("prefix") : null; + String suffix = contact.hasKey("suffix") ? contact.getString("suffix") : null; + String company = contact.hasKey("company") ? contact.getString("company") : null; + String jobTitle = contact.hasKey("jobTitle") ? contact.getString("jobTitle") : null; + String department = contact.hasKey("department") ? contact.getString("department") : null; + String note = contact.hasKey("note") ? contact.getString("note") : null; + String thumbnailPath = contact.hasKey("thumbnailPath") ? contact.getString("thumbnailPath") : null; + + ReadableArray phoneNumbers = contact.hasKey("phoneNumbers") ? contact.getArray("phoneNumbers") : null; + int numOfPhones = 0; + String[] phones = null; + String[] phonesLabels = null; + Integer[] phonesLabelsTypes = null; + if (phoneNumbers != null) { + numOfPhones = phoneNumbers.size(); + phones = new String[numOfPhones]; + phonesLabels = new String[numOfPhones]; + phonesLabelsTypes = new Integer[numOfPhones]; + for (int i = 0; i < numOfPhones; i++) { + phones[i] = phoneNumbers.getMap(i).getString("number"); + String label = phoneNumbers.getMap(i).getString("label"); + phonesLabels[i] = label; + phonesLabelsTypes[i] = mapStringToPhoneType(label); + } + } + + ReadableArray urlAddresses = contact.hasKey("urlAddresses") ? contact.getArray("urlAddresses") : null; + int numOfUrls = 0; + String[] urls = null; + if (urlAddresses != null) { + numOfUrls = urlAddresses.size(); + urls = new String[numOfUrls]; + for (int i = 0; i < numOfUrls; i++) { + urls[i] = urlAddresses.getMap(i).getString("url"); + } + } + + ReadableArray emailAddresses = contact.hasKey("emailAddresses") ? contact.getArray("emailAddresses") : null; + int numOfEmails = 0; + String[] emails = null; + Integer[] emailsLabels = null; + if (emailAddresses != null) { + numOfEmails = emailAddresses.size(); + emails = new String[numOfEmails]; + emailsLabels = new Integer[numOfEmails]; + for (int i = 0; i < numOfEmails; i++) { + emails[i] = emailAddresses.getMap(i).getString("email"); + String label = emailAddresses.getMap(i).getString("label"); + emailsLabels[i] = mapStringToEmailType(label); + } + } + + ReadableArray postalAddresses = contact.hasKey("postalAddresses") ? contact.getArray("postalAddresses") : null; + int numOfPostalAddresses = 0; + String[] postalAddressesStreet = null; + String[] postalAddressesCity = null; + String[] postalAddressesState = null; + String[] postalAddressesRegion = null; + String[] postalAddressesPostCode = null; + String[] postalAddressesCountry = null; + String[] postalAddressesFormattedAddress = null; + String[] postalAddressesLabel = null; + Integer[] postalAddressesType = null; + + if (postalAddresses != null) { + numOfPostalAddresses = postalAddresses.size(); + postalAddressesStreet = new String[numOfPostalAddresses]; + postalAddressesCity = new String[numOfPostalAddresses]; + postalAddressesState = new String[numOfPostalAddresses]; + postalAddressesRegion = new String[numOfPostalAddresses]; + postalAddressesPostCode = new String[numOfPostalAddresses]; + postalAddressesCountry = new String[numOfPostalAddresses]; + postalAddressesFormattedAddress = new String[numOfPostalAddresses]; + postalAddressesLabel = new String[numOfPostalAddresses]; + postalAddressesType = new Integer[numOfPostalAddresses]; + for (int i = 0; i < numOfPostalAddresses; i++) { + postalAddressesStreet[i] = postalAddresses.getMap(i).getString("street"); + postalAddressesCity[i] = postalAddresses.getMap(i).getString("city"); + postalAddressesState[i] = postalAddresses.getMap(i).getString("state"); + postalAddressesRegion[i] = postalAddresses.getMap(i).getString("region"); + postalAddressesPostCode[i] = postalAddresses.getMap(i).getString("postCode"); + postalAddressesCountry[i] = postalAddresses.getMap(i).getString("country"); + postalAddressesFormattedAddress[i] = postalAddresses.getMap(i).getString("formattedAddress"); + postalAddressesLabel[i] = postalAddresses.getMap(i).getString("label"); + postalAddressesType[i] = mapStringToPostalAddressType(postalAddresses.getMap(i).getString("label")); + } + } + + ReadableArray imAddresses = contact.hasKey("imAddresses") ? contact.getArray("imAddresses") : null; + int numOfIMAddresses = 0; + String[] imAccounts = null; + String[] imProtocols = null; + if (imAddresses != null) { + numOfIMAddresses = imAddresses.size(); + imAccounts = new String[numOfIMAddresses]; + imProtocols = new String[numOfIMAddresses]; + for (int i = 0; i < numOfIMAddresses; i++) { + imAccounts[i] = imAddresses.getMap(i).getString("username"); + imProtocols[i] = imAddresses.getMap(i).getString("service"); + } + } + + ArrayList contactData = new ArrayList<>(); + + ContentValues name = new ContentValues(); + name.put(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Identity.CONTENT_ITEM_TYPE); + name.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, givenName); + name.put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, familyName); + name.put(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, middleName); + name.put(ContactsContract.CommonDataKinds.StructuredName.PREFIX, prefix); + name.put(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, suffix); + contactData.add(name); + + ContentValues organization = new ContentValues(); + organization.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE); + organization.put(ContactsContract.CommonDataKinds.Organization.COMPANY, company); + organization.put(ContactsContract.CommonDataKinds.Organization.TITLE, jobTitle); + organization.put(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, department); + contactData.add(organization); + + for (int i = 0; i < numOfUrls; i++) { + ContentValues url = new ContentValues(); + url.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE); + url.put(ContactsContract.CommonDataKinds.Website.URL, urls[i]); + contactData.add(url); + } + + for (int i = 0; i < numOfEmails; i++) { + ContentValues email = new ContentValues(); + email.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE); + email.put(ContactsContract.CommonDataKinds.Email.TYPE, emailsLabels[i]); + email.put(ContactsContract.CommonDataKinds.Email.ADDRESS, emails[i]); + contactData.add(email); + } + + for (int i = 0; i < numOfPhones; i++) { + ContentValues phone = new ContentValues(); + phone.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); + phone.put(ContactsContract.CommonDataKinds.Phone.TYPE, phonesLabelsTypes[i]); + phone.put(ContactsContract.CommonDataKinds.Phone.LABEL, phonesLabels[i]); + phone.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phones[i]); + + contactData.add(phone); + } + + for (int i = 0; i < numOfPostalAddresses; i++) { + ContentValues structuredPostal = new ContentValues(); + structuredPostal.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE); + structuredPostal.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, postalAddressesStreet[i]); + structuredPostal.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, postalAddressesCity[i]); + structuredPostal.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, postalAddressesRegion[i]); + structuredPostal.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, postalAddressesCountry[i]); + structuredPostal.put(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, postalAddressesPostCode[i]); + structuredPostal.put(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, + postalAddressesFormattedAddress[i]); + structuredPostal.put(ContactsContract.CommonDataKinds.StructuredPostal.LABEL, postalAddressesLabel[i]); + structuredPostal.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, postalAddressesType[i]); + // No state column in StructuredPostal + // structuredPostal.put(CommonDataKinds.StructuredPostal.???, + // postalAddressesState[i]); + contactData.add(structuredPostal); + } + + for (int i = 0; i < numOfIMAddresses; i++) { + ContentValues imAddress = new ContentValues(); + imAddress.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE); + imAddress.put(ContactsContract.CommonDataKinds.Im.DATA, imAccounts[i]); + imAddress.put(ContactsContract.CommonDataKinds.Im.TYPE, ContactsContract.CommonDataKinds.Im.TYPE_HOME); + imAddress.put(ContactsContract.CommonDataKinds.Im.PROTOCOL, ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM); + imAddress.put(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL, imProtocols[i]); + contactData.add(imAddress); + } + + if (note != null) { + ContentValues structuredNote = new ContentValues(); + structuredNote.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE); + structuredNote.put(ContactsContract.CommonDataKinds.Note.NOTE, note); + contactData.add(structuredNote); + } + + if (thumbnailPath != null && !thumbnailPath.isEmpty()) { + Bitmap photo = getThumbnailBitmap(thumbnailPath); + + if (photo != null) { + ContentValues thumbnail = new ContentValues(); + thumbnail.put(ContactsContract.Data.RAW_CONTACT_ID, 0); + thumbnail.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1); + thumbnail.put(ContactsContract.CommonDataKinds.Photo.PHOTO, toByteArray(photo)); + thumbnail.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); + contactData.add(thumbnail); + } + } + + Intent intent = new Intent(Intent.ACTION_INSERT, ContactsContract.Contacts.CONTENT_URI); + intent.putExtra(ContactsContract.Intents.Insert.NAME, displayName); + intent.putExtra("finishActivityOnSaveCompleted", true); + intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData); + + updateContactPromise = promise; + getReactApplicationContext().startActivityForResult(intent, REQUEST_OPEN_CONTACT_FORM, Bundle.EMPTY); + } + + /* + * Open contact in native app + */ + public void openExistingContact(ReadableMap contact, Promise promise) { + + String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; + + try { + Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, recordID); + Intent intent = new Intent(Intent.ACTION_EDIT); + intent.setDataAndType(uri, ContactsContract.Contacts.CONTENT_ITEM_TYPE); + intent.putExtra("finishActivityOnSaveCompleted", true); + + updateContactPromise = promise; + getReactApplicationContext().startActivityForResult(intent, REQUEST_OPEN_EXISTING_CONTACT, Bundle.EMPTY); + + } catch (Exception e) { + promise.reject(e.toString()); + } + } + + /* + * View contact in native app + */ + public void viewExistingContact(ReadableMap contact, Promise promise) { + + String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; + + try { + Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, recordID); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(uri, ContactsContract.Contacts.CONTENT_ITEM_TYPE); + intent.putExtra("finishActivityOnSaveCompleted", true); + + updateContactPromise = promise; + getReactApplicationContext().startActivityForResult(intent, REQUEST_OPEN_EXISTING_CONTACT, Bundle.EMPTY); + + } catch (Exception e) { + promise.reject(e.toString()); + } + } + + /* + * Edit contact in native app + */ + public void editExistingContact(ReadableMap contact, Promise promise) { + + String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; + + try { + Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, recordID); + + ReadableArray phoneNumbers = contact.hasKey("phoneNumbers") ? contact.getArray("phoneNumbers") : null; + int numOfPhones = 0; + String[] phones = null; + Integer[] phonesLabels = null; + if (phoneNumbers != null) { + numOfPhones = phoneNumbers.size(); + phones = new String[numOfPhones]; + phonesLabels = new Integer[numOfPhones]; + for (int i = 0; i < numOfPhones; i++) { + phones[i] = phoneNumbers.getMap(i).getString("number"); + String label = phoneNumbers.getMap(i).getString("label"); + phonesLabels[i] = mapStringToPhoneType(label); + } + } + + ArrayList contactData = new ArrayList<>(); + for (int i = 0; i < numOfPhones; i++) { + ContentValues phone = new ContentValues(); + phone.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); + phone.put(ContactsContract.CommonDataKinds.Phone.TYPE, phonesLabels[i]); + phone.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phones[i]); + contactData.add(phone); + } + + Intent intent = new Intent(Intent.ACTION_EDIT); + intent.setDataAndType(uri, ContactsContract.Contacts.CONTENT_ITEM_TYPE); + intent.putExtra("finishActivityOnSaveCompleted", true); + intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData); + + updateContactPromise = promise; + getReactApplicationContext().startActivityForResult(intent, REQUEST_OPEN_EXISTING_CONTACT, Bundle.EMPTY); + + } catch (Exception e) { + promise.reject(e.toString()); + } + } + + /* + * Adds contact to phone's addressbook + */ + public void addContact(ReadableMap contact, Promise promise) { + if (contact == null) { + promise.reject("New contact cannot be null."); + return; + } + String givenName = contact.hasKey("givenName") ? contact.getString("givenName") : null; + String middleName = contact.hasKey("middleName") ? contact.getString("middleName") : null; + String familyName = contact.hasKey("familyName") ? contact.getString("familyName") : null; + String prefix = contact.hasKey("prefix") ? contact.getString("prefix") : null; + String suffix = contact.hasKey("suffix") ? contact.getString("suffix") : null; + String company = contact.hasKey("company") ? contact.getString("company") : null; + String jobTitle = contact.hasKey("jobTitle") ? contact.getString("jobTitle") : null; + String department = contact.hasKey("department") ? contact.getString("department") : null; + String note = contact.hasKey("note") ? contact.getString("note") : null; + String thumbnailPath = contact.hasKey("thumbnailPath") ? contact.getString("thumbnailPath") : null; + + ReadableArray phoneNumbers = contact.hasKey("phoneNumbers") ? contact.getArray("phoneNumbers") : null; + int numOfPhones = 0; + String[] phones = null; + Integer[] phonesTypes = null; + String[] phonesLabels = null; + if (phoneNumbers != null) { + numOfPhones = phoneNumbers.size(); + phones = new String[numOfPhones]; + phonesTypes = new Integer[numOfPhones]; + phonesLabels = new String[numOfPhones]; + for (int i = 0; i < numOfPhones; i++) { + phones[i] = phoneNumbers.getMap(i).getString("number"); + String label = phoneNumbers.getMap(i).getString("label"); + phonesTypes[i] = mapStringToPhoneType(label); + phonesLabels[i] = label; + } + } + + ReadableArray urlAddresses = contact.hasKey("urlAddresses") ? contact.getArray("urlAddresses") : null; + int numOfUrls = 0; + String[] urls = null; + if (urlAddresses != null) { + numOfUrls = urlAddresses.size(); + urls = new String[numOfUrls]; + for (int i = 0; i < numOfUrls; i++) { + urls[i] = urlAddresses.getMap(i).getString("url"); + } + } + + ReadableArray emailAddresses = contact.hasKey("emailAddresses") ? contact.getArray("emailAddresses") : null; + int numOfEmails = 0; + String[] emails = null; + Integer[] emailsTypes = null; + String[] emailsLabels = null; + if (emailAddresses != null) { + numOfEmails = emailAddresses.size(); + emails = new String[numOfEmails]; + emailsTypes = new Integer[numOfEmails]; + emailsLabels = new String[numOfEmails]; + for (int i = 0; i < numOfEmails; i++) { + emails[i] = emailAddresses.getMap(i).getString("email"); + String label = emailAddresses.getMap(i).getString("label"); + emailsTypes[i] = mapStringToEmailType(label); + emailsLabels[i] = label; + } + } + + ReadableArray imAddresses = contact.hasKey("imAddresses") ? contact.getArray("imAddresses") : null; + int numOfIMAddresses = 0; + String[] imAccounts = null; + String[] imProtocols = null; + if (imAddresses != null) { + numOfIMAddresses = imAddresses.size(); + imAccounts = new String[numOfIMAddresses]; + imProtocols = new String[numOfIMAddresses]; + for (int i = 0; i < numOfIMAddresses; i++) { + imAccounts[i] = imAddresses.getMap(i).getString("username"); + imProtocols[i] = imAddresses.getMap(i).getString("service"); + } + } + + ArrayList ops = new ArrayList(); + + ContentProviderOperation.Builder op = ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null) + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null); + ops.add(op.build()); + + op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + // .withValue(StructuredName.DISPLAY_NAME, name) + .withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, givenName) + .withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, middleName) + .withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, familyName) + .withValue(ContactsContract.CommonDataKinds.StructuredName.PREFIX, prefix) + .withValue(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, suffix); + ops.add(op.build()); + + op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Note.NOTE, note); + ops.add(op.build()); + + op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Organization.COMPANY, company) + .withValue(ContactsContract.CommonDataKinds.Organization.TITLE, jobTitle) + .withValue(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, department); + ops.add(op.build()); + + // TODO not sure where to allow yields + op.withYieldAllowed(true); + + for (int i = 0; i < numOfPhones; i++) { + op = 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, phones[i]) + .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phonesTypes[i]) + .withValue(ContactsContract.CommonDataKinds.Phone.LABEL, phonesLabels[i]); + ops.add(op.build()); + } + + for (int i = 0; i < numOfUrls; i++) { + op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Website.URL, urls[i]); + ops.add(op.build()); + } + + for (int i = 0; i < numOfEmails; i++) { + op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, emails[i]) + .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailsTypes[i]) + .withValue(ContactsContract.CommonDataKinds.Email.LABEL, emailsLabels[i]); + ops.add(op.build()); + } + + if (thumbnailPath != null && !thumbnailPath.isEmpty()) { + Bitmap photo = getThumbnailBitmap(thumbnailPath); + + if (photo != null) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, toByteArray(photo)) + .build()); + } + } + + ReadableArray postalAddresses = contact.hasKey("postalAddresses") ? contact.getArray("postalAddresses") : null; + if (postalAddresses != null) { + for (int i = 0; i < postalAddresses.size(); i++) { + ReadableMap address = postalAddresses.getMap(i); + + op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, + mapStringToPostalAddressType(address.getString("label"))) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.LABEL, address.getString("label")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, address.getString("street")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, address.getString("city")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, address.getString("state")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, address.getString("postCode")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, address.getString("country")); + + ops.add(op.build()); + } + } + + for (int i = 0; i < numOfIMAddresses; i++) { + op = 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, imAccounts[i]) + .withValue(ContactsContract.CommonDataKinds.Im.TYPE, ContactsContract.CommonDataKinds.Im.TYPE_HOME) + .withValue(ContactsContract.CommonDataKinds.Im.PROTOCOL, ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM) + .withValue(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL, imProtocols[i]); + ops.add(op.build()); + } + + Context ctx = getReactApplicationContext(); + try { + ContentResolver cr = ctx.getContentResolver(); + ContentProviderResult[] result = cr.applyBatch(ContactsContract.AUTHORITY, ops); + + if (result != null && result.length > 0) { + + String rawId = String.valueOf(ContentUris.parseId(result[0].uri)); + + ContactsProvider contactsProvider = new ContactsProvider(cr); + WritableMap newlyAddedContact = contactsProvider.getContactByRawId(rawId); + + promise.resolve(newlyAddedContact); // success + } + } catch (Exception e) { + promise.reject(e.toString()); + } + } + + public byte[] toByteArray(Bitmap bitmap) { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream); + return stream.toByteArray(); + } + + /* + * Update contact to phone's addressbook + */ + public void updateContact(ReadableMap contact, Promise promise) { + + String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; + String rawContactId = contact.hasKey("rawContactId") ? contact.getString("rawContactId") : null; + + if (rawContactId == null || recordID == null) { + promise.reject("Invalid recordId or rawContactId"); + return; + } + + String givenName = contact.hasKey("givenName") ? contact.getString("givenName") : null; + String middleName = contact.hasKey("middleName") ? contact.getString("middleName") : null; + String familyName = contact.hasKey("familyName") ? contact.getString("familyName") : null; + String prefix = contact.hasKey("prefix") ? contact.getString("prefix") : null; + String suffix = contact.hasKey("suffix") ? contact.getString("suffix") : null; + String company = contact.hasKey("company") ? contact.getString("company") : null; + String jobTitle = contact.hasKey("jobTitle") ? contact.getString("jobTitle") : null; + String department = contact.hasKey("department") ? contact.getString("department") : null; + String note = contact.hasKey("note") ? contact.getString("note") : null; + String thumbnailPath = contact.hasKey("thumbnailPath") ? contact.getString("thumbnailPath") : null; + + ReadableArray phoneNumbers = contact.hasKey("phoneNumbers") ? contact.getArray("phoneNumbers") : null; + int numOfPhones = 0; + String[] phones = null; + Integer[] phonesTypes = null; + String[] phonesLabels = null; + String[] phoneIds = null; + if (phoneNumbers != null) { + numOfPhones = phoneNumbers.size(); + phones = new String[numOfPhones]; + phonesTypes = new Integer[numOfPhones]; + phonesLabels = new String[numOfPhones]; + phoneIds = new String[numOfPhones]; + for (int i = 0; i < numOfPhones; i++) { + ReadableMap phoneMap = phoneNumbers.getMap(i); + String phoneNumber = phoneMap.getString("number"); + String phoneLabel = phoneMap.getString("label"); + String phoneId = phoneMap.hasKey("id") ? phoneMap.getString("id") : null; + phones[i] = phoneNumber; + phonesTypes[i] = mapStringToPhoneType(phoneLabel); + phonesLabels[i] = phoneLabel; + phoneIds[i] = phoneId; + } + } + + ReadableArray urlAddresses = contact.hasKey("urlAddresses") ? contact.getArray("urlAddresses") : null; + int numOfUrls = 0; + String[] urls = null; + String[] urlIds = null; + + if (urlAddresses != null) { + numOfUrls = urlAddresses.size(); + urls = new String[numOfUrls]; + urlIds = new String[numOfUrls]; + for (int i = 0; i < numOfUrls; i++) { + ReadableMap urlMap = urlAddresses.getMap(i); + urls[i] = urlMap.getString("url"); + urlIds[i] = urlMap.hasKey("id") ? urlMap.getString("id") : null; + } + } + + ReadableArray emailAddresses = contact.hasKey("emailAddresses") ? contact.getArray("emailAddresses") : null; + int numOfEmails = 0; + String[] emails = null; + Integer[] emailsTypes = null; + String[] emailsLabels = null; + String[] emailIds = null; + + if (emailAddresses != null) { + numOfEmails = emailAddresses.size(); + emails = new String[numOfEmails]; + emailIds = new String[numOfEmails]; + emailsTypes = new Integer[numOfEmails]; + emailsLabels = new String[numOfEmails]; + for (int i = 0; i < numOfEmails; i++) { + ReadableMap emailMap = emailAddresses.getMap(i); + emails[i] = emailMap.getString("email"); + String label = emailMap.getString("label"); + emailsTypes[i] = mapStringToEmailType(label); + emailsLabels[i] = label; + emailIds[i] = emailMap.hasKey("id") ? emailMap.getString("id") : null; + } + } + + ReadableArray postalAddresses = contact.hasKey("postalAddresses") ? contact.getArray("postalAddresses") : null; + int numOfPostalAddresses = 0; + String[] postalAddressesStreet = null; + String[] postalAddressesCity = null; + String[] postalAddressesState = null; + String[] postalAddressesRegion = null; + String[] postalAddressesPostCode = null; + String[] postalAddressesCountry = null; + Integer[] postalAddressesType = null; + String[] postalAddressesLabel = null; + if (postalAddresses != null) { + numOfPostalAddresses = postalAddresses.size(); + postalAddressesStreet = new String[numOfPostalAddresses]; + postalAddressesCity = new String[numOfPostalAddresses]; + postalAddressesState = new String[numOfPostalAddresses]; + postalAddressesRegion = new String[numOfPostalAddresses]; + postalAddressesPostCode = new String[numOfPostalAddresses]; + postalAddressesCountry = new String[numOfPostalAddresses]; + postalAddressesType = new Integer[numOfPostalAddresses]; + postalAddressesLabel = new String[numOfPostalAddresses]; + for (int i = 0; i < numOfPostalAddresses; i++) { + String postalLabel = getValueFromKey(postalAddresses.getMap(i), "label"); + postalAddressesStreet[i] = getValueFromKey(postalAddresses.getMap(i), "street"); + postalAddressesCity[i] = getValueFromKey(postalAddresses.getMap(i), "city"); + postalAddressesState[i] = getValueFromKey(postalAddresses.getMap(i), "state"); + postalAddressesRegion[i] = getValueFromKey(postalAddresses.getMap(i), "region"); + postalAddressesPostCode[i] = getValueFromKey(postalAddresses.getMap(i), "postCode"); + postalAddressesCountry[i] = getValueFromKey(postalAddresses.getMap(i), "country"); + postalAddressesType[i] = mapStringToPostalAddressType(postalLabel); + postalAddressesLabel[i] = postalLabel; + } + } + + ReadableArray imAddresses = contact.hasKey("imAddresses") ? contact.getArray("imAddresses") : null; + int numOfIMAddresses = 0; + String[] imAccounts = null; + String[] imProtocols = null; + String[] imAddressIds = null; + + if (imAddresses != null) { + numOfIMAddresses = imAddresses.size(); + imAccounts = new String[numOfIMAddresses]; + imProtocols = new String[numOfIMAddresses]; + imAddressIds = new String[numOfIMAddresses]; + for (int i = 0; i < numOfIMAddresses; i++) { + ReadableMap imAddressMap = imAddresses.getMap(i); + imAccounts[i] = imAddressMap.getString("username"); + imProtocols[i] = imAddressMap.getString("service"); + imAddressIds[i] = imAddressMap.hasKey("id") ? imAddressMap.getString("id") : null; + } + } + + ArrayList ops = new ArrayList(); + + ContentProviderOperation.Builder op = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.CONTACT_ID + "=?", new String[] { String.valueOf(recordID) }) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, givenName) + .withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, middleName) + .withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, familyName) + .withValue(ContactsContract.CommonDataKinds.StructuredName.PREFIX, prefix) + .withValue(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, suffix); + ops.add(op.build()); + + op = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + " = ?", + new String[] { String.valueOf(recordID), ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE }) + .withValue(ContactsContract.CommonDataKinds.Organization.COMPANY, company) + .withValue(ContactsContract.CommonDataKinds.Organization.TITLE, jobTitle) + .withValue(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, department); + ops.add(op.build()); + + op.withYieldAllowed(true); + + if (phoneNumbers != null) { + // remove existing phoneNumbers first + op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) + .withSelection( + ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", + new String[] { String.valueOf(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE), + String.valueOf(rawContactId) }); + ops.add(op.build()); + + // add passed phonenumbers + for (int i = 0; i < numOfPhones; i++) { + op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phones[i]) + .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phonesTypes[i]) + .withValue(ContactsContract.CommonDataKinds.Phone.LABEL, phonesLabels[i]); + ops.add(op.build()); + } + } + + for (int i = 0; i < numOfUrls; i++) { + if (urlIds[i] == null) { + op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Website.URL, urls[i]); + } else { + op = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data._ID + "=?", new String[] { String.valueOf(urlIds[i]) }) + .withValue(ContactsContract.CommonDataKinds.Website.URL, urls[i]); + } + ops.add(op.build()); + } + + if (emailAddresses != null) { + // remove existing emails first + op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) + .withSelection( + ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", + new String[] { String.valueOf(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE), + String.valueOf(rawContactId) }); + ops.add(op.build()); + + // add passed email addresses + for (int i = 0; i < numOfEmails; i++) { + op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, emails[i]) + .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailsTypes[i]) + .withValue(ContactsContract.CommonDataKinds.Email.LABEL, emailsLabels[i]); + ops.add(op.build()); + } + } + + // remove existing note first + op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) + .withSelection( + ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", + new String[] { String.valueOf(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE), String.valueOf(rawContactId) }); + ops.add(op.build()); + + if (note != null) { + op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Note.NOTE, note); + ops.add(op.build()); + } + + if (thumbnailPath != null && !thumbnailPath.isEmpty()) { + Bitmap photo = getThumbnailBitmap(thumbnailPath); + + if (photo != null) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, toByteArray(photo)) + .build()); + } + } + + if (postalAddresses != null) { + // remove existing addresses + op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) + .withSelection( + ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", + new String[] { String.valueOf(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE), + String.valueOf(rawContactId) }); + ops.add(op.build()); + + for (int i = 0; i < numOfPostalAddresses; i++) { + op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, postalAddressesType[i]) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.LABEL, postalAddressesLabel[i]) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, postalAddressesStreet[i]) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, postalAddressesCity[i]) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, postalAddressesState[i]) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, postalAddressesPostCode[i]) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, postalAddressesCountry[i]); + ops.add(op.build()); + } + } + + if (imAddresses != null) { + // remove existing IM addresses + op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) + .withSelection( + ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", + new String[] { String.valueOf(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE), + String.valueOf(rawContactId) }); + ops.add(op.build()); + + // add passed IM addresses + for (int i = 0; i < numOfIMAddresses; i++) { + op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Im.DATA, imAccounts[i]) + .withValue(ContactsContract.CommonDataKinds.Im.TYPE, ContactsContract.CommonDataKinds.Im.TYPE_HOME) + .withValue(ContactsContract.CommonDataKinds.Im.PROTOCOL, ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM) + .withValue(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL, imProtocols[i]); + ops.add(op.build()); + } + } + + Context ctx = getReactApplicationContext(); + try { + ContentResolver cr = ctx.getContentResolver(); + ContentProviderResult[] result = cr.applyBatch(ContactsContract.AUTHORITY, ops); + + if (result != null && result.length > 0) { + + ContactsProvider contactsProvider = new ContactsProvider(cr); + WritableMap updatedContact = contactsProvider.getContactById(recordID); + + promise.resolve(updatedContact); // success + } + } catch (Exception e) { + promise.reject(e.toString()); + } + } + + /* + * Update contact to phone's addressbook + */ + public void deleteContact(ReadableMap contact, Promise promise) { + + String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; + + try { + Context ctx = getReactApplicationContext(); + + Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, recordID); + ContentResolver cr = ctx.getContentResolver(); + int deleted = cr.delete(uri, null, null); + + if (deleted > 0) + promise.resolve(recordID); // success + else + promise.resolve(null); // something was wrong + + } catch (Exception e) { + promise.reject(e.toString()); + } + } + + /* + * Check permission + */ + public void checkPermission(Promise promise) { + promise.resolve(isPermissionGranted()); + } + + /* + * Request permission + */ + public void requestPermission(Promise promise) { + requestReadContactsPermission(promise); + } + + /* + * Enable note usage + */ + public void iosEnableNotesUsage(boolean enabled) { + // this method is only needed for iOS + } + + private void requestReadContactsPermission(Promise promise) { + Activity currentActivity = getCurrentActivity(); + if (currentActivity == null) { + promise.reject(PERMISSION_DENIED); + return; + } + + if (isPermissionGranted().equals(PERMISSION_AUTHORIZED)) { + promise.resolve(PERMISSION_AUTHORIZED); + return; + } + + requestPromise = promise; + ActivityCompat.requestPermissions(currentActivity, new String[] { PERMISSION_READ_CONTACTS }, + PERMISSION_REQUEST_CODE); + } + + protected static void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + if (requestPromise == null) { + return; + } + + if (requestCode != PERMISSION_REQUEST_CODE) { + requestPromise.resolve(PERMISSION_DENIED); + return; + } + + Hashtable results = new Hashtable<>(); + for (int i = 0; i < permissions.length; i++) { + results.put(permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED); + } + + if (results.containsKey(PERMISSION_READ_CONTACTS) && results.get(PERMISSION_READ_CONTACTS)) { + requestPromise.resolve(PERMISSION_AUTHORIZED); + } else { + requestPromise.resolve(PERMISSION_DENIED); + } + + requestPromise = null; + } + + /* + * Get string value from key + */ + private String getValueFromKey(ReadableMap item, String key) { + return item.hasKey(key) ? item.getString(key) : ""; + } + + /* + * Check if READ_CONTACTS permission is granted + */ + private String isPermissionGranted() { + // return -1 for denied and 1 + int res = ActivityCompat.checkSelfPermission(getReactApplicationContext(), PERMISSION_READ_CONTACTS); + return (res == PackageManager.PERMISSION_GRANTED) ? PERMISSION_AUTHORIZED : PERMISSION_DENIED; + } + + /* + * TODO support all phone types + * http://developer.android.com/reference/android/provider/ContactsContract. + * CommonDataKinds.Phone.html + */ + private int mapStringToPhoneType(String label) { + int phoneType; + switch (label) { + case "home": + phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_HOME; + break; + case "work": + phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_WORK; + break; + case "mobile": + phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE; + break; + case "main": + phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_MAIN; + break; + case "work fax": + phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK; + break; + case "home fax": + phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME; + break; + case "pager": + phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_PAGER; + break; + case "work_pager": + phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER; + break; + case "work_mobile": + phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE; + break; + case "other": + phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_OTHER; + break; + case "cell": + phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE; + break; + default: + phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM; + break; + } + return phoneType; + } + + /* + * TODO support TYPE_CUSTOM + * http://developer.android.com/reference/android/provider/ContactsContract. + * CommonDataKinds.Email.html + */ + private int mapStringToEmailType(String label) { + int emailType; + switch (label) { + case "home": + emailType = ContactsContract.CommonDataKinds.Email.TYPE_HOME; + break; + case "work": + emailType = ContactsContract.CommonDataKinds.Email.TYPE_WORK; + break; + case "mobile": + emailType = ContactsContract.CommonDataKinds.Email.TYPE_MOBILE; + break; + case "other": + emailType = ContactsContract.CommonDataKinds.Email.TYPE_OTHER; + break; + case "personal": + emailType = ContactsContract.CommonDataKinds.Email.TYPE_HOME; + break; + default: + emailType = ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM; + break; + } + return emailType; + } + + private int mapStringToPostalAddressType(String label) { + int postalAddressType; + switch (label) { + case "home": + postalAddressType = ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME; + break; + case "work": + postalAddressType = ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK; + break; + default: + postalAddressType = ContactsContract.CommonDataKinds.StructuredPostal.TYPE_CUSTOM; + break; + } + return postalAddressType; + } + + + public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { + if (requestCode != REQUEST_OPEN_CONTACT_FORM && requestCode != REQUEST_OPEN_EXISTING_CONTACT) { + return; + } + + if (updateContactPromise == null) { + return; + } + + if (resultCode != Activity.RESULT_OK) { + updateContactPromise.resolve(null); // user probably pressed cancel + updateContactPromise = null; + return; + } + + if (data == null) { + updateContactPromise.reject("Error received activity result with no data!"); + updateContactPromise = null; + return; + } + + try { + Uri contactUri = data.getData(); + + if (contactUri == null) { + updateContactPromise.reject("Error wrong data. No content uri found!"); // something was wrong + updateContactPromise = null; + return; + } + + Context ctx = getReactApplicationContext(); + ContentResolver cr = ctx.getContentResolver(); + ContactsProvider contactsProvider = new ContactsProvider(cr); + WritableMap newlyModifiedContact = contactsProvider.getContactById(contactUri.getLastPathSegment()); + + updateContactPromise.resolve(newlyModifiedContact); // success + } catch (Exception e) { + updateContactPromise.reject(e.getMessage()); + } + updateContactPromise = null; + } + + + public void onNewIntent(Intent intent) { + } + +} diff --git a/android/src/newarch/com/rt2zz/reactnativecontacts/ContactsManager.java b/android/src/newarch/com/rt2zz/reactnativecontacts/ContactsManager.java index 20d70136..78f5e0ea 100644 --- a/android/src/newarch/com/rt2zz/reactnativecontacts/ContactsManager.java +++ b/android/src/newarch/com/rt2zz/reactnativecontacts/ContactsManager.java @@ -2,64 +2,28 @@ import android.Manifest; import android.app.Activity; -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.provider.ContactsContract; -import android.provider.ContactsContract.CommonDataKinds; -import android.provider.ContactsContract.CommonDataKinds.Note; -import android.provider.ContactsContract.CommonDataKinds.Organization; -import android.provider.ContactsContract.CommonDataKinds.StructuredName; -import android.provider.ContactsContract.RawContacts; - -import androidx.annotation.NonNull; -import androidx.core.app.ActivityCompat; import com.facebook.react.bridge.ActivityEventListener; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableMap; -import com.rt2zz.reactnativecontacts.ContactsProvider; -import com.rt2zz.reactnativecontacts.NativeContactsSpec; +import com.rt2zz.reactnativecontacts.impl.ContactsManagerImpl; import java.io.ByteArrayOutputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Hashtable; public class ContactsManager extends NativeContactsSpec implements ActivityEventListener { - private static final String PERMISSION_DENIED = "denied"; - private static final String PERMISSION_AUTHORIZED = "authorized"; - private static final String PERMISSION_READ_CONTACTS = Manifest.permission.READ_CONTACTS; - private static final int PERMISSION_REQUEST_CODE = 888; - - private static final int REQUEST_OPEN_CONTACT_FORM = 52941; - private static final int REQUEST_OPEN_EXISTING_CONTACT = 52942; - - private static Promise updateContactPromise; - private static Promise requestPromise; + private final ContactsManagerImpl contactsManagerImpl; public ContactsManager(ReactApplicationContext reactContext) { super(reactContext); + this.contactsManagerImpl = new ContactsManagerImpl(reactContext, true); reactContext.addActivityEventListener(this); } @@ -69,7 +33,7 @@ public ContactsManager(ReactApplicationContext reactContext) { */ @Override public void getAll(Promise promise) { - getAllContacts(promise); + contactsManagerImpl.getAll(promise); } /** @@ -79,51 +43,14 @@ public void getAll(Promise promise) { */ @Override public void getAllWithoutPhotos(Promise promise) { - getAllContacts(promise); + contactsManagerImpl.getAllWithoutPhotos(promise); } - /** - * Retrieves contacts. - * Uses raw URI when rawUri is true, makes assets copy - * otherwise. - */ - private void getAllContacts(final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableArray contacts = contactsProvider.getContacts(); - promise.resolve(contacts); - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); - } @Override public void getCount(final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - - ContactsProvider contactsProvider = new ContactsProvider(cr); - try { - Integer contacts = contactsProvider.getContactsCount(); - promise.resolve(contacts); - } catch (Exception e) { - promise.reject(e); - } - - return null; - - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + contactsManagerImpl.getCount(promise); } /** @@ -135,19 +62,7 @@ protected Void doInBackground(final Void... params) { */ @Override public void getContactsMatchingString(final String searchString, final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableArray contacts = contactsProvider.getContactsMatchingString(searchString); - - promise.resolve(contacts); - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + contactsManagerImpl.getContactsMatchingString(searchString, promise); } /** @@ -159,19 +74,7 @@ protected Void doInBackground(final Void... params) { */ @Override public void getContactsByPhoneNumber(final String phoneNumber, final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableArray contacts = contactsProvider.getContactsByPhoneNumber(phoneNumber); - - promise.resolve(contacts); - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + contactsManagerImpl.getContactsByPhoneNumber(phoneNumber, promise); } /** @@ -183,19 +86,7 @@ protected Void doInBackground(final Void... params) { */ @Override public void getContactsByEmailAddress(final String emailAddress, final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableArray contacts = contactsProvider.getContactsByEmailAddress(emailAddress); - - promise.resolve(contacts); - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + contactsManagerImpl.getContactsByEmailAddress(emailAddress, promise); } /** @@ -206,19 +97,7 @@ protected Void doInBackground(final Void... params) { */ @Override public void getPhotoForId(final String contactId, final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - ContactsProvider contactsProvider = new ContactsProvider(cr); - String photoUri = contactsProvider.getPhotoUriFromContactId(contactId); - - promise.resolve(photoUri); - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + contactsManagerImpl.getPhotoForId(contactId, promise); } /** @@ -229,54 +108,12 @@ protected Void doInBackground(final Void... params) { */ @Override public void getContactById(final String contactId, final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableMap contact = contactsProvider.getContactById(contactId); - - promise.resolve(contact); - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + contactsManagerImpl.getContactById(contactId, promise); } @Override public void writePhotoToPath(final String contactId, final String file, final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - - Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.parseLong(contactId)); - InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(cr, uri); - OutputStream outputStream = null; - try { - outputStream = new FileOutputStream(file); - BitmapFactory.decodeStream(inputStream).compress(Bitmap.CompressFormat.PNG, 100, outputStream); - promise.resolve(true); - } catch (FileNotFoundException e) { - promise.reject(e.toString()); - } finally { - try { - outputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - try { - inputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + contactsManagerImpl.writePhotoToPath(contactId, file, promise); } private Bitmap getThumbnailBitmap(String thumbnailPath) { @@ -304,211 +141,7 @@ private Bitmap getThumbnailBitmap(String thumbnailPath) { */ @Override public void openContactForm(ReadableMap contact, Promise promise) { - - String givenName = contact.hasKey("givenName") ? contact.getString("givenName") : null; - String middleName = contact.hasKey("middleName") ? contact.getString("middleName") : null; - String displayName = contact.hasKey("displayName") ? contact.getString("displayName") : null; - String familyName = contact.hasKey("familyName") ? contact.getString("familyName") : null; - String prefix = contact.hasKey("prefix") ? contact.getString("prefix") : null; - String suffix = contact.hasKey("suffix") ? contact.getString("suffix") : null; - String company = contact.hasKey("company") ? contact.getString("company") : null; - String jobTitle = contact.hasKey("jobTitle") ? contact.getString("jobTitle") : null; - String department = contact.hasKey("department") ? contact.getString("department") : null; - String note = contact.hasKey("note") ? contact.getString("note") : null; - String thumbnailPath = contact.hasKey("thumbnailPath") ? contact.getString("thumbnailPath") : null; - - ReadableArray phoneNumbers = contact.hasKey("phoneNumbers") ? contact.getArray("phoneNumbers") : null; - int numOfPhones = 0; - String[] phones = null; - String[] phonesLabels = null; - Integer[] phonesLabelsTypes = null; - if (phoneNumbers != null) { - numOfPhones = phoneNumbers.size(); - phones = new String[numOfPhones]; - phonesLabels = new String[numOfPhones]; - phonesLabelsTypes = new Integer[numOfPhones]; - for (int i = 0; i < numOfPhones; i++) { - phones[i] = phoneNumbers.getMap(i).getString("number"); - String label = phoneNumbers.getMap(i).getString("label"); - phonesLabels[i] = label; - phonesLabelsTypes[i] = mapStringToPhoneType(label); - } - } - - ReadableArray urlAddresses = contact.hasKey("urlAddresses") ? contact.getArray("urlAddresses") : null; - int numOfUrls = 0; - String[] urls = null; - if (urlAddresses != null) { - numOfUrls = urlAddresses.size(); - urls = new String[numOfUrls]; - for (int i = 0; i < numOfUrls; i++) { - urls[i] = urlAddresses.getMap(i).getString("url"); - } - } - - ReadableArray emailAddresses = contact.hasKey("emailAddresses") ? contact.getArray("emailAddresses") : null; - int numOfEmails = 0; - String[] emails = null; - Integer[] emailsLabels = null; - if (emailAddresses != null) { - numOfEmails = emailAddresses.size(); - emails = new String[numOfEmails]; - emailsLabels = new Integer[numOfEmails]; - for (int i = 0; i < numOfEmails; i++) { - emails[i] = emailAddresses.getMap(i).getString("email"); - String label = emailAddresses.getMap(i).getString("label"); - emailsLabels[i] = mapStringToEmailType(label); - } - } - - ReadableArray postalAddresses = contact.hasKey("postalAddresses") ? contact.getArray("postalAddresses") : null; - int numOfPostalAddresses = 0; - String[] postalAddressesStreet = null; - String[] postalAddressesCity = null; - String[] postalAddressesState = null; - String[] postalAddressesRegion = null; - String[] postalAddressesPostCode = null; - String[] postalAddressesCountry = null; - String[] postalAddressesFormattedAddress = null; - String[] postalAddressesLabel = null; - Integer[] postalAddressesType = null; - - if (postalAddresses != null) { - numOfPostalAddresses = postalAddresses.size(); - postalAddressesStreet = new String[numOfPostalAddresses]; - postalAddressesCity = new String[numOfPostalAddresses]; - postalAddressesState = new String[numOfPostalAddresses]; - postalAddressesRegion = new String[numOfPostalAddresses]; - postalAddressesPostCode = new String[numOfPostalAddresses]; - postalAddressesCountry = new String[numOfPostalAddresses]; - postalAddressesFormattedAddress = new String[numOfPostalAddresses]; - postalAddressesLabel = new String[numOfPostalAddresses]; - postalAddressesType = new Integer[numOfPostalAddresses]; - for (int i = 0; i < numOfPostalAddresses; i++) { - postalAddressesStreet[i] = postalAddresses.getMap(i).getString("street"); - postalAddressesCity[i] = postalAddresses.getMap(i).getString("city"); - postalAddressesState[i] = postalAddresses.getMap(i).getString("state"); - postalAddressesRegion[i] = postalAddresses.getMap(i).getString("region"); - postalAddressesPostCode[i] = postalAddresses.getMap(i).getString("postCode"); - postalAddressesCountry[i] = postalAddresses.getMap(i).getString("country"); - postalAddressesFormattedAddress[i] = postalAddresses.getMap(i).getString("formattedAddress"); - postalAddressesLabel[i] = postalAddresses.getMap(i).getString("label"); - postalAddressesType[i] = mapStringToPostalAddressType(postalAddresses.getMap(i).getString("label")); - } - } - - ReadableArray imAddresses = contact.hasKey("imAddresses") ? contact.getArray("imAddresses") : null; - int numOfIMAddresses = 0; - String[] imAccounts = null; - String[] imProtocols = null; - if (imAddresses != null) { - numOfIMAddresses = imAddresses.size(); - imAccounts = new String[numOfIMAddresses]; - imProtocols = new String[numOfIMAddresses]; - for (int i = 0; i < numOfIMAddresses; i++) { - imAccounts[i] = imAddresses.getMap(i).getString("username"); - imProtocols[i] = imAddresses.getMap(i).getString("service"); - } - } - - ArrayList contactData = new ArrayList<>(); - - ContentValues name = new ContentValues(); - name.put(ContactsContract.Contacts.Data.MIMETYPE, CommonDataKinds.Identity.CONTENT_ITEM_TYPE); - name.put(StructuredName.GIVEN_NAME, givenName); - name.put(StructuredName.FAMILY_NAME, familyName); - name.put(StructuredName.MIDDLE_NAME, middleName); - name.put(StructuredName.PREFIX, prefix); - name.put(StructuredName.SUFFIX, suffix); - contactData.add(name); - - ContentValues organization = new ContentValues(); - organization.put(ContactsContract.Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE); - organization.put(Organization.COMPANY, company); - organization.put(Organization.TITLE, jobTitle); - organization.put(Organization.DEPARTMENT, department); - contactData.add(organization); - - for (int i = 0; i < numOfUrls; i++) { - ContentValues url = new ContentValues(); - url.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Website.CONTENT_ITEM_TYPE); - url.put(CommonDataKinds.Website.URL, urls[i]); - contactData.add(url); - } - - for (int i = 0; i < numOfEmails; i++) { - ContentValues email = new ContentValues(); - email.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE); - email.put(CommonDataKinds.Email.TYPE, emailsLabels[i]); - email.put(CommonDataKinds.Email.ADDRESS, emails[i]); - contactData.add(email); - } - - for (int i = 0; i < numOfPhones; i++) { - ContentValues phone = new ContentValues(); - phone.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE); - phone.put(CommonDataKinds.Phone.TYPE, phonesLabelsTypes[i]); - phone.put(CommonDataKinds.Phone.LABEL, phonesLabels[i]); - phone.put(CommonDataKinds.Phone.NUMBER, phones[i]); - - contactData.add(phone); - } - - for (int i = 0; i < numOfPostalAddresses; i++) { - ContentValues structuredPostal = new ContentValues(); - structuredPostal.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE); - structuredPostal.put(CommonDataKinds.StructuredPostal.STREET, postalAddressesStreet[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.CITY, postalAddressesCity[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.REGION, postalAddressesRegion[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.COUNTRY, postalAddressesCountry[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.POSTCODE, postalAddressesPostCode[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, - postalAddressesFormattedAddress[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.LABEL, postalAddressesLabel[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.TYPE, postalAddressesType[i]); - // No state column in StructuredPostal - // structuredPostal.put(CommonDataKinds.StructuredPostal.???, - // postalAddressesState[i]); - contactData.add(structuredPostal); - } - - for (int i = 0; i < numOfIMAddresses; i++) { - ContentValues imAddress = new ContentValues(); - imAddress.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE); - imAddress.put(CommonDataKinds.Im.DATA, imAccounts[i]); - imAddress.put(CommonDataKinds.Im.TYPE, CommonDataKinds.Im.TYPE_HOME); - imAddress.put(CommonDataKinds.Im.PROTOCOL, CommonDataKinds.Im.PROTOCOL_CUSTOM); - imAddress.put(CommonDataKinds.Im.CUSTOM_PROTOCOL, imProtocols[i]); - contactData.add(imAddress); - } - - if (note != null) { - ContentValues structuredNote = new ContentValues(); - structuredNote.put(ContactsContract.Data.MIMETYPE, Note.CONTENT_ITEM_TYPE); - structuredNote.put(Note.NOTE, note); - contactData.add(structuredNote); - } - - if (thumbnailPath != null && !thumbnailPath.isEmpty()) { - Bitmap photo = getThumbnailBitmap(thumbnailPath); - - if (photo != null) { - ContentValues thumbnail = new ContentValues(); - thumbnail.put(ContactsContract.Data.RAW_CONTACT_ID, 0); - thumbnail.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1); - thumbnail.put(CommonDataKinds.Photo.PHOTO, toByteArray(photo)); - thumbnail.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE); - contactData.add(thumbnail); - } - } - - Intent intent = new Intent(Intent.ACTION_INSERT, ContactsContract.Contacts.CONTENT_URI); - intent.putExtra(ContactsContract.Intents.Insert.NAME, displayName); - intent.putExtra("finishActivityOnSaveCompleted", true); - intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData); - - updateContactPromise = promise; - getReactApplicationContext().startActivityForResult(intent, REQUEST_OPEN_CONTACT_FORM, Bundle.EMPTY); + contactsManagerImpl.openContactForm(contact, promise); } /* @@ -516,21 +149,7 @@ public void openContactForm(ReadableMap contact, Promise promise) { */ @Override public void openExistingContact(ReadableMap contact, Promise promise) { - - String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; - - try { - Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, recordID); - Intent intent = new Intent(Intent.ACTION_EDIT); - intent.setDataAndType(uri, ContactsContract.Contacts.CONTENT_ITEM_TYPE); - intent.putExtra("finishActivityOnSaveCompleted", true); - - updateContactPromise = promise; - getReactApplicationContext().startActivityForResult(intent, REQUEST_OPEN_EXISTING_CONTACT, Bundle.EMPTY); - - } catch (Exception e) { - promise.reject(e.toString()); - } + contactsManagerImpl.openExistingContact(contact, promise); } /* @@ -538,21 +157,7 @@ public void openExistingContact(ReadableMap contact, Promise promise) { */ @Override public void viewExistingContact(ReadableMap contact, Promise promise) { - - String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; - - try { - Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, recordID); - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(uri, ContactsContract.Contacts.CONTENT_ITEM_TYPE); - intent.putExtra("finishActivityOnSaveCompleted", true); - - updateContactPromise = promise; - getReactApplicationContext().startActivityForResult(intent, REQUEST_OPEN_EXISTING_CONTACT, Bundle.EMPTY); - - } catch (Exception e) { - promise.reject(e.toString()); - } + contactsManagerImpl.viewExistingContact(contact, promise); } /* @@ -560,47 +165,7 @@ public void viewExistingContact(ReadableMap contact, Promise promise) { */ @Override public void editExistingContact(ReadableMap contact, Promise promise) { - - String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; - - try { - Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, recordID); - - ReadableArray phoneNumbers = contact.hasKey("phoneNumbers") ? contact.getArray("phoneNumbers") : null; - int numOfPhones = 0; - String[] phones = null; - Integer[] phonesLabels = null; - if (phoneNumbers != null) { - numOfPhones = phoneNumbers.size(); - phones = new String[numOfPhones]; - phonesLabels = new Integer[numOfPhones]; - for (int i = 0; i < numOfPhones; i++) { - phones[i] = phoneNumbers.getMap(i).getString("number"); - String label = phoneNumbers.getMap(i).getString("label"); - phonesLabels[i] = mapStringToPhoneType(label); - } - } - - ArrayList contactData = new ArrayList<>(); - for (int i = 0; i < numOfPhones; i++) { - ContentValues phone = new ContentValues(); - phone.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE); - phone.put(CommonDataKinds.Phone.TYPE, phonesLabels[i]); - phone.put(CommonDataKinds.Phone.NUMBER, phones[i]); - contactData.add(phone); - } - - Intent intent = new Intent(Intent.ACTION_EDIT); - intent.setDataAndType(uri, ContactsContract.Contacts.CONTENT_ITEM_TYPE); - intent.putExtra("finishActivityOnSaveCompleted", true); - intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData); - - updateContactPromise = promise; - getReactApplicationContext().startActivityForResult(intent, REQUEST_OPEN_EXISTING_CONTACT, Bundle.EMPTY); - - } catch (Exception e) { - promise.reject(e.toString()); - } + contactsManagerImpl.editExistingContact(contact, promise); } /* @@ -608,207 +173,7 @@ public void editExistingContact(ReadableMap contact, Promise promise) { */ @Override public void addContact(ReadableMap contact, Promise promise) { - if (contact == null) { - promise.reject("New contact cannot be null."); - return; - } - String givenName = contact.hasKey("givenName") ? contact.getString("givenName") : null; - String middleName = contact.hasKey("middleName") ? contact.getString("middleName") : null; - String familyName = contact.hasKey("familyName") ? contact.getString("familyName") : null; - String prefix = contact.hasKey("prefix") ? contact.getString("prefix") : null; - String suffix = contact.hasKey("suffix") ? contact.getString("suffix") : null; - String company = contact.hasKey("company") ? contact.getString("company") : null; - String jobTitle = contact.hasKey("jobTitle") ? contact.getString("jobTitle") : null; - String department = contact.hasKey("department") ? contact.getString("department") : null; - String note = contact.hasKey("note") ? contact.getString("note") : null; - String thumbnailPath = contact.hasKey("thumbnailPath") ? contact.getString("thumbnailPath") : null; - - ReadableArray phoneNumbers = contact.hasKey("phoneNumbers") ? contact.getArray("phoneNumbers") : null; - int numOfPhones = 0; - String[] phones = null; - Integer[] phonesTypes = null; - String[] phonesLabels = null; - if (phoneNumbers != null) { - numOfPhones = phoneNumbers.size(); - phones = new String[numOfPhones]; - phonesTypes = new Integer[numOfPhones]; - phonesLabels = new String[numOfPhones]; - for (int i = 0; i < numOfPhones; i++) { - phones[i] = phoneNumbers.getMap(i).getString("number"); - String label = phoneNumbers.getMap(i).getString("label"); - phonesTypes[i] = mapStringToPhoneType(label); - phonesLabels[i] = label; - } - } - - ReadableArray urlAddresses = contact.hasKey("urlAddresses") ? contact.getArray("urlAddresses") : null; - int numOfUrls = 0; - String[] urls = null; - if (urlAddresses != null) { - numOfUrls = urlAddresses.size(); - urls = new String[numOfUrls]; - for (int i = 0; i < numOfUrls; i++) { - urls[i] = urlAddresses.getMap(i).getString("url"); - } - } - - ReadableArray emailAddresses = contact.hasKey("emailAddresses") ? contact.getArray("emailAddresses") : null; - int numOfEmails = 0; - String[] emails = null; - Integer[] emailsTypes = null; - String[] emailsLabels = null; - if (emailAddresses != null) { - numOfEmails = emailAddresses.size(); - emails = new String[numOfEmails]; - emailsTypes = new Integer[numOfEmails]; - emailsLabels = new String[numOfEmails]; - for (int i = 0; i < numOfEmails; i++) { - emails[i] = emailAddresses.getMap(i).getString("email"); - String label = emailAddresses.getMap(i).getString("label"); - emailsTypes[i] = mapStringToEmailType(label); - emailsLabels[i] = label; - } - } - - ReadableArray imAddresses = contact.hasKey("imAddresses") ? contact.getArray("imAddresses") : null; - int numOfIMAddresses = 0; - String[] imAccounts = null; - String[] imProtocols = null; - if (imAddresses != null) { - numOfIMAddresses = imAddresses.size(); - imAccounts = new String[numOfIMAddresses]; - imProtocols = new String[numOfIMAddresses]; - for (int i = 0; i < numOfIMAddresses; i++) { - imAccounts[i] = imAddresses.getMap(i).getString("username"); - imProtocols[i] = imAddresses.getMap(i).getString("service"); - } - } - - ArrayList ops = new ArrayList(); - - ContentProviderOperation.Builder op = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) - .withValue(RawContacts.ACCOUNT_TYPE, null) - .withValue(RawContacts.ACCOUNT_NAME, null); - ops.add(op.build()); - - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) - // .withValue(StructuredName.DISPLAY_NAME, name) - .withValue(StructuredName.GIVEN_NAME, givenName) - .withValue(StructuredName.MIDDLE_NAME, middleName) - .withValue(StructuredName.FAMILY_NAME, familyName) - .withValue(StructuredName.PREFIX, prefix) - .withValue(StructuredName.SUFFIX, suffix); - ops.add(op.build()); - - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, Note.CONTENT_ITEM_TYPE) - .withValue(Note.NOTE, note); - ops.add(op.build()); - - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE) - .withValue(Organization.COMPANY, company) - .withValue(Organization.TITLE, jobTitle) - .withValue(Organization.DEPARTMENT, department); - ops.add(op.build()); - - // TODO not sure where to allow yields - op.withYieldAllowed(true); - - for (int i = 0; i < numOfPhones; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Phone.NUMBER, phones[i]) - .withValue(CommonDataKinds.Phone.TYPE, phonesTypes[i]) - .withValue(CommonDataKinds.Phone.LABEL, phonesLabels[i]); - ops.add(op.build()); - } - - for (int i = 0; i < numOfUrls; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Website.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Website.URL, urls[i]); - ops.add(op.build()); - } - - for (int i = 0; i < numOfEmails; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Email.ADDRESS, emails[i]) - .withValue(CommonDataKinds.Email.TYPE, emailsTypes[i]) - .withValue(CommonDataKinds.Email.LABEL, emailsLabels[i]); - ops.add(op.build()); - } - - if (thumbnailPath != null && !thumbnailPath.isEmpty()) { - Bitmap photo = getThumbnailBitmap(thumbnailPath); - - if (photo != null) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, - CommonDataKinds.Photo.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Photo.PHOTO, toByteArray(photo)) - .build()); - } - } - - ReadableArray postalAddresses = contact.hasKey("postalAddresses") ? contact.getArray("postalAddresses") : null; - if (postalAddresses != null) { - for (int i = 0; i < postalAddresses.size(); i++) { - ReadableMap address = postalAddresses.getMap(i); - - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.StructuredPostal.TYPE, - mapStringToPostalAddressType(address.getString("label"))) - .withValue(CommonDataKinds.StructuredPostal.LABEL, address.getString("label")) - .withValue(CommonDataKinds.StructuredPostal.STREET, address.getString("street")) - .withValue(CommonDataKinds.StructuredPostal.CITY, address.getString("city")) - .withValue(CommonDataKinds.StructuredPostal.REGION, address.getString("state")) - .withValue(CommonDataKinds.StructuredPostal.POSTCODE, address.getString("postCode")) - .withValue(CommonDataKinds.StructuredPostal.COUNTRY, address.getString("country")); - - ops.add(op.build()); - } - } - - for (int i = 0; i < numOfIMAddresses; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Im.DATA, imAccounts[i]) - .withValue(CommonDataKinds.Im.TYPE, CommonDataKinds.Im.TYPE_HOME) - .withValue(CommonDataKinds.Im.PROTOCOL, CommonDataKinds.Im.PROTOCOL_CUSTOM) - .withValue(CommonDataKinds.Im.CUSTOM_PROTOCOL, imProtocols[i]); - ops.add(op.build()); - } - - Context ctx = getReactApplicationContext(); - try { - ContentResolver cr = ctx.getContentResolver(); - ContentProviderResult[] result = cr.applyBatch(ContactsContract.AUTHORITY, ops); - - if (result != null && result.length > 0) { - - String rawId = String.valueOf(ContentUris.parseId(result[0].uri)); - - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableMap newlyAddedContact = contactsProvider.getContactByRawId(rawId); - - promise.resolve(newlyAddedContact); // success - } - } catch (Exception e) { - promise.reject(e.toString()); - } + contactsManagerImpl.addContact(contact, promise); } public byte[] toByteArray(Bitmap bitmap) { @@ -822,308 +187,7 @@ public byte[] toByteArray(Bitmap bitmap) { */ @Override public void updateContact(ReadableMap contact, Promise promise) { - - String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; - String rawContactId = contact.hasKey("rawContactId") ? contact.getString("rawContactId") : null; - - if (rawContactId == null || recordID == null) { - promise.reject("Invalid recordId or rawContactId"); - return; - } - - String givenName = contact.hasKey("givenName") ? contact.getString("givenName") : null; - String middleName = contact.hasKey("middleName") ? contact.getString("middleName") : null; - String familyName = contact.hasKey("familyName") ? contact.getString("familyName") : null; - String prefix = contact.hasKey("prefix") ? contact.getString("prefix") : null; - String suffix = contact.hasKey("suffix") ? contact.getString("suffix") : null; - String company = contact.hasKey("company") ? contact.getString("company") : null; - String jobTitle = contact.hasKey("jobTitle") ? contact.getString("jobTitle") : null; - String department = contact.hasKey("department") ? contact.getString("department") : null; - String note = contact.hasKey("note") ? contact.getString("note") : null; - String thumbnailPath = contact.hasKey("thumbnailPath") ? contact.getString("thumbnailPath") : null; - - ReadableArray phoneNumbers = contact.hasKey("phoneNumbers") ? contact.getArray("phoneNumbers") : null; - int numOfPhones = 0; - String[] phones = null; - Integer[] phonesTypes = null; - String[] phonesLabels = null; - String[] phoneIds = null; - if (phoneNumbers != null) { - numOfPhones = phoneNumbers.size(); - phones = new String[numOfPhones]; - phonesTypes = new Integer[numOfPhones]; - phonesLabels = new String[numOfPhones]; - phoneIds = new String[numOfPhones]; - for (int i = 0; i < numOfPhones; i++) { - ReadableMap phoneMap = phoneNumbers.getMap(i); - String phoneNumber = phoneMap.getString("number"); - String phoneLabel = phoneMap.getString("label"); - String phoneId = phoneMap.hasKey("id") ? phoneMap.getString("id") : null; - phones[i] = phoneNumber; - phonesTypes[i] = mapStringToPhoneType(phoneLabel); - phonesLabels[i] = phoneLabel; - phoneIds[i] = phoneId; - } - } - - ReadableArray urlAddresses = contact.hasKey("urlAddresses") ? contact.getArray("urlAddresses") : null; - int numOfUrls = 0; - String[] urls = null; - String[] urlIds = null; - - if (urlAddresses != null) { - numOfUrls = urlAddresses.size(); - urls = new String[numOfUrls]; - urlIds = new String[numOfUrls]; - for (int i = 0; i < numOfUrls; i++) { - ReadableMap urlMap = urlAddresses.getMap(i); - urls[i] = urlMap.getString("url"); - urlIds[i] = urlMap.hasKey("id") ? urlMap.getString("id") : null; - } - } - - ReadableArray emailAddresses = contact.hasKey("emailAddresses") ? contact.getArray("emailAddresses") : null; - int numOfEmails = 0; - String[] emails = null; - Integer[] emailsTypes = null; - String[] emailsLabels = null; - String[] emailIds = null; - - if (emailAddresses != null) { - numOfEmails = emailAddresses.size(); - emails = new String[numOfEmails]; - emailIds = new String[numOfEmails]; - emailsTypes = new Integer[numOfEmails]; - emailsLabels = new String[numOfEmails]; - for (int i = 0; i < numOfEmails; i++) { - ReadableMap emailMap = emailAddresses.getMap(i); - emails[i] = emailMap.getString("email"); - String label = emailMap.getString("label"); - emailsTypes[i] = mapStringToEmailType(label); - emailsLabels[i] = label; - emailIds[i] = emailMap.hasKey("id") ? emailMap.getString("id") : null; - } - } - - ReadableArray postalAddresses = contact.hasKey("postalAddresses") ? contact.getArray("postalAddresses") : null; - int numOfPostalAddresses = 0; - String[] postalAddressesStreet = null; - String[] postalAddressesCity = null; - String[] postalAddressesState = null; - String[] postalAddressesRegion = null; - String[] postalAddressesPostCode = null; - String[] postalAddressesCountry = null; - Integer[] postalAddressesType = null; - String[] postalAddressesLabel = null; - if (postalAddresses != null) { - numOfPostalAddresses = postalAddresses.size(); - postalAddressesStreet = new String[numOfPostalAddresses]; - postalAddressesCity = new String[numOfPostalAddresses]; - postalAddressesState = new String[numOfPostalAddresses]; - postalAddressesRegion = new String[numOfPostalAddresses]; - postalAddressesPostCode = new String[numOfPostalAddresses]; - postalAddressesCountry = new String[numOfPostalAddresses]; - postalAddressesType = new Integer[numOfPostalAddresses]; - postalAddressesLabel = new String[numOfPostalAddresses]; - for (int i = 0; i < numOfPostalAddresses; i++) { - String postalLabel = getValueFromKey(postalAddresses.getMap(i), "label"); - postalAddressesStreet[i] = getValueFromKey(postalAddresses.getMap(i), "street"); - postalAddressesCity[i] = getValueFromKey(postalAddresses.getMap(i), "city"); - postalAddressesState[i] = getValueFromKey(postalAddresses.getMap(i), "state"); - postalAddressesRegion[i] = getValueFromKey(postalAddresses.getMap(i), "region"); - postalAddressesPostCode[i] = getValueFromKey(postalAddresses.getMap(i), "postCode"); - postalAddressesCountry[i] = getValueFromKey(postalAddresses.getMap(i), "country"); - postalAddressesType[i] = mapStringToPostalAddressType(postalLabel); - postalAddressesLabel[i] = postalLabel; - } - } - - ReadableArray imAddresses = contact.hasKey("imAddresses") ? contact.getArray("imAddresses") : null; - int numOfIMAddresses = 0; - String[] imAccounts = null; - String[] imProtocols = null; - String[] imAddressIds = null; - - if (imAddresses != null) { - numOfIMAddresses = imAddresses.size(); - imAccounts = new String[numOfIMAddresses]; - imProtocols = new String[numOfIMAddresses]; - imAddressIds = new String[numOfIMAddresses]; - for (int i = 0; i < numOfIMAddresses; i++) { - ReadableMap imAddressMap = imAddresses.getMap(i); - imAccounts[i] = imAddressMap.getString("username"); - imProtocols[i] = imAddressMap.getString("service"); - imAddressIds[i] = imAddressMap.hasKey("id") ? imAddressMap.getString("id") : null; - } - } - - ArrayList ops = new ArrayList(); - - ContentProviderOperation.Builder op = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) - .withSelection(ContactsContract.Data.CONTACT_ID + "=?", new String[] { String.valueOf(recordID) }) - .withValue(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) - .withValue(StructuredName.GIVEN_NAME, givenName) - .withValue(StructuredName.MIDDLE_NAME, middleName) - .withValue(StructuredName.FAMILY_NAME, familyName) - .withValue(StructuredName.PREFIX, prefix) - .withValue(StructuredName.SUFFIX, suffix); - ops.add(op.build()); - - op = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) - .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + " = ?", - new String[] { String.valueOf(recordID), Organization.CONTENT_ITEM_TYPE }) - .withValue(Organization.COMPANY, company) - .withValue(Organization.TITLE, jobTitle) - .withValue(Organization.DEPARTMENT, department); - ops.add(op.build()); - - op.withYieldAllowed(true); - - if (phoneNumbers != null) { - // remove existing phoneNumbers first - op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) - .withSelection( - ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", - new String[] { String.valueOf(CommonDataKinds.Phone.CONTENT_ITEM_TYPE), - String.valueOf(rawContactId) }); - ops.add(op.build()); - - // add passed phonenumbers - for (int i = 0; i < numOfPhones; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Phone.NUMBER, phones[i]) - .withValue(CommonDataKinds.Phone.TYPE, phonesTypes[i]) - .withValue(CommonDataKinds.Phone.LABEL, phonesLabels[i]); - ops.add(op.build()); - } - } - - for (int i = 0; i < numOfUrls; i++) { - if (urlIds[i] == null) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Website.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Website.URL, urls[i]); - } else { - op = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) - .withSelection(ContactsContract.Data._ID + "=?", new String[] { String.valueOf(urlIds[i]) }) - .withValue(CommonDataKinds.Website.URL, urls[i]); - } - ops.add(op.build()); - } - - if (emailAddresses != null) { - // remove existing emails first - op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) - .withSelection( - ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", - new String[] { String.valueOf(CommonDataKinds.Email.CONTENT_ITEM_TYPE), - String.valueOf(rawContactId) }); - ops.add(op.build()); - - // add passed email addresses - for (int i = 0; i < numOfEmails; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Email.ADDRESS, emails[i]) - .withValue(CommonDataKinds.Email.TYPE, emailsTypes[i]) - .withValue(CommonDataKinds.Email.LABEL, emailsLabels[i]); - ops.add(op.build()); - } - } - - // remove existing note first - op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) - .withSelection( - ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", - new String[] { String.valueOf(Note.CONTENT_ITEM_TYPE), String.valueOf(rawContactId) }); - ops.add(op.build()); - - if (note != null) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) - .withValue(ContactsContract.Data.MIMETYPE, Note.CONTENT_ITEM_TYPE) - .withValue(Note.NOTE, note); - ops.add(op.build()); - } - - if (thumbnailPath != null && !thumbnailPath.isEmpty()) { - Bitmap photo = getThumbnailBitmap(thumbnailPath); - - if (photo != null) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, - CommonDataKinds.Photo.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Photo.PHOTO, toByteArray(photo)) - .build()); - } - } - - if (postalAddresses != null) { - // remove existing addresses - op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) - .withSelection( - ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", - new String[] { String.valueOf(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE), - String.valueOf(rawContactId) }); - ops.add(op.build()); - - for (int i = 0; i < numOfPostalAddresses; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.StructuredPostal.TYPE, postalAddressesType[i]) - .withValue(CommonDataKinds.StructuredPostal.LABEL, postalAddressesLabel[i]) - .withValue(CommonDataKinds.StructuredPostal.STREET, postalAddressesStreet[i]) - .withValue(CommonDataKinds.StructuredPostal.CITY, postalAddressesCity[i]) - .withValue(CommonDataKinds.StructuredPostal.REGION, postalAddressesState[i]) - .withValue(CommonDataKinds.StructuredPostal.POSTCODE, postalAddressesPostCode[i]) - .withValue(CommonDataKinds.StructuredPostal.COUNTRY, postalAddressesCountry[i]); - ops.add(op.build()); - } - } - - if (imAddresses != null) { - // remove existing IM addresses - op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) - .withSelection( - ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", - new String[] { String.valueOf(CommonDataKinds.Im.CONTENT_ITEM_TYPE), - String.valueOf(rawContactId) }); - ops.add(op.build()); - - // add passed IM addresses - for (int i = 0; i < numOfIMAddresses; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Im.DATA, imAccounts[i]) - .withValue(CommonDataKinds.Im.TYPE, CommonDataKinds.Im.TYPE_HOME) - .withValue(CommonDataKinds.Im.PROTOCOL, CommonDataKinds.Im.PROTOCOL_CUSTOM) - .withValue(CommonDataKinds.Im.CUSTOM_PROTOCOL, imProtocols[i]); - ops.add(op.build()); - } - } - - Context ctx = getReactApplicationContext(); - try { - ContentResolver cr = ctx.getContentResolver(); - ContentProviderResult[] result = cr.applyBatch(ContactsContract.AUTHORITY, ops); - - if (result != null && result.length > 0) { - - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableMap updatedContact = contactsProvider.getContactById(recordID); - - promise.resolve(updatedContact); // success - } - } catch (Exception e) { - promise.reject(e.toString()); - } + contactsManagerImpl.updateContact(contact, promise); } /* @@ -1131,24 +195,7 @@ public void updateContact(ReadableMap contact, Promise promise) { */ @Override public void deleteContact(ReadableMap contact, Promise promise) { - - String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; - - try { - Context ctx = getReactApplicationContext(); - - Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, recordID); - ContentResolver cr = ctx.getContentResolver(); - int deleted = cr.delete(uri, null, null); - - if (deleted > 0) - promise.resolve(recordID); // success - else - promise.resolve(null); // something was wrong - - } catch (Exception e) { - promise.reject(e.toString()); - } + contactsManagerImpl.deleteContact(contact, promise); } /* @@ -1156,7 +203,7 @@ public void deleteContact(ReadableMap contact, Promise promise) { */ @Override public void checkPermission(Promise promise) { - promise.resolve(isPermissionGranted()); + contactsManagerImpl.checkPermission(promise); } /* @@ -1164,7 +211,7 @@ public void checkPermission(Promise promise) { */ @Override public void requestPermission(Promise promise) { - requestReadContactsPermission(promise); + contactsManagerImpl.requestPermission(promise); } /* @@ -1175,23 +222,8 @@ public void iosEnableNotesUsage(boolean enabled) { // this method is only needed for iOS } - private void requestReadContactsPermission(Promise promise) { - Activity currentActivity = getCurrentActivity(); - if (currentActivity == null) { - promise.reject(PERMISSION_DENIED); - return; - } - - if (isPermissionGranted().equals(PERMISSION_AUTHORIZED)) { - promise.resolve(PERMISSION_AUTHORIZED); - return; - } - - requestPromise = promise; - ActivityCompat.requestPermissions(currentActivity, new String[] { PERMISSION_READ_CONTACTS }, - PERMISSION_REQUEST_CODE); - } + /* protected static void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestPromise == null) { @@ -1215,117 +247,7 @@ protected static void onRequestPermissionsResult(int requestCode, @NonNull Strin } requestPromise = null; - } - - /* - * Get string value from key - */ - private String getValueFromKey(ReadableMap item, String key) { - return item.hasKey(key) ? item.getString(key) : ""; - } - - /* - * Check if READ_CONTACTS permission is granted - */ - private String isPermissionGranted() { - // return -1 for denied and 1 - int res = getReactApplicationContext().checkSelfPermission(PERMISSION_READ_CONTACTS); - return (res == PackageManager.PERMISSION_GRANTED) ? PERMISSION_AUTHORIZED : PERMISSION_DENIED; - } - - /* - * TODO support all phone types - * http://developer.android.com/reference/android/provider/ContactsContract. - * CommonDataKinds.Phone.html - */ - private int mapStringToPhoneType(String label) { - int phoneType; - switch (label) { - case "home": - phoneType = CommonDataKinds.Phone.TYPE_HOME; - break; - case "work": - phoneType = CommonDataKinds.Phone.TYPE_WORK; - break; - case "mobile": - phoneType = CommonDataKinds.Phone.TYPE_MOBILE; - break; - case "main": - phoneType = CommonDataKinds.Phone.TYPE_MAIN; - break; - case "work fax": - phoneType = CommonDataKinds.Phone.TYPE_FAX_WORK; - break; - case "home fax": - phoneType = CommonDataKinds.Phone.TYPE_FAX_HOME; - break; - case "pager": - phoneType = CommonDataKinds.Phone.TYPE_PAGER; - break; - case "work_pager": - phoneType = CommonDataKinds.Phone.TYPE_WORK_PAGER; - break; - case "work_mobile": - phoneType = CommonDataKinds.Phone.TYPE_WORK_MOBILE; - break; - case "other": - phoneType = CommonDataKinds.Phone.TYPE_OTHER; - break; - case "cell": - phoneType = CommonDataKinds.Phone.TYPE_MOBILE; - break; - default: - phoneType = CommonDataKinds.Phone.TYPE_CUSTOM; - break; - } - return phoneType; - } - - /* - * TODO support TYPE_CUSTOM - * http://developer.android.com/reference/android/provider/ContactsContract. - * CommonDataKinds.Email.html - */ - private int mapStringToEmailType(String label) { - int emailType; - switch (label) { - case "home": - emailType = CommonDataKinds.Email.TYPE_HOME; - break; - case "work": - emailType = CommonDataKinds.Email.TYPE_WORK; - break; - case "mobile": - emailType = CommonDataKinds.Email.TYPE_MOBILE; - break; - case "other": - emailType = CommonDataKinds.Email.TYPE_OTHER; - break; - case "personal": - emailType = CommonDataKinds.Email.TYPE_HOME; - break; - default: - emailType = CommonDataKinds.Email.TYPE_CUSTOM; - break; - } - return emailType; - } - - private int mapStringToPostalAddressType(String label) { - int postalAddressType; - switch (label) { - case "home": - postalAddressType = CommonDataKinds.StructuredPostal.TYPE_HOME; - break; - case "work": - postalAddressType = CommonDataKinds.StructuredPostal.TYPE_WORK; - break; - default: - postalAddressType = CommonDataKinds.StructuredPostal.TYPE_CUSTOM; - break; - } - return postalAddressType; - } + }*/ @Override public String getName() { @@ -1337,45 +259,8 @@ public String getName() { */ @Override public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { - if (requestCode != REQUEST_OPEN_CONTACT_FORM && requestCode != REQUEST_OPEN_EXISTING_CONTACT) { - return; - } + contactsManagerImpl.onActivityResult(activity, requestCode, resultCode, data); - if (updateContactPromise == null) { - return; - } - - if (resultCode != Activity.RESULT_OK) { - updateContactPromise.resolve(null); // user probably pressed cancel - updateContactPromise = null; - return; - } - - if (data == null) { - updateContactPromise.reject("Error received activity result with no data!"); - updateContactPromise = null; - return; - } - - try { - Uri contactUri = data.getData(); - - if (contactUri == null) { - updateContactPromise.reject("Error wrong data. No content uri found!"); // something was wrong - updateContactPromise = null; - return; - } - - Context ctx = getReactApplicationContext(); - ContentResolver cr = ctx.getContentResolver(); - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableMap newlyModifiedContact = contactsProvider.getContactById(contactUri.getLastPathSegment()); - - updateContactPromise.resolve(newlyModifiedContact); // success - } catch (Exception e) { - updateContactPromise.reject(e.getMessage()); - } - updateContactPromise = null; } /* @@ -1383,6 +268,7 @@ public void onActivityResult(Activity activity, int requestCode, int resultCode, */ @Override public void onNewIntent(Intent intent) { + contactsManagerImpl.onNewIntent(intent); } } diff --git a/android/src/oldarch/com/rt2zz/reactnativecontacts/ContactsManager.java b/android/src/oldarch/com/rt2zz/reactnativecontacts/ContactsManager.java index 6dc4273a..769beac3 100644 --- a/android/src/oldarch/com/rt2zz/reactnativecontacts/ContactsManager.java +++ b/android/src/oldarch/com/rt2zz/reactnativecontacts/ContactsManager.java @@ -34,6 +34,7 @@ import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableArray; +import com.rt2zz.reactnativecontacts.impl.ContactsManagerImpl; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; @@ -46,19 +47,11 @@ public class ContactsManager extends ReactContextBaseJavaModule implements ActivityEventListener { - private static final String PERMISSION_DENIED = "denied"; - private static final String PERMISSION_AUTHORIZED = "authorized"; - private static final String PERMISSION_READ_CONTACTS = Manifest.permission.READ_CONTACTS; - private static final int PERMISSION_REQUEST_CODE = 888; - - private static final int REQUEST_OPEN_CONTACT_FORM = 52941; - private static final int REQUEST_OPEN_EXISTING_CONTACT = 52942; - - private static Promise updateContactPromise; - private static Promise requestPromise; + private final ContactsManagerImpl contactsManagerImpl; public ContactsManager(ReactApplicationContext reactContext) { super(reactContext); + contactsManagerImpl = new ContactsManagerImpl(reactContext, true); reactContext.addActivityEventListener(this); } @@ -68,7 +61,7 @@ public ContactsManager(ReactApplicationContext reactContext) { */ @ReactMethod public void getAll(Promise promise) { - getAllContacts(promise); + contactsManagerImpl.getAll(promise); } /** @@ -78,51 +71,13 @@ public void getAll(Promise promise) { */ @ReactMethod public void getAllWithoutPhotos(Promise promise) { - getAllContacts(promise); + contactsManagerImpl.getAllWithoutPhotos(promise); } - /** - * Retrieves contacts. - * Uses raw URI when rawUri is true, makes assets copy - * otherwise. - */ - private void getAllContacts(final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableArray contacts = contactsProvider.getContacts(); - promise.resolve(contacts); - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); - } @ReactMethod public void getCount(final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - - ContactsProvider contactsProvider = new ContactsProvider(cr); - try { - Integer contacts = contactsProvider.getContactsCount(); - promise.resolve(contacts); - } catch (Exception e) { - promise.reject(e); - } - - return null; - - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + contactsManagerImpl.getCount(promise); } /** @@ -134,19 +89,7 @@ protected Void doInBackground(final Void... params) { */ @ReactMethod public void getContactsMatchingString(final String searchString, final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableArray contacts = contactsProvider.getContactsMatchingString(searchString); - - promise.resolve(contacts); - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + contactsManagerImpl.getContactsMatchingString(searchString, promise); } /** @@ -158,19 +101,7 @@ protected Void doInBackground(final Void... params) { */ @ReactMethod public void getContactsByPhoneNumber(final String phoneNumber, final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableArray contacts = contactsProvider.getContactsByPhoneNumber(phoneNumber); - - promise.resolve(contacts); - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + contactsManagerImpl.getContactsByPhoneNumber(phoneNumber, promise); } /** @@ -182,19 +113,7 @@ protected Void doInBackground(final Void... params) { */ @ReactMethod public void getContactsByEmailAddress(final String emailAddress, final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableArray contacts = contactsProvider.getContactsByEmailAddress(emailAddress); - - promise.resolve(contacts); - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + contactsManagerImpl.getContactsByEmailAddress(emailAddress, promise); } /** @@ -205,19 +124,7 @@ protected Void doInBackground(final Void... params) { */ @ReactMethod public void getPhotoForId(final String contactId, final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - ContactsProvider contactsProvider = new ContactsProvider(cr); - String photoUri = contactsProvider.getPhotoUriFromContactId(contactId); - - promise.resolve(photoUri); - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + contactsManagerImpl.getPhotoForId(contactId, promise); } /** @@ -228,74 +135,12 @@ protected Void doInBackground(final Void... params) { */ @ReactMethod public void getContactById(final String contactId, final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableMap contact = contactsProvider.getContactById(contactId); - - promise.resolve(contact); - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + contactsManagerImpl.getContactById(contactId, promise); } @ReactMethod public void writePhotoToPath(final String contactId, final String file, final Promise promise) { - AsyncTask myAsyncTask = new AsyncTask() { - @Override - protected Void doInBackground(final Void... params) { - Context context = getReactApplicationContext(); - ContentResolver cr = context.getContentResolver(); - - Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.parseLong(contactId)); - InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(cr, uri); - OutputStream outputStream = null; - try { - outputStream = new FileOutputStream(file); - BitmapFactory.decodeStream(inputStream).compress(Bitmap.CompressFormat.PNG, 100, outputStream); - promise.resolve(true); - } catch (FileNotFoundException e) { - promise.reject(e.toString()); - } finally { - try { - outputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - try { - inputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - return null; - } - }; - myAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); - } - - private Bitmap getThumbnailBitmap(String thumbnailPath) { - // Thumbnail from absolute path - Bitmap photo = BitmapFactory.decodeFile(thumbnailPath); - - if (photo == null) { - // Try to find the thumbnail from assets - AssetManager assetManager = getReactApplicationContext().getAssets(); - InputStream inputStream = null; - try { - inputStream = assetManager.open(thumbnailPath); - photo = BitmapFactory.decodeStream(inputStream); - inputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - return photo; + contactsManagerImpl.writePhotoToPath(contactId, file, promise); } /* @@ -303,211 +148,7 @@ private Bitmap getThumbnailBitmap(String thumbnailPath) { */ @ReactMethod public void openContactForm(ReadableMap contact, Promise promise) { - - String givenName = contact.hasKey("givenName") ? contact.getString("givenName") : null; - String middleName = contact.hasKey("middleName") ? contact.getString("middleName") : null; - String displayName = contact.hasKey("displayName") ? contact.getString("displayName") : null; - String familyName = contact.hasKey("familyName") ? contact.getString("familyName") : null; - String prefix = contact.hasKey("prefix") ? contact.getString("prefix") : null; - String suffix = contact.hasKey("suffix") ? contact.getString("suffix") : null; - String company = contact.hasKey("company") ? contact.getString("company") : null; - String jobTitle = contact.hasKey("jobTitle") ? contact.getString("jobTitle") : null; - String department = contact.hasKey("department") ? contact.getString("department") : null; - String note = contact.hasKey("note") ? contact.getString("note") : null; - String thumbnailPath = contact.hasKey("thumbnailPath") ? contact.getString("thumbnailPath") : null; - - ReadableArray phoneNumbers = contact.hasKey("phoneNumbers") ? contact.getArray("phoneNumbers") : null; - int numOfPhones = 0; - String[] phones = null; - String[] phonesLabels = null; - Integer[] phonesLabelsTypes = null; - if (phoneNumbers != null) { - numOfPhones = phoneNumbers.size(); - phones = new String[numOfPhones]; - phonesLabels = new String[numOfPhones]; - phonesLabelsTypes = new Integer[numOfPhones]; - for (int i = 0; i < numOfPhones; i++) { - phones[i] = phoneNumbers.getMap(i).getString("number"); - String label = phoneNumbers.getMap(i).getString("label"); - phonesLabels[i] = label; - phonesLabelsTypes[i] = mapStringToPhoneType(label); - } - } - - ReadableArray urlAddresses = contact.hasKey("urlAddresses") ? contact.getArray("urlAddresses") : null; - int numOfUrls = 0; - String[] urls = null; - if (urlAddresses != null) { - numOfUrls = urlAddresses.size(); - urls = new String[numOfUrls]; - for (int i = 0; i < numOfUrls; i++) { - urls[i] = urlAddresses.getMap(i).getString("url"); - } - } - - ReadableArray emailAddresses = contact.hasKey("emailAddresses") ? contact.getArray("emailAddresses") : null; - int numOfEmails = 0; - String[] emails = null; - Integer[] emailsLabels = null; - if (emailAddresses != null) { - numOfEmails = emailAddresses.size(); - emails = new String[numOfEmails]; - emailsLabels = new Integer[numOfEmails]; - for (int i = 0; i < numOfEmails; i++) { - emails[i] = emailAddresses.getMap(i).getString("email"); - String label = emailAddresses.getMap(i).getString("label"); - emailsLabels[i] = mapStringToEmailType(label); - } - } - - ReadableArray postalAddresses = contact.hasKey("postalAddresses") ? contact.getArray("postalAddresses") : null; - int numOfPostalAddresses = 0; - String[] postalAddressesStreet = null; - String[] postalAddressesCity = null; - String[] postalAddressesState = null; - String[] postalAddressesRegion = null; - String[] postalAddressesPostCode = null; - String[] postalAddressesCountry = null; - String[] postalAddressesFormattedAddress = null; - String[] postalAddressesLabel = null; - Integer[] postalAddressesType = null; - - if (postalAddresses != null) { - numOfPostalAddresses = postalAddresses.size(); - postalAddressesStreet = new String[numOfPostalAddresses]; - postalAddressesCity = new String[numOfPostalAddresses]; - postalAddressesState = new String[numOfPostalAddresses]; - postalAddressesRegion = new String[numOfPostalAddresses]; - postalAddressesPostCode = new String[numOfPostalAddresses]; - postalAddressesCountry = new String[numOfPostalAddresses]; - postalAddressesFormattedAddress = new String[numOfPostalAddresses]; - postalAddressesLabel = new String[numOfPostalAddresses]; - postalAddressesType = new Integer[numOfPostalAddresses]; - for (int i = 0; i < numOfPostalAddresses; i++) { - postalAddressesStreet[i] = postalAddresses.getMap(i).getString("street"); - postalAddressesCity[i] = postalAddresses.getMap(i).getString("city"); - postalAddressesState[i] = postalAddresses.getMap(i).getString("state"); - postalAddressesRegion[i] = postalAddresses.getMap(i).getString("region"); - postalAddressesPostCode[i] = postalAddresses.getMap(i).getString("postCode"); - postalAddressesCountry[i] = postalAddresses.getMap(i).getString("country"); - postalAddressesFormattedAddress[i] = postalAddresses.getMap(i).getString("formattedAddress"); - postalAddressesLabel[i] = postalAddresses.getMap(i).getString("label"); - postalAddressesType[i] = mapStringToPostalAddressType(postalAddresses.getMap(i).getString("label")); - } - } - - ReadableArray imAddresses = contact.hasKey("imAddresses") ? contact.getArray("imAddresses") : null; - int numOfIMAddresses = 0; - String[] imAccounts = null; - String[] imProtocols = null; - if (imAddresses != null) { - numOfIMAddresses = imAddresses.size(); - imAccounts = new String[numOfIMAddresses]; - imProtocols = new String[numOfIMAddresses]; - for (int i = 0; i < numOfIMAddresses; i++) { - imAccounts[i] = imAddresses.getMap(i).getString("username"); - imProtocols[i] = imAddresses.getMap(i).getString("service"); - } - } - - ArrayList contactData = new ArrayList<>(); - - ContentValues name = new ContentValues(); - name.put(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Identity.CONTENT_ITEM_TYPE); - name.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, givenName); - name.put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, familyName); - name.put(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, middleName); - name.put(ContactsContract.CommonDataKinds.StructuredName.PREFIX, prefix); - name.put(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, suffix); - contactData.add(name); - - ContentValues organization = new ContentValues(); - organization.put(ContactsContract.Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE); - organization.put(Organization.COMPANY, company); - organization.put(Organization.TITLE, jobTitle); - organization.put(Organization.DEPARTMENT, department); - contactData.add(organization); - - for (int i = 0; i < numOfUrls; i++) { - ContentValues url = new ContentValues(); - url.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Website.CONTENT_ITEM_TYPE); - url.put(CommonDataKinds.Website.URL, urls[i]); - contactData.add(url); - } - - for (int i = 0; i < numOfEmails; i++) { - ContentValues email = new ContentValues(); - email.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE); - email.put(CommonDataKinds.Email.TYPE, emailsLabels[i]); - email.put(CommonDataKinds.Email.ADDRESS, emails[i]); - contactData.add(email); - } - - for (int i = 0; i < numOfPhones; i++) { - ContentValues phone = new ContentValues(); - phone.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE); - phone.put(CommonDataKinds.Phone.TYPE, phonesLabelsTypes[i]); - phone.put(CommonDataKinds.Phone.LABEL, phonesLabels[i]); - phone.put(CommonDataKinds.Phone.NUMBER, phones[i]); - - contactData.add(phone); - } - - for (int i = 0; i < numOfPostalAddresses; i++) { - ContentValues structuredPostal = new ContentValues(); - structuredPostal.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE); - structuredPostal.put(CommonDataKinds.StructuredPostal.STREET, postalAddressesStreet[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.CITY, postalAddressesCity[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.REGION, postalAddressesRegion[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.COUNTRY, postalAddressesCountry[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.POSTCODE, postalAddressesPostCode[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, - postalAddressesFormattedAddress[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.LABEL, postalAddressesLabel[i]); - structuredPostal.put(CommonDataKinds.StructuredPostal.TYPE, postalAddressesType[i]); - // No state column in StructuredPostal - // structuredPostal.put(CommonDataKinds.StructuredPostal.???, - // postalAddressesState[i]); - contactData.add(structuredPostal); - } - - for (int i = 0; i < numOfIMAddresses; i++) { - ContentValues imAddress = new ContentValues(); - imAddress.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE); - imAddress.put(CommonDataKinds.Im.DATA, imAccounts[i]); - imAddress.put(CommonDataKinds.Im.TYPE, CommonDataKinds.Im.TYPE_HOME); - imAddress.put(CommonDataKinds.Im.PROTOCOL, CommonDataKinds.Im.PROTOCOL_CUSTOM); - imAddress.put(CommonDataKinds.Im.CUSTOM_PROTOCOL, imProtocols[i]); - contactData.add(imAddress); - } - - if (note != null) { - ContentValues structuredNote = new ContentValues(); - structuredNote.put(ContactsContract.Data.MIMETYPE, Note.CONTENT_ITEM_TYPE); - structuredNote.put(ContactsContract.CommonDataKinds.Note.NOTE, note); - contactData.add(structuredNote); - } - - if (thumbnailPath != null && !thumbnailPath.isEmpty()) { - Bitmap photo = getThumbnailBitmap(thumbnailPath); - - if (photo != null) { - ContentValues thumbnail = new ContentValues(); - thumbnail.put(ContactsContract.Data.RAW_CONTACT_ID, 0); - thumbnail.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1); - thumbnail.put(ContactsContract.CommonDataKinds.Photo.PHOTO, toByteArray(photo)); - thumbnail.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); - contactData.add(thumbnail); - } - } - - Intent intent = new Intent(Intent.ACTION_INSERT, ContactsContract.Contacts.CONTENT_URI); - intent.putExtra(ContactsContract.Intents.Insert.NAME, displayName); - intent.putExtra("finishActivityOnSaveCompleted", true); - intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData); - - updateContactPromise = promise; - getReactApplicationContext().startActivityForResult(intent, REQUEST_OPEN_CONTACT_FORM, Bundle.EMPTY); + contactsManagerImpl.openContactForm(contact, promise); } /* @@ -515,21 +156,7 @@ public void openContactForm(ReadableMap contact, Promise promise) { */ @ReactMethod public void openExistingContact(ReadableMap contact, Promise promise) { - - String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; - - try { - Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, recordID); - Intent intent = new Intent(Intent.ACTION_EDIT); - intent.setDataAndType(uri, ContactsContract.Contacts.CONTENT_ITEM_TYPE); - intent.putExtra("finishActivityOnSaveCompleted", true); - - updateContactPromise = promise; - getReactApplicationContext().startActivityForResult(intent, REQUEST_OPEN_EXISTING_CONTACT, Bundle.EMPTY); - - } catch (Exception e) { - promise.reject(e.toString()); - } + contactsManagerImpl.openExistingContact(contact, promise); } /* @@ -537,21 +164,7 @@ public void openExistingContact(ReadableMap contact, Promise promise) { */ @ReactMethod public void viewExistingContact(ReadableMap contact, Promise promise) { - - String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; - - try { - Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, recordID); - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(uri, ContactsContract.Contacts.CONTENT_ITEM_TYPE); - intent.putExtra("finishActivityOnSaveCompleted", true); - - updateContactPromise = promise; - getReactApplicationContext().startActivityForResult(intent, REQUEST_OPEN_EXISTING_CONTACT, Bundle.EMPTY); - - } catch (Exception e) { - promise.reject(e.toString()); - } + contactsManagerImpl.viewExistingContact(contact, promise); } /* @@ -559,47 +172,7 @@ public void viewExistingContact(ReadableMap contact, Promise promise) { */ @ReactMethod public void editExistingContact(ReadableMap contact, Promise promise) { - - String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; - - try { - Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, recordID); - - ReadableArray phoneNumbers = contact.hasKey("phoneNumbers") ? contact.getArray("phoneNumbers") : null; - int numOfPhones = 0; - String[] phones = null; - Integer[] phonesLabels = null; - if (phoneNumbers != null) { - numOfPhones = phoneNumbers.size(); - phones = new String[numOfPhones]; - phonesLabels = new Integer[numOfPhones]; - for (int i = 0; i < numOfPhones; i++) { - phones[i] = phoneNumbers.getMap(i).getString("number"); - String label = phoneNumbers.getMap(i).getString("label"); - phonesLabels[i] = mapStringToPhoneType(label); - } - } - - ArrayList contactData = new ArrayList<>(); - for (int i = 0; i < numOfPhones; i++) { - ContentValues phone = new ContentValues(); - phone.put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE); - phone.put(CommonDataKinds.Phone.TYPE, phonesLabels[i]); - phone.put(CommonDataKinds.Phone.NUMBER, phones[i]); - contactData.add(phone); - } - - Intent intent = new Intent(Intent.ACTION_EDIT); - intent.setDataAndType(uri, ContactsContract.Contacts.CONTENT_ITEM_TYPE); - intent.putExtra("finishActivityOnSaveCompleted", true); - intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData); - - updateContactPromise = promise; - getReactApplicationContext().startActivityForResult(intent, REQUEST_OPEN_EXISTING_CONTACT, Bundle.EMPTY); - - } catch (Exception e) { - promise.reject(e.toString()); - } + contactsManagerImpl.editExistingContact(contact, promise); } /* @@ -607,213 +180,7 @@ public void editExistingContact(ReadableMap contact, Promise promise) { */ @ReactMethod public void addContact(ReadableMap contact, Promise promise) { - if (contact == null) { - promise.reject("New contact cannot be null."); - return; - } - String givenName = contact.hasKey("givenName") ? contact.getString("givenName") : null; - String middleName = contact.hasKey("middleName") ? contact.getString("middleName") : null; - String familyName = contact.hasKey("familyName") ? contact.getString("familyName") : null; - String prefix = contact.hasKey("prefix") ? contact.getString("prefix") : null; - String suffix = contact.hasKey("suffix") ? contact.getString("suffix") : null; - String company = contact.hasKey("company") ? contact.getString("company") : null; - String jobTitle = contact.hasKey("jobTitle") ? contact.getString("jobTitle") : null; - String department = contact.hasKey("department") ? contact.getString("department") : null; - String note = contact.hasKey("note") ? contact.getString("note") : null; - String thumbnailPath = contact.hasKey("thumbnailPath") ? contact.getString("thumbnailPath") : null; - - ReadableArray phoneNumbers = contact.hasKey("phoneNumbers") ? contact.getArray("phoneNumbers") : null; - int numOfPhones = 0; - String[] phones = null; - Integer[] phonesTypes = null; - String[] phonesLabels = null; - if (phoneNumbers != null) { - numOfPhones = phoneNumbers.size(); - phones = new String[numOfPhones]; - phonesTypes = new Integer[numOfPhones]; - phonesLabels = new String[numOfPhones]; - for (int i = 0; i < numOfPhones; i++) { - phones[i] = phoneNumbers.getMap(i).getString("number"); - String label = phoneNumbers.getMap(i).getString("label"); - phonesTypes[i] = mapStringToPhoneType(label); - phonesLabels[i] = label; - } - } - - ReadableArray urlAddresses = contact.hasKey("urlAddresses") ? contact.getArray("urlAddresses") : null; - int numOfUrls = 0; - String[] urls = null; - if (urlAddresses != null) { - numOfUrls = urlAddresses.size(); - urls = new String[numOfUrls]; - for (int i = 0; i < numOfUrls; i++) { - urls[i] = urlAddresses.getMap(i).getString("url"); - } - } - - ReadableArray emailAddresses = contact.hasKey("emailAddresses") ? contact.getArray("emailAddresses") : null; - int numOfEmails = 0; - String[] emails = null; - Integer[] emailsTypes = null; - String[] emailsLabels = null; - if (emailAddresses != null) { - numOfEmails = emailAddresses.size(); - emails = new String[numOfEmails]; - emailsTypes = new Integer[numOfEmails]; - emailsLabels = new String[numOfEmails]; - for (int i = 0; i < numOfEmails; i++) { - emails[i] = emailAddresses.getMap(i).getString("email"); - String label = emailAddresses.getMap(i).getString("label"); - emailsTypes[i] = mapStringToEmailType(label); - emailsLabels[i] = label; - } - } - - ReadableArray imAddresses = contact.hasKey("imAddresses") ? contact.getArray("imAddresses") : null; - int numOfIMAddresses = 0; - String[] imAccounts = null; - String[] imProtocols = null; - if (imAddresses != null) { - numOfIMAddresses = imAddresses.size(); - imAccounts = new String[numOfIMAddresses]; - imProtocols = new String[numOfIMAddresses]; - for (int i = 0; i < numOfIMAddresses; i++) { - imAccounts[i] = imAddresses.getMap(i).getString("username"); - imProtocols[i] = imAddresses.getMap(i).getString("service"); - } - } - - ArrayList ops = new ArrayList(); - - ContentProviderOperation.Builder op = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) - .withValue(RawContacts.ACCOUNT_TYPE, null) - .withValue(RawContacts.ACCOUNT_NAME, null); - ops.add(op.build()); - - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) - // .withValue(StructuredName.DISPLAY_NAME, name) - .withValue(StructuredName.GIVEN_NAME, givenName) - .withValue(StructuredName.MIDDLE_NAME, middleName) - .withValue(StructuredName.FAMILY_NAME, familyName) - .withValue(StructuredName.PREFIX, prefix) - .withValue(StructuredName.SUFFIX, suffix); - ops.add(op.build()); - - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, Note.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Note.NOTE, note); - ops.add(op.build()); - - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE) - .withValue(Organization.COMPANY, company) - .withValue(Organization.TITLE, jobTitle) - .withValue(Organization.DEPARTMENT, department); - ops.add(op.build()); - - // TODO not sure where to allow yields - op.withYieldAllowed(true); - - for (int i = 0; i < numOfPhones; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Phone.NUMBER, phones[i]) - .withValue(CommonDataKinds.Phone.TYPE, phonesTypes[i]) - .withValue(CommonDataKinds.Phone.LABEL, phonesLabels[i]); - ops.add(op.build()); - } - - for (int i = 0; i < numOfUrls; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Website.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Website.URL, urls[i]); - ops.add(op.build()); - } - - for (int i = 0; i < numOfEmails; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Email.ADDRESS, emails[i]) - .withValue(CommonDataKinds.Email.TYPE, emailsTypes[i]) - .withValue(CommonDataKinds.Email.LABEL, emailsLabels[i]); - ops.add(op.build()); - } - - if (thumbnailPath != null && !thumbnailPath.isEmpty()) { - Bitmap photo = getThumbnailBitmap(thumbnailPath); - - if (photo != null) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, - ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, toByteArray(photo)) - .build()); - } - } - - ReadableArray postalAddresses = contact.hasKey("postalAddresses") ? contact.getArray("postalAddresses") : null; - if (postalAddresses != null) { - for (int i = 0; i < postalAddresses.size(); i++) { - ReadableMap address = postalAddresses.getMap(i); - - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.StructuredPostal.TYPE, - mapStringToPostalAddressType(address.getString("label"))) - .withValue(CommonDataKinds.StructuredPostal.LABEL, address.getString("label")) - .withValue(CommonDataKinds.StructuredPostal.STREET, address.getString("street")) - .withValue(CommonDataKinds.StructuredPostal.CITY, address.getString("city")) - .withValue(CommonDataKinds.StructuredPostal.REGION, address.getString("state")) - .withValue(CommonDataKinds.StructuredPostal.POSTCODE, address.getString("postCode")) - .withValue(CommonDataKinds.StructuredPostal.COUNTRY, address.getString("country")); - - ops.add(op.build()); - } - } - - for (int i = 0; i < numOfIMAddresses; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Im.DATA, imAccounts[i]) - .withValue(CommonDataKinds.Im.TYPE, CommonDataKinds.Im.TYPE_HOME) - .withValue(CommonDataKinds.Im.PROTOCOL, CommonDataKinds.Im.PROTOCOL_CUSTOM) - .withValue(CommonDataKinds.Im.CUSTOM_PROTOCOL, imProtocols[i]); - ops.add(op.build()); - } - - Context ctx = getReactApplicationContext(); - try { - ContentResolver cr = ctx.getContentResolver(); - ContentProviderResult[] result = cr.applyBatch(ContactsContract.AUTHORITY, ops); - - if (result != null && result.length > 0) { - - String rawId = String.valueOf(ContentUris.parseId(result[0].uri)); - - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableMap newlyAddedContact = contactsProvider.getContactByRawId(rawId); - - promise.resolve(newlyAddedContact); // success - } - } catch (Exception e) { - promise.reject(e.toString()); - } - } - - public byte[] toByteArray(Bitmap bitmap) { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream); - return stream.toByteArray(); + contactsManagerImpl.addContact(contact, promise); } /* @@ -821,308 +188,7 @@ public byte[] toByteArray(Bitmap bitmap) { */ @ReactMethod public void updateContact(ReadableMap contact, Promise promise) { - - String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; - String rawContactId = contact.hasKey("rawContactId") ? contact.getString("rawContactId") : null; - - if (rawContactId == null || recordID == null) { - promise.reject("Invalid recordId or rawContactId"); - return; - } - - String givenName = contact.hasKey("givenName") ? contact.getString("givenName") : null; - String middleName = contact.hasKey("middleName") ? contact.getString("middleName") : null; - String familyName = contact.hasKey("familyName") ? contact.getString("familyName") : null; - String prefix = contact.hasKey("prefix") ? contact.getString("prefix") : null; - String suffix = contact.hasKey("suffix") ? contact.getString("suffix") : null; - String company = contact.hasKey("company") ? contact.getString("company") : null; - String jobTitle = contact.hasKey("jobTitle") ? contact.getString("jobTitle") : null; - String department = contact.hasKey("department") ? contact.getString("department") : null; - String note = contact.hasKey("note") ? contact.getString("note") : null; - String thumbnailPath = contact.hasKey("thumbnailPath") ? contact.getString("thumbnailPath") : null; - - ReadableArray phoneNumbers = contact.hasKey("phoneNumbers") ? contact.getArray("phoneNumbers") : null; - int numOfPhones = 0; - String[] phones = null; - Integer[] phonesTypes = null; - String[] phonesLabels = null; - String[] phoneIds = null; - if (phoneNumbers != null) { - numOfPhones = phoneNumbers.size(); - phones = new String[numOfPhones]; - phonesTypes = new Integer[numOfPhones]; - phonesLabels = new String[numOfPhones]; - phoneIds = new String[numOfPhones]; - for (int i = 0; i < numOfPhones; i++) { - ReadableMap phoneMap = phoneNumbers.getMap(i); - String phoneNumber = phoneMap.getString("number"); - String phoneLabel = phoneMap.getString("label"); - String phoneId = phoneMap.hasKey("id") ? phoneMap.getString("id") : null; - phones[i] = phoneNumber; - phonesTypes[i] = mapStringToPhoneType(phoneLabel); - phonesLabels[i] = phoneLabel; - phoneIds[i] = phoneId; - } - } - - ReadableArray urlAddresses = contact.hasKey("urlAddresses") ? contact.getArray("urlAddresses") : null; - int numOfUrls = 0; - String[] urls = null; - String[] urlIds = null; - - if (urlAddresses != null) { - numOfUrls = urlAddresses.size(); - urls = new String[numOfUrls]; - urlIds = new String[numOfUrls]; - for (int i = 0; i < numOfUrls; i++) { - ReadableMap urlMap = urlAddresses.getMap(i); - urls[i] = urlMap.getString("url"); - urlIds[i] = urlMap.hasKey("id") ? urlMap.getString("id") : null; - } - } - - ReadableArray emailAddresses = contact.hasKey("emailAddresses") ? contact.getArray("emailAddresses") : null; - int numOfEmails = 0; - String[] emails = null; - Integer[] emailsTypes = null; - String[] emailsLabels = null; - String[] emailIds = null; - - if (emailAddresses != null) { - numOfEmails = emailAddresses.size(); - emails = new String[numOfEmails]; - emailIds = new String[numOfEmails]; - emailsTypes = new Integer[numOfEmails]; - emailsLabels = new String[numOfEmails]; - for (int i = 0; i < numOfEmails; i++) { - ReadableMap emailMap = emailAddresses.getMap(i); - emails[i] = emailMap.getString("email"); - String label = emailMap.getString("label"); - emailsTypes[i] = mapStringToEmailType(label); - emailsLabels[i] = label; - emailIds[i] = emailMap.hasKey("id") ? emailMap.getString("id") : null; - } - } - - ReadableArray postalAddresses = contact.hasKey("postalAddresses") ? contact.getArray("postalAddresses") : null; - int numOfPostalAddresses = 0; - String[] postalAddressesStreet = null; - String[] postalAddressesCity = null; - String[] postalAddressesState = null; - String[] postalAddressesRegion = null; - String[] postalAddressesPostCode = null; - String[] postalAddressesCountry = null; - Integer[] postalAddressesType = null; - String[] postalAddressesLabel = null; - if (postalAddresses != null) { - numOfPostalAddresses = postalAddresses.size(); - postalAddressesStreet = new String[numOfPostalAddresses]; - postalAddressesCity = new String[numOfPostalAddresses]; - postalAddressesState = new String[numOfPostalAddresses]; - postalAddressesRegion = new String[numOfPostalAddresses]; - postalAddressesPostCode = new String[numOfPostalAddresses]; - postalAddressesCountry = new String[numOfPostalAddresses]; - postalAddressesType = new Integer[numOfPostalAddresses]; - postalAddressesLabel = new String[numOfPostalAddresses]; - for (int i = 0; i < numOfPostalAddresses; i++) { - String postalLabel = getValueFromKey(postalAddresses.getMap(i), "label"); - postalAddressesStreet[i] = getValueFromKey(postalAddresses.getMap(i), "street"); - postalAddressesCity[i] = getValueFromKey(postalAddresses.getMap(i), "city"); - postalAddressesState[i] = getValueFromKey(postalAddresses.getMap(i), "state"); - postalAddressesRegion[i] = getValueFromKey(postalAddresses.getMap(i), "region"); - postalAddressesPostCode[i] = getValueFromKey(postalAddresses.getMap(i), "postCode"); - postalAddressesCountry[i] = getValueFromKey(postalAddresses.getMap(i), "country"); - postalAddressesType[i] = mapStringToPostalAddressType(postalLabel); - postalAddressesLabel[i] = postalLabel; - } - } - - ReadableArray imAddresses = contact.hasKey("imAddresses") ? contact.getArray("imAddresses") : null; - int numOfIMAddresses = 0; - String[] imAccounts = null; - String[] imProtocols = null; - String[] imAddressIds = null; - - if (imAddresses != null) { - numOfIMAddresses = imAddresses.size(); - imAccounts = new String[numOfIMAddresses]; - imProtocols = new String[numOfIMAddresses]; - imAddressIds = new String[numOfIMAddresses]; - for (int i = 0; i < numOfIMAddresses; i++) { - ReadableMap imAddressMap = imAddresses.getMap(i); - imAccounts[i] = imAddressMap.getString("username"); - imProtocols[i] = imAddressMap.getString("service"); - imAddressIds[i] = imAddressMap.hasKey("id") ? imAddressMap.getString("id") : null; - } - } - - ArrayList ops = new ArrayList(); - - ContentProviderOperation.Builder op = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) - .withSelection(ContactsContract.Data.CONTACT_ID + "=?", new String[] { String.valueOf(recordID) }) - .withValue(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) - .withValue(StructuredName.GIVEN_NAME, givenName) - .withValue(StructuredName.MIDDLE_NAME, middleName) - .withValue(StructuredName.FAMILY_NAME, familyName) - .withValue(StructuredName.PREFIX, prefix) - .withValue(StructuredName.SUFFIX, suffix); - ops.add(op.build()); - - op = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) - .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + " = ?", - new String[] { String.valueOf(recordID), Organization.CONTENT_ITEM_TYPE }) - .withValue(Organization.COMPANY, company) - .withValue(Organization.TITLE, jobTitle) - .withValue(Organization.DEPARTMENT, department); - ops.add(op.build()); - - op.withYieldAllowed(true); - - if (phoneNumbers != null) { - // remove existing phoneNumbers first - op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) - .withSelection( - ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", - new String[] { String.valueOf(CommonDataKinds.Phone.CONTENT_ITEM_TYPE), - String.valueOf(rawContactId) }); - ops.add(op.build()); - - // add passed phonenumbers - for (int i = 0; i < numOfPhones; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Phone.NUMBER, phones[i]) - .withValue(CommonDataKinds.Phone.TYPE, phonesTypes[i]) - .withValue(CommonDataKinds.Phone.LABEL, phonesLabels[i]); - ops.add(op.build()); - } - } - - for (int i = 0; i < numOfUrls; i++) { - if (urlIds[i] == null) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Website.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Website.URL, urls[i]); - } else { - op = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) - .withSelection(ContactsContract.Data._ID + "=?", new String[] { String.valueOf(urlIds[i]) }) - .withValue(CommonDataKinds.Website.URL, urls[i]); - } - ops.add(op.build()); - } - - if (emailAddresses != null) { - // remove existing emails first - op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) - .withSelection( - ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", - new String[] { String.valueOf(CommonDataKinds.Email.CONTENT_ITEM_TYPE), - String.valueOf(rawContactId) }); - ops.add(op.build()); - - // add passed email addresses - for (int i = 0; i < numOfEmails; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Email.ADDRESS, emails[i]) - .withValue(CommonDataKinds.Email.TYPE, emailsTypes[i]) - .withValue(CommonDataKinds.Email.LABEL, emailsLabels[i]); - ops.add(op.build()); - } - } - - // remove existing note first - op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) - .withSelection( - ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", - new String[] { String.valueOf(Note.CONTENT_ITEM_TYPE), String.valueOf(rawContactId) }); - ops.add(op.build()); - - if (note != null) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) - .withValue(ContactsContract.Data.MIMETYPE, Note.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Note.NOTE, note); - ops.add(op.build()); - } - - if (thumbnailPath != null && !thumbnailPath.isEmpty()) { - Bitmap photo = getThumbnailBitmap(thumbnailPath); - - if (photo != null) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, - ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, toByteArray(photo)) - .build()); - } - } - - if (postalAddresses != null) { - // remove existing addresses - op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) - .withSelection( - ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", - new String[] { String.valueOf(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE), - String.valueOf(rawContactId) }); - ops.add(op.build()); - - for (int i = 0; i < numOfPostalAddresses; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.StructuredPostal.TYPE, postalAddressesType[i]) - .withValue(CommonDataKinds.StructuredPostal.LABEL, postalAddressesLabel[i]) - .withValue(CommonDataKinds.StructuredPostal.STREET, postalAddressesStreet[i]) - .withValue(CommonDataKinds.StructuredPostal.CITY, postalAddressesCity[i]) - .withValue(CommonDataKinds.StructuredPostal.REGION, postalAddressesState[i]) - .withValue(CommonDataKinds.StructuredPostal.POSTCODE, postalAddressesPostCode[i]) - .withValue(CommonDataKinds.StructuredPostal.COUNTRY, postalAddressesCountry[i]); - ops.add(op.build()); - } - } - - if (imAddresses != null) { - // remove existing IM addresses - op = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI) - .withSelection( - ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ?", - new String[] { String.valueOf(CommonDataKinds.Im.CONTENT_ITEM_TYPE), - String.valueOf(rawContactId) }); - ops.add(op.build()); - - // add passed IM addresses - for (int i = 0; i < numOfIMAddresses; i++) { - op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, String.valueOf(rawContactId)) - .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE) - .withValue(CommonDataKinds.Im.DATA, imAccounts[i]) - .withValue(CommonDataKinds.Im.TYPE, CommonDataKinds.Im.TYPE_HOME) - .withValue(CommonDataKinds.Im.PROTOCOL, CommonDataKinds.Im.PROTOCOL_CUSTOM) - .withValue(CommonDataKinds.Im.CUSTOM_PROTOCOL, imProtocols[i]); - ops.add(op.build()); - } - } - - Context ctx = getReactApplicationContext(); - try { - ContentResolver cr = ctx.getContentResolver(); - ContentProviderResult[] result = cr.applyBatch(ContactsContract.AUTHORITY, ops); - - if (result != null && result.length > 0) { - - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableMap updatedContact = contactsProvider.getContactById(recordID); - - promise.resolve(updatedContact); // success - } - } catch (Exception e) { - promise.reject(e.toString()); - } + contactsManagerImpl.updateContact(contact, promise); } /* @@ -1130,24 +196,7 @@ public void updateContact(ReadableMap contact, Promise promise) { */ @ReactMethod public void deleteContact(ReadableMap contact, Promise promise) { - - String recordID = contact.hasKey("recordID") ? contact.getString("recordID") : null; - - try { - Context ctx = getReactApplicationContext(); - - Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, recordID); - ContentResolver cr = ctx.getContentResolver(); - int deleted = cr.delete(uri, null, null); - - if (deleted > 0) - promise.resolve(recordID); // success - else - promise.resolve(null); // something was wrong - - } catch (Exception e) { - promise.reject(e.toString()); - } + contactsManagerImpl.deleteContact(contact, promise); } /* @@ -1155,7 +204,7 @@ public void deleteContact(ReadableMap contact, Promise promise) { */ @ReactMethod public void checkPermission(Promise promise) { - promise.resolve(isPermissionGranted()); + contactsManagerImpl.checkPermission(promise); } /* @@ -1163,7 +212,7 @@ public void checkPermission(Promise promise) { */ @ReactMethod public void requestPermission(Promise promise) { - requestReadContactsPermission(promise); + contactsManagerImpl.requestPermission(promise); } /* @@ -1174,23 +223,7 @@ public void iosEnableNotesUsage(boolean enabled) { // this method is only needed for iOS } - private void requestReadContactsPermission(Promise promise) { - Activity currentActivity = getCurrentActivity(); - if (currentActivity == null) { - promise.reject(PERMISSION_DENIED); - return; - } - - if (isPermissionGranted().equals(PERMISSION_AUTHORIZED)) { - promise.resolve(PERMISSION_AUTHORIZED); - return; - } - - requestPromise = promise; - ActivityCompat.requestPermissions(currentActivity, new String[] { PERMISSION_READ_CONTACTS }, - PERMISSION_REQUEST_CODE); - } - + /* protected static void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestPromise == null) { @@ -1216,115 +249,8 @@ protected static void onRequestPermissionsResult(int requestCode, @NonNull Strin requestPromise = null; } - /* - * Get string value from key - */ - private String getValueFromKey(ReadableMap item, String key) { - return item.hasKey(key) ? item.getString(key) : ""; - } - - /* - * Check if READ_CONTACTS permission is granted - */ - private String isPermissionGranted() { - // return -1 for denied and 1 - int res = ActivityCompat.checkSelfPermission(getReactApplicationContext(), PERMISSION_READ_CONTACTS); - return (res == PackageManager.PERMISSION_GRANTED) ? PERMISSION_AUTHORIZED : PERMISSION_DENIED; - } - - /* - * TODO support all phone types - * http://developer.android.com/reference/android/provider/ContactsContract. - * CommonDataKinds.Phone.html */ - private int mapStringToPhoneType(String label) { - int phoneType; - switch (label) { - case "home": - phoneType = CommonDataKinds.Phone.TYPE_HOME; - break; - case "work": - phoneType = CommonDataKinds.Phone.TYPE_WORK; - break; - case "mobile": - phoneType = CommonDataKinds.Phone.TYPE_MOBILE; - break; - case "main": - phoneType = CommonDataKinds.Phone.TYPE_MAIN; - break; - case "work fax": - phoneType = CommonDataKinds.Phone.TYPE_FAX_WORK; - break; - case "home fax": - phoneType = CommonDataKinds.Phone.TYPE_FAX_HOME; - break; - case "pager": - phoneType = CommonDataKinds.Phone.TYPE_PAGER; - break; - case "work_pager": - phoneType = CommonDataKinds.Phone.TYPE_WORK_PAGER; - break; - case "work_mobile": - phoneType = CommonDataKinds.Phone.TYPE_WORK_MOBILE; - break; - case "other": - phoneType = CommonDataKinds.Phone.TYPE_OTHER; - break; - case "cell": - phoneType = CommonDataKinds.Phone.TYPE_MOBILE; - break; - default: - phoneType = CommonDataKinds.Phone.TYPE_CUSTOM; - break; - } - return phoneType; - } - /* - * TODO support TYPE_CUSTOM - * http://developer.android.com/reference/android/provider/ContactsContract. - * CommonDataKinds.Email.html - */ - private int mapStringToEmailType(String label) { - int emailType; - switch (label) { - case "home": - emailType = CommonDataKinds.Email.TYPE_HOME; - break; - case "work": - emailType = CommonDataKinds.Email.TYPE_WORK; - break; - case "mobile": - emailType = CommonDataKinds.Email.TYPE_MOBILE; - break; - case "other": - emailType = CommonDataKinds.Email.TYPE_OTHER; - break; - case "personal": - emailType = CommonDataKinds.Email.TYPE_HOME; - break; - default: - emailType = CommonDataKinds.Email.TYPE_CUSTOM; - break; - } - return emailType; - } - - private int mapStringToPostalAddressType(String label) { - int postalAddressType; - switch (label) { - case "home": - postalAddressType = CommonDataKinds.StructuredPostal.TYPE_HOME; - break; - case "work": - postalAddressType = CommonDataKinds.StructuredPostal.TYPE_WORK; - break; - default: - postalAddressType = CommonDataKinds.StructuredPostal.TYPE_CUSTOM; - break; - } - return postalAddressType; - } @Override public String getName() { @@ -1336,45 +262,7 @@ public String getName() { */ @Override public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { - if (requestCode != REQUEST_OPEN_CONTACT_FORM && requestCode != REQUEST_OPEN_EXISTING_CONTACT) { - return; - } - - if (updateContactPromise == null) { - return; - } - - if (resultCode != Activity.RESULT_OK) { - updateContactPromise.resolve(null); // user probably pressed cancel - updateContactPromise = null; - return; - } - - if (data == null) { - updateContactPromise.reject("Error received activity result with no data!"); - updateContactPromise = null; - return; - } - - try { - Uri contactUri = data.getData(); - - if (contactUri == null) { - updateContactPromise.reject("Error wrong data. No content uri found!"); // something was wrong - updateContactPromise = null; - return; - } - - Context ctx = getReactApplicationContext(); - ContentResolver cr = ctx.getContentResolver(); - ContactsProvider contactsProvider = new ContactsProvider(cr); - WritableMap newlyModifiedContact = contactsProvider.getContactById(contactUri.getLastPathSegment()); - - updateContactPromise.resolve(newlyModifiedContact); // success - } catch (Exception e) { - updateContactPromise.reject(e.getMessage()); - } - updateContactPromise = null; + contactsManagerImpl.onActivityResult(activity, requestCode, resultCode, data); } /* @@ -1382,6 +270,7 @@ public void onActivityResult(Activity activity, int requestCode, int resultCode, */ @Override public void onNewIntent(Intent intent) { + contactsManagerImpl.onNewIntent(intent); } }