Java tutorial
// Taken from Android tutorials /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation * Copyright (c) 2011, Giant Leap Technologies AS */ /* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.phonegap; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.accounts.Account; import android.accounts.AccountManager; import android.app.Activity; import android.content.ContentProviderOperation; import android.content.ContentUris; import android.content.ContentValues; import android.content.OperationApplicationException; import android.database.Cursor; import android.net.Uri; import android.os.RemoteException; import android.provider.ContactsContract; import android.util.Log; import android.webkit.WebView; /** * An implementation of {@link ContactAccessor} that uses current Contacts API. * This class should be used on Eclair or beyond, but would not work on any earlier * release of Android. As a matter of fact, it could not even be loaded. * <p> * This implementation has several advantages: * <ul> * <li>It sees contacts from multiple accounts. * <li>It works with aggregated contacts. So for example, if the contact is the result * of aggregation of two raw contacts from different accounts, it may return the name from * one and the phone number from the other. * <li>It is efficient because it uses the more efficient current API. * <li>Not obvious in this particular example, but it has access to new kinds * of data available exclusively through the new APIs. Exercise for the reader: add support * for nickname (see {@link android.provider.ContactsContract.CommonDataKinds.Nickname}) or * social status updates (see {@link android.provider.ContactsContract.StatusUpdates}). * </ul> */ public class ContactAccessorSdk5 extends ContactAccessor { /** * Keep the photo size under the 1 MB blog limit. */ private static final long MAX_PHOTO_SIZE = 1048576; private static final String EMAIL_REGEXP = ".+@.+\\.+.+"; /* <anything>@<anything>.<anything>*/ /** * A static map that converts the JavaScript property name to Android database column name. */ private static final Map<String, String> dbMap = new HashMap<String, String>(); static { dbMap.put("id", ContactsContract.Contacts._ID); dbMap.put("displayName", ContactsContract.Contacts.DISPLAY_NAME); dbMap.put("name", ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME); dbMap.put("name.formatted", ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME); dbMap.put("name.familyName", ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME); dbMap.put("name.givenName", ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME); dbMap.put("name.middleName", ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME); dbMap.put("name.honorificPrefix", ContactsContract.CommonDataKinds.StructuredName.PREFIX); dbMap.put("name.honorificSuffix", ContactsContract.CommonDataKinds.StructuredName.SUFFIX); dbMap.put("nickname", ContactsContract.CommonDataKinds.Nickname.NAME); dbMap.put("phoneNumbers", ContactsContract.CommonDataKinds.Phone.NUMBER); dbMap.put("phoneNumbers.value", ContactsContract.CommonDataKinds.Phone.NUMBER); dbMap.put("emails", ContactsContract.CommonDataKinds.Email.DATA); dbMap.put("emails.value", ContactsContract.CommonDataKinds.Email.DATA); dbMap.put("addresses", ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS); dbMap.put("addresses.formatted", ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS); dbMap.put("addresses.streetAddress", ContactsContract.CommonDataKinds.StructuredPostal.STREET); dbMap.put("addresses.locality", ContactsContract.CommonDataKinds.StructuredPostal.CITY); dbMap.put("addresses.region", ContactsContract.CommonDataKinds.StructuredPostal.REGION); dbMap.put("addresses.postalCode", ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE); dbMap.put("addresses.country", ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY); dbMap.put("ims", ContactsContract.CommonDataKinds.Im.DATA); dbMap.put("ims.value", ContactsContract.CommonDataKinds.Im.DATA); dbMap.put("organizations", ContactsContract.CommonDataKinds.Organization.COMPANY); dbMap.put("organizations.name", ContactsContract.CommonDataKinds.Organization.COMPANY); dbMap.put("organizations.department", ContactsContract.CommonDataKinds.Organization.DEPARTMENT); dbMap.put("organizations.title", ContactsContract.CommonDataKinds.Organization.TITLE); //dbMap.put("revision", null); dbMap.put("birthday", ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE); dbMap.put("note", ContactsContract.CommonDataKinds.Note.NOTE); dbMap.put("photos.value", ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); //dbMap.put("categories.value", null); dbMap.put("urls", ContactsContract.CommonDataKinds.Website.URL); dbMap.put("urls.value", ContactsContract.CommonDataKinds.Website.URL); //dbMap.put("timezone", null); } /** * Create an contact accessor. */ public ContactAccessorSdk5(WebView view, Activity app) { mApp = app; mView = view; } /** * This method takes the fields required and search options in order to produce an * array of contacts that matches the criteria provided. * @param fields an array of items to be used as search criteria * @param options that can be applied to contact searching * @return an array of contacts */ @Override public JSONArray search(JSONArray fields, JSONObject options) { long totalEnd; long totalStart = System.currentTimeMillis(); // Get the find options String searchTerm = ""; int limit = Integer.MAX_VALUE; boolean multiple = true; if (options != null) { searchTerm = options.optString("filter"); if (searchTerm.length() == 0) { searchTerm = "%"; } else { searchTerm = "%" + searchTerm + "%"; } try { multiple = options.getBoolean("multiple"); if (!multiple) { limit = 1; } } catch (JSONException e) { // Multiple was not specified so we assume the default is true. } } else { searchTerm = "%"; } //Log.d(LOG_TAG, "Search Term = " + searchTerm); //Log.d(LOG_TAG, "Field Length = " + fields.length()); //Log.d(LOG_TAG, "Fields = " + fields.toString()); // Loop through the fields the user provided to see what data should be returned. HashMap<String, Boolean> populate = buildPopulationSet(fields); // Build the ugly where clause and where arguments for one big query. WhereOptions whereOptions = buildWhereClause(fields, searchTerm); // Get all the id's where the search term matches the fields passed in. Cursor idCursor = mApp.getContentResolver().query(ContactsContract.Data.CONTENT_URI, new String[] { ContactsContract.Data.CONTACT_ID }, whereOptions.getWhere(), whereOptions.getWhereArgs(), ContactsContract.Data.CONTACT_ID + " ASC"); // Create a set of unique ids //Log.d(LOG_TAG, "ID cursor query returns = " + idCursor.getCount()); Set<String> contactIds = new HashSet<String>(); while (idCursor.moveToNext()) { contactIds.add(idCursor.getString(idCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID))); } idCursor.close(); // Build a query that only looks at ids WhereOptions idOptions = buildIdClause(contactIds, searchTerm); // Do the id query Cursor c = mApp.getContentResolver().query(ContactsContract.Data.CONTENT_URI, null, idOptions.getWhere(), idOptions.getWhereArgs(), ContactsContract.Data.CONTACT_ID + " ASC"); //Log.d(LOG_TAG, "Cursor length = " + c.getCount()); String contactId = ""; String rawId = ""; String oldContactId = ""; boolean newContact = true; String mimetype = ""; JSONArray contacts = new JSONArray(); JSONObject contact = new JSONObject(); JSONArray organizations = new JSONArray(); JSONArray addresses = new JSONArray(); JSONArray phones = new JSONArray(); JSONArray emails = new JSONArray(); JSONArray ims = new JSONArray(); JSONArray websites = new JSONArray(); JSONArray photos = new JSONArray(); if (c.getCount() > 0) { while (c.moveToNext() && (contacts.length() <= (limit - 1))) { try { contactId = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID)); rawId = c.getString(c.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID)); // If we are in the first row set the oldContactId if (c.getPosition() == 0) { oldContactId = contactId; } // When the contact ID changes we need to push the Contact object // to the array of contacts and create new objects. if (!oldContactId.equals(contactId)) { // Populate the Contact object with it's arrays // and push the contact into the contacts array contacts.put(populateContact(contact, organizations, addresses, phones, emails, ims, websites, photos)); // Clean up the objects contact = new JSONObject(); organizations = new JSONArray(); addresses = new JSONArray(); phones = new JSONArray(); emails = new JSONArray(); ims = new JSONArray(); websites = new JSONArray(); photos = new JSONArray(); // Set newContact to true as we are starting to populate a new contact newContact = true; } // When we detect a new contact set the ID and display name. // These fields are available in every row in the result set returned. if (newContact) { newContact = false; contact.put("id", contactId); contact.put("rawId", rawId); contact.put("displayName", c.getString( c.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME))); } // Grab the mimetype of the current row as it will be used in a lot of comparisons mimetype = c.getString(c.getColumnIndex(ContactsContract.Data.MIMETYPE)); if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) && isRequired("name", populate)) { contact.put("name", nameQuery(c)); } else if (mimetype.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) && isRequired("phoneNumbers", populate)) { phones.put(phoneQuery(c)); } else if (mimetype.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) && isRequired("emails", populate)) { emails.put(emailQuery(c)); } else if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) && isRequired("addresses", populate)) { addresses.put(addressQuery(c)); } else if (mimetype.equals(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE) && isRequired("organizations", populate)) { organizations.put(organizationQuery(c)); } else if (mimetype.equals(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE) && isRequired("ims", populate)) { ims.put(imQuery(c)); } else if (mimetype.equals(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE) && isRequired("note", populate)) { contact.put("note", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Note.NOTE))); } else if (mimetype.equals(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE) && isRequired("nickname", populate)) { contact.put("nickname", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.NAME))); } else if (mimetype.equals(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE) && isRequired("urls", populate)) { websites.put(websiteQuery(c)); } else if (mimetype.equals(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)) { if (ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY == c .getInt(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE)) && isRequired("birthday", populate)) { contact.put("birthday", c.getString( c.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE))); } } else if (mimetype.equals(ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) && isRequired("photos", populate)) { photos.put(photoQuery(c, contactId)); } } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); } // Set the old contact ID oldContactId = contactId; } // Push the last contact into the contacts array if (contacts.length() < limit) { contacts.put( populateContact(contact, organizations, addresses, phones, emails, ims, websites, photos)); } } c.close(); totalEnd = System.currentTimeMillis(); Log.d(LOG_TAG, "Total time = " + (totalEnd - totalStart)); return contacts; } /** * Builds a where clause all all the ids passed into the method * @param contactIds a set of unique contact ids * @param searchTerm what to search for * @return an object containing the selection and selection args */ private WhereOptions buildIdClause(Set<String> contactIds, String searchTerm) { WhereOptions options = new WhereOptions(); // If the user is searching for every contact then short circuit the method // and return a shorter where clause to be searched. if (searchTerm.equals("%")) { options.setWhere("(" + ContactsContract.Data.CONTACT_ID + " LIKE ? )"); options.setWhereArgs(new String[] { searchTerm }); return options; } // This clause means that there are specific ID's to be populated Iterator<String> it = contactIds.iterator(); StringBuffer buffer = new StringBuffer("("); while (it.hasNext()) { buffer.append("'" + it.next() + "'"); if (it.hasNext()) { buffer.append(","); } } buffer.append(")"); options.setWhere(ContactsContract.Data.CONTACT_ID + " IN " + buffer.toString()); options.setWhereArgs(null); return options; } /** * Create a new contact using a JSONObject to hold all the data. * @param contact * @param organizations array of organizations * @param addresses array of addresses * @param phones array of phones * @param emails array of emails * @param ims array of instant messenger addresses * @param websites array of websites * @param photos * @return */ private JSONObject populateContact(JSONObject contact, JSONArray organizations, JSONArray addresses, JSONArray phones, JSONArray emails, JSONArray ims, JSONArray websites, JSONArray photos) { try { contact.put("organizations", organizations); contact.put("addresses", addresses); contact.put("phoneNumbers", phones); contact.put("emails", emails); contact.put("ims", ims); contact.put("websites", websites); contact.put("photos", photos); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); } return contact; } /** * Take the search criteria passed into the method and create a SQL WHERE clause. * @param fields the properties to search against * @param searchTerm the string to search for * @return an object containing the selection and selection args */ private WhereOptions buildWhereClause(JSONArray fields, String searchTerm) { ArrayList<String> where = new ArrayList<String>(); ArrayList<String> whereArgs = new ArrayList<String>(); WhereOptions options = new WhereOptions(); /* * Special case for when the user wants all the contacts */ if ("%".equals(searchTerm)) { options.setWhere("(" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? )"); options.setWhereArgs(new String[] { searchTerm }); return options; } String key; try { //Log.d(LOG_TAG, "How many fields do we have = " + fields.length()); for (int i = 0; i < fields.length(); i++) { key = fields.getString(i); if (key.startsWith("displayName")) { where.add("(" + dbMap.get(key) + " LIKE ? )"); whereArgs.add(searchTerm); } else if (key.startsWith("name")) { where.add("(" + dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); } else if (key.startsWith("nickname")) { where.add("(" + dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE); } else if (key.startsWith("phoneNumbers")) { where.add("(" + dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); } else if (key.startsWith("emails")) { where.add("(" + dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE); } else if (key.startsWith("addresses")) { where.add("(" + dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE); } else if (key.startsWith("ims")) { where.add("(" + dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE); } else if (key.startsWith("organizations")) { where.add("(" + dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE); } // else if (key.startsWith("birthday")) { // where.add("(" + dbMap.get(key) + " LIKE ? AND " // + ContactsContract.Data.MIMETYPE + " = ? )"); // } else if (key.startsWith("note")) { where.add("(" + dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE); } else if (key.startsWith("urls")) { where.add("(" + dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE); } } } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); } // Creating the where string StringBuffer selection = new StringBuffer(); for (int i = 0; i < where.size(); i++) { selection.append(where.get(i)); if (i != (where.size() - 1)) { selection.append(" OR "); } } options.setWhere(selection.toString()); // Creating the where args array String[] selectionArgs = new String[whereArgs.size()]; for (int i = 0; i < whereArgs.size(); i++) { selectionArgs[i] = whereArgs.get(i); } options.setWhereArgs(selectionArgs); return options; } /** * Create a ContactOrganization JSONObject * @param cursor the current database row * @return a JSONObject representing a ContactOrganization */ private JSONObject organizationQuery(Cursor cursor) { JSONObject organization = new JSONObject(); try { organization.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization._ID))); organization.put("department", cursor .getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.DEPARTMENT))); organization.put("name", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.COMPANY))); organization.put("title", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.TITLE))); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); } return organization; } /** * Create a ContactAddress JSONObject * @param cursor the current database row * @return a JSONObject representing a ContactAddress */ private JSONObject addressQuery(Cursor cursor) { JSONObject address = new JSONObject(); try { address.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal._ID))); address.put("formatted", cursor.getString( cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS))); address.put("streetAddress", cursor .getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.STREET))); address.put("locality", cursor .getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.CITY))); address.put("region", cursor .getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.REGION))); address.put("postalCode", cursor .getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE))); address.put("country", cursor .getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY))); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); } return address; } /** * Create a ContactName JSONObject * @param cursor the current database row * @return a JSONObject representing a ContactName */ private JSONObject nameQuery(Cursor cursor) { JSONObject contactName = new JSONObject(); try { String familyName = cursor .getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME)); String givenName = cursor .getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME)); String middleName = cursor .getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME)); String honorificPrefix = cursor .getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.PREFIX)); String honorificSuffix = cursor .getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.SUFFIX)); // Create the formatted name StringBuffer formatted = new StringBuffer(""); if (honorificPrefix != null) { formatted.append(honorificPrefix + " "); } if (givenName != null) { formatted.append(givenName + " "); } if (middleName != null) { formatted.append(middleName + " "); } if (familyName != null) { formatted.append(familyName + " "); } if (honorificSuffix != null) { formatted.append(honorificSuffix + " "); } contactName.put("familyName", familyName); contactName.put("givenName", givenName); contactName.put("middleName", middleName); contactName.put("honorificPrefix", honorificPrefix); contactName.put("honorificSuffix", honorificSuffix); contactName.put("formatted", formatted); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); } return contactName; } /** * Create a ContactField JSONObject * @param cursor the current database row * @return a JSONObject representing a ContactField */ private JSONObject phoneQuery(Cursor cursor) { JSONObject phoneNumber = new JSONObject(); try { phoneNumber.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID))); phoneNumber.put("pref", false); // Android does not store pref attribute phoneNumber.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))); phoneNumber.put("type", getPhoneType( cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE)))); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); } catch (Exception excp) { Log.e(LOG_TAG, excp.getMessage(), excp); } return phoneNumber; } /** * Create a ContactField JSONObject * @param cursor the current database row * @return a JSONObject representing a ContactField */ private JSONObject emailQuery(Cursor cursor) { JSONObject email = new JSONObject(); try { email.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email._ID))); email.put("pref", false); // Android does not store pref attribute email.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA))); email.put("type", getContactType( cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE)))); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); } return email; } /** * Create a ContactField JSONObject * @param cursor the current database row * @return a JSONObject representing a ContactField */ private JSONObject imQuery(Cursor cursor) { JSONObject im = new JSONObject(); try { im.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im._ID))); im.put("pref", false); // Android does not store pref attribute im.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA))); im.put("type", getContactType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im.TYPE)))); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); } return im; } /** * Create a ContactField JSONObject * @param cursor the current database row * @return a JSONObject representing a ContactField */ private JSONObject websiteQuery(Cursor cursor) { JSONObject website = new JSONObject(); try { website.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Website._ID))); website.put("pref", false); // Android does not store pref attribute website.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Website.URL))); website.put("type", getContactType( cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Website.TYPE)))); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); } return website; } /** * Create a ContactField JSONObject * @param contactId * @return a JSONObject representing a ContactField */ private JSONObject photoQuery(Cursor cursor, String contactId) { JSONObject photo = new JSONObject(); try { photo.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Photo._ID))); photo.put("pref", false); photo.put("type", "url"); Uri person = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, (new Long(contactId))); Uri photoUri = Uri.withAppendedPath(person, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY); photo.put("value", photoUri.toString()); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); } return photo; } @Override /** * This method will save a contact object into the devices contacts database. * * @param contact the contact to be saved. * @returns true if the contact is successfully saved, false otherwise. */ public boolean save(JSONObject contact) { AccountManager mgr = AccountManager.get(mApp); Account[] accounts = mgr.getAccounts(); Account account = null; if (accounts.length == 1) account = accounts[0]; else if (accounts.length > 1) { for (Account a : accounts) { if (a.type.contains("eas") && a.name.matches(EMAIL_REGEXP)) /*Exchange ActiveSync*/ { account = a; break; } } if (account == null) { for (Account a : accounts) { if (a.type.contains("com.google") && a.name.matches(EMAIL_REGEXP)) /*Google sync provider*/ { account = a; break; } } } if (account == null) { for (Account a : accounts) { if (a.name.matches(EMAIL_REGEXP)) /*Last resort, just look for an email address...*/ { account = a; break; } } } } if (account == null) return false; String id = getJsonString(contact, "id"); // Create new contact if (id == null) { return createNewContact(contact, account); } // Modify existing contact else { return modifyContact(id, contact, account); } } /** * Creates a new contact and stores it in the database * * @param id the raw contact id which is required for linking items to the contact * @param contact the contact to be saved * @param account the account to be saved under */ private boolean modifyContact(String id, JSONObject contact, Account account) { // Get the RAW_CONTACT_ID which is needed to insert new values in an already existing contact. // But not needed to update existing values. int rawId = (new Integer(getJsonString(contact, "rawId"))).intValue(); // Create a list of attributes to add to the contact database ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); //Add contact type ops.add(ContentProviderOperation.newUpdate(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, account.name).build()); // Modify name JSONObject name; try { String displayName = getJsonString(contact, "displayName"); name = contact.getJSONObject("name"); if (displayName != null || name != null) { ContentProviderOperation.Builder builder = ContentProviderOperation .newUpdate(ContactsContract.Data.CONTENT_URI).withSelection( ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", new String[] { id, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE }); if (displayName != null) { builder.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName); } String familyName = getJsonString(name, "familyName"); if (familyName != null) { builder.withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, familyName); } String middleName = getJsonString(name, "middleName"); if (middleName != null) { builder.withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, middleName); } String givenName = getJsonString(name, "givenName"); if (givenName != null) { builder.withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, givenName); } String honorificPrefix = getJsonString(name, "honorificPrefix"); if (honorificPrefix != null) { builder.withValue(ContactsContract.CommonDataKinds.StructuredName.PREFIX, honorificPrefix); } String honorificSuffix = getJsonString(name, "honorificSuffix"); if (honorificSuffix != null) { builder.withValue(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, honorificSuffix); } ops.add(builder.build()); } } catch (JSONException e1) { Log.d(LOG_TAG, "Could not get name"); } // Modify phone numbers JSONArray phones = null; try { phones = contact.getJSONArray("phoneNumbers"); if (phones != null) { for (int i = 0; i < phones.length(); i++) { JSONObject phone = (JSONObject) phones.get(i); String phoneId = getJsonString(phone, "id"); // This is a new phone so do a DB insert if (phoneId == null) { ContentValues contentValues = new ContentValues(); contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); contentValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value")); contentValues.put(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type"))); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValues(contentValues).build()); } // This is an existing phone so do a DB update else { ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI).withSelection( ContactsContract.CommonDataKinds.Phone._ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", new String[] { phoneId, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE }) .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value")) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type"))) .build()); } } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get phone numbers"); } // Modify emails JSONArray emails = null; try { emails = contact.getJSONArray("emails"); if (emails != null) { for (int i = 0; i < emails.length(); i++) { JSONObject email = (JSONObject) emails.get(i); String emailId = getJsonString(email, "id"); // This is a new email so do a DB insert if (emailId == null) { ContentValues contentValues = new ContentValues(); contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE); contentValues.put(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value")); contentValues.put(ContactsContract.CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type"))); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValues(contentValues).build()); } // This is an existing email so do a DB update else { ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI).withSelection( ContactsContract.CommonDataKinds.Email._ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", new String[] { emailId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE }) .withValue(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value")) .withValue(ContactsContract.CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type"))) .build()); } } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get emails"); } // Modify addresses JSONArray addresses = null; try { addresses = contact.getJSONArray("addresses"); if (addresses != null) { for (int i = 0; i < addresses.length(); i++) { JSONObject address = (JSONObject) addresses.get(i); String addressId = getJsonString(address, "id"); // This is a new address so do a DB insert if (addressId == null) { ContentValues contentValues = new ContentValues(); contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE); contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted")); contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress")); contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality")); contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region")); contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode")); contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country")); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValues(contentValues).build()); } // This is an existing address so do a DB update else { ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI).withSelection( ContactsContract.CommonDataKinds.StructuredPostal._ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", new String[] { addressId, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE }) .withValue(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted")) .withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress")) .withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality")) .withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region")) .withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode")) .withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country")) .build()); } } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get addresses"); } // Modify organizations JSONArray organizations = null; try { organizations = contact.getJSONArray("organizations"); if (organizations != null) { for (int i = 0; i < organizations.length(); i++) { JSONObject org = (JSONObject) organizations.get(i); String orgId = getJsonString(org, "id"); ; // This is a new organization so do a DB insert if (orgId == null) { ContentValues contentValues = new ContentValues(); contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE); contentValues.put(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department")); contentValues.put(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name")); contentValues.put(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title")); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValues(contentValues).build()); } // This is an existing organization so do a DB update else { ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection( ContactsContract.CommonDataKinds.Organization._ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", new String[] { orgId, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE }) .withValue(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department")) .withValue(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name")) .withValue(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title")) .build()); } } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get organizations"); } // Modify IMs JSONArray ims = null; try { ims = contact.getJSONArray("ims"); if (ims != null) { for (int i = 0; i < ims.length(); i++) { JSONObject im = (JSONObject) ims.get(i); String imId = getJsonString(im, "id"); ; // This is a new IM so do a DB insert if (imId == null) { ContentValues contentValues = new ContentValues(); contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE); contentValues.put(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value")); contentValues.put(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type"))); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValues(contentValues).build()); } // This is an existing IM so do a DB update else { ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI).withSelection( ContactsContract.CommonDataKinds.Im._ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", new String[] { imId, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE }) .withValue(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value")) .withValue(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type"))) .build()); } } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get emails"); } // Modify note String note = getJsonString(contact, "note"); ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", new String[] { id, ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE }) .withValue(ContactsContract.CommonDataKinds.Note.NOTE, note).build()); // Modify nickname String nickname = getJsonString(contact, "nickname"); if (nickname != null) { ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection( ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", new String[] { id, ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE }) .withValue(ContactsContract.CommonDataKinds.Nickname.NAME, nickname).build()); } // Modify urls JSONArray websites = null; try { websites = contact.getJSONArray("websites"); if (websites != null) { for (int i = 0; i < websites.length(); i++) { JSONObject website = (JSONObject) websites.get(i); String websiteId = getJsonString(website, "id"); ; // This is a new website so do a DB insert if (websiteId == null) { ContentValues contentValues = new ContentValues(); contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE); contentValues.put(ContactsContract.CommonDataKinds.Website.DATA, getJsonString(website, "value")); contentValues.put(ContactsContract.CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type"))); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValues(contentValues).build()); } // This is an existing website so do a DB update else { ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection( ContactsContract.CommonDataKinds.Website._ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", new String[] { websiteId, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE }) .withValue(ContactsContract.CommonDataKinds.Website.DATA, getJsonString(website, "value")) .withValue(ContactsContract.CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type"))) .build()); } } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get websites"); } // Modify birthday String birthday = getJsonString(contact, "birthday"); if (birthday != null) { ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withSelection( ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.CommonDataKinds.Event.TYPE + "=?", new String[] { id, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, new String("" + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY) }) .withValue(ContactsContract.CommonDataKinds.Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY) .withValue(ContactsContract.CommonDataKinds.Event.START_DATE, birthday).build()); } // Modify photos JSONArray photos = null; try { photos = contact.getJSONArray("photos"); if (photos != null) { for (int i = 0; i < photos.length(); i++) { JSONObject photo = (JSONObject) photos.get(i); String photoId = getJsonString(photo, "id"); byte[] bytes = getPhotoBytes(getJsonString(photo, "value")); // This is a new photo so do a DB insert if (photoId == null) { ContentValues contentValues = new ContentValues(); contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); contentValues.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1); contentValues.put(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValues(contentValues).build()); } // This is an existing photo so do a DB update else { ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI).withSelection( ContactsContract.CommonDataKinds.Photo._ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", new String[] { photoId, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE }) .withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1) .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes).build()); } } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get photos"); } boolean retVal = true; //Modify contact try { mApp.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); } catch (RemoteException e) { Log.e(LOG_TAG, e.getMessage(), e); Log.e(LOG_TAG, Log.getStackTraceString(e), e); retVal = false; } catch (OperationApplicationException e) { Log.e(LOG_TAG, e.getMessage(), e); Log.e(LOG_TAG, Log.getStackTraceString(e), e); retVal = false; } return retVal; } /** * Add a website to a list of database actions to be performed * * @param ops the list of database actions * @param website the item to be inserted */ private void insertWebsite(ArrayList<ContentProviderOperation> ops, JSONObject website) { ops.add(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.DATA, getJsonString(website, "value")) .withValue(ContactsContract.CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type"))) .build()); } /** * Add an im to a list of database actions to be performed * * @param ops the list of database actions * @param im the item to be inserted */ private void insertIm(ArrayList<ContentProviderOperation> ops, JSONObject im) { ops.add(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, getJsonString(im, "value")) .withValue(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type"))) .build()); } /** * Add an organization to a list of database actions to be performed * * @param ops the list of database actions * @param org the item to be inserted */ private void insertOrganization(ArrayList<ContentProviderOperation> ops, JSONObject org) { ops.add(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.DEPARTMENT, getJsonString(org, "department")) .withValue(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name")) .withValue(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title")) .build()); } /** * Add an address to a list of database actions to be performed * * @param ops the list of database actions * @param address the item to be inserted */ private void insertAddress(ArrayList<ContentProviderOperation> ops, JSONObject address) { ops.add(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.FORMATTED_ADDRESS, getJsonString(address, "formatted")) .withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress")) .withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality")) .withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region")) .withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode")) .withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country")) .build()); } /** * Add an email to a list of database actions to be performed * * @param ops the list of database actions * @param email the item to be inserted */ private void insertEmail(ArrayList<ContentProviderOperation> ops, JSONObject email) { ops.add(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.DATA, getJsonString(email, "value")) .withValue(ContactsContract.CommonDataKinds.Email.TYPE, getPhoneType(getJsonString(email, "type"))) .build()); } /** * Add a phone to a list of database actions to be performed * * @param ops the list of database actions * @param phone the item to be inserted */ private void insertPhone(ArrayList<ContentProviderOperation> ops, JSONObject phone) { ops.add(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, getJsonString(phone, "value")) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type"))) .build()); } /** * Add a phone to a list of database actions to be performed * * @param ops the list of database actions * @param phone the item to be inserted */ private void insertPhoto(ArrayList<ContentProviderOperation> ops, JSONObject photo) { byte[] bytes = getPhotoBytes(getJsonString(photo, "value")); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes).build()); } /** * Gets the raw bytes from the supplied filename * * @param filename the file to read the bytes from * @return a byte array * @throws IOException */ private byte[] getPhotoBytes(String filename) { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); try { int bytesRead = 0; long totalBytesRead = 0; byte[] data = new byte[8192]; InputStream in = getPathFromUri(filename); while ((bytesRead = in.read(data, 0, data.length)) != -1 && totalBytesRead <= MAX_PHOTO_SIZE) { buffer.write(data, 0, bytesRead); totalBytesRead += bytesRead; } in.close(); buffer.flush(); } catch (FileNotFoundException e) { Log.e(LOG_TAG, e.getMessage(), e); } catch (IOException e) { Log.e(LOG_TAG, e.getMessage(), e); } return buffer.toByteArray(); } /** * Get an input stream based on file path or uri content://, http://, file:// * * @param path * @return an input stream * @throws IOException */ private InputStream getPathFromUri(String path) throws IOException { if (path.startsWith("content:")) { Uri uri = Uri.parse(path); return mApp.getContentResolver().openInputStream(uri); } if (path.startsWith("http:") || path.startsWith("file:")) { URL url = new URL(path); return url.openStream(); } else { return new FileInputStream(path); } } /** * Creates a new contact and stores it in the database * * @param contact the contact to be saved * @param account the account to be saved under */ private boolean createNewContact(JSONObject contact, Account account) { // Create a list of attributes to add to the contact database ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); //Add contact type ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, account.name).build()); // Add name try { JSONObject name = contact.optJSONObject("name"); String displayName = contact.getString("displayName"); if (displayName != null || name != null) { ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) .withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, getJsonString(name, "familyName")) .withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, getJsonString(name, "middleName")) .withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, getJsonString(name, "givenName")) .withValue(ContactsContract.CommonDataKinds.StructuredName.PREFIX, getJsonString(name, "honorificPrefix")) .withValue(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, getJsonString(name, "honorificSuffix")) .build()); } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get name object"); } //Add phone numbers JSONArray phones = null; try { phones = contact.getJSONArray("phoneNumbers"); if (phones != null) { for (int i = 0; i < phones.length(); i++) { JSONObject phone = (JSONObject) phones.get(i); insertPhone(ops, phone); } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get phone numbers"); } // Add emails JSONArray emails = null; try { emails = contact.getJSONArray("emails"); if (emails != null) { for (int i = 0; i < emails.length(); i++) { JSONObject email = (JSONObject) emails.get(i); insertEmail(ops, email); } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get emails"); } // Add addresses JSONArray addresses = null; try { addresses = contact.getJSONArray("addresses"); if (addresses != null) { for (int i = 0; i < addresses.length(); i++) { JSONObject address = (JSONObject) addresses.get(i); insertAddress(ops, address); } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get addresses"); } // Add organizations JSONArray organizations = null; try { organizations = contact.getJSONArray("organizations"); if (organizations != null) { for (int i = 0; i < organizations.length(); i++) { JSONObject org = (JSONObject) organizations.get(i); insertOrganization(ops, org); } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get organizations"); } // Add IMs JSONArray ims = null; try { ims = contact.getJSONArray("ims"); if (ims != null) { for (int i = 0; i < ims.length(); i++) { JSONObject im = (JSONObject) ims.get(i); insertIm(ops, im); } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get emails"); } // Add note String note = getJsonString(contact, "note"); if (note != null) { ops.add(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).build()); } // Add nickname String nickname = getJsonString(contact, "nickname"); if (nickname != null) { ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Nickname.NAME, nickname).build()); } // Add urls JSONArray websites = null; try { websites = contact.getJSONArray("websites"); if (websites != null) { for (int i = 0; i < websites.length(); i++) { JSONObject website = (JSONObject) websites.get(i); insertWebsite(ops, website); } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get websites"); } // Add birthday String birthday = getJsonString(contact, "birthday"); if (birthday != null) { ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY) .withValue(ContactsContract.CommonDataKinds.Event.START_DATE, birthday).build()); } // Add photos JSONArray photos = null; try { photos = contact.getJSONArray("photos"); if (photos != null) { for (int i = 0; i < photos.length(); i++) { JSONObject photo = (JSONObject) photos.get(i); insertPhoto(ops, photo); } } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get photos"); } boolean retVal = true; //Add contact try { mApp.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); } catch (RemoteException e) { Log.e(LOG_TAG, e.getMessage(), e); retVal = false; } catch (OperationApplicationException e) { Log.e(LOG_TAG, e.getMessage(), e); retVal = false; } return retVal; } @Override /** * This method will remove a Contact from the database based on ID. * @param id the unique ID of the contact to remove */ public boolean remove(String id) { int result = mApp.getContentResolver().delete(ContactsContract.Data.CONTENT_URI, ContactsContract.Data.CONTACT_ID + " = ?", new String[] { id }); return (result > 0) ? true : false; } /************************************************************************** * * All methods below this comment are used to convert from JavaScript * text types to Android integer types and vice versa. * *************************************************************************/ /** * Converts a string from the W3C Contact API to it's Android int value. * @param string * @return Android int value */ private int getPhoneType(String string) { int type = ContactsContract.CommonDataKinds.Phone.TYPE_OTHER; if ("home".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_HOME; } else if ("mobile".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE; } else if ("work".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_WORK; } else if ("work fax".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK; } else if ("home fax".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME; } else if ("fax".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK; } else if ("pager".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_PAGER; } else if ("other".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_OTHER; } else if ("car".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_CAR; } else if ("company main".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_COMPANY_MAIN; } else if ("isdn".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_ISDN; } else if ("main".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_MAIN; } else if ("other fax".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_OTHER_FAX; } else if ("radio".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_RADIO; } else if ("telex".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_TELEX; } else if ("work mobile".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE; } else if ("work pager".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER; } else if ("assistant".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_ASSISTANT; } else if ("mms".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_MMS; } else if ("callback".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_CALLBACK; } else if ("tty ttd".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_TTY_TDD; } else if ("custom".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM; } return type; } /** * getPhoneType converts an Android phone type into a string * @param type * @return phone type as string. */ private String getPhoneType(int type) { String stringType; switch (type) { case ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM: stringType = "custom"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME: stringType = "home fax"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK: stringType = "work fax"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_HOME: stringType = "home"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE: stringType = "mobile"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_PAGER: stringType = "pager"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_WORK: stringType = "work"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_CALLBACK: stringType = "callback"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_CAR: stringType = "car"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_COMPANY_MAIN: stringType = "company main"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_OTHER_FAX: stringType = "other fax"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_RADIO: stringType = "radio"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_TELEX: stringType = "telex"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_TTY_TDD: stringType = "tty tdd"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE: stringType = "work mobile"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER: stringType = "work pager"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_ASSISTANT: stringType = "assistant"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_MMS: stringType = "mms"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_ISDN: stringType = "isdn"; break; case ContactsContract.CommonDataKinds.Phone.TYPE_OTHER: default: stringType = "other"; break; } return stringType; } /** * Converts a string from the W3C Contact API to it's Android int value. * @param string * @return Android int value */ private int getContactType(String string) { int type = ContactsContract.CommonDataKinds.Email.TYPE_OTHER; if (string != null) { if ("home".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Email.TYPE_HOME; } else if ("work".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Email.TYPE_WORK; } else if ("other".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Email.TYPE_OTHER; } else if ("mobile".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Email.TYPE_MOBILE; } else if ("custom".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM; } } return type; } /** * getPhoneType converts an Android phone type into a string * @param type * @return phone type as string. */ private String getContactType(int type) { String stringType; switch (type) { case ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM: stringType = "custom"; break; case ContactsContract.CommonDataKinds.Email.TYPE_HOME: stringType = "home"; break; case ContactsContract.CommonDataKinds.Email.TYPE_WORK: stringType = "work"; break; case ContactsContract.CommonDataKinds.Email.TYPE_MOBILE: stringType = "mobile"; break; case ContactsContract.CommonDataKinds.Email.TYPE_OTHER: default: stringType = "other"; break; } return stringType; } }