deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/ContactService.java
+++ /dev/null
@@ -1,2015 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map.Entry;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.util.GeckoEventListener;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.DialogInterface;
-import android.content.OperationApplicationException;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Event;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Nickname;
-import android.provider.ContactsContract.CommonDataKinds.Note;
-import android.provider.ContactsContract.CommonDataKinds.Organization;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.RawContacts.Entity;
-import android.telephony.PhoneNumberUtils;
-import android.util.Log;
-
-public class ContactService implements GeckoEventListener {
- private static final String LOGTAG = "GeckoContactService";
- private static final boolean DEBUG = false;
-
- private final static int GROUP_ACCOUNT_NAME = 0;
- private final static int GROUP_ACCOUNT_TYPE = 1;
- private final static int GROUP_ID = 2;
- private final static int GROUP_TITLE = 3;
- private final static int GROUP_AUTO_ADD = 4;
-
- private final static String CARRIER_COLUMN = Data.DATA5;
- private final static String CUSTOM_DATA_COLUMN = Data.DATA1;
-
- // Pre-Honeycomb versions of Android have a "My Contacts" system group that all contacts are
- // assigned to by default for a given account. After Honeycomb, an AUTO_ADD database column
- // was added to denote groups that contacts are automatically added to
- private final static String PRE_HONEYCOMB_DEFAULT_GROUP = "System Group: My Contacts";
- private final static String MIMETYPE_ADDITIONAL_NAME = "org.mozilla.gecko/additional_name";
- private final static String MIMETYPE_SEX = "org.mozilla.gecko/sex";
- private final static String MIMETYPE_GENDER_IDENTITY = "org.mozilla.gecko/gender_identity";
- private final static String MIMETYPE_KEY = "org.mozilla.gecko/key";
- private final static String MIMETYPE_MOZILLA_CONTACTS_FLAG = "org.mozilla.gecko/contact_flag";
-
- private final EventDispatcher mEventDispatcher;
-
- private String mAccountName;
- private String mAccountType;
- private String mGroupTitle;
- private long mGroupId;
- private boolean mGotDeviceAccount;
-
- private HashMap<String, String> mColumnNameConstantsMap;
- private HashMap<String, String> mMimeTypeConstantsMap;
- private HashMap<String, Integer> mAddressTypesMap;
- private HashMap<String, Integer> mPhoneTypesMap;
- private HashMap<String, Integer> mEmailTypesMap;
- private HashMap<String, Integer> mWebsiteTypesMap;
- private HashMap<String, Integer> mImTypesMap;
-
- private final ContentResolver mContentResolver;
- private final Activity mActivity;
-
- public ContactService(EventDispatcher eventDispatcher, Activity activity) {
- mEventDispatcher = eventDispatcher;
- mActivity = activity;
- mContentResolver = mActivity.getContentResolver();
-
- EventDispatcher.getInstance().registerGeckoThreadListener(this,
- "Android:Contacts:Clear",
- "Android:Contacts:Find",
- "Android:Contacts:GetAll",
- "Android:Contacts:GetCount",
- "Android:Contact:Remove",
- "Android:Contact:Save");
- }
-
- public void destroy() {
- EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
- "Android:Contacts:Clear",
- "Android:Contacts:Find",
- "Android:Contacts:GetAll",
- "Android:Contacts:GetCount",
- "Android:Contact:Remove",
- "Android:Contact:Save");
- }
-
- @Override
- public void handleMessage(final String event, final JSONObject message) {
- // If the account chooser dialog needs shown to the user, the message handling becomes
- // asynchronous so it needs posted to a background thread from the UI thread when the
- // account chooser dialog is dismissed by the user.
- Runnable handleMessage = new Runnable() {
- @Override
- public void run() {
- try {
- if (DEBUG) {
- Log.d(LOGTAG, "Event: " + event + "\nMessage: " + message.toString(3));
- }
-
- final JSONObject messageData = message.getJSONObject("data");
- final String requestID = messageData.getString("requestID");
-
- // Options may not exist for all operations
- JSONObject contactOptions = messageData.optJSONObject("options");
-
- if ("Android:Contacts:Find".equals(event)) {
- findContacts(contactOptions, requestID);
- } else if ("Android:Contacts:GetAll".equals(event)) {
- getAllContacts(messageData, requestID);
- } else if ("Android:Contacts:Clear".equals(event)) {
- clearAllContacts(contactOptions, requestID);
- } else if ("Android:Contact:Save".equals(event)) {
- saveContact(contactOptions, requestID);
- } else if ("Android:Contact:Remove".equals(event)) {
- removeContact(contactOptions, requestID);
- } else if ("Android:Contacts:GetCount".equals(event)) {
- getContactsCount(requestID);
- } else {
- throw new IllegalArgumentException("Unexpected event: " + event);
- }
- } catch (JSONException e) {
- throw new IllegalArgumentException("Message: " + e);
- }
- }
- };
-
- // Get the account name/type if they haven't been set yet
- if (!mGotDeviceAccount) {
- getDeviceAccount(handleMessage);
- } else {
- handleMessage.run();
- }
- }
-
- private void findContacts(final JSONObject contactOptions, final String requestID) {
- long[] rawContactIds = findContactsRawIds(contactOptions);
- Log.i(LOGTAG, "Got " + (rawContactIds != null ? rawContactIds.length : "null") + " raw contact IDs");
-
- final String[] sortOptions = getSortOptionsFromJSON(contactOptions);
-
- if (rawContactIds == null || sortOptions == null) {
- sendCallbackToJavascript("Android:Contacts:Find:Return:KO", requestID, null, null);
- } else {
- sendCallbackToJavascript("Android:Contacts:Find:Return:OK", requestID,
- new String[] {"contacts"},
- new Object[] {getContactsAsJSONArray(rawContactIds, sortOptions[0],
- sortOptions[1])});
- }
- }
-
- private void getAllContacts(final JSONObject contactOptions, final String requestID) {
- long[] rawContactIds = getAllRawContactIds();
- Log.i(LOGTAG, "Got " + rawContactIds.length + " raw contact IDs");
-
- final String[] sortOptions = getSortOptionsFromJSON(contactOptions);
-
- if (rawContactIds == null || sortOptions == null) {
- // There's no failure message for getAll
- return;
- } else {
- sendCallbackToJavascript("Android:Contacts:GetAll:Next", requestID,
- new String[] {"contacts"},
- new Object[] {getContactsAsJSONArray(rawContactIds, sortOptions[0],
- sortOptions[1])});
- }
- }
-
- private static String[] getSortOptionsFromJSON(final JSONObject contactOptions) {
- String sortBy = null;
- String sortOrder = null;
-
- try {
- final JSONObject findOptions = contactOptions.getJSONObject("findOptions");
- sortBy = findOptions.optString("sortBy").toLowerCase();
- sortOrder = findOptions.optString("sortOrder").toLowerCase();
-
- if ("".equals(sortBy)) {
- sortBy = null;
- }
- if ("".equals(sortOrder)) {
- sortOrder = "ascending";
- }
-
- // Only "familyname" and "givenname" are valid sortBy values and only "ascending"
- // and "descending" are valid sortOrder values
- if ((sortBy != null && !"familyname".equals(sortBy) && !"givenname".equals(sortBy)) ||
- (!"ascending".equals(sortOrder) && !"descending".equals(sortOrder))) {
- return null;
- }
- } catch (JSONException e) {
- throw new IllegalArgumentException(e);
- }
-
- return new String[] {sortBy, sortOrder};
- }
-
- private long[] findContactsRawIds(final JSONObject contactOptions) {
- List<Long> rawContactIds = new ArrayList<Long>();
- Cursor cursor = null;
-
- try {
- final JSONObject findOptions = contactOptions.getJSONObject("findOptions");
- String filterValue = findOptions.optString("filterValue");
- JSONArray filterBy = findOptions.optJSONArray("filterBy");
- final String filterOp = findOptions.optString("filterOp");
- final int filterLimit = findOptions.getInt("filterLimit");
- final int substringMatching = findOptions.getInt("substringMatching");
-
- // If filter value is undefined, avoid all the logic below and just return
- // all available raw contact IDs
- if ("".equals(filterValue) || "".equals(filterOp)) {
- long[] allRawContactIds = getAllRawContactIds();
-
- // Truncate the raw contacts IDs array if necessary
- if (filterLimit > 0 && allRawContactIds.length > filterLimit) {
- long[] truncatedRawContactIds = new long[filterLimit];
- System.arraycopy(allRawContactIds, 0, truncatedRawContactIds, 0, filterLimit);
- return truncatedRawContactIds;
- }
- return allRawContactIds;
- }
-
- // "match" can only be used with the "tel" field
- if ("match".equals(filterOp)) {
- for (int i = 0; i < filterBy.length(); i++) {
- if (!"tel".equals(filterBy.getString(i))) {
- Log.w(LOGTAG, "\"match\" filterBy option is only valid for the \"tel\" field");
- return null;
- }
- }
- }
-
- // Only select contacts from the selected account
- String selection = null;
- String[] selectionArgs = null;
-
- if (mAccountName != null) {
- selection = RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?";
- selectionArgs = new String[] {mAccountName, mAccountType};
- }
-
-
- final String[] columnsToGet;
-
- // If a filterBy value was not specified, search all columns
- if (filterBy == null || filterBy.length() == 0) {
- columnsToGet = null;
- } else {
- // Only get the columns given in the filterBy array
- List<String> columnsToGetList = new ArrayList<String>();
-
- columnsToGetList.add(Data.RAW_CONTACT_ID);
- columnsToGetList.add(Data.MIMETYPE);
- for (int i = 0; i < filterBy.length(); i++) {
- final String field = filterBy.getString(i);
-
- // If one of the filterBy fields is the ID, just return the filter value
- // which should be the ID
- if ("id".equals(field)) {
- try {
- return new long[] {Long.valueOf(filterValue)};
- } catch (NumberFormatException e) {
- // If the ID couldn't be converted to a long, it's invalid data
- // so return null for failure
- return null;
- }
- }
-
- final String columnName = getColumnNameConstant(field);
-
- if (columnName != null) {
- columnsToGetList.add(columnName);
- } else {
- Log.w(LOGTAG, "Unknown filter option: " + field);
- }
- }
-
- columnsToGet = columnsToGetList.toArray(new String[columnsToGetList.size()]);
- }
-
- // Execute the query
- cursor = mContentResolver.query(Data.CONTENT_URI, columnsToGet, selection,
- selectionArgs, null);
-
- if (cursor.getCount() > 0) {
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- String mimeType = cursor.getString(cursor.getColumnIndex(Data.MIMETYPE));
-
- // Check if the current mimetype is one of the types to filter by
- if (filterBy != null && filterBy.length() > 0) {
- for (int i = 0; i < filterBy.length(); i++) {
- String currentFilterBy = filterBy.getString(i);
-
- if (mimeType.equals(getMimeTypeOfField(currentFilterBy))) {
- String columnName = getColumnNameConstant(currentFilterBy);
- int columnIndex = cursor.getColumnIndex(columnName);
- String databaseValue = cursor.getString(columnIndex);
-
- boolean isPhone = false;
- if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
- isPhone = true;
- } else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
- // Translate the group ID to the group name for matching
- try {
- databaseValue = getGroupName(Long.valueOf(databaseValue));
- } catch (NumberFormatException e) {
- Log.e(LOGTAG, "Number Format Exception", e);
- continue;
- }
- } else if (databaseValue == null) {
- continue;
- }
-
- // Check if the value matches the filter value
- if (isFindMatch(filterOp, filterValue, databaseValue, isPhone, substringMatching)) {
- addMatchToList(cursor, rawContactIds);
- break;
- }
- }
- }
- } else {
- // If no filterBy options were given, check each column for a match
- int numColumns = cursor.getColumnCount();
- for (int i = 0; i < numColumns; i++) {
- String databaseValue = cursor.getString(i);
- if (databaseValue != null && isFindMatch(filterOp, filterValue, databaseValue, false, substringMatching)) {
- addMatchToList(cursor, rawContactIds);
- break;
- }
- }
- }
-
- // If the max found contacts size has been hit, stop looking for contacts
- // A filter limit of 0 denotes there is no limit
- if (filterLimit > 0 && filterLimit <= rawContactIds.size()) {
- break;
- }
- }
- }
- } catch (JSONException e) {
- throw new IllegalArgumentException(e);
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- // Return the contact IDs list converted to an array
- return convertLongListToArray(rawContactIds);
- }
-
- private boolean isFindMatch(final String filterOp, String filterValue, String databaseValue,
- final boolean isPhone, final int substringMatching) {
- Log.i(LOGTAG, "matching: filterOp: " + filterOp);
- if (DEBUG) {
- Log.d(LOGTAG, "matching: filterValue: " + filterValue);
- Log.d(LOGTAG, "matching: databaseValue: " + databaseValue);
- }
- Log.i(LOGTAG, "matching: isPhone: " + isPhone);
- Log.i(LOGTAG, "matching: substringMatching: " + substringMatching);
-
- if (databaseValue == null) {
- return false;
- }
-
- filterValue = filterValue.toLowerCase();
- databaseValue = databaseValue.toLowerCase();
-
- switch (filterOp) {
- case "match":
- // If substring matching is a positive number, only pay attention to the last X characters
- // of both the filter and database values
- if (substringMatching > 0) {
- databaseValue = substringStartFromEnd(cleanPhoneNumber(databaseValue), substringMatching);
- filterValue = substringStartFromEnd(cleanPhoneNumber(filterValue), substringMatching);
- return databaseValue.startsWith(filterValue);
- }
-
- return databaseValue.equals(filterValue);
- case "equals":
- if (isPhone) {
- return PhoneNumberUtils.compare(filterValue, databaseValue);
- }
-
- return databaseValue.equals(filterValue);
- case "contains":
- if (isPhone) {
- filterValue = cleanPhoneNumber(filterValue);
- databaseValue = cleanPhoneNumber(databaseValue);
- }
-
- return databaseValue.contains(filterValue);
- case "startsWith":
- // If a phone number, remove non-dialable characters and then only pay attention to
- // the last X digits given by the substring matching values (see bug 877302)
- if (isPhone) {
- String cleanedDatabasePhone = cleanPhoneNumber(databaseValue);
- if (substringMatching > 0) {
- cleanedDatabasePhone = substringStartFromEnd(cleanedDatabasePhone, substringMatching);
- }
-
- if (cleanedDatabasePhone.startsWith(filterValue)) {
- return true;
- }
- }
-
- return databaseValue.startsWith(filterValue);
- }
-
- return false;
- }
-
- private static String cleanPhoneNumber(String phone) {
- return phone.replace(" ", "").replace("(", "").replace(")", "").replace("-", "");
- }
-
- private static String substringStartFromEnd(final String string, final int distanceFromEnd) {
- int stringLen = string.length();
- if (stringLen < distanceFromEnd) {
- return string;
- }
- return string.substring(stringLen - distanceFromEnd);
- }
-
- private static void addMatchToList(final Cursor cursor, List<Long> rawContactIds) {
- long rawContactId = cursor.getLong(cursor.getColumnIndex(Data.RAW_CONTACT_ID));
- if (!rawContactIds.contains(rawContactId)) {
- rawContactIds.add(rawContactId);
- }
- }
-
- private JSONArray getContactsAsJSONArray(final long[] rawContactIds, final String sortBy, final String sortOrder) {
- List<JSONObject> contactsList = new ArrayList<JSONObject>();
- JSONArray contactsArray = new JSONArray();
-
- // Get each contact as a JSON object
- for (int i = 0; i < rawContactIds.length; i++) {
- contactsList.add(getContactAsJSONObject(rawContactIds[i]));
- }
-
- // Sort the contacts
- if (sortBy != null) {
- Collections.sort(contactsList, new ContactsComparator(sortBy, sortOrder));
- }
-
- // Convert the contacts list to a JSON array
- for (int i = 0; i < contactsList.size(); i++) {
- contactsArray.put(contactsList.get(i));
- }
-
- return contactsArray;
- }
-
- private JSONObject getContactAsJSONObject(long rawContactId) {
- // ContactManager wants a contact object with it's properties wrapped in an array of objects
- JSONObject contact = new JSONObject();
- JSONObject contactProperties = new JSONObject();
-
- JSONArray names = new JSONArray();
- JSONArray givenNames = new JSONArray();
- JSONArray familyNames = new JSONArray();
- JSONArray honorificPrefixes = new JSONArray();
- JSONArray honorificSuffixes = new JSONArray();
- JSONArray additionalNames = new JSONArray();
- JSONArray nicknames = new JSONArray();
- JSONArray addresses = new JSONArray();
- JSONArray phones = new JSONArray();
- JSONArray emails = new JSONArray();
- JSONArray organizations = new JSONArray();
- JSONArray jobTitles = new JSONArray();
- JSONArray notes = new JSONArray();
- JSONArray urls = new JSONArray();
- JSONArray impps = new JSONArray();
- JSONArray categories = new JSONArray();
- String bday = null;
- String anniversary = null;
- String sex = null;
- String genderIdentity = null;
- JSONArray key = new JSONArray();
-
- // Get all the data columns
- final String[] columnsToGet = getAllColumns();
-
- Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
- Uri entityUri = Uri.withAppendedPath(rawContactUri, Entity.CONTENT_DIRECTORY);
-
- Cursor cursor = mContentResolver.query(entityUri, columnsToGet, null, null, null);
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- String mimeType = cursor.getString(cursor.getColumnIndex(Data.MIMETYPE));
-
- // Put the proper fields for each mimetype into the JSON arrays
- try {
- if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final String displayName = cursor.getString(cursor.getColumnIndex(StructuredName.DISPLAY_NAME));
- final String givenName = cursor.getString(cursor.getColumnIndex(StructuredName.GIVEN_NAME));
- final String familyName = cursor.getString(cursor.getColumnIndex(StructuredName.FAMILY_NAME));
- final String prefix = cursor.getString(cursor.getColumnIndex(StructuredName.PREFIX));
- final String suffix = cursor.getString(cursor.getColumnIndex(StructuredName.SUFFIX));
-
- if (displayName != null) {
- names.put(displayName);
- }
- if (givenName != null) {
- givenNames.put(givenName);
- }
- if (familyName != null) {
- familyNames.put(familyName);
- }
- if (prefix != null) {
- honorificPrefixes.put(prefix);
- }
- if (suffix != null) {
- honorificSuffixes.put(suffix);
- }
-
- } else if (MIMETYPE_ADDITIONAL_NAME.equals(mimeType)) {
- additionalNames.put(cursor.getString(cursor.getColumnIndex(CUSTOM_DATA_COLUMN)));
-
- } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
- nicknames.put(cursor.getString(cursor.getColumnIndex(Nickname.NAME)));
-
- } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
- initAddressTypesMap();
- getAddressDataAsJSONObject(cursor, addresses);
-
- } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
- initPhoneTypesMap();
- getPhoneDataAsJSONObject(cursor, phones);
-
- } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
- initEmailTypesMap();
- getGenericDataAsJSONObject(cursor, emails, Email.ADDRESS, Email.TYPE, Email.LABEL, mEmailTypesMap);
-
- } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
- getOrganizationDataAsJSONObject(cursor, organizations, jobTitles);
-
- } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType)) {
- notes.put(cursor.getString(cursor.getColumnIndex(Note.NOTE)));
-
- } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) {
- initWebsiteTypesMap();
- getGenericDataAsJSONObject(cursor, urls, Website.URL, Website.TYPE, Website.LABEL, mWebsiteTypesMap);
-
- } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) {
- initImTypesMap();
- getGenericDataAsJSONObject(cursor, impps, Im.DATA, Im.TYPE, Im.LABEL, mImTypesMap);
-
- } else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
- long groupId = cursor.getLong(cursor.getColumnIndex(GroupMembership.GROUP_ROW_ID));
- String groupName = getGroupName(groupId);
- if (!doesJSONArrayContainString(categories, groupName)) {
- categories.put(groupName);
- }
-
- } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) {
- int type = cursor.getInt(cursor.getColumnIndex(Event.TYPE));
- String date = cursor.getString(cursor.getColumnIndex(Event.START_DATE));
-
- // Add the time info onto the date so it correctly parses into a JS date object
- date += "T00:00:00";
-
- switch (type) {
- case Event.TYPE_BIRTHDAY:
- bday = date;
- break;
-
- case Event.TYPE_ANNIVERSARY:
- anniversary = date;
- break;
- }
-
- } else if (MIMETYPE_SEX.equals(mimeType)) {
- sex = cursor.getString(cursor.getColumnIndex(CUSTOM_DATA_COLUMN));
-
- } else if (MIMETYPE_GENDER_IDENTITY.equals(mimeType)) {
- genderIdentity = cursor.getString(cursor.getColumnIndex(CUSTOM_DATA_COLUMN));
-
- } else if (MIMETYPE_KEY.equals(mimeType)) {
- key.put(cursor.getString(cursor.getColumnIndex(CUSTOM_DATA_COLUMN)));
- }
- } catch (JSONException e) {
- throw new IllegalArgumentException(e);
- }
- }
- cursor.close();
-
- try {
- // Add the fields to the contact properties object
- contactProperties.put("name", names);
- contactProperties.put("givenName", givenNames);
- contactProperties.put("familyName", familyNames);
- contactProperties.put("honorificPrefix", honorificPrefixes);
- contactProperties.put("honorificSuffix", honorificSuffixes);
- contactProperties.put("additionalName", additionalNames);
- contactProperties.put("nickname", nicknames);
- contactProperties.put("adr", addresses);
- contactProperties.put("tel", phones);
- contactProperties.put("email", emails);
- contactProperties.put("org", organizations);
- contactProperties.put("jobTitle", jobTitles);
- contactProperties.put("note", notes);
- contactProperties.put("url", urls);
- contactProperties.put("impp", impps);
- contactProperties.put("category", categories);
- contactProperties.put("key", key);
-
- putPossibleNullValueInJSONObject("bday", bday, contactProperties);
- putPossibleNullValueInJSONObject("anniversary", anniversary, contactProperties);
- putPossibleNullValueInJSONObject("sex", sex, contactProperties);
- putPossibleNullValueInJSONObject("genderIdentity", genderIdentity, contactProperties);
-
- // Add the raw contact ID and the properties to the contact
- contact.put("id", String.valueOf(rawContactId));
- contact.put("updated", null);
- contact.put("published", null);
- contact.put("properties", contactProperties);
- } catch (JSONException e) {
- throw new IllegalArgumentException(e);
- }
-
- if (DEBUG) {
- try {
- Log.d(LOGTAG, "Got contact: " + contact.toString(3));
- } catch (JSONException e) { }
- }
-
- return contact;
- }
-
- private boolean bool(int integer) {
- return integer != 0;
- }
-
- private void getGenericDataAsJSONObject(Cursor cursor, JSONArray array, final String dataColumn,
- final String typeColumn, final String typeLabelColumn,
- final HashMap<String, Integer> typeMap) throws JSONException {
- String value = cursor.getString(cursor.getColumnIndex(dataColumn));
- int typeConstant = cursor.getInt(cursor.getColumnIndex(typeColumn));
- String type;
- if (typeConstant == BaseTypes.TYPE_CUSTOM) {
- type = cursor.getString(cursor.getColumnIndex(typeLabelColumn));
- } else {
- type = getKeyFromMapValue(typeMap, typeConstant);
- }
-
- // Since an object may have multiple types, it may have already been added,
- // but still needs the new type added
- boolean found = false;
- if (type != null) {
- for (int i = 0; i < array.length(); i++) {
- JSONObject object = array.getJSONObject(i);
- if (value.equals(object.getString("value"))) {
- found = true;
-
- JSONArray types = object.getJSONArray("type");
- if (!doesJSONArrayContainString(types, type)) {
- types.put(type);
- break;
- }
- }
- }
- }
-
- // If an existing object wasn't found, make a new one
- if (!found) {
- JSONObject object = new JSONObject();
- JSONArray types = new JSONArray();
- object.put("value", value);
- types.put(type);
- object.put("type", types);
- object.put("pref", bool(cursor.getInt(cursor.getColumnIndex(Data.IS_SUPER_PRIMARY))));
-
- array.put(object);
- }
- }
-
- private void getPhoneDataAsJSONObject(Cursor cursor, JSONArray phones) throws JSONException {
- String value = cursor.getString(cursor.getColumnIndex(Phone.NUMBER));
- int typeConstant = cursor.getInt(cursor.getColumnIndex(Phone.TYPE));
- String type;
- if (typeConstant == Phone.TYPE_CUSTOM) {
- type = cursor.getString(cursor.getColumnIndex(Phone.LABEL));
- } else {
- type = getKeyFromMapValue(mPhoneTypesMap, typeConstant);
- }
-
- // Since a phone may have multiple types, it may have already been added,
- // but still needs the new type added
- boolean found = false;
- if (type != null) {
- for (int i = 0; i < phones.length(); i++) {
- JSONObject phone = phones.getJSONObject(i);
- if (value.equals(phone.getString("value"))) {
- found = true;
-
- JSONArray types = phone.getJSONArray("type");
- if (!doesJSONArrayContainString(types, type)) {
- types.put(type);
- break;
- }
- }
- }
- }
-
- // If an existing phone wasn't found, make a new one
- if (!found) {
- JSONObject phone = new JSONObject();
- JSONArray types = new JSONArray();
- phone.put("value", value);
- phone.put("type", type);
- types.put(type);
- phone.put("type", types);
- phone.put("carrier", cursor.getString(cursor.getColumnIndex(CARRIER_COLUMN)));
- phone.put("pref", bool(cursor.getInt(cursor.getColumnIndex(Phone.IS_SUPER_PRIMARY))));
-
- phones.put(phone);
- }
- }
-
- private void getAddressDataAsJSONObject(Cursor cursor, JSONArray addresses) throws JSONException {
- String streetAddress = cursor.getString(cursor.getColumnIndex(StructuredPostal.STREET));
- String locality = cursor.getString(cursor.getColumnIndex(StructuredPostal.CITY));
- String region = cursor.getString(cursor.getColumnIndex(StructuredPostal.REGION));
- String postalCode = cursor.getString(cursor.getColumnIndex(StructuredPostal.POSTCODE));
- String countryName = cursor.getString(cursor.getColumnIndex(StructuredPostal.COUNTRY));
- int typeConstant = cursor.getInt(cursor.getColumnIndex(StructuredPostal.TYPE));
- String type;
- if (typeConstant == StructuredPostal.TYPE_CUSTOM) {
- type = cursor.getString(cursor.getColumnIndex(StructuredPostal.LABEL));
- } else {
- type = getKeyFromMapValue(mAddressTypesMap, typeConstant);
- }
-
- // Since an email may have multiple types, it may have already been added,
- // but still needs the new type added
- boolean found = false;
- if (type != null) {
- for (int i = 0; i < addresses.length(); i++) {
- JSONObject address = addresses.getJSONObject(i);
- if (streetAddress.equals(address.getString("streetAddress")) &&
- locality.equals(address.getString("locality")) &&
- region.equals(address.getString("region")) &&
- countryName.equals(address.getString("countryName")) &&
- postalCode.equals(address.getString("postalCode"))) {
- found = true;
-
- JSONArray types = address.getJSONArray("type");
- if (!doesJSONArrayContainString(types, type)) {
- types.put(type);
- break;
- }
- }
- }
- }
-
- // If an existing email wasn't found, make a new one
- if (!found) {
- JSONObject address = new JSONObject();
- JSONArray types = new JSONArray();
- address.put("streetAddress", streetAddress);
- address.put("locality", locality);
- address.put("region", region);
- address.put("countryName", countryName);
- address.put("postalCode", postalCode);
- types.put(type);
- address.put("type", types);
- address.put("pref", bool(cursor.getInt(cursor.getColumnIndex(StructuredPostal.IS_SUPER_PRIMARY))));
-
- addresses.put(address);
- }
- }
-
- private void getOrganizationDataAsJSONObject(Cursor cursor, JSONArray organizations,
- JSONArray jobTitles) throws JSONException {
- int organizationColumnIndex = cursor.getColumnIndex(Organization.COMPANY);
- int titleColumnIndex = cursor.getColumnIndex(Organization.TITLE);
-
- if (!cursor.isNull(organizationColumnIndex)) {
- organizations.put(cursor.getString(organizationColumnIndex));
- }
- if (!cursor.isNull(titleColumnIndex)) {
- jobTitles.put(cursor.getString(titleColumnIndex));
- }
- }
-
- private class ContactsComparator implements Comparator<JSONObject> {
- final String mSortBy;
- final String mSortOrder;
-
- public ContactsComparator(final String sortBy, final String sortOrder) {
- mSortBy = sortBy.toLowerCase();
- mSortOrder = sortOrder.toLowerCase();
- }
-
- @Override
- public int compare(JSONObject left, JSONObject right) {
- // Determine if sorting by "family name, given name" or "given name, family name"
- boolean familyFirst = false;
- if ("familyname".equals(mSortBy)) {
- familyFirst = true;
- }
-
- JSONObject leftProperties;
- JSONObject rightProperties;
- try {
- leftProperties = left.getJSONObject("properties");
- rightProperties = right.getJSONObject("properties");
- } catch (JSONException e) {
- throw new IllegalArgumentException(e);
- }
-
- JSONArray leftFamilyNames = leftProperties.optJSONArray("familyName");
- JSONArray leftGivenNames = leftProperties.optJSONArray("givenName");
- JSONArray rightFamilyNames = rightProperties.optJSONArray("familyName");
- JSONArray rightGivenNames = rightProperties.optJSONArray("givenName");
-
- // If any of the name arrays didn't exist (are null), create empty arrays
- // to avoid doing a bunch of null checking below
- if (leftFamilyNames == null) {
- leftFamilyNames = new JSONArray();
- }
- if (leftGivenNames == null) {
- leftGivenNames = new JSONArray();
- }
- if (rightFamilyNames == null) {
- rightFamilyNames = new JSONArray();
- }
- if (rightGivenNames == null) {
- rightGivenNames = new JSONArray();
- }
-
- int maxArrayLength = max(leftFamilyNames.length(), leftGivenNames.length(),
- rightFamilyNames.length(), rightGivenNames.length());
-
- int index = 0;
- int compareResult;
- do {
- // Join together the given name and family name per the pattern above
- String leftName = "";
- String rightName = "";
-
- if (familyFirst) {
- leftName = leftFamilyNames.optString(index, "") + leftGivenNames.optString(index, "");
- rightName = rightFamilyNames.optString(index, "") + rightGivenNames.optString(index, "");
- } else {
- leftName = leftGivenNames.optString(index, "") + leftFamilyNames.optString(index, "");
- rightName = rightGivenNames.optString(index, "") + rightFamilyNames.optString(index, "");
- }
-
- index++;
- compareResult = leftName.compareTo(rightName);
-
- } while (compareResult == 0 && index < maxArrayLength);
-
- // If descending order, flip the result
- if (compareResult != 0 && "descending".equals(mSortOrder)) {
- compareResult = -compareResult;
- }
-
- return compareResult;
- }
- }
-
- private void clearAllContacts(final JSONObject contactOptions, final String requestID) {
- ArrayList<ContentProviderOperation> deleteOptions = new ArrayList<ContentProviderOperation>();
-
- // Delete all contacts from the selected account
- ContentProviderOperation.Builder deleteOptionsBuilder = ContentProviderOperation.newDelete(RawContacts.CONTENT_URI);
- if (mAccountName != null) {
- deleteOptionsBuilder.withSelection(RawContacts.ACCOUNT_NAME + "=?", new String[] {mAccountName})
- .withSelection(RawContacts.ACCOUNT_TYPE + "=?", new String[] {mAccountType});
- }
-
- deleteOptions.add(deleteOptionsBuilder.build());
-
- // Clear the contacts
- String returnStatus = "KO";
- if (applyBatch(deleteOptions) != null) {
- returnStatus = "OK";
- }
-
- Log.i(LOGTAG, "Sending return status: " + returnStatus);
-
- sendCallbackToJavascript("Android:Contacts:Clear:Return:" + returnStatus, requestID,
- new String[] {"contactID"}, new Object[] {"undefined"});
-
- }
-
- private boolean deleteContact(String rawContactId) {
- ContentProviderOperation deleteOptions = ContentProviderOperation.newDelete(RawContacts.CONTENT_URI)
- .withSelection(RawContacts._ID + "=?",
- new String[] {rawContactId})
- .build();
-
- ArrayList<ContentProviderOperation> deleteOptionsList = new ArrayList<ContentProviderOperation>();
- deleteOptionsList.add(deleteOptions);
-
- return checkForPositiveCountInResults(applyBatch(deleteOptionsList));
- }
-
- private void removeContact(final JSONObject contactOptions, final String requestID) {
- String rawContactId;
- try {
- rawContactId = contactOptions.getString("id");
- Log.i(LOGTAG, "Removing contact with ID: " + rawContactId);
- } catch (JSONException e) {
- // We can't continue without a raw contact ID
- sendCallbackToJavascript("Android:Contact:Remove:Return:KO", requestID, null, null);
- return;
- }
-
- String returnStatus = "KO";
- if (deleteContact(rawContactId)) {
- returnStatus = "OK";
- }
-
- sendCallbackToJavascript("Android:Contact:Remove:Return:" + returnStatus, requestID,
- new String[] {"contactID"}, new Object[] {rawContactId});
- }
-
- private void saveContact(final JSONObject contactOptions, final String requestID) {
- try {
- String reason = contactOptions.getString("reason");
- JSONObject contact = contactOptions.getJSONObject("contact");
- JSONObject contactProperties = contact.getJSONObject("properties");
-
- if ("update".equals(reason)) {
- updateContact(contactProperties, contact.getLong("id"), requestID);
- } else {
- insertContact(contactProperties, requestID);
- }
- } catch (JSONException e) {
- throw new IllegalArgumentException(e);
- }
- }
-
- private void insertContact(final JSONObject contactProperties, final String requestID) throws JSONException {
- ArrayList<ContentProviderOperation> newContactOptions = new ArrayList<ContentProviderOperation>();
-
- // Account to save the contact under
- newContactOptions.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
- .withValue(RawContacts.ACCOUNT_NAME, mAccountName)
- .withValue(RawContacts.ACCOUNT_TYPE, mAccountType)
- .build());
-
- List<ContentValues> newContactValues = getContactValues(contactProperties);
-
- for (ContentValues values : newContactValues) {
- newContactOptions.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
- .withValueBackReference(Data.RAW_CONTACT_ID, 0)
- .withValues(values)
- .build());
- }
-
- String returnStatus = "KO";
- Long newRawContactId = -1L;
-
- // Insert the contact!
- ContentProviderResult[] insertResults = applyBatch(newContactOptions);
-
- if (insertResults != null) {
- try {
- // Get the ID of the newly created contact
- newRawContactId = getRawContactIdFromContentProviderResults(insertResults);
-
- if (newRawContactId != null) {
- returnStatus = "OK";
- }
- } catch (NumberFormatException e) {
- Log.e(LOGTAG, "NumberFormatException", e);
- }
-
- Log.i(LOGTAG, "Newly created contact ID: " + newRawContactId);
- }
-
- Log.i(LOGTAG, "Sending return status: " + returnStatus);
-
- sendCallbackToJavascript("Android:Contact:Save:Return:" + returnStatus, requestID,
- new String[] {"contactID", "reason"},
- new Object[] {newRawContactId, "create"});
- }
-
- private void updateContact(final JSONObject contactProperties, final long rawContactId, final String requestID) throws JSONException {
- // Why is updating a contact so weird and horribly inefficient? Because Android doesn't
- // like multiple values for contact fields, but the Mozilla contacts API calls for this.
- // This means the Android update function is essentially completely useless. Why not just
- // delete the contact and re-insert it? Because that would change the contact ID and the
- // Mozilla contacts API shouldn't have this behavior. The solution is to delete each
- // row from the contacts data table that belongs to the contact, and insert the new
- // fields. But then why not just delete all the data from the data in one go and
- // insert the new data in another? Because if all the data relating to a contact is
- // deleted, Android will "conveniently" remove the ID making it impossible to insert data
- // under the old ID. To work around this, we put a Mozilla contact flag in the database
-
- ContentProviderOperation removeOptions = ContentProviderOperation.newDelete(Data.CONTENT_URI)
- .withSelection(Data.RAW_CONTACT_ID + "=? AND " +
- Data.MIMETYPE + " != '" + MIMETYPE_MOZILLA_CONTACTS_FLAG + "'",
- new String[] {String.valueOf(rawContactId)})
- .build();
-
- ArrayList<ContentProviderOperation> removeOptionsList = new ArrayList<ContentProviderOperation>();
- removeOptionsList.add(removeOptions);
-
- ContentProviderResult[] removeResults = applyBatch(removeOptionsList);
-
- // Check if the remove failed
- if (removeResults == null || !checkForPositiveCountInResults(removeResults)) {
- Log.w(LOGTAG, "Null or 0 remove results");
-
- sendCallbackToJavascript("Android:Contact:Save:Return:KO", requestID, null, null);
- return;
- }
-
- List<ContentValues> updateContactValues = getContactValues(contactProperties);
- ArrayList<ContentProviderOperation> updateContactOptions = new ArrayList<ContentProviderOperation>();
-
- for (ContentValues values : updateContactValues) {
- updateContactOptions.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
- .withValue(Data.RAW_CONTACT_ID, rawContactId)
- .withValues(values)
- .build());
- }
-
- String returnStatus = "KO";
-
- // Update the contact!
- applyBatch(updateContactOptions);
-
- sendCallbackToJavascript("Android:Contact:Save:Return:OK", requestID,
- new String[] {"contactID", "reason"},
- new Object[] {rawContactId, "update"});
- }
-
- private List<ContentValues> getContactValues(final JSONObject contactProperties) throws JSONException {
- List<ContentValues> contactValues = new ArrayList<ContentValues>();
-
- // Add the contact to the default group so it is shown in other apps
- // like the Contacts or People app
- ContentValues defaultGroupValues = new ContentValues();
- defaultGroupValues.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
- defaultGroupValues.put(GroupMembership.GROUP_ROW_ID, mGroupId);
- contactValues.add(defaultGroupValues);
-
- // Create all the values that will be inserted into the new contact
- getNameValues(contactProperties.optJSONArray("name"),
- contactProperties.optJSONArray("givenName"),
- contactProperties.optJSONArray("familyName"),
- contactProperties.optJSONArray("honorificPrefix"),
- contactProperties.optJSONArray("honorificSuffix"),
- contactValues);
-
- getGenericValues(MIMETYPE_ADDITIONAL_NAME, CUSTOM_DATA_COLUMN,
- contactProperties.optJSONArray("additionalName"), contactValues);
-
- getNicknamesValues(contactProperties.optJSONArray("nickname"), contactValues);
-
- getAddressesValues(contactProperties.optJSONArray("adr"), contactValues);
-
- getPhonesValues(contactProperties.optJSONArray("tel"), contactValues);
-
- getEmailsValues(contactProperties.optJSONArray("email"), contactValues);
-
- //getPhotosValues(contactProperties.optJSONArray("photo"), contactValues);
-
- getGenericValues(Organization.CONTENT_ITEM_TYPE, Organization.COMPANY,
- contactProperties.optJSONArray("org"), contactValues);
-
- getGenericValues(Organization.CONTENT_ITEM_TYPE, Organization.TITLE,
- contactProperties.optJSONArray("jobTitle"), contactValues);
-
- getNotesValues(contactProperties.optJSONArray("note"), contactValues);
-
- getWebsitesValues(contactProperties.optJSONArray("url"), contactValues);
-
- getImsValues(contactProperties.optJSONArray("impp"), contactValues);
-
- getCategoriesValues(contactProperties.optJSONArray("category"), contactValues);
-
- getEventValues(contactProperties.optString("bday"), Event.TYPE_BIRTHDAY, contactValues);
-
- getEventValues(contactProperties.optString("anniversary"), Event.TYPE_ANNIVERSARY, contactValues);
-
- getCustomMimetypeValues(contactProperties.optString("sex"), MIMETYPE_SEX, contactValues);
-
- getCustomMimetypeValues(contactProperties.optString("genderIdentity"), MIMETYPE_GENDER_IDENTITY, contactValues);
-
- getGenericValues(MIMETYPE_KEY, CUSTOM_DATA_COLUMN, contactProperties.optJSONArray("key"),
- contactValues);
-
- return contactValues;
- }
-
- private void getGenericValues(final String mimeType, final String dataType, final JSONArray fields,
- List<ContentValues> newContactValues) throws JSONException {
- if (fields == null) {
- return;
- }
-
- for (int i = 0; i < fields.length(); i++) {
- ContentValues contentValues = new ContentValues();
- contentValues.put(Data.MIMETYPE, mimeType);
- contentValues.put(dataType, fields.getString(i));
- newContactValues.add(contentValues);
- }
- }
-
- private void getNameValues(final JSONArray displayNames, final JSONArray givenNames,
- final JSONArray familyNames, final JSONArray prefixes,
- final JSONArray suffixes, List<ContentValues> newContactValues) throws JSONException {
- int maxLen = max((displayNames != null ? displayNames.length() : 0),
- (givenNames != null ? givenNames.length() : 0),
- (familyNames != null ? familyNames.length() : 0),
- (prefixes != null ? prefixes.length() : 0),
- (suffixes != null ? suffixes.length() : 0));
-
- for (int i = 0; i < maxLen; i++) {
- ContentValues contentValues = new ContentValues();
- contentValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
-
- final String displayName = (displayNames != null ? displayNames.optString(i, null) : null);
- final String givenName = (givenNames != null ? givenNames.optString(i, null) : null);
- final String familyName = (familyNames != null ? familyNames.optString(i, null) : null);
- final String prefix = (prefixes != null ? prefixes.optString(i, null) : null);
- final String suffix = (suffixes != null ? suffixes.optString(i, null) : null);
-
- if (displayName != null) {
- contentValues.put(StructuredName.DISPLAY_NAME, displayName);
- }
- if (givenName != null) {
- contentValues.put(StructuredName.GIVEN_NAME, givenName);
- }
- if (familyName != null) {
- contentValues.put(StructuredName.FAMILY_NAME, familyName);
- }
- if (prefix != null) {
- contentValues.put(StructuredName.PREFIX, prefix);
- }
- if (suffix != null) {
- contentValues.put(StructuredName.SUFFIX, suffix);
- }
-
- newContactValues.add(contentValues);
- }
- }
-
- private void getNicknamesValues(final JSONArray nicknames, List<ContentValues> newContactValues) throws JSONException {
- if (nicknames == null) {
- return;
- }
-
- for (int i = 0; i < nicknames.length(); i++) {
- ContentValues contentValues = new ContentValues();
- contentValues.put(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE);
- contentValues.put(Nickname.NAME, nicknames.getString(i));
- contentValues.put(Nickname.TYPE, Nickname.TYPE_DEFAULT);
- newContactValues.add(contentValues);
- }
- }
-
- private void getAddressesValues(final JSONArray addresses, List<ContentValues> newContactValues) throws JSONException {
- if (addresses == null) {
- return;
- }
-
- for (int i = 0; i < addresses.length(); i++) {
- JSONObject address = addresses.getJSONObject(i);
- JSONArray addressTypes = address.optJSONArray("type");
-
- if (addressTypes != null) {
- for (int j = 0; j < addressTypes.length(); j++) {
- // Translate the address type string to an integer constant
- // provided by the ContactsContract API
- final String type = addressTypes.getString(j);
- final int typeConstant = getAddressType(type);
-
- newContactValues.add(createAddressContentValues(address, typeConstant, type));
- }
- } else {
- newContactValues.add(createAddressContentValues(address, -1, null));
- }
- }
- }
-
- private ContentValues createAddressContentValues(final JSONObject address, final int typeConstant,
- final String type) throws JSONException {
- ContentValues contentValues = new ContentValues();
- contentValues.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
- contentValues.put(StructuredPostal.STREET, address.optString("streetAddress"));
- contentValues.put(StructuredPostal.CITY, address.optString("locality"));
- contentValues.put(StructuredPostal.REGION, address.optString("region"));
- contentValues.put(StructuredPostal.POSTCODE, address.optString("postalCode"));
- contentValues.put(StructuredPostal.COUNTRY, address.optString("countryName"));
-
- if (type != null) {
- contentValues.put(StructuredPostal.TYPE, typeConstant);
-
- // If a custom type, add a label
- if (typeConstant == BaseTypes.TYPE_CUSTOM) {
- contentValues.put(StructuredPostal.LABEL, type);
- }
- }
-
- if (address.has("pref")) {
- contentValues.put(Data.IS_SUPER_PRIMARY, address.getBoolean("pref") ? 1 : 0);
- }
-
- return contentValues;
- }
-
- private void getPhonesValues(final JSONArray phones, List<ContentValues> newContactValues) throws JSONException {
- if (phones == null) {
- return;
- }
-
- for (int i = 0; i < phones.length(); i++) {
- JSONObject phone = phones.getJSONObject(i);
- JSONArray phoneTypes = phone.optJSONArray("type");
- ContentValues contentValues;
-
- if (phoneTypes != null && phoneTypes.length() > 0) {
- for (int j = 0; j < phoneTypes.length(); j++) {
- // Translate the phone type string to an integer constant
- // provided by the ContactsContract API
- final String type = phoneTypes.getString(j);
- final int typeConstant = getPhoneType(type);
-
- contentValues = createContentValues(Phone.CONTENT_ITEM_TYPE, phone.optString("value"),
- typeConstant, type, phone.optBoolean("pref"));
- if (phone.has("carrier")) {
- contentValues.put(CARRIER_COLUMN, phone.optString("carrier"));
- }
- newContactValues.add(contentValues);
- }
- } else {
- contentValues = createContentValues(Phone.CONTENT_ITEM_TYPE, phone.optString("value"),
- -1, null, phone.optBoolean("pref"));
- if (phone.has("carrier")) {
- contentValues.put(CARRIER_COLUMN, phone.optString("carrier"));
- }
- newContactValues.add(contentValues);
- }
- }
- }
-
- private void getEmailsValues(final JSONArray emails, List<ContentValues> newContactValues) throws JSONException {
- if (emails == null) {
- return;
- }
-
- for (int i = 0; i < emails.length(); i++) {
- JSONObject email = emails.getJSONObject(i);
- JSONArray emailTypes = email.optJSONArray("type");
-
- if (emailTypes != null && emailTypes.length() > 0) {
- for (int j = 0; j < emailTypes.length(); j++) {
- // Translate the email type string to an integer constant
- // provided by the ContactsContract API
- final String type = emailTypes.getString(j);
- final int typeConstant = getEmailType(type);
-
- newContactValues.add(createContentValues(Email.CONTENT_ITEM_TYPE,
- email.optString("value"),
- typeConstant, type,
- email.optBoolean("pref")));
- }
- } else {
- newContactValues.add(createContentValues(Email.CONTENT_ITEM_TYPE,
- email.optString("value"),
- -1, null, email.optBoolean("pref")));
- }
- }
- }
-
- private void getPhotosValues(final JSONArray photos, List<ContentValues> newContactValues) throws JSONException {
- if (photos == null) {
- return;
- }
-
- // TODO: implement this
- }
-
- private void getNotesValues(final JSONArray notes, List<ContentValues> newContactValues) throws JSONException {
- if (notes == null) {
- return;
- }
-
- for (int i = 0; i < notes.length(); i++) {
- ContentValues contentValues = new ContentValues();
- contentValues.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
- contentValues.put(Note.NOTE, notes.getString(i));
- newContactValues.add(contentValues);
- }
- }
-
- private void getWebsitesValues(final JSONArray websites, List<ContentValues> newContactValues) throws JSONException {
- if (websites == null) {
- return;
- }
-
- for (int i = 0; i < websites.length(); i++) {
- JSONObject website = websites.getJSONObject(i);
- JSONArray websiteTypes = website.optJSONArray("type");
-
- if (websiteTypes != null && websiteTypes.length() > 0) {
- for (int j = 0; j < websiteTypes.length(); j++) {
- // Translate the website type string to an integer constant
- // provided by the ContactsContract API
- final String type = websiteTypes.getString(j);
- final int typeConstant = getWebsiteType(type);
-
- newContactValues.add(createContentValues(Website.CONTENT_ITEM_TYPE,
- website.optString("value"),
- typeConstant, type,
- website.optBoolean("pref")));
- }
- } else {
- newContactValues.add(createContentValues(Website.CONTENT_ITEM_TYPE,
- website.optString("value"),
- -1, null, website.optBoolean("pref")));
- }
- }
- }
-
- private void getImsValues(final JSONArray ims, List<ContentValues> newContactValues) throws JSONException {
- if (ims == null) {
- return;
- }
-
- for (int i = 0; i < ims.length(); i++) {
- JSONObject im = ims.getJSONObject(i);
- JSONArray imTypes = im.optJSONArray("type");
-
- if (imTypes != null && imTypes.length() > 0) {
- for (int j = 0; j < imTypes.length(); j++) {
- // Translate the IM type string to an integer constant
- // provided by the ContactsContract API
- final String type = imTypes.getString(j);
- final int typeConstant = getImType(type);
-
- newContactValues.add(createContentValues(Im.CONTENT_ITEM_TYPE,
- im.optString("value"),
- typeConstant, type,
- im.optBoolean("pref")));
- }
- } else {
- newContactValues.add(createContentValues(Im.CONTENT_ITEM_TYPE,
- im.optString("value"),
- -1, null, im.optBoolean("pref")));
- }
- }
- }
-
- private void getCategoriesValues(final JSONArray categories, List<ContentValues> newContactValues) throws JSONException {
- if (categories == null) {
- return;
- }
-
- for (int i = 0; i < categories.length(); i++) {
- String category = categories.getString(i);
-
- if ("my contacts".equals(category.toLowerCase()) ||
- PRE_HONEYCOMB_DEFAULT_GROUP.equalsIgnoreCase(category)) {
- Log.w(LOGTAG, "New contacts are implicitly added to the default group.");
- continue;
- }
-
- // Find the group ID of the given category
- long groupId = getGroupId(category);
-
- // Create the group if it doesn't already exist
- if (groupId == -1) {
- groupId = createGroup(category);
- // If the group is still -1, we failed to create the group
- if (groupId == -1) {
- // Only log the category name if in debug
- if (DEBUG) {
- Log.d(LOGTAG, "Failed to create new group for category \"" + category + "\"");
- } else {
- Log.w(LOGTAG, "Failed to create new group for given category.");
- }
- continue;
- }
- }
-
- ContentValues contentValues = new ContentValues();
- contentValues.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
- contentValues.put(GroupMembership.GROUP_ROW_ID, groupId);
- newContactValues.add(contentValues);
-
- newContactValues.add(contentValues);
- }
- }
-
- private void getEventValues(final String event, final int type, List<ContentValues> newContactValues) {
- if (event == null || event.length() < 11) {
- return;
- }
-
- ContentValues contentValues = new ContentValues();
- contentValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
- contentValues.put(Event.START_DATE, event.substring(0, 10));
- contentValues.put(Event.TYPE, type);
- newContactValues.add(contentValues);
- }
-
- private void getCustomMimetypeValues(final String value, final String mimeType, List<ContentValues> newContactValues) {
- if (value == null || "null".equals(value)) {
- return;
- }
-
- ContentValues contentValues = new ContentValues();
- contentValues.put(Data.MIMETYPE, mimeType);
- contentValues.put(CUSTOM_DATA_COLUMN, value);
- newContactValues.add(contentValues);
- }
-
- private void getMozillaContactFlagValues(List<ContentValues> newContactValues) {
- try {
- JSONArray mozillaContactsFlag = new JSONArray();
- mozillaContactsFlag.put("1");
- getGenericValues(MIMETYPE_MOZILLA_CONTACTS_FLAG, CUSTOM_DATA_COLUMN, mozillaContactsFlag, newContactValues);
- } catch (JSONException e) {
- throw new IllegalArgumentException(e);
- }
- }
-
- private ContentValues createContentValues(final String mimeType, final String value, final int typeConstant,
- final String type, final boolean preferredValue) {
- ContentValues contentValues = new ContentValues();
- contentValues.put(Data.MIMETYPE, mimeType);
- contentValues.put(Data.DATA1, value);
- contentValues.put(Data.IS_SUPER_PRIMARY, preferredValue ? 1 : 0);
-
- if (type != null) {
- contentValues.put(Data.DATA2, typeConstant);
-
- // If a custom type, add a label
- if (typeConstant == BaseTypes.TYPE_CUSTOM) {
- contentValues.put(Data.DATA3, type);
- }
- }
-
- return contentValues;
- }
-
- private void getContactsCount(final String requestID) {
- Cursor cursor = getAllRawContactIdsCursor();
- Integer numContacts = cursor.getCount();
- cursor.close();
-
- sendCallbackToJavascript("Android:Contacts:Count", requestID, new String[] {"count"},
- new Object[] {numContacts});
- }
-
- private void sendCallbackToJavascript(final String subject, final String requestID,
- final String[] argNames, final Object[] argValues) {
- // Check the same number of argument names and arguments were given
- if (argNames != null && argNames.length != argValues.length) {
- throw new IllegalArgumentException("Argument names and argument values lengths do not match. " +
- "Names length = " + argNames.length + ", Values length = " +
- argValues.length);
- }
-
- try {
- JSONObject callbackMessage = new JSONObject();
- callbackMessage.put("requestID", requestID);
-
- if (argNames != null) {
- for (int i = 0; i < argNames.length; i++) {
- callbackMessage.put(argNames[i], argValues[i]);
- }
- }
-
- GeckoAppShell.notifyObservers(subject, callbackMessage.toString());
- } catch (JSONException e) {
- throw new IllegalArgumentException(e);
- }
- }
-
- private ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) {
- try {
- return mContentResolver.applyBatch(ContactsContract.AUTHORITY, operations);
- } catch (RemoteException e) {
- Log.e(LOGTAG, "RemoteException", e);
- } catch (OperationApplicationException e) {
- Log.e(LOGTAG, "OperationApplicationException", e);
- }
- return null;
- }
-
- private void getDeviceAccount(final Runnable handleMessage) {
- Account[] accounts = AccountManager.get(mActivity).getAccounts();
-
- if (accounts.length == 0) {
- Log.w(LOGTAG, "No accounts available");
- gotDeviceAccount(handleMessage);
- } else if (accounts.length > 1) {
- // Show the accounts chooser dialog if more than one dialog exists
- showAccountsDialog(accounts, handleMessage);
- } else {
- // If only one account exists, use it
- mAccountName = accounts[0].name;
- mAccountType = accounts[0].type;
- gotDeviceAccount(handleMessage);
- }
-
- mGotDeviceAccount = true;
- }
-
- private void showAccountsDialog(final Account[] accounts, final Runnable handleMessage) {
- String[] accountNames = new String[accounts.length];
- for (int i = 0; i < accounts.length; i++) {
- accountNames[i] = accounts[i].name;
- }
-
- final AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
- builder.setTitle(mActivity.getResources().getString(R.string.contacts_account_chooser_dialog_title2))
- .setSingleChoiceItems(accountNames, 0, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int position) {
- // Set the account name and type when an item is selected and dismiss the dialog
- mAccountName = accounts[position].name;
- mAccountType = accounts[position].type;
- dialog.dismiss();
- gotDeviceAccount(handleMessage);
- }
- });
-
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- builder.show();
- }
- });
- }
-
- private void gotDeviceAccount(final Runnable handleMessage) {
- // Force the handleMessage runnable and getDefaultGroupId to run on the background thread
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- getDefaultGroupId();
-
- // Don't log a user's account if not debug mode. Otherwise, just log a message
- // saying that we got an account to use
- if (mAccountName == null) {
- Log.i(LOGTAG, "No device account selected. Leaving account as null.");
- } else if (DEBUG) {
- Log.d(LOGTAG, "Using account: " + mAccountName + " (type: " + mAccountType + ")");
- } else {
- Log.i(LOGTAG, "Got device account to use for contact operations.");
- }
- handleMessage.run();
- }
- };
-
- ThreadUtils.postToBackgroundThread(runnable);
- }
-
- private void getDefaultGroupId() {
- Cursor cursor = getAllGroups();
-
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- // Check if the account name and type for the group match the account name and type of
- // the account we're working with
- final String groupAccountName = cursor.getString(GROUP_ACCOUNT_NAME);
- if (!groupAccountName.equals(mAccountName)) {
- continue;
- }
-
- final String groupAccountType = cursor.getString(GROUP_ACCOUNT_TYPE);
- if (!groupAccountType.equals(mAccountType)) {
- continue;
- }
-
- // For all honeycomb and up, the default group is the first one which has the AUTO_ADD flag set
- if (isAutoAddGroup(cursor)) {
- mGroupTitle = cursor.getString(GROUP_TITLE);
- mGroupId = cursor.getLong(GROUP_ID);
- break;
- } else if (PRE_HONEYCOMB_DEFAULT_GROUP.equals(cursor.getString(GROUP_TITLE))) {
- mGroupId = cursor.getLong(GROUP_ID);
- mGroupTitle = PRE_HONEYCOMB_DEFAULT_GROUP;
- break;
- }
- }
- cursor.close();
-
- if (mGroupId == 0) {
- Log.w(LOGTAG, "Default group ID not found. Newly created contacts will not belong to any groups.");
- } else if (DEBUG) {
- Log.i(LOGTAG, "Using group ID: " + mGroupId + " (" + mGroupTitle + ")");
- }
- }
-
- private static boolean isAutoAddGroup(Cursor cursor) {
- // For Honeycomb and up, the default group is the first one which has the AUTO_ADD flag set.
- // For everything below Honeycomb, use the default "System Group: My Contacts" group
- return (Versions.feature11Plus &&
- !cursor.isNull(GROUP_AUTO_ADD) &&
- cursor.getInt(GROUP_AUTO_ADD) != 0);
- }
-
- private long getGroupId(String groupName) {
- long groupId = -1;
- Cursor cursor = getGroups(Groups.TITLE + " = '" + groupName + "'");
-
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- String groupAccountName = cursor.getString(GROUP_ACCOUNT_NAME);
- String groupAccountType = cursor.getString(GROUP_ACCOUNT_TYPE);
-
- // Check if the account name and type for the group match the account name and type of
- // the account we're working with or the default "Phone" account if no account was found
- if (groupAccountName.equals(mAccountName) && groupAccountType.equals(mAccountType) ||
- (mAccountName == null && "Phone".equals(groupAccountType))) {
- if (groupName.equals(cursor.getString(GROUP_TITLE))) {
- groupId = cursor.getLong(GROUP_ID);
- break;
- }
- }
- }
- cursor.close();
-
- return groupId;
- }
-
- private String getGroupName(long groupId) {
- Cursor cursor = getGroups(Groups._ID + " = " + groupId);
-
- if (cursor.getCount() == 0) {
- cursor.close();
- return null;
- }
-
- cursor.moveToPosition(0);
- String groupName = cursor.getString(cursor.getColumnIndex(Groups.TITLE));
- cursor.close();
-
- return groupName;
- }
-
- private Cursor getAllGroups() {
- return getGroups(null);
- }
-
- private Cursor getGroups(String selectArg) {
- String[] columns = new String[] {
- Groups.ACCOUNT_NAME,
- Groups.ACCOUNT_TYPE,
- Groups._ID,
- Groups.TITLE,
- (Versions.feature11Plus ? Groups.AUTO_ADD : Groups._ID)
- };
-
- if (selectArg != null) {
- selectArg = "AND " + selectArg;
- } else {
- selectArg = "";
- }
-
- return mContentResolver.query(Groups.CONTENT_URI, columns,
- Groups.ACCOUNT_TYPE + " NOT NULL AND " +
- Groups.ACCOUNT_NAME + " NOT NULL " + selectArg, null, null);
- }
-
- private long createGroup(String groupName) {
- if (DEBUG) {
- Log.d(LOGTAG, "Creating group: " + groupName);
- }
-
- ArrayList<ContentProviderOperation> newGroupOptions = new ArrayList<ContentProviderOperation>();
-
- // Create the group under the account we're using
- // If no account is selected, use a default account name/type for the group
- newGroupOptions.add(ContentProviderOperation.newInsert(Groups.CONTENT_URI)
- .withValue(Groups.ACCOUNT_NAME, (mAccountName == null ? "Phone" : mAccountName))
- .withValue(Groups.ACCOUNT_TYPE, (mAccountType == null ? "Phone" : mAccountType))
- .withValue(Groups.TITLE, groupName)
- .withValue(Groups.GROUP_VISIBLE, true)
- .build());
-
- applyBatch(newGroupOptions);
-
- // Return the ID of the newly created group
- return getGroupId(groupName);
- }
-
- private long[] getAllRawContactIds() {
- Cursor cursor = getAllRawContactIdsCursor();
-
- // Put the ids into an array
- long[] ids = new long[cursor.getCount()];
- int index = 0;
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- ids[index] = cursor.getLong(cursor.getColumnIndex(RawContacts._ID));
- index++;
- }
- cursor.close();
-
- return ids;
- }
-
- private Cursor getAllRawContactIdsCursor() {
- // When a contact is deleted, it actually just sets the deleted field to 1 until the
- // sync adapter actually deletes the contact later so ignore any contacts with the deleted
- // flag set
- String selection = RawContacts.DELETED + "=0";
- String[] selectionArgs = null;
-
- // Only get contacts from the selected account
- if (mAccountName != null) {
- selection += " AND " + RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?";
- selectionArgs = new String[] {mAccountName, mAccountType};
- }
-
- // Get the ID's of all contacts and use the number of contact ID's as
- // the total number of contacts
- return mContentResolver.query(RawContacts.CONTENT_URI, new String[] {RawContacts._ID},
- selection, selectionArgs, null);
- }
-
- private static Long getRawContactIdFromContentProviderResults(ContentProviderResult[] results) throws NumberFormatException {
- for (int i = 0; i < results.length; i++) {
- if (results[i].uri == null) {
- continue;
- }
-
- String uri = results[i].uri.toString();
- // Check if the uri is from the raw contacts table
- if (uri.contains("raw_contacts")) {
- // The ID is the after the final forward slash in the URI
- return Long.parseLong(uri.substring(uri.lastIndexOf("/") + 1));
- }
- }
-
- return null;
- }
-
- private static boolean checkForPositiveCountInResults(ContentProviderResult[] results) {
- for (int i = 0; i < results.length; i++) {
- Integer count = results[i].count;
-
- if (DEBUG) {
- Log.d(LOGTAG, "Results count: " + count);
- }
-
- if (count != null && count > 0) {
- return true;
- }
- }
-
- return false;
- }
-
- private static long[] convertLongListToArray(List<Long> list) {
- long[] array = new long[list.size()];
-
- for (int i = 0; i < list.size(); i++) {
- array[i] = list.get(i);
- }
-
- return array;
- }
-
- private static boolean doesJSONArrayContainString(final JSONArray array, final String value) {
- for (int i = 0; i < array.length(); i++) {
- if (value.equals(array.optString(i))) {
- return true;
- }
- }
-
- return false;
- }
-
- private static int max(int... values) {
- int max = values[0];
- for (int value : values) {
- if (value > max) {
- max = value;
- }
- }
- return max;
- }
-
- private static void putPossibleNullValueInJSONObject(final String key, final Object value, JSONObject jsonObject) throws JSONException {
- if (value != null) {
- jsonObject.put(key, value);
- } else {
- jsonObject.put(key, JSONObject.NULL);
- }
- }
-
- private static String getKeyFromMapValue(final HashMap<String, Integer> map, int value) {
- for (Entry<String, Integer> entry : map.entrySet()) {
- if (value == entry.getValue()) {
- return entry.getKey();
- }
- }
- return null;
- }
-
- private String getColumnNameConstant(String field) {
- initColumnNameConstantsMap();
- return mColumnNameConstantsMap.get(field.toLowerCase());
- }
-
- private void initColumnNameConstantsMap() {
- if (mColumnNameConstantsMap != null) {
- return;
- }
- mColumnNameConstantsMap = new HashMap<String, String>();
-
- mColumnNameConstantsMap.put("name", StructuredName.DISPLAY_NAME);
- mColumnNameConstantsMap.put("givenname", StructuredName.GIVEN_NAME);
- mColumnNameConstantsMap.put("familyname", StructuredName.FAMILY_NAME);
- mColumnNameConstantsMap.put("honorificprefix", StructuredName.PREFIX);
- mColumnNameConstantsMap.put("honorificsuffix", StructuredName.SUFFIX);
- mColumnNameConstantsMap.put("additionalname", CUSTOM_DATA_COLUMN);
- mColumnNameConstantsMap.put("nickname", Nickname.NAME);
- mColumnNameConstantsMap.put("adr", StructuredPostal.STREET);
- mColumnNameConstantsMap.put("email", Email.ADDRESS);
- mColumnNameConstantsMap.put("url", Website.URL);
- mColumnNameConstantsMap.put("category", GroupMembership.GROUP_ROW_ID);
- mColumnNameConstantsMap.put("tel", Phone.NUMBER);
- mColumnNameConstantsMap.put("org", Organization.COMPANY);
- mColumnNameConstantsMap.put("jobTitle", Organization.TITLE);
- mColumnNameConstantsMap.put("note", Note.NOTE);
- mColumnNameConstantsMap.put("impp", Im.DATA);
- mColumnNameConstantsMap.put("sex", CUSTOM_DATA_COLUMN);
- mColumnNameConstantsMap.put("genderidentity", CUSTOM_DATA_COLUMN);
- mColumnNameConstantsMap.put("key", CUSTOM_DATA_COLUMN);
- }
-
- private String getMimeTypeOfField(String field) {
- initMimeTypeConstantsMap();
- return mMimeTypeConstantsMap.get(field.toLowerCase());
- }
-
- private void initMimeTypeConstantsMap() {
- if (mMimeTypeConstantsMap != null) {
- return;
- }
- mMimeTypeConstantsMap = new HashMap<String, String>();
-
- mMimeTypeConstantsMap.put("name", StructuredName.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("givenname", StructuredName.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("familyname", StructuredName.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("honorificprefix", StructuredName.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("honorificsuffix", StructuredName.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("additionalname", MIMETYPE_ADDITIONAL_NAME);
- mMimeTypeConstantsMap.put("nickname", Nickname.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("email", Email.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("url", Website.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("category", GroupMembership.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("tel", Phone.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("org", Organization.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("jobTitle", Organization.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("note", Note.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("impp", Im.CONTENT_ITEM_TYPE);
- mMimeTypeConstantsMap.put("sex", MIMETYPE_SEX);
- mMimeTypeConstantsMap.put("genderidentity", MIMETYPE_GENDER_IDENTITY);
- mMimeTypeConstantsMap.put("key", MIMETYPE_KEY);
- }
-
- private int getAddressType(String addressType) {
- initAddressTypesMap();
- Integer type = mAddressTypesMap.get(addressType.toLowerCase());
- return type != null ? type : StructuredPostal.TYPE_CUSTOM;
- }
-
- private void initAddressTypesMap() {
- if (mAddressTypesMap != null) {
- return;
- }
- mAddressTypesMap = new HashMap<String, Integer>();
-
- mAddressTypesMap.put("home", StructuredPostal.TYPE_HOME);
- mAddressTypesMap.put("work", StructuredPostal.TYPE_WORK);
- }
-
- private int getPhoneType(String phoneType) {
- initPhoneTypesMap();
- Integer type = mPhoneTypesMap.get(phoneType.toLowerCase());
- return type != null ? type : Phone.TYPE_CUSTOM;
- }
-
- private void initPhoneTypesMap() {
- if (mPhoneTypesMap != null) {
- return;
- }
- mPhoneTypesMap = new HashMap<String, Integer>();
-
- mPhoneTypesMap.put("home", Phone.TYPE_HOME);
- mPhoneTypesMap.put("mobile", Phone.TYPE_MOBILE);
- mPhoneTypesMap.put("work", Phone.TYPE_WORK);
- mPhoneTypesMap.put("fax home", Phone.TYPE_FAX_HOME);
- mPhoneTypesMap.put("fax work", Phone.TYPE_FAX_WORK);
- mPhoneTypesMap.put("pager", Phone.TYPE_PAGER);
- mPhoneTypesMap.put("callback", Phone.TYPE_CALLBACK);
- mPhoneTypesMap.put("car", Phone.TYPE_CAR);
- mPhoneTypesMap.put("company main", Phone.TYPE_COMPANY_MAIN);
- mPhoneTypesMap.put("isdn", Phone.TYPE_ISDN);
- mPhoneTypesMap.put("main", Phone.TYPE_MAIN);
- mPhoneTypesMap.put("fax other", Phone.TYPE_OTHER_FAX);
- mPhoneTypesMap.put("other fax", Phone.TYPE_OTHER_FAX);
- mPhoneTypesMap.put("radio", Phone.TYPE_RADIO);
- mPhoneTypesMap.put("telex", Phone.TYPE_TELEX);
- mPhoneTypesMap.put("tty", Phone.TYPE_TTY_TDD);
- mPhoneTypesMap.put("ttd", Phone.TYPE_TTY_TDD);
- mPhoneTypesMap.put("work mobile", Phone.TYPE_WORK_MOBILE);
- mPhoneTypesMap.put("work pager", Phone.TYPE_WORK_PAGER);
- mPhoneTypesMap.put("assistant", Phone.TYPE_ASSISTANT);
- mPhoneTypesMap.put("mms", Phone.TYPE_MMS);
- }
-
- private int getEmailType(String emailType) {
- initEmailTypesMap();
- Integer type = mEmailTypesMap.get(emailType.toLowerCase());
- return type != null ? type : Email.TYPE_CUSTOM;
- }
-
- private void initEmailTypesMap() {
- if (mEmailTypesMap != null) {
- return;
- }
- mEmailTypesMap = new HashMap<String, Integer>();
-
- mEmailTypesMap.put("home", Email.TYPE_HOME);
- mEmailTypesMap.put("mobile", Email.TYPE_MOBILE);
- mEmailTypesMap.put("work", Email.TYPE_WORK);
- }
-
- private int getWebsiteType(String websiteType) {
- initWebsiteTypesMap();
- Integer type = mWebsiteTypesMap.get(websiteType.toLowerCase());
- return type != null ? type : Website.TYPE_CUSTOM;
- }
-
- private void initWebsiteTypesMap() {
- if (mWebsiteTypesMap != null) {
- return;
- }
- mWebsiteTypesMap = new HashMap<String, Integer>();
-
- mWebsiteTypesMap.put("homepage", Website.TYPE_HOMEPAGE);
- mWebsiteTypesMap.put("blog", Website.TYPE_BLOG);
- mWebsiteTypesMap.put("profile", Website.TYPE_PROFILE);
- mWebsiteTypesMap.put("home", Website.TYPE_HOME);
- mWebsiteTypesMap.put("work", Website.TYPE_WORK);
- mWebsiteTypesMap.put("ftp", Website.TYPE_FTP);
- }
-
- private int getImType(String imType) {
- initImTypesMap();
- Integer type = mImTypesMap.get(imType.toLowerCase());
- return type != null ? type : Im.TYPE_CUSTOM;
- }
-
- private void initImTypesMap() {
- if (mImTypesMap != null) {
- return;
- }
- mImTypesMap = new HashMap<String, Integer>();
-
- mImTypesMap.put("home", Im.TYPE_HOME);
- mImTypesMap.put("work", Im.TYPE_WORK);
- }
-
- private String[] getAllColumns() {
- return new String[] {Entity.DATA_ID, Data.MIMETYPE, Data.IS_SUPER_PRIMARY,
- Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4,
- Data.DATA5, Data.DATA6, Data.DATA7, Data.DATA8,
- Data.DATA9, Data.DATA10, Data.DATA11, Data.DATA12,
- Data.DATA13, Data.DATA14, Data.DATA15};
- }
-}
deleted file mode 100644
--- a/mobile/android/modules/ContactService.jsm
+++ /dev/null
@@ -1,262 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const DEBUG = false;
-function debug(s) { dump("-*- Android ContactService component: " + s + "\n"); }
-
-const Cu = Components.utils;
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-this.EXPORTED_SYMBOLS = [];
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "ppmm", "@mozilla.org/parentprocessmessagemanager;1",
- "nsIMessageListenerManager");
-
-var ContactService = {
- init: function() {
- if (DEBUG) debug("Init");
- this._requestMessages = {};
-
- // Add listeners for all messages from ContactManager.js
- let messages = ["Contacts:Clear", "Contacts:Find", "Contacts:GetAll",
- "Contacts:GetAll:SendNow", "Contacts:GetCount", "Contacts:GetRevision",
- "Contact:Remove", "Contact:Save",];
- messages.forEach(function(msgName) {
- ppmm.addMessageListener(msgName, this);
- }.bind(this));
-
- // Add listeners for all messages from ContactService.java
- let returnMessages = ["Android:Contacts:Count",
- "Android:Contacts:Clear:Return:OK", "Android:Contacts:Clear:Return:KO",
- "Android:Contacts:Find:Return:OK", "Android:Contacts:Find:Return:KO",
- "Android:Contacts:GetAll:Next", "Android:Contacts:RegisterForMessages",
- "Android:Contact:Remove:Return:OK", "Android:Contact:Remove:Return:KO",
- "Android:Contact:Save:Return:OK", "Android:Contact:Save:Return:KO",];
-
- returnMessages.forEach(function(msgName) {
- Services.obs.addObserver(this, msgName, false);
- }.bind(this));
- },
-
- _sendMessageToJava: function(aMsg) {
- Services.androidBridge.handleGeckoMessage(aMsg);
- },
-
- _sendReturnMessage: function(aTopic, aRequestID, aResult) {
- this._requestMessages[aRequestID].target.sendAsyncMessage(aTopic, aResult);
- },
-
- _sendAndDeleteReturnMessage: function(aTopic, aRequestID, aResult) {
- this._sendReturnMessage(aTopic, aRequestID, aResult)
- delete this._requestMessages[aRequestID];
- },
-
- observe: function(aSubject, aTopic, aData) {
- if (DEBUG) {
- debug("observe: subject: " + aSubject + " topic: " + aTopic + " data: " + aData);
- }
-
- let message = JSON.parse(aData, function date_reviver(k, v) {
- // The Java service sends dates as strings, so convert them to Dates before
- // sending them back to the child.
- if (v != null && v != "null" &&
- ["updated", "published", "anniversary", "bday"].indexOf(k) != -1) {
- return new Date(v);
- }
- return v;
- });
- let requestID = message.requestID;
-
- // The return message topic is the same as the current topic, but without the "Android:" prefix
- let returnMessageTopic = aTopic.substring(8);
-
- switch (aTopic) {
- case "Android:Contacts:Find:Return:OK":
- this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID, contacts: message.contacts});
- break;
-
- case "Android:Contacts:Find:Return:KO":
- this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID});
- break;
-
- case "Android:Contact:Save:Return:OK":
- this._sendReturnMessage(returnMessageTopic, requestID, {requestID: requestID, contactID: message.contactID});
- this._sendAndDeleteReturnMessage("Contact:Changed", requestID, {contactID: message.contactID, reason: message.reason});
- break;
-
- case "Android:Contact:Save:Return:KO":
- this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID});
- break;
-
- case "Android:Contact:Remove:Return:OK":
- this._sendReturnMessage(returnMessageTopic, requestID, {requestID: requestID, contactID: message.contactID});
- this._sendAndDeleteReturnMessage("Contact:Changed", requestID, {contactID: message.contactID, reason: "remove"});
- break;
-
- case "Android:Contact:Remove:Return:KO":
- this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID});
- break;
-
- case "Android:Contacts:Clear:Return:OK":
- this._sendReturnMessage(returnMessageTopic, requestID, {requestID: requestID});
- this._sendAndDeleteReturnMessage("Contact:Changed", requestID, {reason: "remove"});
- break;
-
- case "Android:Contact:Clear:Return:KO":
- this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID});
- break;
-
- case "Android:Contacts:GetAll:Next":
- // GetAll uses a cursor ID instead of a request ID. Translate the request ID back to the cursor ID
- this._sendReturnMessage(returnMessageTopic, requestID, {cursorId: requestID, contacts: message.contacts});
-
- // Send a message with no contacts to denote the end of contacts returned by the query
- this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {cursorId: requestID});
- break;
-
- case "Android:Contacts:Count":
- this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID, count: message.count});
- break;
-
- default:
- throw "Wrong message received: " + aTopic;
- }
- },
-
- assertPermission: function(aMessage, aPerm) {
- if (!aMessage.target.assertPermission(aPerm)) {
- Cu.reportError("Contacts message " + aMessage.name +
- " from a content process with no" + aPerm + " privileges.");
- return false;
- }
- return true;
- },
-
- receiveMessage: function(aMessage) {
- if (DEBUG) debug("receiveMessage " + aMessage.name);
-
- // GetAll uses a cursor ID instead of a request ID, but they can be treated the same from here
- if (!aMessage.data.requestID && aMessage.data.cursorId) {
- aMessage.data.requestID = aMessage.data.cursorId;
- }
- let requestID = aMessage.data.requestID;
-
- // Store the message so it the request callback can be called when the Java side is finished
- this._requestMessages[requestID] = aMessage;
-
- switch (aMessage.name) {
- case "Contacts:Find":
- this.findContacts(aMessage);
- break;
-
- case "Contacts:GetAll":
- this.getAllContacts(aMessage);
- break;
-
- case "Contacts:GetAll:SendNow":
- // Send an empty message to denote there are no most contacts for the getAll query
- this._sendAndDeleteReturnMessage("Contacts:GetAll:Next", requestID, {cursorId: requestID});
- break;
-
- case "Contact:Save":
- this.saveContact(aMessage);
- break;
-
- case "Contact:Remove":
- this.removeContact(aMessage);
- break;
-
- case "Contacts:Clear":
- this.clearContacts(aMessage);
- break;
-
- case "Contacts:GetCount":
- this.getContactsCount(aMessage);
- break;
-
- case "Contacts:GetRevision":
- // Android does not support the get revision function
- this._sendAndDeleteReturnMessage("Contacts:GetRevision:Return:KO", requestID, {requestID: requestID,
- errorMsg: "Android does not support the revision function."});
- break;
-
- case "Contacts:RegisterForMessages":
- delete this._requestMessages[requestID];
- break;
-
- default:
- delete this._requestMessages[requestID];
- throw "Wrong message received: " + aMessage.name;
- }
- },
-
- findContacts: function(aMessage) {
- if (!this.assertPermission(aMessage, "contacts-read")) {
- return;
- }
-
- let countryName = PhoneNumberUtils.getCountryName();
- let substringmatchingPref = "dom.phonenumber.substringmatching." + countryName;
- let substringmatchingValue = 0;
- if (Services.prefs.getPrefType(substringmatchingPref) == Ci.nsIPrefBranch.PREF_INT) {
- substringmatchingValue = Services.prefs.getIntPref(substringmatchingPref);
- }
-
- // Add the substring matching value to the find options JSON
- aMessage.data.options.findOptions.substringMatching = substringmatchingValue;
-
- this._sendMessageToJava({type: "Android:Contacts:Find", data: aMessage.data});
- },
-
- getAllContacts: function(aMessage) {
- if (!this.assertPermission(aMessage, "contacts-read")) {
- return;
- }
-
- this._sendMessageToJava({type: "Android:Contacts:GetAll", data: aMessage.data});
- },
-
- saveContact: function(aMessage) {
- if ((aMessage.data.options.reason === "create" &&
- !this.assertPermission(aMessage, "contacts-create")) ||
- !this.assertPermission(aMessage, "contacts-write")) {
- return;
- }
-
- this._sendMessageToJava({type: "Android:Contact:Save", data: aMessage.data});
- },
-
- removeContact: function(aMessage) {
- if (!this.assertPermission(aMessage, "contacts-write")) {
- return;
- }
-
- this._sendMessageToJava({type: "Android:Contact:Remove", data: aMessage.data});
- },
-
- clearContacts: function(aMessage) {
- if (!this.assertPermission(aMessage, "contacts-write")) {
- return;
- }
-
- this._sendMessageToJava({type: "Android:Contacts:Clear", data: aMessage.data});
- },
-
- getContactsCount: function(aMessage) {
- if (!this.assertPermission(aMessage, "contacts-read")) {
- return;
- }
-
- this._sendMessageToJava({type: "Android:Contacts:GetCount", data: aMessage.data});
- },
-}
-
-ContactService.init();