package com.fsck.k9.helper; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.provider.ContactsContract; import timber.log.Timber; import android.provider.ContactsContract.CommonDataKinds.Photo; import com.fsck.k9.mail.Address; /** * Helper class to access the contacts stored on the device. */ public class Contacts { /** * The order in which the search results are returned by * {@link #getContactByAddress(String)}. */ protected static final String SORT_ORDER = ContactsContract.CommonDataKinds.Email.TIMES_CONTACTED + " DESC, " + ContactsContract.Contacts.DISPLAY_NAME + ", " + ContactsContract.CommonDataKinds.Email._ID; /** * Array of columns to load from the database. */ protected static final String PROJECTION[] = { ContactsContract.CommonDataKinds.Email._ID, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.CommonDataKinds.Email.CONTACT_ID }; /** * Index of the name field in the projection. This must match the order in * {@link #PROJECTION}. */ protected static final int NAME_INDEX = 1; /** * Index of the contact id field in the projection. This must match the order in * {@link #PROJECTION}. */ protected static final int CONTACT_ID_INDEX = 2; /** * Get instance of the Contacts class. * * <p>Note: This is left over from the days when we needed to have SDK-specific code to access * the contacts.</p> * * @param context A {@link Context} instance. * @return Appropriate {@link Contacts} instance for this device. */ public static Contacts getInstance(Context context) { return new Contacts(context); } protected Context mContext; protected ContentResolver mContentResolver; /** * Constructor * * @param context A {@link Context} instance. */ protected Contacts(Context context) { mContext = context; mContentResolver = context.getContentResolver(); } /** * Start the activity to add information to an existing contact or add a * new one. * * @param email An {@link Address} instance containing the email address * of the entity you want to add to the contacts. Optionally * the instance also contains the (display) name of that * entity. */ public void createContact(final Address email) { final Uri contactUri = Uri.fromParts("mailto", email.getAddress(), null); final Intent contactIntent = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT); contactIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); contactIntent.setData(contactUri); // Pass along full E-mail string for possible create dialog contactIntent.putExtra(ContactsContract.Intents.EXTRA_CREATE_DESCRIPTION, email.toString()); // Only provide personal name hint if we have one final String senderPersonal = email.getPersonal(); if (senderPersonal != null) { contactIntent.putExtra(ContactsContract.Intents.Insert.NAME, senderPersonal); } mContext.startActivity(contactIntent); } /** * Start the activity to add a phone number to an existing contact or add a new one. * * @param phoneNumber * The phone number to add to a contact, or to use when creating a new contact. */ public void addPhoneContact(final String phoneNumber) { Intent addIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT); addIntent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE); addIntent.putExtra(ContactsContract.Intents.Insert.PHONE, Uri.decode(phoneNumber)); addIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(addIntent); } /** * Check whether the provided email address belongs to one of the contacts. * * @param emailAddress The email address to look for. * @return <tt>true</tt>, if the email address belongs to a contact. * <tt>false</tt>, otherwise. */ public boolean isInContacts(final String emailAddress) { boolean result = false; final Cursor c = getContactByAddress(emailAddress); if (c != null) { if (c.getCount() > 0) { result = true; } c.close(); } return result; } /** * Check whether one of the provided addresses belongs to one of the contacts. * * @param addresses The addresses to search in contacts * @return <tt>true</tt>, if one address belongs to a contact. * <tt>false</tt>, otherwise. */ public boolean isAnyInContacts(final Address[] addresses) { if (addresses == null) { return false; } for (Address addr : addresses) { if (isInContacts(addr.getAddress())) { return true; } } return false; } /** * Get the name of the contact an email address belongs to. * * @param address The email address to search for. * @return The name of the contact the email address belongs to. Or * <tt>null</tt> if there's no matching contact. */ public String getNameForAddress(String address) { if (address == null) { return null; } final Cursor c = getContactByAddress(address); String name = null; if (c != null) { if (c.getCount() > 0) { c.moveToFirst(); name = c.getString(NAME_INDEX); } c.close(); } return name; } /** * Mark contacts with the provided email addresses as contacted. * * @param addresses Array of {@link Address} objects describing the * contacts to be marked as contacted. */ public void markAsContacted(final Address[] addresses) { //TODO: Optimize! Potentially a lot of database queries for (final Address address : addresses) { final Cursor c = getContactByAddress(address.getAddress()); if (c != null) { if (c.getCount() > 0) { c.moveToFirst(); final long personId = c.getLong(CONTACT_ID_INDEX); ContactsContract.Contacts.markAsContacted(mContentResolver, personId); } c.close(); } } } /** * Creates the intent necessary to open a contact picker. * * @return The intent necessary to open a contact picker. */ public Intent contactPickerIntent() { return new Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Email.CONTENT_URI); } /** * Get URI to the picture of the contact with the supplied email address. * * @param address * An email address. The contact database is searched for a contact with this email * address. * * @return URI to the picture of the contact with the supplied email address. {@code null} if * no such contact could be found or the contact doesn't have a picture. */ public Uri getPhotoUri(String address) { try { final Cursor c = getContactByAddress(address); if (c == null) { return null; } try { if (!c.moveToFirst()) { return null; } final String uriString = c.getString(c.getColumnIndex(Photo.PHOTO_URI)); return Uri.parse(uriString); } catch (IllegalStateException e) { return null; } finally { c.close(); } } catch (Exception e) { Timber.e(e, "Couldn't fetch photo for contact with email %s", address); return null; } } /** * Return a {@link Cursor} instance that can be used to fetch information * about the contact with the given email address. * * @param address The email address to search for. * @return A {@link Cursor} instance that can be used to fetch information * about the contact with the given email address */ private Cursor getContactByAddress(final String address) { final Uri uri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI, Uri.encode(address)); final Cursor c = mContentResolver.query( uri, PROJECTION, null, null, SORT_ORDER); return c; } }