diff --git a/android/src/main/java/flutter/plugins/contactsservice/contactsservice/ContactsServicePlugin.java b/android/src/main/java/flutter/plugins/contactsservice/contactsservice/ContactsServicePlugin.java index f8645a63..39ff612b 100644 --- a/android/src/main/java/flutter/plugins/contactsservice/contactsservice/ContactsServicePlugin.java +++ b/android/src/main/java/flutter/plugins/contactsservice/contactsservice/ContactsServicePlugin.java @@ -1,5 +1,13 @@ package flutter.plugins.contactsservice.contactsservice; +import static android.app.Activity.RESULT_CANCELED; +import static android.provider.ContactsContract.CommonDataKinds; +import static android.provider.ContactsContract.CommonDataKinds.Email; +import static android.provider.ContactsContract.CommonDataKinds.Organization; +import static android.provider.ContactsContract.CommonDataKinds.Phone; +import static android.provider.ContactsContract.CommonDataKinds.StructuredName; +import static android.provider.ContactsContract.CommonDataKinds.StructuredPostal; + import android.annotation.TargetApi; import android.content.ContentProviderOperation; import android.content.ContentResolver; @@ -17,7 +25,16 @@ import android.provider.ContactsContract; import android.text.TextUtils; import android.util.Log; - +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; +import io.flutter.plugin.common.PluginRegistry; +import io.flutter.plugin.common.PluginRegistry.Registrar; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -27,30 +44,12 @@ import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.embedding.engine.plugins.activity.ActivityAware; -import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.MethodChannel.MethodCallHandler; -import io.flutter.plugin.common.MethodChannel.Result; -import io.flutter.plugin.common.PluginRegistry; -import io.flutter.plugin.common.PluginRegistry.Registrar; - -import static android.app.Activity.RESULT_CANCELED; -import static android.provider.ContactsContract.CommonDataKinds; -import static android.provider.ContactsContract.CommonDataKinds.Email; -import static android.provider.ContactsContract.CommonDataKinds.Organization; -import static android.provider.ContactsContract.CommonDataKinds.Phone; -import static android.provider.ContactsContract.CommonDataKinds.StructuredName; -import static android.provider.ContactsContract.CommonDataKinds.StructuredPostal; - @TargetApi(Build.VERSION_CODES.ECLAIR) public class ContactsServicePlugin implements MethodCallHandler, FlutterPlugin, ActivityAware { @@ -102,10 +101,25 @@ public void onDetachedFromEngine(FlutterPluginBinding binding) { public void onMethodCall(MethodCall call, Result result) { switch(call.method){ case "getContacts": { - this.getContacts(call.method, (String)call.argument("query"), (boolean)call.argument("withThumbnails"), (boolean)call.argument("photoHighResolution"), (boolean)call.argument("orderByGivenName"), (boolean)call.argument("androidLocalizedLabels"), result); + this.getContacts( + call.method, + (String) call.argument("query"), + (boolean) call.argument("withThumbnails"), + (boolean) call.argument("photoHighResolution"), + (boolean) call.argument("orderByGivenName"), + (boolean) call.argument("androidLocalizedLabels"), + (List) call.argument("contactInfos"), + result); break; } case "getContactsForPhone": { - this.getContactsForPhone(call.method, (String)call.argument("phone"), (boolean)call.argument("withThumbnails"), (boolean)call.argument("photoHighResolution"), (boolean)call.argument("orderByGivenName"), (boolean)call.argument("androidLocalizedLabels"), result); + this.getContactsForPhone( + call.method, + (String) call.argument("phone"), + (boolean) call.argument("withThumbnails"), + (boolean) call.argument("photoHighResolution"), + (boolean) call.argument("orderByGivenName"), + (boolean) call.argument("androidLocalizedLabels"), + result); break; } case "getAvatar": { final Contact contact = Contact.fromMap((HashMap)call.argument("contact")); @@ -202,14 +216,151 @@ public void onMethodCall(MethodCall call, Result result) { StructuredPostal.COUNTRY, }; + private static final String[] ACCOUNT_PROJECTION = { + ContactsContract.Data.CONTACT_ID, + ContactsContract.Profile.DISPLAY_NAME, + ContactsContract.Contacts.Data.MIMETYPE, + ContactsContract.RawContacts.ACCOUNT_TYPE, + ContactsContract.RawContacts.ACCOUNT_NAME, + }; + + private static final String[] NAME_PROJECTION = { + ContactsContract.Data.CONTACT_ID, + ContactsContract.Contacts.Data.MIMETYPE, + StructuredName.DISPLAY_NAME, + StructuredName.GIVEN_NAME, + StructuredName.MIDDLE_NAME, + StructuredName.FAMILY_NAME, + StructuredName.PREFIX, + StructuredName.SUFFIX, + }; + + private static final String[] NOTE_PROJECTION = { + ContactsContract.Data.CONTACT_ID, + ContactsContract.Contacts.Data.MIMETYPE, + CommonDataKinds.Note.NOTE, + }; + + private static final String[] PHONE_PROJECTION = { + ContactsContract.Data.CONTACT_ID, + ContactsContract.Contacts.Data.MIMETYPE, + Phone.NUMBER, + Phone.TYPE, + Phone.LABEL, + }; + + private static final String[] EMAIL_PROJECTION = { + ContactsContract.Data.CONTACT_ID, + ContactsContract.Contacts.Data.MIMETYPE, + Email.DATA, + Email.ADDRESS, + Email.TYPE, + Email.LABEL, + }; + + private static final String[] ORG_PROJECTION = { + ContactsContract.Data.CONTACT_ID, + ContactsContract.Contacts.Data.MIMETYPE, + Organization.COMPANY, + Organization.TITLE, + }; + + private static final String[] LOCATION_PROJECTION = { + ContactsContract.Data.CONTACT_ID, + ContactsContract.Contacts.Data.MIMETYPE, + StructuredPostal.FORMATTED_ADDRESS, + StructuredPostal.TYPE, + StructuredPostal.LABEL, + StructuredPostal.STREET, + StructuredPostal.POBOX, + StructuredPostal.NEIGHBORHOOD, + StructuredPostal.CITY, + StructuredPostal.REGION, + StructuredPostal.POSTCODE, + StructuredPostal.COUNTRY, + }; @TargetApi(Build.VERSION_CODES.ECLAIR) - private void getContacts(String callMethod, String query, boolean withThumbnails, boolean photoHighResolution, boolean orderByGivenName, boolean localizedLabels, Result result) { - new GetContactsTask(callMethod, result, withThumbnails, photoHighResolution, orderByGivenName, localizedLabels).executeOnExecutor(executor, query, false); + private void getContacts( + String callMethod, + String query, + boolean withThumbnails, + boolean photoHighResolution, + boolean orderByGivenName, + boolean localizedLabels, + List contactInfos, + Result result) { + new GetContactsTask( + callMethod, + result, + withThumbnails, + photoHighResolution, + orderByGivenName, + localizedLabels, + contactInfos) + .executeOnExecutor(executor, query, false); } - private void getContactsForPhone(String callMethod, String phone, boolean withThumbnails, boolean photoHighResolution, boolean orderByGivenName, boolean localizedLabels, Result result) { - new GetContactsTask(callMethod, result, withThumbnails, photoHighResolution, orderByGivenName, localizedLabels).executeOnExecutor(executor, phone, true); + private ArrayList getContacts( + String query, String rawContactId, boolean localizedLabels, List contactInfos) { + String selection = + "(" + + 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 + + "=? OR " + + ContactsContract.RawContacts.ACCOUNT_TYPE + + "=?" + + ")"; + ArrayList selectionArgs = + new ArrayList<>( + Arrays.asList( + CommonDataKinds.Note.CONTENT_ITEM_TYPE, + Email.CONTENT_ITEM_TYPE, + Phone.CONTENT_ITEM_TYPE, + StructuredName.CONTENT_ITEM_TYPE, + Organization.CONTENT_ITEM_TYPE, + StructuredPostal.CONTENT_ITEM_TYPE, + CommonDataKinds.Event.CONTENT_ITEM_TYPE, + ContactsContract.RawContacts.ACCOUNT_TYPE)); + if (query != null) { + selectionArgs = new ArrayList<>(); + selectionArgs.add(query + "%"); + selection = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?"; + } + if (rawContactId != null) { + selectionArgs.add(rawContactId); + selection += " AND " + ContactsContract.Data.CONTACT_ID + " =?"; + } + return queryContacts(selection, selectionArgs, localizedLabels, contactInfos); + } + + private void getContactsForPhone( + String callMethod, + String phone, + boolean withThumbnails, + boolean photoHighResolution, + boolean orderByGivenName, + boolean localizedLabels, + Result result) { + new GetContactsTask( + callMethod, + result, + withThumbnails, + photoHighResolution, + orderByGivenName, + localizedLabels, null) + .executeOnExecutor(executor, phone, true); } @Override @@ -283,7 +434,7 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent intent) Cursor cursor = contentResolver.query(contactUri, null, null, null, null); if (cursor.moveToFirst()) { String id = contactUri.getLastPathSegment(); - getContacts("openDeviceContactPicker", id, false, false, false, localizedLabels, this.result); + getContacts("openDeviceContactPicker", id, false, false, false, localizedLabels, null, this.result); } else { Log.e(LOG_TAG, "onActivityResult - cursor.moveToFirst() returns false"); finishWithResult(FORM_OPERATION_CANCELED); @@ -356,17 +507,17 @@ HashMap getContactByIdentifier(String identifier) { return null; } } - - private void openDeviceContactPicker(Result result, boolean localizedLabels) { - if (delegate != null) { - delegate.setResult(result); - delegate.setLocalizedLabels(localizedLabels); - delegate.openContactPicker(); - } else { - result.success(FORM_COULD_NOT_BE_OPEN); - } + + private void openDeviceContactPicker(Result result, boolean localizedLabels) { + if (delegate != null) { + delegate.setResult(result); + delegate.setLocalizedLabels(localizedLabels); + delegate.openContactPicker(); + } else { + result.success(FORM_COULD_NOT_BE_OPEN); + } } - + private class ContactServiceDelegateOld extends BaseContactsServiceDelegate { private final PluginRegistry.Registrar registrar; @@ -426,23 +577,38 @@ private class GetContactsTask extends AsyncTask private boolean photoHighResolution; private boolean orderByGivenName; private boolean localizedLabels; - - public GetContactsTask(String callMethod, Result result, boolean withThumbnails, boolean photoHighResolution, boolean orderByGivenName, boolean localizedLabels) { + private List contactInfos; + + public GetContactsTask( + String callMethod, + Result result, + boolean withThumbnails, + boolean photoHighResolution, + boolean orderByGivenName, + boolean localizedLabels, + List contactInfos) { this.callMethod = callMethod; this.getContactResult = result; this.withThumbnails = withThumbnails; this.photoHighResolution = photoHighResolution; this.orderByGivenName = orderByGivenName; this.localizedLabels = localizedLabels; + this.contactInfos = contactInfos; } @TargetApi(Build.VERSION_CODES.ECLAIR) protected ArrayList doInBackground(Object... params) { ArrayList contacts; switch (callMethod) { - case "openDeviceContactPicker": contacts = getContactsFrom(getCursor(null, (String) params[0]), localizedLabels); break; - case "getContacts": contacts = getContactsFrom(getCursor((String) params[0], null), localizedLabels); break; - case "getContactsForPhone": contacts = getContactsFrom(getCursorForPhone(((String) params[0])), localizedLabels); break; + case "openDeviceContactPicker": + contacts = getContacts(null, (String) params[0], localizedLabels, contactInfos); + break; + case "getContacts": + contacts = getContacts((String) params[0], null, localizedLabels, contactInfos); + break; + case "getContactsForPhone": + contacts = getContactsFrom(getCursorForPhone(((String) params[0])), localizedLabels); + break; default: return null; } @@ -492,25 +658,280 @@ protected void onPostExecute(ArrayList result) { } } + private ArrayList queryContacts( + String selection, + ArrayList selectionArgs, + boolean localizedLabels, + List contactInfos) { + HashMap map = new LinkedHashMap<>(); + if (contactInfos == null || contactInfos.contains("ContactInfo.account")) { + Cursor accountInfoCursor = + contentResolver.query( + ContactsContract.Data.CONTENT_URI, + ACCOUNT_PROJECTION, + selection, + selectionArgs.toArray(new String[selectionArgs.size()]), + null); + + while (accountInfoCursor != null && accountInfoCursor.moveToNext()) { + populateAccountInfo(map, accountInfoCursor); + } + if (accountInfoCursor != null) { + accountInfoCursor.close(); + } + } - private Cursor getCursor(String query, String rawContactId) { - String selection = "(" + 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 + "=? OR " + ContactsContract.RawContacts.ACCOUNT_TYPE + "=?" + ")"; - ArrayList selectionArgs = new ArrayList<>(Arrays.asList(CommonDataKinds.Note.CONTENT_ITEM_TYPE, Email.CONTENT_ITEM_TYPE, - Phone.CONTENT_ITEM_TYPE, StructuredName.CONTENT_ITEM_TYPE, Organization.CONTENT_ITEM_TYPE, - StructuredPostal.CONTENT_ITEM_TYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE, ContactsContract.RawContacts.ACCOUNT_TYPE)); - if (query != null) { - selectionArgs = new ArrayList<>(); - selectionArgs.add(query + "%"); - selection = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?"; + if (contactInfos == null || contactInfos.contains("ContactInfo.name")) { + Cursor nameCursor = + contentResolver.query( + ContactsContract.Data.CONTENT_URI, + NAME_PROJECTION, + selection, + selectionArgs.toArray(new String[selectionArgs.size()]), + null); + + while (nameCursor != null && nameCursor.moveToNext()) { + populateName(map, nameCursor); + } + if (nameCursor != null) { + nameCursor.close(); + } } - if (rawContactId != null) { - selectionArgs.add(rawContactId); - selection += " AND " + ContactsContract.Data.CONTACT_ID + " =?"; + + if (contactInfos == null || contactInfos.contains("ContactInfo.note")) { + Cursor noteCursor = + contentResolver.query( + ContactsContract.Data.CONTENT_URI, + NOTE_PROJECTION, + selection, + selectionArgs.toArray(new String[selectionArgs.size()]), + null); + + while (noteCursor != null && noteCursor.moveToNext()) { + populateNote(map, noteCursor); + } + if (noteCursor != null) { + noteCursor.close(); + } + } + + if (contactInfos == null || contactInfos.contains("ContactInfo.phone")) { + Cursor phoneCursor = + contentResolver.query( + ContactsContract.Data.CONTENT_URI, + PHONE_PROJECTION, + selection, + selectionArgs.toArray(new String[selectionArgs.size()]), + null); + + while (phoneCursor != null && phoneCursor.moveToNext()) { + populatePhone(map, phoneCursor, localizedLabels); + } + if (phoneCursor != null) { + phoneCursor.close(); + } + } + + if (contactInfos == null || contactInfos.contains("ContactInfo.email")) { + Cursor emailCursor = + contentResolver.query( + ContactsContract.Data.CONTENT_URI, + EMAIL_PROJECTION, + selection, + selectionArgs.toArray(new String[selectionArgs.size()]), + null); + + while (emailCursor != null && emailCursor.moveToNext()) { + populateEmail(map, emailCursor, localizedLabels); + } + if (emailCursor != null) { + emailCursor.close(); + } + } + + if (contactInfos == null || contactInfos.contains("ContactInfo.org")) { + Cursor orgCursor = + contentResolver.query( + ContactsContract.Data.CONTENT_URI, + ORG_PROJECTION, + selection, + selectionArgs.toArray(new String[selectionArgs.size()]), + null); + + while (orgCursor != null && orgCursor.moveToNext()) { + populateOrg(map, orgCursor); + } + if (orgCursor != null) { + orgCursor.close(); + } + } + + if (contactInfos == null || contactInfos.contains("ContactInfo.location")) { + Cursor locationCursor = + contentResolver.query( + ContactsContract.Data.CONTENT_URI, + LOCATION_PROJECTION, + selection, + selectionArgs.toArray(new String[selectionArgs.size()]), + null); + + while (locationCursor != null && locationCursor.moveToNext()) { + populateLocation(map, locationCursor, localizedLabels); + } + if (locationCursor != null) { + locationCursor.close(); + } + } + + return new ArrayList<>(map.values()); + } + + private static void populateAccountInfo(HashMap map, Cursor accountInfoCursor) { + int columnIndex = accountInfoCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID); + String contactId = accountInfoCursor.getString(columnIndex); + + if (!map.containsKey(contactId)) { + map.put(contactId, new Contact(contactId)); + } + Contact contact = map.get(contactId); + + contact.displayName = + accountInfoCursor.getString( + accountInfoCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); + contact.androidAccountType = + accountInfoCursor.getString( + accountInfoCursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE)); + contact.androidAccountName = + accountInfoCursor.getString( + accountInfoCursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_NAME)); + } + + private static void populateName(HashMap map, Cursor nameCursor) { + int columnIndex = nameCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID); + String contactId = nameCursor.getString(columnIndex); + + if (!map.containsKey(contactId)) { + map.put(contactId, new Contact(contactId)); + } + Contact contact = map.get(contactId); + + String mimeType = + nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.Data.MIMETYPE)); + if (mimeType.equals(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) { + contact.givenName = + nameCursor.getString(nameCursor.getColumnIndex(StructuredName.GIVEN_NAME)); + contact.middleName = + nameCursor.getString(nameCursor.getColumnIndex(StructuredName.MIDDLE_NAME)); + contact.familyName = + nameCursor.getString(nameCursor.getColumnIndex(StructuredName.FAMILY_NAME)); + contact.prefix = nameCursor.getString(nameCursor.getColumnIndex(StructuredName.PREFIX)); + contact.suffix = nameCursor.getString(nameCursor.getColumnIndex(StructuredName.SUFFIX)); + } + } + + private static void populateNote(HashMap map, Cursor noteCursor) { + int columnIndex = noteCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID); + String contactId = noteCursor.getString(columnIndex); + + if (!map.containsKey(contactId)) { + map.put(contactId, new Contact(contactId)); + } + Contact contact = map.get(contactId); + + String mimeType = + noteCursor.getString(noteCursor.getColumnIndex(ContactsContract.Data.MIMETYPE)); + if (mimeType.equals(CommonDataKinds.Note.CONTENT_ITEM_TYPE)) { + contact.note = noteCursor.getString(noteCursor.getColumnIndex(CommonDataKinds.Note.NOTE)); + } + } + + private void populatePhone( + HashMap map, Cursor phoneCursor, boolean localizedLabels) { + int columnIndex = phoneCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID); + String contactId = phoneCursor.getString(columnIndex); + + if (!map.containsKey(contactId)) { + map.put(contactId, new Contact(contactId)); + } + Contact contact = map.get(contactId); + + String mimeType = + phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.Data.MIMETYPE)); + if (mimeType.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) { + String phoneNumber = phoneCursor.getString(phoneCursor.getColumnIndex(Phone.NUMBER)); + if (!TextUtils.isEmpty(phoneNumber)) { + int type = phoneCursor.getInt(phoneCursor.getColumnIndex(Phone.TYPE)); + String label = Item.getPhoneLabel(resources, type, phoneCursor, localizedLabels); + contact.phones.add(new Item(label, phoneNumber, type)); + } + } + } + + private void populateEmail( + HashMap map, Cursor emailCursor, boolean localizedLabels) { + int columnIndex = emailCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID); + String contactId = emailCursor.getString(columnIndex); + + if (!map.containsKey(contactId)) { + map.put(contactId, new Contact(contactId)); + } + Contact contact = map.get(contactId); + + String mimeType = + emailCursor.getString(emailCursor.getColumnIndex(ContactsContract.Data.MIMETYPE)); + if (mimeType.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) { + String email = emailCursor.getString(emailCursor.getColumnIndex(Email.ADDRESS)); + int type = emailCursor.getInt(emailCursor.getColumnIndex(Email.TYPE)); + if (!TextUtils.isEmpty(email)) { + String label = Item.getEmailLabel(resources, type, emailCursor, localizedLabels); + contact.emails.add(new Item(label, email, type)); + } + } + } + + private static void populateOrg(HashMap map, Cursor orgCursor) { + int columnIndex = orgCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID); + String contactId = orgCursor.getString(columnIndex); + + if (!map.containsKey(contactId)) { + map.put(contactId, new Contact(contactId)); + } + Contact contact = map.get(contactId); + + String mimeType = orgCursor.getString(orgCursor.getColumnIndex(ContactsContract.Data.MIMETYPE)); + if (mimeType.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) { + contact.company = orgCursor.getString(orgCursor.getColumnIndex(Organization.COMPANY)); + contact.jobTitle = orgCursor.getString(orgCursor.getColumnIndex(Organization.TITLE)); + } + } + + private void populateLocation( + HashMap map, Cursor locationCursor, boolean localizedLabels) { + int columnIndex = locationCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID); + String contactId = locationCursor.getString(columnIndex); + + if (!map.containsKey(contactId)) { + map.put(contactId, new Contact(contactId)); + } + Contact contact = map.get(contactId); + + String mimeType = + locationCursor.getString(locationCursor.getColumnIndex(ContactsContract.Data.MIMETYPE)); + if (mimeType.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) { + int type = locationCursor.getInt(locationCursor.getColumnIndex(StructuredPostal.TYPE)); + String label = PostalAddress.getLabel(resources, type, locationCursor, localizedLabels); + String street = + locationCursor.getString(locationCursor.getColumnIndex(StructuredPostal.STREET)); + String city = locationCursor.getString(locationCursor.getColumnIndex(StructuredPostal.CITY)); + String postcode = + locationCursor.getString(locationCursor.getColumnIndex(StructuredPostal.POSTCODE)); + String region = + locationCursor.getString(locationCursor.getColumnIndex(StructuredPostal.REGION)); + String country = + locationCursor.getString(locationCursor.getColumnIndex(StructuredPostal.COUNTRY)); + contact.postalAddresses.add( + new PostalAddress(label, street, city, postcode, region, country, type)); } - return contentResolver.query(ContactsContract.Data.CONTENT_URI, PROJECTION, selection, selectionArgs.toArray(new String[selectionArgs.size()]), null); } private Cursor getCursorForPhone(String phone) { @@ -539,6 +960,7 @@ private Cursor getCursorForPhone(String phone) { /** * Builds the list of contacts from the cursor + * * @param cursor * @return the list of contacts */ @@ -555,58 +977,22 @@ private ArrayList getContactsFrom(Cursor cursor, boolean localizedLabel Contact contact = map.get(contactId); String mimeType = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.MIMETYPE)); - contact.displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); - contact.androidAccountType = cursor.getString(cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE)); - contact.androidAccountName = cursor.getString(cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_NAME)); - - //NAMES - if (mimeType.equals(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) { - contact.givenName = cursor.getString(cursor.getColumnIndex(StructuredName.GIVEN_NAME)); - contact.middleName = cursor.getString(cursor.getColumnIndex(StructuredName.MIDDLE_NAME)); - contact.familyName = cursor.getString(cursor.getColumnIndex(StructuredName.FAMILY_NAME)); - contact.prefix = cursor.getString(cursor.getColumnIndex(StructuredName.PREFIX)); - contact.suffix = cursor.getString(cursor.getColumnIndex(StructuredName.SUFFIX)); - } + + populateAccountInfo(map, cursor); + // NAMES + populateName(map, cursor); // NOTE - else if (mimeType.equals(CommonDataKinds.Note.CONTENT_ITEM_TYPE)) { - contact.note = cursor.getString(cursor.getColumnIndex(CommonDataKinds.Note.NOTE)); - } - //PHONES - else if (mimeType.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)){ - String phoneNumber = cursor.getString(cursor.getColumnIndex(Phone.NUMBER)); - if (!TextUtils.isEmpty(phoneNumber)){ - int type = cursor.getInt(cursor.getColumnIndex(Phone.TYPE)); - String label = Item.getPhoneLabel(resources, type, cursor, localizedLabels); - contact.phones.add(new Item(label, phoneNumber, type)); - } - } - //MAILS - else if (mimeType.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) { - String email = cursor.getString(cursor.getColumnIndex(Email.ADDRESS)); - int type = cursor.getInt(cursor.getColumnIndex(Email.TYPE)); - if (!TextUtils.isEmpty(email)) { - String label = Item.getEmailLabel(resources, type, cursor, localizedLabels); - contact.emails.add(new Item(label, email, type)); - } - } - //ORG - else if (mimeType.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) { - contact.company = cursor.getString(cursor.getColumnIndex(Organization.COMPANY)); - contact.jobTitle = cursor.getString(cursor.getColumnIndex(Organization.TITLE)); - } - //ADDRESSES - else if (mimeType.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) { - int type = cursor.getInt(cursor.getColumnIndex(StructuredPostal.TYPE)); - String label = PostalAddress.getLabel(resources, type, cursor, localizedLabels); - String street = cursor.getString(cursor.getColumnIndex(StructuredPostal.STREET)); - String city = cursor.getString(cursor.getColumnIndex(StructuredPostal.CITY)); - String postcode = cursor.getString(cursor.getColumnIndex(StructuredPostal.POSTCODE)); - String region = cursor.getString(cursor.getColumnIndex(StructuredPostal.REGION)); - String country = cursor.getString(cursor.getColumnIndex(StructuredPostal.COUNTRY)); - contact.postalAddresses.add(new PostalAddress(label, street, city, postcode, region, country, type)); - } + populateNote(map, cursor); + // PHONES + populatePhone(map, cursor, localizedLabels); + // MAILS + populateEmail(map, cursor, localizedLabels); + // ORG + populateOrg(map, cursor); + // ADDRESSES + populateLocation(map, cursor, localizedLabels); // BIRTHDAY - else if (mimeType.equals(CommonDataKinds.Event.CONTENT_ITEM_TYPE)) { + if (mimeType.equals(CommonDataKinds.Event.CONTENT_ITEM_TYPE)) { int eventType = cursor.getInt(cursor.getColumnIndex(CommonDataKinds.Event.TYPE)); if (eventType == CommonDataKinds.Event.TYPE_BIRTHDAY) { contact.birthday = cursor.getString(cursor.getColumnIndex(CommonDataKinds.Event.START_DATE)); @@ -919,3 +1305,4 @@ private boolean updateContact(Contact contact) { } } + diff --git a/lib/contacts_service.dart b/lib/contacts_service.dart index 9b8523dc..cfd10f78 100644 --- a/lib/contacts_service.dart +++ b/lib/contacts_service.dart @@ -7,19 +7,40 @@ import 'package:quiver/core.dart'; export 'share.dart'; +enum ContactInfo { + account, + name, + note, + phone, + email, + org, + location, +} + class ContactsService { static const MethodChannel _channel = MethodChannel('github.com/clovisnicolas/flutter_contacts'); /// Fetches all contacts, or when specified, the contacts with a name /// matching [query] - static Future> getContacts( - {String? query, + static Future> getContacts({ + String? query, bool withThumbnails = true, bool photoHighResolution = true, bool orderByGivenName = true, bool iOSLocalizedLabels = true, - bool androidLocalizedLabels = true}) async { + bool androidLocalizedLabels = true, + List contactInfos = const [ + ContactInfo.account, + ContactInfo.name, + ContactInfo.note, + ContactInfo.phone, + ContactInfo.email, + ContactInfo.org, + ContactInfo.location + ]}) async { + List contactInfoStrings = contactInfos.map((info) => + info.toString()).toList(); Iterable contacts = await _channel.invokeMethod('getContacts', { 'query': query, @@ -28,6 +49,7 @@ class ContactsService { 'orderByGivenName': orderByGivenName, 'iOSLocalizedLabels': iOSLocalizedLabels, 'androidLocalizedLabels': androidLocalizedLabels, + 'contactInfos': contactInfoStrings, }); return contacts.map((m) => Contact.fromMap(m)); }