package fr.neamar.kiss.loader; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.provider.ContactsContract; import android.util.Log; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import fr.neamar.kiss.normalizer.PhoneNormalizer; import fr.neamar.kiss.normalizer.StringNormalizer; import fr.neamar.kiss.pojo.ContactsPojo; public class LoadContactsPojos extends LoadPojos<ContactsPojo> { public LoadContactsPojos(Context context) { super(context, "contact://"); } @Override protected ArrayList<ContactsPojo> doInBackground(Void... params) { long start = System.nanoTime(); // Run query Cursor cur = context.getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, new String[]{ContactsContract.Contacts.LOOKUP_KEY, ContactsContract.CommonDataKinds.Phone.TIMES_CONTACTED, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Phone.STARRED, ContactsContract.CommonDataKinds.Phone.IS_PRIMARY, ContactsContract.Contacts.PHOTO_ID, ContactsContract.CommonDataKinds.Phone.CONTACT_ID}, null, null, ContactsContract.CommonDataKinds.Phone.TIMES_CONTACTED + " DESC"); // Prevent duplicates by keeping in memory encountered phones. // The string key is "phone" + "|" + "name" (so if two contacts // with distinct name share same number, they both get displayed) Map<String, ArrayList<ContactsPojo>> mapContacts = new HashMap<>(); if (cur != null) { if (cur.getCount() > 0) { int lookupIndex = cur.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY); int timesContactedIndex = cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TIMES_CONTACTED); int displayNameIndex = cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME); int numberIndex = cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER); int starredIndex = cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.STARRED); int isPrimaryIndex = cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.IS_PRIMARY); int photoIdIndex = cur.getColumnIndex(ContactsContract.Contacts.PHOTO_ID); while (cur.moveToNext()) { ContactsPojo contact = new ContactsPojo(); contact.lookupKey = cur.getString(lookupIndex); contact.timesContacted = cur.getInt(timesContactedIndex); contact.setName(cur.getString(displayNameIndex)); contact.phone = cur.getString(numberIndex); if (contact.phone == null) { contact.phone = ""; } contact.phoneSimplified = PhoneNormalizer.simplifyPhoneNumber(contact.phone); contact.starred = cur.getInt(starredIndex) != 0; contact.primary = cur.getInt(isPrimaryIndex) != 0; String photoId = cur.getString(photoIdIndex); if (photoId != null) { contact.icon = ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, Long.parseLong(photoId)); } contact.id = pojoScheme + contact.lookupKey + contact.phone; if (contact.name != null) { contact.nameNormalized = StringNormalizer.normalize(contact.name); if (mapContacts.containsKey(contact.lookupKey)) mapContacts.get(contact.lookupKey).add(contact); else { ArrayList<ContactsPojo> phones = new ArrayList<>(); phones.add(contact); mapContacts.put(contact.lookupKey, phones); } } } } cur.close(); } // Retrieve contacts' nicknames Cursor nickCursor = context.getContentResolver().query( ContactsContract.Data.CONTENT_URI, new String[]{ ContactsContract.CommonDataKinds.Nickname.NAME, ContactsContract.Data.LOOKUP_KEY}, ContactsContract.Data.MIMETYPE + "= ?", new String[]{ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE}, null); if (nickCursor != null) { if (nickCursor.getCount() > 0) { int lookupKeyIndex = nickCursor.getColumnIndex(ContactsContract.Data.LOOKUP_KEY); int nickNameIndex = nickCursor.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.NAME); while (nickCursor.moveToNext()) { String lookupKey = nickCursor.getString(lookupKeyIndex); String nick = nickCursor.getString(nickNameIndex); if (nick != null && lookupKey != null && mapContacts.containsKey(lookupKey)) { for (ContactsPojo contact : mapContacts.get(lookupKey)) { contact.setNickname(nick); } } } } nickCursor.close(); } ArrayList<ContactsPojo> contacts = new ArrayList<>(); Pattern phoneFormatter = Pattern.compile("[ \\.\\(\\)]"); for (List<ContactsPojo> phones : mapContacts.values()) { // Find primary phone and add this one. Boolean hasPrimary = false; for (ContactsPojo contact : phones) { if (contact.primary) { contacts.add(contact); hasPrimary = true; break; } } // If not available, add all (excluding duplicates). if (!hasPrimary) { Map<String, Boolean> added = new HashMap<>(); for (ContactsPojo contact : phones) { String uniqueKey = phoneFormatter.matcher(contact.phone).replaceAll(""); // TODO: what's this supposed to do? //uniqueKey = uniqueKey.replaceAll("^\\+33", "0"); //uniqueKey = uniqueKey.replaceAll("^\\+1", "0"); if (!added.containsKey(uniqueKey)) { added.put(uniqueKey, true); contacts.add(contact); } } } } long end = System.nanoTime(); Log.i("time", Long.toString((end - start) / 1000000) + " milliseconds to list contacts"); return contacts; } }