Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ If you'd like to read/write the contact's notes, call the `iosEnableNotesUsage(t
* `getAll`: Promise<Contact[]> - returns *all* contacts as an array of objects
* `getAllWithoutPhotos` - same as `getAll` on Android, but on iOS it will not return uris for contact photos (because there's a significant overhead in creating the images)
* `getContactById(contactId)`: Promise<Contact> - returns contact with defined contactId (or null if it doesn't exist)
* `getContactDataValue(contactId, mimetype, columnName)`: Promise<any> - returns the raw values in the array or an empty array.
* `getCount()`: Promise<number> - returns the number of contacts
* `getPhotoForId(contactId)`: Promise<string> - returns a URI (or null) for a contacts photo
* `addContact(contact)`: Promise<Contact> - adds a contact to the AddressBook.
Expand Down Expand Up @@ -245,6 +246,14 @@ If you'd like to read/write the contact's notes, call the `iosEnableNotesUsage(t
{ username: 'johndoe123', service: 'Facebook'}
],
isStarred: false,
socialMedia: [{
phone: '5555555555',
rawPhone: 'Message +5555555555',
accountType: 'com.whatsapp',
accountName: 'WhatsApp',
socialId: '0123456789@s.whatsapp.net',
mimeType: 'vnd.android.cursor.item/vnd.com.whatsapp.profile'
}],
}
```

Expand All @@ -253,6 +262,7 @@ If you'd like to read/write the contact's notes, call the `iosEnableNotesUsage(t
* on Android versions below 8 the entire display name is passed in the `givenName` field. `middleName` and `familyName` will be `""`.
* isStarred field
* writePhotoToPath() - writes the contact photo to a given path
* getContactDataValue() - take a raw value from any contact data column

## iOS only

Expand Down Expand Up @@ -348,6 +358,22 @@ Contacts.deleteContact(contact).then((recordId) => {
})
```

## Get contact data values (raw values)
To retrieve data from any column in its original form, you need:
1. Record identifier (`recordID`) – internal contact ID from the Android database.
2. MIME type (`mimeType`) – specific contact data type identifier used in the Android contacts database.
3. Column name (`columnName`) – original column name from the `ContactsContract.Data` table.

Example
```js
const recordID = contact.recordID;
const mimeType = 'vnd.android.cursor.item/phone_v2';
const columnName = 'data1';
Contacts.getContactDataValue(recordID, mimeType, columnName).then(values => {
// values = ["(555) 555-5555", "(444) 444-4444"];
})
```

## Displaying Thumbnails

The thumbnailPath is the direct URI for the temp location of the contact's cropped thumbnail image.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static android.provider.ContactsContract.CommonDataKinds.Contactables;
import static android.provider.ContactsContract.CommonDataKinds.Email;
Expand All @@ -34,6 +36,7 @@ public class ContactsProvider {
public static final int ID_FOR_PROFILE_CONTACT = -1;

public static final String NAME = "RCTContacts";

private static final List<String> JUST_ME_PROJECTION = new ArrayList<String>() {
{
add((ContactsContract.Data._ID));
Expand Down Expand Up @@ -76,6 +79,8 @@ public class ContactsProvider {
add(Im.DATA);
add(Event.START_DATE);
add(Event.TYPE);
add(Contact.SocialMediaItem.ACCOUNT_TYPE);
add(Contact.SocialMediaItem.ACCOUNT_NAME);
}
};

Expand Down Expand Up @@ -233,6 +238,55 @@ public WritableMap getContactById(String contactId) {
return null;
}

public WritableArray getContactDataValue(String contactId, String mimeType, String columnName) {
WritableArray result = Arguments.createArray();
Set<String> uniqueValues = new LinkedHashSet<>();

Cursor cursor = null;
try {
cursor = contentResolver.query(
ContactsContract.Data.CONTENT_URI,
new String[]{ columnName },
ContactsContract.RawContacts.CONTACT_ID + " = ? AND " +
ContactsContract.Data.MIMETYPE + " = ?",
new String[]{
contactId,
mimeType
},
null
);

if (cursor == null) {
return result;
}

int index = cursor.getColumnIndex(columnName);
if (index == -1) {
return result;
}

while (cursor.moveToNext()) {
String value = cursor.getString(index);
if (value != null) {
uniqueValues.add(value);
}
}

for (String value : uniqueValues) {
result.pushString(value);
}
} catch (Exception e) {
Log.w("ContactsProvider. Error getContactDataValue", e.getMessage());
return result;
} finally {
if (cursor != null) {
cursor.close();
}
}

return result;
}

public Integer getContactsCount() {
Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
int count = cursor.getCount();
Expand Down Expand Up @@ -273,6 +327,9 @@ public WritableArray getContacts() {
+ ContactsContract.Data.MIMETYPE + "=? OR "
+ ContactsContract.Data.MIMETYPE + "=? OR "
+ ContactsContract.Data.MIMETYPE + "=? OR "
+ ContactsContract.Data.MIMETYPE + "=? OR "
+ ContactsContract.Data.MIMETYPE + "=? OR "
+ ContactsContract.Data.MIMETYPE + "=? OR "
+ ContactsContract.Data.MIMETYPE + "=?",
new String[] {
Email.CONTENT_ITEM_TYPE,
Expand All @@ -284,6 +341,9 @@ public WritableArray getContacts() {
Website.CONTENT_ITEM_TYPE,
Im.CONTENT_ITEM_TYPE,
Event.CONTENT_ITEM_TYPE,
Contact.SocialMediaItem.WHATSAPP_TYPE,
Contact.SocialMediaItem.TELEGRAM_TYPE,
Contact.SocialMediaItem.FACEBOOK_TYPE,
},
null);

Expand Down Expand Up @@ -562,6 +622,11 @@ private Map<String, Contact> loadContactsFrom(Cursor cursor) {
case Note.CONTENT_ITEM_TYPE:
contact.note = cursor.getString(cursor.getColumnIndex(Note.NOTE));
break;
case Contact.SocialMediaItem.WHATSAPP_TYPE:
case Contact.SocialMediaItem.TELEGRAM_TYPE:
case Contact.SocialMediaItem.FACEBOOK_TYPE:
contact.socialMedia.add(new Contact.SocialMediaItem(cursor, mimeType));
break;
}
}

Expand Down Expand Up @@ -611,6 +676,7 @@ private static class Contact {
private List<Item> emails = new ArrayList<>();
private List<Item> phones = new ArrayList<>();
private List<PostalAddressItem> postalAddresses = new ArrayList<>();
private List<SocialMediaItem> socialMedia = new ArrayList<>();
private Birthday birthday;

public Contact(String contactId) {
Expand Down Expand Up @@ -679,6 +745,12 @@ public WritableMap toMap() {
}
contact.putArray("postalAddresses", postalAddresses);

WritableArray socialMedia = Arguments.createArray();
for (SocialMediaItem item : this.socialMedia) {
socialMedia.pushMap(item.map);
}
contact.putArray("socialMedia", socialMedia);

WritableMap birthdayMap = Arguments.createMap();
if (birthday != null) {
if (birthday.year > 0) {
Expand Down Expand Up @@ -763,5 +835,40 @@ static String getLabel(Cursor cursor) {
return "other";
}
}

public static class SocialMediaItem {
public static final String ACCOUNT_NAME = "account_name";
public static final String ACCOUNT_TYPE = "account_type";

public static final String WHATSAPP_TYPE = "vnd.android.cursor.item/vnd.com.whatsapp.profile";
public static final String TELEGRAM_TYPE = "vnd.android.cursor.item/vnd.org.telegram.messenger.android.profile";
public static final String FACEBOOK_TYPE = "vnd.android.cursor.item/vnd.com.facebook.profile";

public final WritableMap map;

public SocialMediaItem(Cursor cursor, String mimeType) {
map = Arguments.createMap();
map.putString("mimeType", mimeType);
map.putString("socialId", extract(cursor, ContactsContract.Data.DATA1));
map.putString("accountName", extract(cursor, ACCOUNT_NAME));
map.putString("accountType", extract(cursor, ACCOUNT_TYPE));

String rawPhone = extract(cursor, ContactsContract.Data.DATA3);
map.putString("rawPhone", rawPhone);
String formattedPhone = "";
if (rawPhone != null) {
formattedPhone = rawPhone.replaceAll("[^\\d+]", "");
}
map.putString("phone", formattedPhone);
}

public static String extract(Cursor cursor, String field) {
if (field == null) return null;
int index = cursor.getColumnIndex(field);

if (index == -1) return null;
return cursor.getString(index);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,16 @@ public void getContactById(final String contactId, final Promise promise) {
});
}

public void getContactDataValue(final String contactId, final String mimeType, final String key, final Promise promise) {
getExecutor().execute(() -> {
Context context = getReactApplicationContext();
ContentResolver cr = context.getContentResolver();
ContactsProvider contactsProvider = new ContactsProvider(cr);
WritableArray values = contactsProvider.getContactDataValue(contactId, mimeType, key);
promise.resolve(values);
});
}


public void writePhotoToPath(final String contactId, final String file, final Promise promise) {
getExecutor().execute(() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ public void getContactById(final String contactId, final Promise promise) {
contactsManagerImpl.getContactById(contactId, promise);
}

@Override
public void getContactDataValue(final String contactId, final String mimeType, final String columnName, final Promise promise) {
contactsManagerImpl.getContactDataValue(contactId, mimeType, columnName, promise);
}

@Override
public void writePhotoToPath(final String contactId, final String file, final Promise promise) {
contactsManagerImpl.writePhotoToPath(contactId, file, promise);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ public void getContactById(final String contactId, final Promise promise) {
contactsManagerImpl.getContactById(contactId, promise);
}

@ReactMethod
public void getContactDataValue(final String contactId, final String mimeType, final String columnName, final Promise promise) {
contactsManagerImpl.getContactDataValue(contactId, mimeType, columnName, promise);
}

@ReactMethod
public void writePhotoToPath(final String contactId, final String file, final Promise promise) {
contactsManagerImpl.writePhotoToPath(contactId, file, promise);
Expand Down
Loading