Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | package org.mozilla.gecko; |
michael@0 | 6 | |
michael@0 | 7 | import java.util.ArrayList; |
michael@0 | 8 | import java.util.Collections; |
michael@0 | 9 | import java.util.Comparator; |
michael@0 | 10 | import java.util.HashMap; |
michael@0 | 11 | import java.util.List; |
michael@0 | 12 | import java.util.Map.Entry; |
michael@0 | 13 | |
michael@0 | 14 | import org.json.JSONArray; |
michael@0 | 15 | import org.json.JSONException; |
michael@0 | 16 | import org.json.JSONObject; |
michael@0 | 17 | import org.mozilla.gecko.util.GeckoEventListener; |
michael@0 | 18 | import org.mozilla.gecko.util.ThreadUtils; |
michael@0 | 19 | |
michael@0 | 20 | import android.accounts.Account; |
michael@0 | 21 | import android.accounts.AccountManager; |
michael@0 | 22 | import android.app.AlertDialog; |
michael@0 | 23 | import android.content.ContentProviderOperation; |
michael@0 | 24 | import android.content.ContentProviderResult; |
michael@0 | 25 | import android.content.ContentResolver; |
michael@0 | 26 | import android.content.ContentUris; |
michael@0 | 27 | import android.content.ContentValues; |
michael@0 | 28 | import android.content.DialogInterface; |
michael@0 | 29 | import android.content.OperationApplicationException; |
michael@0 | 30 | import android.database.Cursor; |
michael@0 | 31 | import android.net.Uri; |
michael@0 | 32 | import android.os.Build; |
michael@0 | 33 | import android.os.RemoteException; |
michael@0 | 34 | import android.provider.ContactsContract; |
michael@0 | 35 | import android.provider.ContactsContract.CommonDataKinds.BaseTypes; |
michael@0 | 36 | import android.provider.ContactsContract.CommonDataKinds.Email; |
michael@0 | 37 | import android.provider.ContactsContract.CommonDataKinds.Event; |
michael@0 | 38 | import android.provider.ContactsContract.CommonDataKinds.GroupMembership; |
michael@0 | 39 | import android.provider.ContactsContract.CommonDataKinds.Im; |
michael@0 | 40 | import android.provider.ContactsContract.CommonDataKinds.Nickname; |
michael@0 | 41 | import android.provider.ContactsContract.CommonDataKinds.Note; |
michael@0 | 42 | import android.provider.ContactsContract.CommonDataKinds.Organization; |
michael@0 | 43 | import android.provider.ContactsContract.CommonDataKinds.Phone; |
michael@0 | 44 | import android.provider.ContactsContract.CommonDataKinds.StructuredName; |
michael@0 | 45 | import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; |
michael@0 | 46 | import android.provider.ContactsContract.CommonDataKinds.Website; |
michael@0 | 47 | import android.provider.ContactsContract.Data; |
michael@0 | 48 | import android.provider.ContactsContract.Groups; |
michael@0 | 49 | import android.provider.ContactsContract.RawContacts; |
michael@0 | 50 | import android.provider.ContactsContract.RawContacts.Entity; |
michael@0 | 51 | import android.telephony.PhoneNumberUtils; |
michael@0 | 52 | import android.util.Log; |
michael@0 | 53 | |
michael@0 | 54 | public class ContactService implements GeckoEventListener { |
michael@0 | 55 | private static final String LOGTAG = "GeckoContactService"; |
michael@0 | 56 | private static final boolean DEBUG = false; |
michael@0 | 57 | |
michael@0 | 58 | private final static int GROUP_ACCOUNT_NAME = 0; |
michael@0 | 59 | private final static int GROUP_ACCOUNT_TYPE = 1; |
michael@0 | 60 | private final static int GROUP_ID = 2; |
michael@0 | 61 | private final static int GROUP_TITLE = 3; |
michael@0 | 62 | private final static int GROUP_AUTO_ADD = 4; |
michael@0 | 63 | |
michael@0 | 64 | private final static String CARRIER_COLUMN = Data.DATA5; |
michael@0 | 65 | private final static String CUSTOM_DATA_COLUMN = Data.DATA1; |
michael@0 | 66 | |
michael@0 | 67 | // Pre-Honeycomb versions of Android have a "My Contacts" system group that all contacts are |
michael@0 | 68 | // assigned to by default for a given account. After Honeycomb, an AUTO_ADD database column |
michael@0 | 69 | // was added to denote groups that contacts are automatically added to |
michael@0 | 70 | private final static String PRE_HONEYCOMB_DEFAULT_GROUP = "System Group: My Contacts"; |
michael@0 | 71 | private final static String MIMETYPE_ADDITIONAL_NAME = "org.mozilla.gecko/additional_name"; |
michael@0 | 72 | private final static String MIMETYPE_SEX = "org.mozilla.gecko/sex"; |
michael@0 | 73 | private final static String MIMETYPE_GENDER_IDENTITY = "org.mozilla.gecko/gender_identity"; |
michael@0 | 74 | private final static String MIMETYPE_KEY = "org.mozilla.gecko/key"; |
michael@0 | 75 | private final static String MIMETYPE_MOZILLA_CONTACTS_FLAG = "org.mozilla.gecko/contact_flag"; |
michael@0 | 76 | |
michael@0 | 77 | private final EventDispatcher mEventDispatcher; |
michael@0 | 78 | |
michael@0 | 79 | private String mAccountName; |
michael@0 | 80 | private String mAccountType; |
michael@0 | 81 | private String mGroupTitle; |
michael@0 | 82 | private long mGroupId; |
michael@0 | 83 | private boolean mGotDeviceAccount; |
michael@0 | 84 | |
michael@0 | 85 | private HashMap<String, String> mColumnNameConstantsMap; |
michael@0 | 86 | private HashMap<String, String> mMimeTypeConstantsMap; |
michael@0 | 87 | private HashMap<String, Integer> mAddressTypesMap; |
michael@0 | 88 | private HashMap<String, Integer> mPhoneTypesMap; |
michael@0 | 89 | private HashMap<String, Integer> mEmailTypesMap; |
michael@0 | 90 | private HashMap<String, Integer> mWebsiteTypesMap; |
michael@0 | 91 | private HashMap<String, Integer> mImTypesMap; |
michael@0 | 92 | |
michael@0 | 93 | private ContentResolver mContentResolver; |
michael@0 | 94 | private GeckoApp mActivity; |
michael@0 | 95 | |
michael@0 | 96 | ContactService(EventDispatcher eventDispatcher, GeckoApp activity) { |
michael@0 | 97 | mEventDispatcher = eventDispatcher; |
michael@0 | 98 | mActivity = activity; |
michael@0 | 99 | mContentResolver = mActivity.getContentResolver(); |
michael@0 | 100 | mGotDeviceAccount = false; |
michael@0 | 101 | |
michael@0 | 102 | registerEventListener("Android:Contacts:Clear"); |
michael@0 | 103 | registerEventListener("Android:Contacts:Find"); |
michael@0 | 104 | registerEventListener("Android:Contacts:GetAll"); |
michael@0 | 105 | registerEventListener("Android:Contacts:GetCount"); |
michael@0 | 106 | registerEventListener("Android:Contact:Remove"); |
michael@0 | 107 | registerEventListener("Android:Contact:Save"); |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | public void destroy() { |
michael@0 | 111 | unregisterEventListener("Android:Contacts:Clear"); |
michael@0 | 112 | unregisterEventListener("Android:Contacts:Find"); |
michael@0 | 113 | unregisterEventListener("Android:Contacts:GetAll"); |
michael@0 | 114 | unregisterEventListener("Android:Contacts:GetCount"); |
michael@0 | 115 | unregisterEventListener("Android:Contact:Remove"); |
michael@0 | 116 | unregisterEventListener("Android:Contact:Save"); |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | @Override |
michael@0 | 120 | public void handleMessage(final String event, final JSONObject message) { |
michael@0 | 121 | // If the account chooser dialog needs shown to the user, the message handling becomes |
michael@0 | 122 | // asychronous so it needs posted to a background thread from the UI thread when the |
michael@0 | 123 | // account chooser dialog is dismissed by the user. |
michael@0 | 124 | Runnable handleMessage = new Runnable() { |
michael@0 | 125 | @Override |
michael@0 | 126 | public void run() { |
michael@0 | 127 | try { |
michael@0 | 128 | if (DEBUG) { |
michael@0 | 129 | Log.d(LOGTAG, "Event: " + event + "\nMessage: " + message.toString(3)); |
michael@0 | 130 | } |
michael@0 | 131 | |
michael@0 | 132 | final JSONObject messageData = message.getJSONObject("data"); |
michael@0 | 133 | final String requestID = messageData.getString("requestID"); |
michael@0 | 134 | |
michael@0 | 135 | // Options may not exist for all operations |
michael@0 | 136 | JSONObject contactOptions = messageData.optJSONObject("options"); |
michael@0 | 137 | |
michael@0 | 138 | if ("Android:Contacts:Find".equals(event)) { |
michael@0 | 139 | findContacts(contactOptions, requestID); |
michael@0 | 140 | } else if ("Android:Contacts:GetAll".equals(event)) { |
michael@0 | 141 | getAllContacts(messageData, requestID); |
michael@0 | 142 | } else if ("Android:Contacts:Clear".equals(event)) { |
michael@0 | 143 | clearAllContacts(contactOptions, requestID); |
michael@0 | 144 | } else if ("Android:Contact:Save".equals(event)) { |
michael@0 | 145 | saveContact(contactOptions, requestID); |
michael@0 | 146 | } else if ("Android:Contact:Remove".equals(event)) { |
michael@0 | 147 | removeContact(contactOptions, requestID); |
michael@0 | 148 | } else if ("Android:Contacts:GetCount".equals(event)) { |
michael@0 | 149 | getContactsCount(requestID); |
michael@0 | 150 | } else { |
michael@0 | 151 | throw new IllegalArgumentException("Unexpected event: " + event); |
michael@0 | 152 | } |
michael@0 | 153 | } catch (JSONException e) { |
michael@0 | 154 | throw new IllegalArgumentException("Message: " + e); |
michael@0 | 155 | } |
michael@0 | 156 | } |
michael@0 | 157 | }; |
michael@0 | 158 | |
michael@0 | 159 | // Get the account name/type if they haven't been set yet |
michael@0 | 160 | if (!mGotDeviceAccount) { |
michael@0 | 161 | getDeviceAccount(handleMessage); |
michael@0 | 162 | } else { |
michael@0 | 163 | handleMessage.run(); |
michael@0 | 164 | } |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | private void findContacts(final JSONObject contactOptions, final String requestID) { |
michael@0 | 168 | long[] rawContactIds = findContactsRawIds(contactOptions); |
michael@0 | 169 | Log.i(LOGTAG, "Got " + (rawContactIds != null ? rawContactIds.length : "null") + " raw contact IDs"); |
michael@0 | 170 | |
michael@0 | 171 | final String[] sortOptions = getSortOptionsFromJSON(contactOptions); |
michael@0 | 172 | |
michael@0 | 173 | if (rawContactIds == null || sortOptions == null) { |
michael@0 | 174 | sendCallbackToJavascript("Android:Contacts:Find:Return:KO", requestID, null, null); |
michael@0 | 175 | } else { |
michael@0 | 176 | sendCallbackToJavascript("Android:Contacts:Find:Return:OK", requestID, |
michael@0 | 177 | new String[] {"contacts"}, |
michael@0 | 178 | new Object[] {getContactsAsJSONArray(rawContactIds, sortOptions[0], |
michael@0 | 179 | sortOptions[1])}); |
michael@0 | 180 | } |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | private void getAllContacts(final JSONObject contactOptions, final String requestID) { |
michael@0 | 184 | long[] rawContactIds = getAllRawContactIds(); |
michael@0 | 185 | Log.i(LOGTAG, "Got " + rawContactIds.length + " raw contact IDs"); |
michael@0 | 186 | |
michael@0 | 187 | final String[] sortOptions = getSortOptionsFromJSON(contactOptions); |
michael@0 | 188 | |
michael@0 | 189 | if (rawContactIds == null || sortOptions == null) { |
michael@0 | 190 | // There's no failure message for getAll |
michael@0 | 191 | return; |
michael@0 | 192 | } else { |
michael@0 | 193 | sendCallbackToJavascript("Android:Contacts:GetAll:Next", requestID, |
michael@0 | 194 | new String[] {"contacts"}, |
michael@0 | 195 | new Object[] {getContactsAsJSONArray(rawContactIds, sortOptions[0], |
michael@0 | 196 | sortOptions[1])}); |
michael@0 | 197 | } |
michael@0 | 198 | } |
michael@0 | 199 | |
michael@0 | 200 | private static String[] getSortOptionsFromJSON(final JSONObject contactOptions) { |
michael@0 | 201 | String sortBy = null; |
michael@0 | 202 | String sortOrder = null; |
michael@0 | 203 | |
michael@0 | 204 | try { |
michael@0 | 205 | final JSONObject findOptions = contactOptions.getJSONObject("findOptions"); |
michael@0 | 206 | sortBy = findOptions.optString("sortBy").toLowerCase(); |
michael@0 | 207 | sortOrder = findOptions.optString("sortOrder").toLowerCase(); |
michael@0 | 208 | |
michael@0 | 209 | if ("".equals(sortBy)) { |
michael@0 | 210 | sortBy = null; |
michael@0 | 211 | } |
michael@0 | 212 | if ("".equals(sortOrder)) { |
michael@0 | 213 | sortOrder = "ascending"; |
michael@0 | 214 | } |
michael@0 | 215 | |
michael@0 | 216 | // Only "familyname" and "givenname" are valid sortBy values and only "ascending" |
michael@0 | 217 | // and "descending" are valid sortOrder values |
michael@0 | 218 | if ((sortBy != null && !"familyname".equals(sortBy) && !"givenname".equals(sortBy)) || |
michael@0 | 219 | (!"ascending".equals(sortOrder) && !"descending".equals(sortOrder))) { |
michael@0 | 220 | return null; |
michael@0 | 221 | } |
michael@0 | 222 | } catch (JSONException e) { |
michael@0 | 223 | throw new IllegalArgumentException(e); |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | return new String[] {sortBy, sortOrder}; |
michael@0 | 227 | } |
michael@0 | 228 | |
michael@0 | 229 | private long[] findContactsRawIds(final JSONObject contactOptions) { |
michael@0 | 230 | List<Long> rawContactIds = new ArrayList<Long>(); |
michael@0 | 231 | Cursor cursor = null; |
michael@0 | 232 | |
michael@0 | 233 | try { |
michael@0 | 234 | final JSONObject findOptions = contactOptions.getJSONObject("findOptions"); |
michael@0 | 235 | String filterValue = findOptions.optString("filterValue"); |
michael@0 | 236 | JSONArray filterBy = findOptions.optJSONArray("filterBy"); |
michael@0 | 237 | final String filterOp = findOptions.optString("filterOp"); |
michael@0 | 238 | final int filterLimit = findOptions.getInt("filterLimit"); |
michael@0 | 239 | final int substringMatching = findOptions.getInt("substringMatching"); |
michael@0 | 240 | |
michael@0 | 241 | // If filter value is undefined, avoid all the logic below and just return |
michael@0 | 242 | // all available raw contact IDs |
michael@0 | 243 | if ("".equals(filterValue) || "".equals(filterOp)) { |
michael@0 | 244 | long[] allRawContactIds = getAllRawContactIds(); |
michael@0 | 245 | |
michael@0 | 246 | // Truncate the raw contacts IDs array if necessary |
michael@0 | 247 | if (filterLimit > 0 && allRawContactIds.length > filterLimit) { |
michael@0 | 248 | long[] truncatedRawContactIds = new long[filterLimit]; |
michael@0 | 249 | for (int i = 0; i < filterLimit; i++) { |
michael@0 | 250 | truncatedRawContactIds[i] = allRawContactIds[i]; |
michael@0 | 251 | } |
michael@0 | 252 | return truncatedRawContactIds; |
michael@0 | 253 | } |
michael@0 | 254 | return allRawContactIds; |
michael@0 | 255 | } |
michael@0 | 256 | |
michael@0 | 257 | // "match" can only be used with the "tel" field |
michael@0 | 258 | if ("match".equals(filterOp)) { |
michael@0 | 259 | for (int i = 0; i < filterBy.length(); i++) { |
michael@0 | 260 | if (!"tel".equals(filterBy.getString(i))) { |
michael@0 | 261 | Log.w(LOGTAG, "\"match\" filterBy option is only valid for the \"tel\" field"); |
michael@0 | 262 | return null; |
michael@0 | 263 | } |
michael@0 | 264 | } |
michael@0 | 265 | } |
michael@0 | 266 | |
michael@0 | 267 | // Only select contacts from the selected account |
michael@0 | 268 | String selection = null; |
michael@0 | 269 | String[] selectionArgs = null; |
michael@0 | 270 | |
michael@0 | 271 | if (mAccountName != null) { |
michael@0 | 272 | selection = RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?"; |
michael@0 | 273 | selectionArgs = new String[] {mAccountName, mAccountType}; |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | |
michael@0 | 277 | final String[] columnsToGet; |
michael@0 | 278 | |
michael@0 | 279 | // If a filterBy value was not specified, search all columns |
michael@0 | 280 | if (filterBy == null || filterBy.length() == 0) { |
michael@0 | 281 | columnsToGet = null; |
michael@0 | 282 | } else { |
michael@0 | 283 | // Only get the columns given in the filterBy array |
michael@0 | 284 | List<String> columnsToGetList = new ArrayList<String>(); |
michael@0 | 285 | |
michael@0 | 286 | columnsToGetList.add(Data.RAW_CONTACT_ID); |
michael@0 | 287 | columnsToGetList.add(Data.MIMETYPE); |
michael@0 | 288 | for (int i = 0; i < filterBy.length(); i++) { |
michael@0 | 289 | final String field = filterBy.getString(i); |
michael@0 | 290 | |
michael@0 | 291 | // If one of the filterBy fields is the ID, just return the filter value |
michael@0 | 292 | // which should be the ID |
michael@0 | 293 | if ("id".equals(field)) { |
michael@0 | 294 | try { |
michael@0 | 295 | return new long[] {Long.valueOf(filterValue)}; |
michael@0 | 296 | } catch (NumberFormatException e) { |
michael@0 | 297 | // If the ID couldn't be converted to a long, it's invalid data |
michael@0 | 298 | // so return null for failure |
michael@0 | 299 | return null; |
michael@0 | 300 | } |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | final String columnName = getColumnNameConstant(field); |
michael@0 | 304 | |
michael@0 | 305 | if (columnName != null) { |
michael@0 | 306 | columnsToGetList.add(columnName); |
michael@0 | 307 | } else { |
michael@0 | 308 | Log.w(LOGTAG, "Unknown filter option: " + field); |
michael@0 | 309 | } |
michael@0 | 310 | } |
michael@0 | 311 | |
michael@0 | 312 | columnsToGet = columnsToGetList.toArray(new String[columnsToGetList.size()]); |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | // Execute the query |
michael@0 | 316 | cursor = mContentResolver.query(Data.CONTENT_URI, columnsToGet, selection, |
michael@0 | 317 | selectionArgs, null); |
michael@0 | 318 | |
michael@0 | 319 | if (cursor.getCount() > 0) { |
michael@0 | 320 | cursor.moveToPosition(-1); |
michael@0 | 321 | while (cursor.moveToNext()) { |
michael@0 | 322 | String mimeType = cursor.getString(cursor.getColumnIndex(Data.MIMETYPE)); |
michael@0 | 323 | |
michael@0 | 324 | // Check if the current mimetype is one of the types to filter by |
michael@0 | 325 | if (filterBy != null && filterBy.length() > 0) { |
michael@0 | 326 | for (int i = 0; i < filterBy.length(); i++) { |
michael@0 | 327 | String currentFilterBy = filterBy.getString(i); |
michael@0 | 328 | |
michael@0 | 329 | if (mimeType.equals(getMimeTypeOfField(currentFilterBy))) { |
michael@0 | 330 | String columnName = getColumnNameConstant(currentFilterBy); |
michael@0 | 331 | int columnIndex = cursor.getColumnIndex(columnName); |
michael@0 | 332 | String databaseValue = cursor.getString(columnIndex); |
michael@0 | 333 | |
michael@0 | 334 | boolean isPhone = false; |
michael@0 | 335 | if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) { |
michael@0 | 336 | isPhone = true; |
michael@0 | 337 | } else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) { |
michael@0 | 338 | // Translate the group ID to the group name for matching |
michael@0 | 339 | try { |
michael@0 | 340 | databaseValue = getGroupName(Long.valueOf(databaseValue)); |
michael@0 | 341 | } catch (NumberFormatException e) { |
michael@0 | 342 | Log.e(LOGTAG, "Number Format Exception", e); |
michael@0 | 343 | continue; |
michael@0 | 344 | } |
michael@0 | 345 | } else if (databaseValue == null) { |
michael@0 | 346 | continue; |
michael@0 | 347 | } |
michael@0 | 348 | |
michael@0 | 349 | // Check if the value matches the filter value |
michael@0 | 350 | if (isFindMatch(filterOp, filterValue, databaseValue, isPhone, substringMatching)) { |
michael@0 | 351 | addMatchToList(cursor, rawContactIds); |
michael@0 | 352 | break; |
michael@0 | 353 | } |
michael@0 | 354 | } |
michael@0 | 355 | } |
michael@0 | 356 | } else { |
michael@0 | 357 | // If no filterBy options were given, check each column for a match |
michael@0 | 358 | int numColumns = cursor.getColumnCount(); |
michael@0 | 359 | for (int i = 0; i < numColumns; i++) { |
michael@0 | 360 | String databaseValue = cursor.getString(i); |
michael@0 | 361 | if (databaseValue != null && isFindMatch(filterOp, filterValue, databaseValue, false, substringMatching)) { |
michael@0 | 362 | addMatchToList(cursor, rawContactIds); |
michael@0 | 363 | break; |
michael@0 | 364 | } |
michael@0 | 365 | } |
michael@0 | 366 | } |
michael@0 | 367 | |
michael@0 | 368 | // If the max found contacts size has been hit, stop looking for contacts |
michael@0 | 369 | // A filter limit of 0 denotes there is no limit |
michael@0 | 370 | if (filterLimit > 0 && filterLimit <= rawContactIds.size()) { |
michael@0 | 371 | break; |
michael@0 | 372 | } |
michael@0 | 373 | } |
michael@0 | 374 | } |
michael@0 | 375 | } catch (JSONException e) { |
michael@0 | 376 | throw new IllegalArgumentException(e); |
michael@0 | 377 | } finally { |
michael@0 | 378 | if (cursor != null) { |
michael@0 | 379 | cursor.close(); |
michael@0 | 380 | } |
michael@0 | 381 | } |
michael@0 | 382 | |
michael@0 | 383 | // Return the contact IDs list converted to an array |
michael@0 | 384 | return convertLongListToArray(rawContactIds); |
michael@0 | 385 | } |
michael@0 | 386 | |
michael@0 | 387 | private boolean isFindMatch(final String filterOp, String filterValue, String databaseValue, |
michael@0 | 388 | final boolean isPhone, final int substringMatching) { |
michael@0 | 389 | Log.i(LOGTAG, "matching: filterOp: " + filterOp); |
michael@0 | 390 | if (DEBUG) { |
michael@0 | 391 | Log.d(LOGTAG, "matching: filterValue: " + filterValue); |
michael@0 | 392 | Log.d(LOGTAG, "matching: databaseValue: " + databaseValue); |
michael@0 | 393 | } |
michael@0 | 394 | Log.i(LOGTAG, "matching: isPhone: " + isPhone); |
michael@0 | 395 | Log.i(LOGTAG, "matching: substringMatching: " + substringMatching); |
michael@0 | 396 | |
michael@0 | 397 | if (databaseValue == null) { |
michael@0 | 398 | return false; |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | filterValue = filterValue.toLowerCase(); |
michael@0 | 402 | databaseValue = databaseValue.toLowerCase(); |
michael@0 | 403 | |
michael@0 | 404 | if ("match".equals(filterOp)) { |
michael@0 | 405 | // If substring matching is a positive number, only pay attention to the last X characters |
michael@0 | 406 | // of both the filter and database values |
michael@0 | 407 | if (substringMatching > 0) { |
michael@0 | 408 | databaseValue = substringStartFromEnd(cleanPhoneNumber(databaseValue), substringMatching); |
michael@0 | 409 | filterValue = substringStartFromEnd(cleanPhoneNumber(filterValue), substringMatching); |
michael@0 | 410 | return databaseValue.startsWith(filterValue); |
michael@0 | 411 | } |
michael@0 | 412 | return databaseValue.equals(filterValue); |
michael@0 | 413 | } else if ("equals".equals(filterOp)) { |
michael@0 | 414 | if (isPhone) { |
michael@0 | 415 | return PhoneNumberUtils.compare(filterValue, databaseValue); |
michael@0 | 416 | } |
michael@0 | 417 | return databaseValue.equals(filterValue); |
michael@0 | 418 | } else if ("contains".equals(filterOp)) { |
michael@0 | 419 | if (isPhone) { |
michael@0 | 420 | filterValue = cleanPhoneNumber(filterValue); |
michael@0 | 421 | databaseValue = cleanPhoneNumber(databaseValue); |
michael@0 | 422 | } |
michael@0 | 423 | return databaseValue.contains(filterValue); |
michael@0 | 424 | } else if ("startsWith".equals(filterOp)) { |
michael@0 | 425 | // If a phone number, remove non-dialable characters and then only pay attention to |
michael@0 | 426 | // the last X digits given by the substring matching values (see bug 877302) |
michael@0 | 427 | if (isPhone) { |
michael@0 | 428 | String cleanedDatabasePhone = cleanPhoneNumber(databaseValue); |
michael@0 | 429 | if (substringMatching > 0) { |
michael@0 | 430 | cleanedDatabasePhone = substringStartFromEnd(cleanedDatabasePhone, substringMatching); |
michael@0 | 431 | } |
michael@0 | 432 | |
michael@0 | 433 | if (cleanedDatabasePhone.startsWith(filterValue)) { |
michael@0 | 434 | return true; |
michael@0 | 435 | } |
michael@0 | 436 | } |
michael@0 | 437 | return databaseValue.startsWith(filterValue); |
michael@0 | 438 | } |
michael@0 | 439 | return false; |
michael@0 | 440 | } |
michael@0 | 441 | |
michael@0 | 442 | private static String cleanPhoneNumber(String phone) { |
michael@0 | 443 | return phone.replace(" ", "").replace("(", "").replace(")", "").replace("-", ""); |
michael@0 | 444 | } |
michael@0 | 445 | |
michael@0 | 446 | private static String substringStartFromEnd(final String string, final int distanceFromEnd) { |
michael@0 | 447 | int stringLen = string.length(); |
michael@0 | 448 | if (stringLen < distanceFromEnd) { |
michael@0 | 449 | return string; |
michael@0 | 450 | } |
michael@0 | 451 | return string.substring(stringLen - distanceFromEnd); |
michael@0 | 452 | } |
michael@0 | 453 | |
michael@0 | 454 | private static void addMatchToList(final Cursor cursor, List<Long> rawContactIds) { |
michael@0 | 455 | long rawContactId = cursor.getLong(cursor.getColumnIndex(Data.RAW_CONTACT_ID)); |
michael@0 | 456 | if (!rawContactIds.contains(rawContactId)) { |
michael@0 | 457 | rawContactIds.add(rawContactId); |
michael@0 | 458 | } |
michael@0 | 459 | } |
michael@0 | 460 | |
michael@0 | 461 | private JSONArray getContactsAsJSONArray(final long[] rawContactIds, final String sortBy, final String sortOrder) { |
michael@0 | 462 | List<JSONObject> contactsList = new ArrayList<JSONObject>(); |
michael@0 | 463 | JSONArray contactsArray = new JSONArray(); |
michael@0 | 464 | |
michael@0 | 465 | // Get each contact as a JSON object |
michael@0 | 466 | for (int i = 0; i < rawContactIds.length; i++) { |
michael@0 | 467 | contactsList.add(getContactAsJSONObject(rawContactIds[i])); |
michael@0 | 468 | } |
michael@0 | 469 | |
michael@0 | 470 | // Sort the contacts |
michael@0 | 471 | if (sortBy != null) { |
michael@0 | 472 | Collections.sort(contactsList, new ContactsComparator(sortBy, sortOrder)); |
michael@0 | 473 | } |
michael@0 | 474 | |
michael@0 | 475 | // Convert the contacts list to a JSON array |
michael@0 | 476 | for (int i = 0; i < contactsList.size(); i++) { |
michael@0 | 477 | contactsArray.put(contactsList.get(i)); |
michael@0 | 478 | } |
michael@0 | 479 | |
michael@0 | 480 | return contactsArray; |
michael@0 | 481 | } |
michael@0 | 482 | |
michael@0 | 483 | private JSONObject getContactAsJSONObject(long rawContactId) { |
michael@0 | 484 | // ContactManager wants a contact object with it's properties wrapped in an array of objects |
michael@0 | 485 | JSONObject contact = new JSONObject(); |
michael@0 | 486 | JSONObject contactProperties = new JSONObject(); |
michael@0 | 487 | |
michael@0 | 488 | JSONArray names = new JSONArray(); |
michael@0 | 489 | JSONArray givenNames = new JSONArray(); |
michael@0 | 490 | JSONArray familyNames = new JSONArray(); |
michael@0 | 491 | JSONArray honorificPrefixes = new JSONArray(); |
michael@0 | 492 | JSONArray honorificSuffixes = new JSONArray(); |
michael@0 | 493 | JSONArray additionalNames = new JSONArray(); |
michael@0 | 494 | JSONArray nicknames = new JSONArray(); |
michael@0 | 495 | JSONArray addresses = new JSONArray(); |
michael@0 | 496 | JSONArray phones = new JSONArray(); |
michael@0 | 497 | JSONArray emails = new JSONArray(); |
michael@0 | 498 | JSONArray organizations = new JSONArray(); |
michael@0 | 499 | JSONArray jobTitles = new JSONArray(); |
michael@0 | 500 | JSONArray notes = new JSONArray(); |
michael@0 | 501 | JSONArray urls = new JSONArray(); |
michael@0 | 502 | JSONArray impps = new JSONArray(); |
michael@0 | 503 | JSONArray categories = new JSONArray(); |
michael@0 | 504 | String bday = null; |
michael@0 | 505 | String anniversary = null; |
michael@0 | 506 | String sex = null; |
michael@0 | 507 | String genderIdentity = null; |
michael@0 | 508 | JSONArray key = new JSONArray(); |
michael@0 | 509 | |
michael@0 | 510 | // Get all the data columns |
michael@0 | 511 | final String[] columnsToGet = getAllColumns(); |
michael@0 | 512 | |
michael@0 | 513 | Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); |
michael@0 | 514 | Uri entityUri = Uri.withAppendedPath(rawContactUri, Entity.CONTENT_DIRECTORY); |
michael@0 | 515 | |
michael@0 | 516 | Cursor cursor = mContentResolver.query(entityUri, columnsToGet, null, null, null); |
michael@0 | 517 | cursor.moveToPosition(-1); |
michael@0 | 518 | while (cursor.moveToNext()) { |
michael@0 | 519 | String mimeType = cursor.getString(cursor.getColumnIndex(Data.MIMETYPE)); |
michael@0 | 520 | |
michael@0 | 521 | // Put the proper fields for each mimetype into the JSON arrays |
michael@0 | 522 | try { |
michael@0 | 523 | if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) { |
michael@0 | 524 | final String displayName = cursor.getString(cursor.getColumnIndex(StructuredName.DISPLAY_NAME)); |
michael@0 | 525 | final String givenName = cursor.getString(cursor.getColumnIndex(StructuredName.GIVEN_NAME)); |
michael@0 | 526 | final String familyName = cursor.getString(cursor.getColumnIndex(StructuredName.FAMILY_NAME)); |
michael@0 | 527 | final String prefix = cursor.getString(cursor.getColumnIndex(StructuredName.PREFIX)); |
michael@0 | 528 | final String suffix = cursor.getString(cursor.getColumnIndex(StructuredName.SUFFIX)); |
michael@0 | 529 | |
michael@0 | 530 | if (displayName != null) { |
michael@0 | 531 | names.put(displayName); |
michael@0 | 532 | } |
michael@0 | 533 | if (givenName != null) { |
michael@0 | 534 | givenNames.put(givenName); |
michael@0 | 535 | } |
michael@0 | 536 | if (familyName != null) { |
michael@0 | 537 | familyNames.put(familyName); |
michael@0 | 538 | } |
michael@0 | 539 | if (prefix != null) { |
michael@0 | 540 | honorificPrefixes.put(prefix); |
michael@0 | 541 | } |
michael@0 | 542 | if (suffix != null) { |
michael@0 | 543 | honorificSuffixes.put(suffix); |
michael@0 | 544 | } |
michael@0 | 545 | |
michael@0 | 546 | } else if (MIMETYPE_ADDITIONAL_NAME.equals(mimeType)) { |
michael@0 | 547 | additionalNames.put(cursor.getString(cursor.getColumnIndex(CUSTOM_DATA_COLUMN))); |
michael@0 | 548 | |
michael@0 | 549 | } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) { |
michael@0 | 550 | nicknames.put(cursor.getString(cursor.getColumnIndex(Nickname.NAME))); |
michael@0 | 551 | |
michael@0 | 552 | } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) { |
michael@0 | 553 | initAddressTypesMap(); |
michael@0 | 554 | getAddressDataAsJSONObject(cursor, addresses); |
michael@0 | 555 | |
michael@0 | 556 | } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) { |
michael@0 | 557 | initPhoneTypesMap(); |
michael@0 | 558 | getPhoneDataAsJSONObject(cursor, phones); |
michael@0 | 559 | |
michael@0 | 560 | } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) { |
michael@0 | 561 | initEmailTypesMap(); |
michael@0 | 562 | getGenericDataAsJSONObject(cursor, emails, Email.ADDRESS, Email.TYPE, Email.LABEL, mEmailTypesMap); |
michael@0 | 563 | |
michael@0 | 564 | } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) { |
michael@0 | 565 | getOrganizationDataAsJSONObject(cursor, organizations, jobTitles); |
michael@0 | 566 | |
michael@0 | 567 | } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType)) { |
michael@0 | 568 | notes.put(cursor.getString(cursor.getColumnIndex(Note.NOTE))); |
michael@0 | 569 | |
michael@0 | 570 | } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) { |
michael@0 | 571 | initWebsiteTypesMap(); |
michael@0 | 572 | getGenericDataAsJSONObject(cursor, urls, Website.URL, Website.TYPE, Website.LABEL, mWebsiteTypesMap); |
michael@0 | 573 | |
michael@0 | 574 | } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) { |
michael@0 | 575 | initImTypesMap(); |
michael@0 | 576 | getGenericDataAsJSONObject(cursor, impps, Im.DATA, Im.TYPE, Im.LABEL, mImTypesMap); |
michael@0 | 577 | |
michael@0 | 578 | } else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) { |
michael@0 | 579 | long groupId = cursor.getLong(cursor.getColumnIndex(GroupMembership.GROUP_ROW_ID)); |
michael@0 | 580 | String groupName = getGroupName(groupId); |
michael@0 | 581 | if (!doesJSONArrayContainString(categories, groupName)) { |
michael@0 | 582 | categories.put(groupName); |
michael@0 | 583 | } |
michael@0 | 584 | |
michael@0 | 585 | } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) { |
michael@0 | 586 | int type = cursor.getInt(cursor.getColumnIndex(Event.TYPE)); |
michael@0 | 587 | String date = cursor.getString(cursor.getColumnIndex(Event.START_DATE)); |
michael@0 | 588 | |
michael@0 | 589 | // Add the time info onto the date so it correctly parses into a JS date object |
michael@0 | 590 | date += "T00:00:00"; |
michael@0 | 591 | |
michael@0 | 592 | switch (type) { |
michael@0 | 593 | case Event.TYPE_BIRTHDAY: |
michael@0 | 594 | bday = date; |
michael@0 | 595 | break; |
michael@0 | 596 | |
michael@0 | 597 | case Event.TYPE_ANNIVERSARY: |
michael@0 | 598 | anniversary = date; |
michael@0 | 599 | break; |
michael@0 | 600 | } |
michael@0 | 601 | |
michael@0 | 602 | } else if (MIMETYPE_SEX.equals(mimeType)) { |
michael@0 | 603 | sex = cursor.getString(cursor.getColumnIndex(CUSTOM_DATA_COLUMN)); |
michael@0 | 604 | |
michael@0 | 605 | } else if (MIMETYPE_GENDER_IDENTITY.equals(mimeType)) { |
michael@0 | 606 | genderIdentity = cursor.getString(cursor.getColumnIndex(CUSTOM_DATA_COLUMN)); |
michael@0 | 607 | |
michael@0 | 608 | } else if (MIMETYPE_KEY.equals(mimeType)) { |
michael@0 | 609 | key.put(cursor.getString(cursor.getColumnIndex(CUSTOM_DATA_COLUMN))); |
michael@0 | 610 | } |
michael@0 | 611 | } catch (JSONException e) { |
michael@0 | 612 | throw new IllegalArgumentException(e); |
michael@0 | 613 | } |
michael@0 | 614 | } |
michael@0 | 615 | cursor.close(); |
michael@0 | 616 | |
michael@0 | 617 | try { |
michael@0 | 618 | // Add the fields to the contact properties object |
michael@0 | 619 | contactProperties.put("name", names); |
michael@0 | 620 | contactProperties.put("givenName", givenNames); |
michael@0 | 621 | contactProperties.put("familyName", familyNames); |
michael@0 | 622 | contactProperties.put("honorificPrefix", honorificPrefixes); |
michael@0 | 623 | contactProperties.put("honorificSuffix", honorificSuffixes); |
michael@0 | 624 | contactProperties.put("additionalName", additionalNames); |
michael@0 | 625 | contactProperties.put("nickname", nicknames); |
michael@0 | 626 | contactProperties.put("adr", addresses); |
michael@0 | 627 | contactProperties.put("tel", phones); |
michael@0 | 628 | contactProperties.put("email", emails); |
michael@0 | 629 | contactProperties.put("org", organizations); |
michael@0 | 630 | contactProperties.put("jobTitle", jobTitles); |
michael@0 | 631 | contactProperties.put("note", notes); |
michael@0 | 632 | contactProperties.put("url", urls); |
michael@0 | 633 | contactProperties.put("impp", impps); |
michael@0 | 634 | contactProperties.put("category", categories); |
michael@0 | 635 | contactProperties.put("key", key); |
michael@0 | 636 | |
michael@0 | 637 | putPossibleNullValueInJSONObject("bday", bday, contactProperties); |
michael@0 | 638 | putPossibleNullValueInJSONObject("anniversary", anniversary, contactProperties); |
michael@0 | 639 | putPossibleNullValueInJSONObject("sex", sex, contactProperties); |
michael@0 | 640 | putPossibleNullValueInJSONObject("genderIdentity", genderIdentity, contactProperties); |
michael@0 | 641 | |
michael@0 | 642 | // Add the raw contact ID and the properties to the contact |
michael@0 | 643 | contact.put("id", String.valueOf(rawContactId)); |
michael@0 | 644 | contact.put("updated", null); |
michael@0 | 645 | contact.put("published", null); |
michael@0 | 646 | contact.put("properties", contactProperties); |
michael@0 | 647 | } catch (JSONException e) { |
michael@0 | 648 | throw new IllegalArgumentException(e); |
michael@0 | 649 | } |
michael@0 | 650 | |
michael@0 | 651 | if (DEBUG) { |
michael@0 | 652 | try { |
michael@0 | 653 | Log.d(LOGTAG, "Got contact: " + contact.toString(3)); |
michael@0 | 654 | } catch (JSONException e) {} |
michael@0 | 655 | } |
michael@0 | 656 | |
michael@0 | 657 | return contact; |
michael@0 | 658 | } |
michael@0 | 659 | |
michael@0 | 660 | private boolean bool(int integer) { |
michael@0 | 661 | return integer != 0 ? true : false; |
michael@0 | 662 | } |
michael@0 | 663 | |
michael@0 | 664 | private void getGenericDataAsJSONObject(Cursor cursor, JSONArray array, final String dataColumn, |
michael@0 | 665 | final String typeColumn, final String typeLabelColumn, |
michael@0 | 666 | final HashMap<String, Integer> typeMap) throws JSONException { |
michael@0 | 667 | String value = cursor.getString(cursor.getColumnIndex(dataColumn)); |
michael@0 | 668 | int typeConstant = cursor.getInt(cursor.getColumnIndex(typeColumn)); |
michael@0 | 669 | String type; |
michael@0 | 670 | if (typeConstant == BaseTypes.TYPE_CUSTOM) { |
michael@0 | 671 | type = cursor.getString(cursor.getColumnIndex(typeLabelColumn)); |
michael@0 | 672 | } else { |
michael@0 | 673 | type = getKeyFromMapValue(typeMap, Integer.valueOf(typeConstant)); |
michael@0 | 674 | } |
michael@0 | 675 | |
michael@0 | 676 | // Since an object may have multiple types, it may have already been added, |
michael@0 | 677 | // but still needs the new type added |
michael@0 | 678 | boolean found = false; |
michael@0 | 679 | if (type != null) { |
michael@0 | 680 | for (int i = 0; i < array.length(); i++) { |
michael@0 | 681 | JSONObject object = array.getJSONObject(i); |
michael@0 | 682 | if (value.equals(object.getString("value"))) { |
michael@0 | 683 | found = true; |
michael@0 | 684 | |
michael@0 | 685 | JSONArray types = object.getJSONArray("type"); |
michael@0 | 686 | if (!doesJSONArrayContainString(types, type)) { |
michael@0 | 687 | types.put(type); |
michael@0 | 688 | break; |
michael@0 | 689 | } |
michael@0 | 690 | } |
michael@0 | 691 | } |
michael@0 | 692 | } |
michael@0 | 693 | |
michael@0 | 694 | // If an existing object wasn't found, make a new one |
michael@0 | 695 | if (!found) { |
michael@0 | 696 | JSONObject object = new JSONObject(); |
michael@0 | 697 | JSONArray types = new JSONArray(); |
michael@0 | 698 | object.put("value", value); |
michael@0 | 699 | types.put(type); |
michael@0 | 700 | object.put("type", types); |
michael@0 | 701 | object.put("pref", bool(cursor.getInt(cursor.getColumnIndex(Data.IS_SUPER_PRIMARY)))); |
michael@0 | 702 | |
michael@0 | 703 | array.put(object); |
michael@0 | 704 | } |
michael@0 | 705 | } |
michael@0 | 706 | |
michael@0 | 707 | private void getPhoneDataAsJSONObject(Cursor cursor, JSONArray phones) throws JSONException { |
michael@0 | 708 | String value = cursor.getString(cursor.getColumnIndex(Phone.NUMBER)); |
michael@0 | 709 | int typeConstant = cursor.getInt(cursor.getColumnIndex(Phone.TYPE)); |
michael@0 | 710 | String type; |
michael@0 | 711 | if (typeConstant == Phone.TYPE_CUSTOM) { |
michael@0 | 712 | type = cursor.getString(cursor.getColumnIndex(Phone.LABEL)); |
michael@0 | 713 | } else { |
michael@0 | 714 | type = getKeyFromMapValue(mPhoneTypesMap, Integer.valueOf(typeConstant)); |
michael@0 | 715 | } |
michael@0 | 716 | |
michael@0 | 717 | // Since a phone may have multiple types, it may have already been added, |
michael@0 | 718 | // but still needs the new type added |
michael@0 | 719 | boolean found = false; |
michael@0 | 720 | if (type != null) { |
michael@0 | 721 | for (int i = 0; i < phones.length(); i++) { |
michael@0 | 722 | JSONObject phone = phones.getJSONObject(i); |
michael@0 | 723 | if (value.equals(phone.getString("value"))) { |
michael@0 | 724 | found = true; |
michael@0 | 725 | |
michael@0 | 726 | JSONArray types = phone.getJSONArray("type"); |
michael@0 | 727 | if (!doesJSONArrayContainString(types, type)) { |
michael@0 | 728 | types.put(type); |
michael@0 | 729 | break; |
michael@0 | 730 | } |
michael@0 | 731 | } |
michael@0 | 732 | } |
michael@0 | 733 | } |
michael@0 | 734 | |
michael@0 | 735 | // If an existing phone wasn't found, make a new one |
michael@0 | 736 | if (!found) { |
michael@0 | 737 | JSONObject phone = new JSONObject(); |
michael@0 | 738 | JSONArray types = new JSONArray(); |
michael@0 | 739 | phone.put("value", value); |
michael@0 | 740 | phone.put("type", type); |
michael@0 | 741 | types.put(type); |
michael@0 | 742 | phone.put("type", types); |
michael@0 | 743 | phone.put("carrier", cursor.getString(cursor.getColumnIndex(CARRIER_COLUMN))); |
michael@0 | 744 | phone.put("pref", bool(cursor.getInt(cursor.getColumnIndex(Phone.IS_SUPER_PRIMARY)))); |
michael@0 | 745 | |
michael@0 | 746 | phones.put(phone); |
michael@0 | 747 | } |
michael@0 | 748 | } |
michael@0 | 749 | |
michael@0 | 750 | private void getAddressDataAsJSONObject(Cursor cursor, JSONArray addresses) throws JSONException { |
michael@0 | 751 | String streetAddress = cursor.getString(cursor.getColumnIndex(StructuredPostal.STREET)); |
michael@0 | 752 | String locality = cursor.getString(cursor.getColumnIndex(StructuredPostal.CITY)); |
michael@0 | 753 | String region = cursor.getString(cursor.getColumnIndex(StructuredPostal.REGION)); |
michael@0 | 754 | String postalCode = cursor.getString(cursor.getColumnIndex(StructuredPostal.POSTCODE)); |
michael@0 | 755 | String countryName = cursor.getString(cursor.getColumnIndex(StructuredPostal.COUNTRY)); |
michael@0 | 756 | int typeConstant = cursor.getInt(cursor.getColumnIndex(StructuredPostal.TYPE)); |
michael@0 | 757 | String type; |
michael@0 | 758 | if (typeConstant == StructuredPostal.TYPE_CUSTOM) { |
michael@0 | 759 | type = cursor.getString(cursor.getColumnIndex(StructuredPostal.LABEL)); |
michael@0 | 760 | } else { |
michael@0 | 761 | type = getKeyFromMapValue(mAddressTypesMap, Integer.valueOf(typeConstant)); |
michael@0 | 762 | } |
michael@0 | 763 | |
michael@0 | 764 | // Since an email may have multiple types, it may have already been added, |
michael@0 | 765 | // but still needs the new type added |
michael@0 | 766 | boolean found = false; |
michael@0 | 767 | if (type != null) { |
michael@0 | 768 | for (int i = 0; i < addresses.length(); i++) { |
michael@0 | 769 | JSONObject address = addresses.getJSONObject(i); |
michael@0 | 770 | if (streetAddress.equals(address.getString("streetAddress")) && |
michael@0 | 771 | locality.equals(address.getString("locality")) && |
michael@0 | 772 | region.equals(address.getString("region")) && |
michael@0 | 773 | countryName.equals(address.getString("countryName")) && |
michael@0 | 774 | postalCode.equals(address.getString("postalCode"))) { |
michael@0 | 775 | found = true; |
michael@0 | 776 | |
michael@0 | 777 | JSONArray types = address.getJSONArray("type"); |
michael@0 | 778 | if (!doesJSONArrayContainString(types, type)) { |
michael@0 | 779 | types.put(type); |
michael@0 | 780 | break; |
michael@0 | 781 | } |
michael@0 | 782 | } |
michael@0 | 783 | } |
michael@0 | 784 | } |
michael@0 | 785 | |
michael@0 | 786 | // If an existing email wasn't found, make a new one |
michael@0 | 787 | if (!found) { |
michael@0 | 788 | JSONObject address = new JSONObject(); |
michael@0 | 789 | JSONArray types = new JSONArray(); |
michael@0 | 790 | address.put("streetAddress", streetAddress); |
michael@0 | 791 | address.put("locality", locality); |
michael@0 | 792 | address.put("region", region); |
michael@0 | 793 | address.put("countryName", countryName); |
michael@0 | 794 | address.put("postalCode", postalCode); |
michael@0 | 795 | types.put(type); |
michael@0 | 796 | address.put("type", types); |
michael@0 | 797 | address.put("pref", bool(cursor.getInt(cursor.getColumnIndex(StructuredPostal.IS_SUPER_PRIMARY)))); |
michael@0 | 798 | |
michael@0 | 799 | addresses.put(address); |
michael@0 | 800 | } |
michael@0 | 801 | } |
michael@0 | 802 | |
michael@0 | 803 | private void getOrganizationDataAsJSONObject(Cursor cursor, JSONArray organizations, |
michael@0 | 804 | JSONArray jobTitles) throws JSONException { |
michael@0 | 805 | int organizationColumnIndex = cursor.getColumnIndex(Organization.COMPANY); |
michael@0 | 806 | int titleColumnIndex = cursor.getColumnIndex(Organization.TITLE); |
michael@0 | 807 | |
michael@0 | 808 | if (!cursor.isNull(organizationColumnIndex)) { |
michael@0 | 809 | organizations.put(cursor.getString(organizationColumnIndex)); |
michael@0 | 810 | } |
michael@0 | 811 | if (!cursor.isNull(titleColumnIndex)) { |
michael@0 | 812 | jobTitles.put(cursor.getString(titleColumnIndex)); |
michael@0 | 813 | } |
michael@0 | 814 | } |
michael@0 | 815 | |
michael@0 | 816 | private class ContactsComparator implements Comparator<JSONObject> { |
michael@0 | 817 | final String mSortBy; |
michael@0 | 818 | final String mSortOrder; |
michael@0 | 819 | |
michael@0 | 820 | public ContactsComparator(final String sortBy, final String sortOrder) { |
michael@0 | 821 | mSortBy = sortBy.toLowerCase(); |
michael@0 | 822 | mSortOrder = sortOrder.toLowerCase(); |
michael@0 | 823 | } |
michael@0 | 824 | |
michael@0 | 825 | @Override |
michael@0 | 826 | public int compare(JSONObject left, JSONObject right) { |
michael@0 | 827 | // Determine if sorting by "family name, given name" or "given name, family name" |
michael@0 | 828 | boolean familyFirst = false; |
michael@0 | 829 | if ("familyname".equals(mSortBy)) { |
michael@0 | 830 | familyFirst = true; |
michael@0 | 831 | } |
michael@0 | 832 | |
michael@0 | 833 | JSONObject leftProperties; |
michael@0 | 834 | JSONObject rightProperties; |
michael@0 | 835 | try { |
michael@0 | 836 | leftProperties = left.getJSONObject("properties"); |
michael@0 | 837 | rightProperties = right.getJSONObject("properties"); |
michael@0 | 838 | } catch (JSONException e) { |
michael@0 | 839 | throw new IllegalArgumentException(e); |
michael@0 | 840 | } |
michael@0 | 841 | |
michael@0 | 842 | JSONArray leftFamilyNames = leftProperties.optJSONArray("familyName"); |
michael@0 | 843 | JSONArray leftGivenNames = leftProperties.optJSONArray("givenName"); |
michael@0 | 844 | JSONArray rightFamilyNames = rightProperties.optJSONArray("familyName"); |
michael@0 | 845 | JSONArray rightGivenNames = rightProperties.optJSONArray("givenName"); |
michael@0 | 846 | |
michael@0 | 847 | // If any of the name arrays didn't exist (are null), create empty arrays |
michael@0 | 848 | // to avoid doing a bunch of null checking below |
michael@0 | 849 | if (leftFamilyNames == null) { |
michael@0 | 850 | leftFamilyNames = new JSONArray(); |
michael@0 | 851 | } |
michael@0 | 852 | if (leftGivenNames == null) { |
michael@0 | 853 | leftGivenNames = new JSONArray(); |
michael@0 | 854 | } |
michael@0 | 855 | if (rightFamilyNames == null) { |
michael@0 | 856 | rightFamilyNames = new JSONArray(); |
michael@0 | 857 | } |
michael@0 | 858 | if (rightGivenNames == null) { |
michael@0 | 859 | rightGivenNames = new JSONArray(); |
michael@0 | 860 | } |
michael@0 | 861 | |
michael@0 | 862 | int maxArrayLength = max(leftFamilyNames.length(), leftGivenNames.length(), |
michael@0 | 863 | rightFamilyNames.length(), rightGivenNames.length()); |
michael@0 | 864 | |
michael@0 | 865 | int index = 0; |
michael@0 | 866 | int compareResult; |
michael@0 | 867 | do { |
michael@0 | 868 | // Join together the given name and family name per the pattern above |
michael@0 | 869 | String leftName = ""; |
michael@0 | 870 | String rightName = ""; |
michael@0 | 871 | |
michael@0 | 872 | if (familyFirst) { |
michael@0 | 873 | leftName = leftFamilyNames.optString(index, "") + leftGivenNames.optString(index, ""); |
michael@0 | 874 | rightName = rightFamilyNames.optString(index, "") + rightGivenNames.optString(index, ""); |
michael@0 | 875 | } else { |
michael@0 | 876 | leftName = leftGivenNames.optString(index, "") + leftFamilyNames.optString(index, ""); |
michael@0 | 877 | rightName = rightGivenNames.optString(index, "") + rightFamilyNames.optString(index, ""); |
michael@0 | 878 | } |
michael@0 | 879 | |
michael@0 | 880 | index++; |
michael@0 | 881 | compareResult = leftName.compareTo(rightName); |
michael@0 | 882 | |
michael@0 | 883 | } while (compareResult == 0 && index < maxArrayLength); |
michael@0 | 884 | |
michael@0 | 885 | // If descending order, flip the result |
michael@0 | 886 | if (compareResult != 0 && "descending".equals(mSortOrder)) { |
michael@0 | 887 | compareResult = -compareResult; |
michael@0 | 888 | } |
michael@0 | 889 | |
michael@0 | 890 | return compareResult; |
michael@0 | 891 | } |
michael@0 | 892 | } |
michael@0 | 893 | |
michael@0 | 894 | private void clearAllContacts(final JSONObject contactOptions, final String requestID) { |
michael@0 | 895 | ArrayList<ContentProviderOperation> deleteOptions = new ArrayList<ContentProviderOperation>(); |
michael@0 | 896 | |
michael@0 | 897 | // Delete all contacts from the selected account |
michael@0 | 898 | ContentProviderOperation.Builder deleteOptionsBuilder = ContentProviderOperation.newDelete(RawContacts.CONTENT_URI); |
michael@0 | 899 | if (mAccountName != null) { |
michael@0 | 900 | deleteOptionsBuilder.withSelection(RawContacts.ACCOUNT_NAME + "=?", new String[] {mAccountName}) |
michael@0 | 901 | .withSelection(RawContacts.ACCOUNT_TYPE + "=?", new String[] {mAccountType}); |
michael@0 | 902 | } |
michael@0 | 903 | |
michael@0 | 904 | deleteOptions.add(deleteOptionsBuilder.build()); |
michael@0 | 905 | |
michael@0 | 906 | // Clear the contacts |
michael@0 | 907 | String returnStatus = "KO"; |
michael@0 | 908 | if (applyBatch(deleteOptions) != null) { |
michael@0 | 909 | returnStatus = "OK"; |
michael@0 | 910 | } |
michael@0 | 911 | |
michael@0 | 912 | Log.i(LOGTAG, "Sending return status: " + returnStatus); |
michael@0 | 913 | |
michael@0 | 914 | sendCallbackToJavascript("Android:Contacts:Clear:Return:" + returnStatus, requestID, |
michael@0 | 915 | new String[] {"contactID"}, new Object[] {"undefined"}); |
michael@0 | 916 | |
michael@0 | 917 | } |
michael@0 | 918 | |
michael@0 | 919 | private boolean deleteContact(String rawContactId) { |
michael@0 | 920 | ContentProviderOperation deleteOptions = ContentProviderOperation.newDelete(RawContacts.CONTENT_URI) |
michael@0 | 921 | .withSelection(RawContacts._ID + "=?", |
michael@0 | 922 | new String[] {rawContactId}) |
michael@0 | 923 | .build(); |
michael@0 | 924 | |
michael@0 | 925 | ArrayList<ContentProviderOperation> deleteOptionsList = new ArrayList<ContentProviderOperation>(); |
michael@0 | 926 | deleteOptionsList.add(deleteOptions); |
michael@0 | 927 | |
michael@0 | 928 | return checkForPositiveCountInResults(applyBatch(deleteOptionsList)); |
michael@0 | 929 | } |
michael@0 | 930 | |
michael@0 | 931 | private void removeContact(final JSONObject contactOptions, final String requestID) { |
michael@0 | 932 | String rawContactId; |
michael@0 | 933 | try { |
michael@0 | 934 | rawContactId = contactOptions.getString("id"); |
michael@0 | 935 | Log.i(LOGTAG, "Removing contact with ID: " + rawContactId); |
michael@0 | 936 | } catch (JSONException e) { |
michael@0 | 937 | // We can't continue without a raw contact ID |
michael@0 | 938 | sendCallbackToJavascript("Android:Contact:Remove:Return:KO", requestID, null, null); |
michael@0 | 939 | return; |
michael@0 | 940 | } |
michael@0 | 941 | |
michael@0 | 942 | String returnStatus = "KO"; |
michael@0 | 943 | if(deleteContact(rawContactId)) { |
michael@0 | 944 | returnStatus = "OK"; |
michael@0 | 945 | } |
michael@0 | 946 | |
michael@0 | 947 | sendCallbackToJavascript("Android:Contact:Remove:Return:" + returnStatus, requestID, |
michael@0 | 948 | new String[] {"contactID"}, new Object[] {rawContactId}); |
michael@0 | 949 | } |
michael@0 | 950 | |
michael@0 | 951 | private void saveContact(final JSONObject contactOptions, final String requestID) { |
michael@0 | 952 | try { |
michael@0 | 953 | String reason = contactOptions.getString("reason"); |
michael@0 | 954 | JSONObject contact = contactOptions.getJSONObject("contact"); |
michael@0 | 955 | JSONObject contactProperties = contact.getJSONObject("properties"); |
michael@0 | 956 | |
michael@0 | 957 | if ("update".equals(reason)) { |
michael@0 | 958 | updateContact(contactProperties, contact.getLong("id"), requestID); |
michael@0 | 959 | } else { |
michael@0 | 960 | insertContact(contactProperties, requestID); |
michael@0 | 961 | } |
michael@0 | 962 | } catch (JSONException e) { |
michael@0 | 963 | throw new IllegalArgumentException(e); |
michael@0 | 964 | } |
michael@0 | 965 | } |
michael@0 | 966 | |
michael@0 | 967 | private void insertContact(final JSONObject contactProperties, final String requestID) throws JSONException { |
michael@0 | 968 | ArrayList<ContentProviderOperation> newContactOptions = new ArrayList<ContentProviderOperation>(); |
michael@0 | 969 | |
michael@0 | 970 | // Account to save the contact under |
michael@0 | 971 | newContactOptions.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) |
michael@0 | 972 | .withValue(RawContacts.ACCOUNT_NAME, mAccountName) |
michael@0 | 973 | .withValue(RawContacts.ACCOUNT_TYPE, mAccountType) |
michael@0 | 974 | .build()); |
michael@0 | 975 | |
michael@0 | 976 | List<ContentValues> newContactValues = getContactValues(contactProperties); |
michael@0 | 977 | |
michael@0 | 978 | for (ContentValues values : newContactValues) { |
michael@0 | 979 | newContactOptions.add(ContentProviderOperation.newInsert(Data.CONTENT_URI) |
michael@0 | 980 | .withValueBackReference(Data.RAW_CONTACT_ID, 0) |
michael@0 | 981 | .withValues(values) |
michael@0 | 982 | .build()); |
michael@0 | 983 | } |
michael@0 | 984 | |
michael@0 | 985 | String returnStatus = "KO"; |
michael@0 | 986 | Long newRawContactId = new Long(-1); |
michael@0 | 987 | |
michael@0 | 988 | // Insert the contact! |
michael@0 | 989 | ContentProviderResult[] insertResults = applyBatch(newContactOptions); |
michael@0 | 990 | |
michael@0 | 991 | if (insertResults != null) { |
michael@0 | 992 | try { |
michael@0 | 993 | // Get the ID of the newly created contact |
michael@0 | 994 | newRawContactId = getRawContactIdFromContentProviderResults(insertResults); |
michael@0 | 995 | |
michael@0 | 996 | if (newRawContactId != null) { |
michael@0 | 997 | returnStatus = "OK"; |
michael@0 | 998 | } |
michael@0 | 999 | } catch (NumberFormatException e) { |
michael@0 | 1000 | Log.e(LOGTAG, "NumberFormatException", e); |
michael@0 | 1001 | } |
michael@0 | 1002 | |
michael@0 | 1003 | Log.i(LOGTAG, "Newly created contact ID: " + newRawContactId); |
michael@0 | 1004 | } |
michael@0 | 1005 | |
michael@0 | 1006 | Log.i(LOGTAG, "Sending return status: " + returnStatus); |
michael@0 | 1007 | |
michael@0 | 1008 | sendCallbackToJavascript("Android:Contact:Save:Return:" + returnStatus, requestID, |
michael@0 | 1009 | new String[] {"contactID", "reason"}, |
michael@0 | 1010 | new Object[] {newRawContactId, "create"}); |
michael@0 | 1011 | } |
michael@0 | 1012 | |
michael@0 | 1013 | private void updateContact(final JSONObject contactProperties, final long rawContactId, final String requestID) throws JSONException { |
michael@0 | 1014 | // Why is updating a contact so weird and horribly inefficient? Because Android doesn't |
michael@0 | 1015 | // like multiple values for contact fields, but the Mozilla contacts API calls for this. |
michael@0 | 1016 | // This means the Android update function is essentially completely useless. Why not just |
michael@0 | 1017 | // delete the contact and re-insert it? Because that would change the contact ID and the |
michael@0 | 1018 | // Mozilla contacts API shouldn't have this behavior. The solution is to delete each |
michael@0 | 1019 | // row from the contacts data table that belongs to the contact, and insert the new |
michael@0 | 1020 | // fields. But then why not just delete all the data from the data in one go and |
michael@0 | 1021 | // insert the new data in another? Because if all the data relating to a contact is |
michael@0 | 1022 | // deleted, Android will "conviently" remove the ID making it impossible to insert data |
michael@0 | 1023 | // under the old ID. To work around this, we put a Mozilla contact flag in the database |
michael@0 | 1024 | |
michael@0 | 1025 | ContentProviderOperation removeOptions = ContentProviderOperation.newDelete(Data.CONTENT_URI) |
michael@0 | 1026 | .withSelection(Data.RAW_CONTACT_ID + "=? AND " + |
michael@0 | 1027 | Data.MIMETYPE + " != '" + MIMETYPE_MOZILLA_CONTACTS_FLAG + "'", |
michael@0 | 1028 | new String[] {String.valueOf(rawContactId)}) |
michael@0 | 1029 | .build(); |
michael@0 | 1030 | |
michael@0 | 1031 | ArrayList<ContentProviderOperation> removeOptionsList = new ArrayList<ContentProviderOperation>(); |
michael@0 | 1032 | removeOptionsList.add(removeOptions); |
michael@0 | 1033 | |
michael@0 | 1034 | ContentProviderResult[] removeResults = applyBatch(removeOptionsList); |
michael@0 | 1035 | |
michael@0 | 1036 | // Check if the remove failed |
michael@0 | 1037 | if (removeResults == null || !checkForPositiveCountInResults(removeResults)) { |
michael@0 | 1038 | Log.w(LOGTAG, "Null or 0 remove results"); |
michael@0 | 1039 | |
michael@0 | 1040 | sendCallbackToJavascript("Android:Contact:Save:Return:KO", requestID, null, null); |
michael@0 | 1041 | return; |
michael@0 | 1042 | } |
michael@0 | 1043 | |
michael@0 | 1044 | List<ContentValues> updateContactValues = getContactValues(contactProperties); |
michael@0 | 1045 | ArrayList<ContentProviderOperation> updateContactOptions = new ArrayList<ContentProviderOperation>(); |
michael@0 | 1046 | |
michael@0 | 1047 | for (ContentValues values : updateContactValues) { |
michael@0 | 1048 | updateContactOptions.add(ContentProviderOperation.newInsert(Data.CONTENT_URI) |
michael@0 | 1049 | .withValue(Data.RAW_CONTACT_ID, rawContactId) |
michael@0 | 1050 | .withValues(values) |
michael@0 | 1051 | .build()); |
michael@0 | 1052 | } |
michael@0 | 1053 | |
michael@0 | 1054 | String returnStatus = "KO"; |
michael@0 | 1055 | |
michael@0 | 1056 | // Update the contact! |
michael@0 | 1057 | applyBatch(updateContactOptions); |
michael@0 | 1058 | |
michael@0 | 1059 | sendCallbackToJavascript("Android:Contact:Save:Return:OK", requestID, |
michael@0 | 1060 | new String[] {"contactID", "reason"}, |
michael@0 | 1061 | new Object[] {rawContactId, "update"}); |
michael@0 | 1062 | } |
michael@0 | 1063 | |
michael@0 | 1064 | private List<ContentValues> getContactValues(final JSONObject contactProperties) throws JSONException { |
michael@0 | 1065 | List<ContentValues> contactValues = new ArrayList<ContentValues>(); |
michael@0 | 1066 | |
michael@0 | 1067 | // Add the contact to the default group so it is shown in other apps |
michael@0 | 1068 | // like the Contacts or People app |
michael@0 | 1069 | ContentValues defaultGroupValues = new ContentValues(); |
michael@0 | 1070 | defaultGroupValues.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE); |
michael@0 | 1071 | defaultGroupValues.put(GroupMembership.GROUP_ROW_ID, mGroupId); |
michael@0 | 1072 | contactValues.add(defaultGroupValues); |
michael@0 | 1073 | |
michael@0 | 1074 | // Create all the values that will be inserted into the new contact |
michael@0 | 1075 | getNameValues(contactProperties.optJSONArray("name"), |
michael@0 | 1076 | contactProperties.optJSONArray("givenName"), |
michael@0 | 1077 | contactProperties.optJSONArray("familyName"), |
michael@0 | 1078 | contactProperties.optJSONArray("honorificPrefix"), |
michael@0 | 1079 | contactProperties.optJSONArray("honorificSuffix"), |
michael@0 | 1080 | contactValues); |
michael@0 | 1081 | |
michael@0 | 1082 | getGenericValues(MIMETYPE_ADDITIONAL_NAME, CUSTOM_DATA_COLUMN, |
michael@0 | 1083 | contactProperties.optJSONArray("additionalName"), contactValues); |
michael@0 | 1084 | |
michael@0 | 1085 | getNicknamesValues(contactProperties.optJSONArray("nickname"), contactValues); |
michael@0 | 1086 | |
michael@0 | 1087 | getAddressesValues(contactProperties.optJSONArray("adr"), contactValues); |
michael@0 | 1088 | |
michael@0 | 1089 | getPhonesValues(contactProperties.optJSONArray("tel"), contactValues); |
michael@0 | 1090 | |
michael@0 | 1091 | getEmailsValues(contactProperties.optJSONArray("email"), contactValues); |
michael@0 | 1092 | |
michael@0 | 1093 | //getPhotosValues(contactProperties.optJSONArray("photo"), contactValues); |
michael@0 | 1094 | |
michael@0 | 1095 | getGenericValues(Organization.CONTENT_ITEM_TYPE, Organization.COMPANY, |
michael@0 | 1096 | contactProperties.optJSONArray("org"), contactValues); |
michael@0 | 1097 | |
michael@0 | 1098 | getGenericValues(Organization.CONTENT_ITEM_TYPE, Organization.TITLE, |
michael@0 | 1099 | contactProperties.optJSONArray("jobTitle"), contactValues); |
michael@0 | 1100 | |
michael@0 | 1101 | getNotesValues(contactProperties.optJSONArray("note"), contactValues); |
michael@0 | 1102 | |
michael@0 | 1103 | getWebsitesValues(contactProperties.optJSONArray("url"), contactValues); |
michael@0 | 1104 | |
michael@0 | 1105 | getImsValues(contactProperties.optJSONArray("impp"), contactValues); |
michael@0 | 1106 | |
michael@0 | 1107 | getCategoriesValues(contactProperties.optJSONArray("category"), contactValues); |
michael@0 | 1108 | |
michael@0 | 1109 | getEventValues(contactProperties.optString("bday"), Event.TYPE_BIRTHDAY, contactValues); |
michael@0 | 1110 | |
michael@0 | 1111 | getEventValues(contactProperties.optString("anniversary"), Event.TYPE_ANNIVERSARY, contactValues); |
michael@0 | 1112 | |
michael@0 | 1113 | getCustomMimetypeValues(contactProperties.optString("sex"), MIMETYPE_SEX, contactValues); |
michael@0 | 1114 | |
michael@0 | 1115 | getCustomMimetypeValues(contactProperties.optString("genderIdentity"), MIMETYPE_GENDER_IDENTITY, contactValues); |
michael@0 | 1116 | |
michael@0 | 1117 | getGenericValues(MIMETYPE_KEY, CUSTOM_DATA_COLUMN, contactProperties.optJSONArray("key"), |
michael@0 | 1118 | contactValues); |
michael@0 | 1119 | |
michael@0 | 1120 | return contactValues; |
michael@0 | 1121 | } |
michael@0 | 1122 | |
michael@0 | 1123 | private void getGenericValues(final String mimeType, final String dataType, final JSONArray fields, |
michael@0 | 1124 | List<ContentValues> newContactValues) throws JSONException { |
michael@0 | 1125 | if (fields == null) { |
michael@0 | 1126 | return; |
michael@0 | 1127 | } |
michael@0 | 1128 | |
michael@0 | 1129 | for (int i = 0; i < fields.length(); i++) { |
michael@0 | 1130 | ContentValues contentValues = new ContentValues(); |
michael@0 | 1131 | contentValues.put(Data.MIMETYPE, mimeType); |
michael@0 | 1132 | contentValues.put(dataType, fields.getString(i)); |
michael@0 | 1133 | newContactValues.add(contentValues); |
michael@0 | 1134 | } |
michael@0 | 1135 | } |
michael@0 | 1136 | |
michael@0 | 1137 | private void getNameValues(final JSONArray displayNames, final JSONArray givenNames, |
michael@0 | 1138 | final JSONArray familyNames, final JSONArray prefixes, |
michael@0 | 1139 | final JSONArray suffixes, List<ContentValues> newContactValues) throws JSONException { |
michael@0 | 1140 | int maxLen = max((displayNames != null ? displayNames.length() : 0), |
michael@0 | 1141 | (givenNames != null ? givenNames.length() : 0), |
michael@0 | 1142 | (familyNames != null ? familyNames.length() : 0), |
michael@0 | 1143 | (prefixes != null ? prefixes.length() : 0), |
michael@0 | 1144 | (suffixes != null ? suffixes.length() : 0)); |
michael@0 | 1145 | |
michael@0 | 1146 | for (int i = 0; i < maxLen; i++) { |
michael@0 | 1147 | ContentValues contentValues = new ContentValues(); |
michael@0 | 1148 | contentValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); |
michael@0 | 1149 | |
michael@0 | 1150 | final String displayName = (displayNames != null ? displayNames.optString(i, null) : null); |
michael@0 | 1151 | final String givenName = (givenNames != null ? givenNames.optString(i, null) : null); |
michael@0 | 1152 | final String familyName = (familyNames != null ? familyNames.optString(i, null) : null); |
michael@0 | 1153 | final String prefix = (prefixes != null ? prefixes.optString(i, null) : null); |
michael@0 | 1154 | final String suffix = (suffixes != null ? suffixes.optString(i, null) : null); |
michael@0 | 1155 | |
michael@0 | 1156 | if (displayName != null) { |
michael@0 | 1157 | contentValues.put(StructuredName.DISPLAY_NAME, displayName); |
michael@0 | 1158 | } |
michael@0 | 1159 | if (givenName != null) { |
michael@0 | 1160 | contentValues.put(StructuredName.GIVEN_NAME, givenName); |
michael@0 | 1161 | } |
michael@0 | 1162 | if (familyName != null) { |
michael@0 | 1163 | contentValues.put(StructuredName.FAMILY_NAME, familyName); |
michael@0 | 1164 | } |
michael@0 | 1165 | if (prefix != null) { |
michael@0 | 1166 | contentValues.put(StructuredName.PREFIX, prefix); |
michael@0 | 1167 | } |
michael@0 | 1168 | if (suffix != null) { |
michael@0 | 1169 | contentValues.put(StructuredName.SUFFIX, suffix); |
michael@0 | 1170 | } |
michael@0 | 1171 | |
michael@0 | 1172 | newContactValues.add(contentValues); |
michael@0 | 1173 | } |
michael@0 | 1174 | } |
michael@0 | 1175 | |
michael@0 | 1176 | private void getNicknamesValues(final JSONArray nicknames, List<ContentValues> newContactValues) throws JSONException { |
michael@0 | 1177 | if (nicknames == null) { |
michael@0 | 1178 | return; |
michael@0 | 1179 | } |
michael@0 | 1180 | |
michael@0 | 1181 | for (int i = 0; i < nicknames.length(); i++) { |
michael@0 | 1182 | ContentValues contentValues = new ContentValues(); |
michael@0 | 1183 | contentValues.put(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE); |
michael@0 | 1184 | contentValues.put(Nickname.NAME, nicknames.getString(i)); |
michael@0 | 1185 | contentValues.put(Nickname.TYPE, Nickname.TYPE_DEFAULT); |
michael@0 | 1186 | newContactValues.add(contentValues); |
michael@0 | 1187 | } |
michael@0 | 1188 | } |
michael@0 | 1189 | |
michael@0 | 1190 | private void getAddressesValues(final JSONArray addresses, List<ContentValues> newContactValues) throws JSONException { |
michael@0 | 1191 | if (addresses == null) { |
michael@0 | 1192 | return; |
michael@0 | 1193 | } |
michael@0 | 1194 | |
michael@0 | 1195 | for (int i = 0; i < addresses.length(); i++) { |
michael@0 | 1196 | JSONObject address = addresses.getJSONObject(i); |
michael@0 | 1197 | JSONArray addressTypes = address.optJSONArray("type"); |
michael@0 | 1198 | |
michael@0 | 1199 | if (addressTypes != null) { |
michael@0 | 1200 | for (int j = 0; j < addressTypes.length(); j++) { |
michael@0 | 1201 | // Translate the address type string to an integer constant |
michael@0 | 1202 | // provided by the ContactsContract API |
michael@0 | 1203 | final String type = addressTypes.getString(j); |
michael@0 | 1204 | final int typeConstant = getAddressType(type); |
michael@0 | 1205 | |
michael@0 | 1206 | newContactValues.add(createAddressContentValues(address, typeConstant, type)); |
michael@0 | 1207 | } |
michael@0 | 1208 | } else { |
michael@0 | 1209 | newContactValues.add(createAddressContentValues(address, -1, null)); |
michael@0 | 1210 | } |
michael@0 | 1211 | } |
michael@0 | 1212 | } |
michael@0 | 1213 | |
michael@0 | 1214 | private ContentValues createAddressContentValues(final JSONObject address, final int typeConstant, |
michael@0 | 1215 | final String type) throws JSONException { |
michael@0 | 1216 | ContentValues contentValues = new ContentValues(); |
michael@0 | 1217 | contentValues.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); |
michael@0 | 1218 | contentValues.put(StructuredPostal.STREET, address.optString("streetAddress")); |
michael@0 | 1219 | contentValues.put(StructuredPostal.CITY, address.optString("locality")); |
michael@0 | 1220 | contentValues.put(StructuredPostal.REGION, address.optString("region")); |
michael@0 | 1221 | contentValues.put(StructuredPostal.POSTCODE, address.optString("postalCode")); |
michael@0 | 1222 | contentValues.put(StructuredPostal.COUNTRY, address.optString("countryName")); |
michael@0 | 1223 | |
michael@0 | 1224 | if (type != null) { |
michael@0 | 1225 | contentValues.put(StructuredPostal.TYPE, typeConstant); |
michael@0 | 1226 | |
michael@0 | 1227 | // If a custom type, add a label |
michael@0 | 1228 | if (typeConstant == BaseTypes.TYPE_CUSTOM) { |
michael@0 | 1229 | contentValues.put(StructuredPostal.LABEL, type); |
michael@0 | 1230 | } |
michael@0 | 1231 | } |
michael@0 | 1232 | |
michael@0 | 1233 | if (address.has("pref")) { |
michael@0 | 1234 | contentValues.put(Data.IS_SUPER_PRIMARY, address.getBoolean("pref") ? 1 : 0); |
michael@0 | 1235 | } |
michael@0 | 1236 | |
michael@0 | 1237 | return contentValues; |
michael@0 | 1238 | } |
michael@0 | 1239 | |
michael@0 | 1240 | private void getPhonesValues(final JSONArray phones, List<ContentValues> newContactValues) throws JSONException { |
michael@0 | 1241 | if (phones == null) { |
michael@0 | 1242 | return; |
michael@0 | 1243 | } |
michael@0 | 1244 | |
michael@0 | 1245 | for (int i = 0; i < phones.length(); i++) { |
michael@0 | 1246 | JSONObject phone = phones.getJSONObject(i); |
michael@0 | 1247 | JSONArray phoneTypes = phone.optJSONArray("type"); |
michael@0 | 1248 | ContentValues contentValues; |
michael@0 | 1249 | |
michael@0 | 1250 | if (phoneTypes != null && phoneTypes.length() > 0) { |
michael@0 | 1251 | for (int j = 0; j < phoneTypes.length(); j++) { |
michael@0 | 1252 | // Translate the phone type string to an integer constant |
michael@0 | 1253 | // provided by the ContactsContract API |
michael@0 | 1254 | final String type = phoneTypes.getString(j); |
michael@0 | 1255 | final int typeConstant = getPhoneType(type); |
michael@0 | 1256 | |
michael@0 | 1257 | contentValues = createContentValues(Phone.CONTENT_ITEM_TYPE, phone.optString("value"), |
michael@0 | 1258 | typeConstant, type, phone.optBoolean("pref")); |
michael@0 | 1259 | if (phone.has("carrier")) { |
michael@0 | 1260 | contentValues.put(CARRIER_COLUMN, phone.optString("carrier")); |
michael@0 | 1261 | } |
michael@0 | 1262 | newContactValues.add(contentValues); |
michael@0 | 1263 | } |
michael@0 | 1264 | } else { |
michael@0 | 1265 | contentValues = createContentValues(Phone.CONTENT_ITEM_TYPE, phone.optString("value"), |
michael@0 | 1266 | -1, null, phone.optBoolean("pref")); |
michael@0 | 1267 | if (phone.has("carrier")) { |
michael@0 | 1268 | contentValues.put(CARRIER_COLUMN, phone.optString("carrier")); |
michael@0 | 1269 | } |
michael@0 | 1270 | newContactValues.add(contentValues); |
michael@0 | 1271 | } |
michael@0 | 1272 | } |
michael@0 | 1273 | } |
michael@0 | 1274 | |
michael@0 | 1275 | private void getEmailsValues(final JSONArray emails, List<ContentValues> newContactValues) throws JSONException { |
michael@0 | 1276 | if (emails == null) { |
michael@0 | 1277 | return; |
michael@0 | 1278 | } |
michael@0 | 1279 | |
michael@0 | 1280 | for (int i = 0; i < emails.length(); i++) { |
michael@0 | 1281 | JSONObject email = emails.getJSONObject(i); |
michael@0 | 1282 | JSONArray emailTypes = email.optJSONArray("type"); |
michael@0 | 1283 | |
michael@0 | 1284 | if (emailTypes != null && emailTypes.length() > 0) { |
michael@0 | 1285 | for (int j = 0; j < emailTypes.length(); j++) { |
michael@0 | 1286 | // Translate the email type string to an integer constant |
michael@0 | 1287 | // provided by the ContactsContract API |
michael@0 | 1288 | final String type = emailTypes.getString(j); |
michael@0 | 1289 | final int typeConstant = getEmailType(type); |
michael@0 | 1290 | |
michael@0 | 1291 | newContactValues.add(createContentValues(Email.CONTENT_ITEM_TYPE, |
michael@0 | 1292 | email.optString("value"), |
michael@0 | 1293 | typeConstant, type, |
michael@0 | 1294 | email.optBoolean("pref"))); |
michael@0 | 1295 | } |
michael@0 | 1296 | } else { |
michael@0 | 1297 | newContactValues.add(createContentValues(Email.CONTENT_ITEM_TYPE, |
michael@0 | 1298 | email.optString("value"), |
michael@0 | 1299 | -1, null, email.optBoolean("pref"))); |
michael@0 | 1300 | } |
michael@0 | 1301 | } |
michael@0 | 1302 | } |
michael@0 | 1303 | |
michael@0 | 1304 | private void getPhotosValues(final JSONArray photos, List<ContentValues> newContactValues) throws JSONException { |
michael@0 | 1305 | if (photos == null) { |
michael@0 | 1306 | return; |
michael@0 | 1307 | } |
michael@0 | 1308 | |
michael@0 | 1309 | // TODO: implement this |
michael@0 | 1310 | } |
michael@0 | 1311 | |
michael@0 | 1312 | private void getNotesValues(final JSONArray notes, List<ContentValues> newContactValues) throws JSONException { |
michael@0 | 1313 | if (notes == null) { |
michael@0 | 1314 | return; |
michael@0 | 1315 | } |
michael@0 | 1316 | |
michael@0 | 1317 | for (int i = 0; i < notes.length(); i++) { |
michael@0 | 1318 | ContentValues contentValues = new ContentValues(); |
michael@0 | 1319 | contentValues.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE); |
michael@0 | 1320 | contentValues.put(Note.NOTE, notes.getString(i)); |
michael@0 | 1321 | newContactValues.add(contentValues); |
michael@0 | 1322 | } |
michael@0 | 1323 | } |
michael@0 | 1324 | |
michael@0 | 1325 | private void getWebsitesValues(final JSONArray websites, List<ContentValues> newContactValues) throws JSONException { |
michael@0 | 1326 | if (websites == null) { |
michael@0 | 1327 | return; |
michael@0 | 1328 | } |
michael@0 | 1329 | |
michael@0 | 1330 | for (int i = 0; i < websites.length(); i++) { |
michael@0 | 1331 | JSONObject website = websites.getJSONObject(i); |
michael@0 | 1332 | JSONArray websiteTypes = website.optJSONArray("type"); |
michael@0 | 1333 | |
michael@0 | 1334 | if (websiteTypes != null && websiteTypes.length() > 0) { |
michael@0 | 1335 | for (int j = 0; j < websiteTypes.length(); j++) { |
michael@0 | 1336 | // Translate the website type string to an integer constant |
michael@0 | 1337 | // provided by the ContactsContract API |
michael@0 | 1338 | final String type = websiteTypes.getString(j); |
michael@0 | 1339 | final int typeConstant = getWebsiteType(type); |
michael@0 | 1340 | |
michael@0 | 1341 | newContactValues.add(createContentValues(Website.CONTENT_ITEM_TYPE, |
michael@0 | 1342 | website.optString("value"), |
michael@0 | 1343 | typeConstant, type, |
michael@0 | 1344 | website.optBoolean("pref"))); |
michael@0 | 1345 | } |
michael@0 | 1346 | } else { |
michael@0 | 1347 | newContactValues.add(createContentValues(Website.CONTENT_ITEM_TYPE, |
michael@0 | 1348 | website.optString("value"), |
michael@0 | 1349 | -1, null, website.optBoolean("pref"))); |
michael@0 | 1350 | } |
michael@0 | 1351 | } |
michael@0 | 1352 | } |
michael@0 | 1353 | |
michael@0 | 1354 | private void getImsValues(final JSONArray ims, List<ContentValues> newContactValues) throws JSONException { |
michael@0 | 1355 | if (ims == null) { |
michael@0 | 1356 | return; |
michael@0 | 1357 | } |
michael@0 | 1358 | |
michael@0 | 1359 | for (int i = 0; i < ims.length(); i++) { |
michael@0 | 1360 | JSONObject im = ims.getJSONObject(i); |
michael@0 | 1361 | JSONArray imTypes = im.optJSONArray("type"); |
michael@0 | 1362 | |
michael@0 | 1363 | if (imTypes != null && imTypes.length() > 0) { |
michael@0 | 1364 | for (int j = 0; j < imTypes.length(); j++) { |
michael@0 | 1365 | // Translate the IM type string to an integer constant |
michael@0 | 1366 | // provided by the ContactsContract API |
michael@0 | 1367 | final String type = imTypes.getString(j); |
michael@0 | 1368 | final int typeConstant = getImType(type); |
michael@0 | 1369 | |
michael@0 | 1370 | newContactValues.add(createContentValues(Im.CONTENT_ITEM_TYPE, |
michael@0 | 1371 | im.optString("value"), |
michael@0 | 1372 | typeConstant, type, |
michael@0 | 1373 | im.optBoolean("pref"))); |
michael@0 | 1374 | } |
michael@0 | 1375 | } else { |
michael@0 | 1376 | newContactValues.add(createContentValues(Im.CONTENT_ITEM_TYPE, |
michael@0 | 1377 | im.optString("value"), |
michael@0 | 1378 | -1, null, im.optBoolean("pref"))); |
michael@0 | 1379 | } |
michael@0 | 1380 | } |
michael@0 | 1381 | } |
michael@0 | 1382 | |
michael@0 | 1383 | private void getCategoriesValues(final JSONArray categories, List<ContentValues> newContactValues) throws JSONException { |
michael@0 | 1384 | if (categories == null) { |
michael@0 | 1385 | return; |
michael@0 | 1386 | } |
michael@0 | 1387 | |
michael@0 | 1388 | for (int i = 0; i < categories.length(); i++) { |
michael@0 | 1389 | String category = categories.getString(i); |
michael@0 | 1390 | |
michael@0 | 1391 | if ("my contacts".equals(category.toLowerCase()) || |
michael@0 | 1392 | PRE_HONEYCOMB_DEFAULT_GROUP.equalsIgnoreCase(category)) { |
michael@0 | 1393 | Log.w(LOGTAG, "New contacts are implicitly added to the default group."); |
michael@0 | 1394 | continue; |
michael@0 | 1395 | } |
michael@0 | 1396 | |
michael@0 | 1397 | // Find the group ID of the given category |
michael@0 | 1398 | long groupId = getGroupId(category); |
michael@0 | 1399 | |
michael@0 | 1400 | // Create the group if it doesn't already exist |
michael@0 | 1401 | if (groupId == -1) { |
michael@0 | 1402 | groupId = createGroup(category); |
michael@0 | 1403 | // If the group is still -1, we failed to create the group |
michael@0 | 1404 | if (groupId == -1) { |
michael@0 | 1405 | // Only log the category name if in debug |
michael@0 | 1406 | if (DEBUG) { |
michael@0 | 1407 | Log.d(LOGTAG, "Failed to create new group for category \"" + category + "\""); |
michael@0 | 1408 | } else { |
michael@0 | 1409 | Log.w(LOGTAG, "Failed to create new group for given category."); |
michael@0 | 1410 | } |
michael@0 | 1411 | continue; |
michael@0 | 1412 | } |
michael@0 | 1413 | } |
michael@0 | 1414 | |
michael@0 | 1415 | ContentValues contentValues = new ContentValues(); |
michael@0 | 1416 | contentValues.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE); |
michael@0 | 1417 | contentValues.put(GroupMembership.GROUP_ROW_ID, groupId); |
michael@0 | 1418 | newContactValues.add(contentValues); |
michael@0 | 1419 | |
michael@0 | 1420 | newContactValues.add(contentValues); |
michael@0 | 1421 | } |
michael@0 | 1422 | } |
michael@0 | 1423 | |
michael@0 | 1424 | private void getEventValues(final String event, final int type, List<ContentValues> newContactValues) { |
michael@0 | 1425 | if (event == null || event.length() < 11) { |
michael@0 | 1426 | return; |
michael@0 | 1427 | } |
michael@0 | 1428 | |
michael@0 | 1429 | ContentValues contentValues = new ContentValues(); |
michael@0 | 1430 | contentValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE); |
michael@0 | 1431 | contentValues.put(Event.START_DATE, event.substring(0, 10)); |
michael@0 | 1432 | contentValues.put(Event.TYPE, type); |
michael@0 | 1433 | newContactValues.add(contentValues); |
michael@0 | 1434 | } |
michael@0 | 1435 | |
michael@0 | 1436 | private void getCustomMimetypeValues(final String value, final String mimeType, List<ContentValues> newContactValues) { |
michael@0 | 1437 | if (value == null || "null".equals(value)) { |
michael@0 | 1438 | return; |
michael@0 | 1439 | } |
michael@0 | 1440 | |
michael@0 | 1441 | ContentValues contentValues = new ContentValues(); |
michael@0 | 1442 | contentValues.put(Data.MIMETYPE, mimeType); |
michael@0 | 1443 | contentValues.put(CUSTOM_DATA_COLUMN, value); |
michael@0 | 1444 | newContactValues.add(contentValues); |
michael@0 | 1445 | } |
michael@0 | 1446 | |
michael@0 | 1447 | private void getMozillaContactFlagValues(List<ContentValues> newContactValues) { |
michael@0 | 1448 | try { |
michael@0 | 1449 | JSONArray mozillaContactsFlag = new JSONArray(); |
michael@0 | 1450 | mozillaContactsFlag.put("1"); |
michael@0 | 1451 | getGenericValues(MIMETYPE_MOZILLA_CONTACTS_FLAG, CUSTOM_DATA_COLUMN, mozillaContactsFlag, newContactValues); |
michael@0 | 1452 | } catch (JSONException e) { |
michael@0 | 1453 | throw new IllegalArgumentException(e); |
michael@0 | 1454 | } |
michael@0 | 1455 | } |
michael@0 | 1456 | |
michael@0 | 1457 | private ContentValues createContentValues(final String mimeType, final String value, final int typeConstant, |
michael@0 | 1458 | final String type, final boolean preferredValue) { |
michael@0 | 1459 | ContentValues contentValues = new ContentValues(); |
michael@0 | 1460 | contentValues.put(Data.MIMETYPE, mimeType); |
michael@0 | 1461 | contentValues.put(Data.DATA1, value); |
michael@0 | 1462 | contentValues.put(Data.IS_SUPER_PRIMARY, preferredValue ? 1 : 0); |
michael@0 | 1463 | |
michael@0 | 1464 | if (type != null) { |
michael@0 | 1465 | contentValues.put(Data.DATA2, typeConstant); |
michael@0 | 1466 | |
michael@0 | 1467 | // If a custom type, add a label |
michael@0 | 1468 | if (typeConstant == BaseTypes.TYPE_CUSTOM) { |
michael@0 | 1469 | contentValues.put(Data.DATA3, type); |
michael@0 | 1470 | } |
michael@0 | 1471 | } |
michael@0 | 1472 | |
michael@0 | 1473 | return contentValues; |
michael@0 | 1474 | } |
michael@0 | 1475 | |
michael@0 | 1476 | private void getContactsCount(final String requestID) { |
michael@0 | 1477 | Cursor cursor = getAllRawContactIdsCursor(); |
michael@0 | 1478 | Integer numContacts = Integer.valueOf(cursor.getCount()); |
michael@0 | 1479 | cursor.close(); |
michael@0 | 1480 | |
michael@0 | 1481 | sendCallbackToJavascript("Android:Contacts:Count", requestID, new String[] {"count"}, |
michael@0 | 1482 | new Object[] {numContacts}); |
michael@0 | 1483 | } |
michael@0 | 1484 | |
michael@0 | 1485 | private void sendCallbackToJavascript(final String subject, final String requestID, |
michael@0 | 1486 | final String[] argNames, final Object[] argValues) { |
michael@0 | 1487 | // Check the same number of argument names and arguments were given |
michael@0 | 1488 | if (argNames != null && argNames.length != argValues.length) { |
michael@0 | 1489 | throw new IllegalArgumentException("Argument names and argument values lengths do not match. " + |
michael@0 | 1490 | "Names length = " + argNames.length + ", Values length = " + |
michael@0 | 1491 | argValues.length); |
michael@0 | 1492 | } |
michael@0 | 1493 | |
michael@0 | 1494 | try { |
michael@0 | 1495 | JSONObject callbackMessage = new JSONObject(); |
michael@0 | 1496 | callbackMessage.put("requestID", requestID); |
michael@0 | 1497 | |
michael@0 | 1498 | if (argNames != null) { |
michael@0 | 1499 | for (int i = 0; i < argNames.length; i++) { |
michael@0 | 1500 | callbackMessage.put(argNames[i], argValues[i]); |
michael@0 | 1501 | } |
michael@0 | 1502 | } |
michael@0 | 1503 | |
michael@0 | 1504 | GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(subject, callbackMessage.toString())); |
michael@0 | 1505 | } catch (JSONException e) { |
michael@0 | 1506 | throw new IllegalArgumentException(e); |
michael@0 | 1507 | } |
michael@0 | 1508 | } |
michael@0 | 1509 | |
michael@0 | 1510 | private void registerEventListener(final String event) { |
michael@0 | 1511 | mEventDispatcher.registerEventListener(event, this); |
michael@0 | 1512 | } |
michael@0 | 1513 | |
michael@0 | 1514 | private void unregisterEventListener(final String event) { |
michael@0 | 1515 | mEventDispatcher.unregisterEventListener(event, this); |
michael@0 | 1516 | } |
michael@0 | 1517 | |
michael@0 | 1518 | private ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) { |
michael@0 | 1519 | try { |
michael@0 | 1520 | return mContentResolver.applyBatch(ContactsContract.AUTHORITY, operations); |
michael@0 | 1521 | } catch (RemoteException e) { |
michael@0 | 1522 | Log.e(LOGTAG, "RemoteException", e); |
michael@0 | 1523 | } catch (OperationApplicationException e) { |
michael@0 | 1524 | Log.e(LOGTAG, "OperationApplicationException", e); |
michael@0 | 1525 | } |
michael@0 | 1526 | return null; |
michael@0 | 1527 | } |
michael@0 | 1528 | |
michael@0 | 1529 | private void getDeviceAccount(final Runnable handleMessage) { |
michael@0 | 1530 | Account[] accounts = AccountManager.get(mActivity).getAccounts(); |
michael@0 | 1531 | |
michael@0 | 1532 | if (accounts.length == 0) { |
michael@0 | 1533 | Log.w(LOGTAG, "No accounts available"); |
michael@0 | 1534 | gotDeviceAccount(handleMessage); |
michael@0 | 1535 | } else if (accounts.length > 1) { |
michael@0 | 1536 | // Show the accounts chooser dialog if more than one dialog exists |
michael@0 | 1537 | showAccountsDialog(accounts, handleMessage); |
michael@0 | 1538 | } else { |
michael@0 | 1539 | // If only one account exists, use it |
michael@0 | 1540 | mAccountName = accounts[0].name; |
michael@0 | 1541 | mAccountType = accounts[0].type; |
michael@0 | 1542 | gotDeviceAccount(handleMessage); |
michael@0 | 1543 | } |
michael@0 | 1544 | |
michael@0 | 1545 | mGotDeviceAccount = true; |
michael@0 | 1546 | } |
michael@0 | 1547 | |
michael@0 | 1548 | private void showAccountsDialog(final Account[] accounts, final Runnable handleMessage) { |
michael@0 | 1549 | String[] accountNames = new String[accounts.length]; |
michael@0 | 1550 | for (int i = 0; i < accounts.length; i++) { |
michael@0 | 1551 | accountNames[i] = accounts[i].name; |
michael@0 | 1552 | } |
michael@0 | 1553 | |
michael@0 | 1554 | final AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); |
michael@0 | 1555 | builder.setTitle(mActivity.getResources().getString(R.string.contacts_account_chooser_dialog_title)) |
michael@0 | 1556 | .setSingleChoiceItems(accountNames, 0, new DialogInterface.OnClickListener() { |
michael@0 | 1557 | @Override |
michael@0 | 1558 | public void onClick(DialogInterface dialog, int position) { |
michael@0 | 1559 | // Set the account name and type when an item is selected and dismiss the dialog |
michael@0 | 1560 | mAccountName = accounts[position].name; |
michael@0 | 1561 | mAccountType = accounts[position].type; |
michael@0 | 1562 | dialog.dismiss(); |
michael@0 | 1563 | gotDeviceAccount(handleMessage); |
michael@0 | 1564 | } |
michael@0 | 1565 | }); |
michael@0 | 1566 | |
michael@0 | 1567 | mActivity.runOnUiThread(new Runnable() { |
michael@0 | 1568 | public void run() { |
michael@0 | 1569 | builder.show(); |
michael@0 | 1570 | } |
michael@0 | 1571 | }); |
michael@0 | 1572 | } |
michael@0 | 1573 | |
michael@0 | 1574 | private void gotDeviceAccount(final Runnable handleMessage) { |
michael@0 | 1575 | // Force the handleMessage runnable and getDefaultGroupId to run on the background thread |
michael@0 | 1576 | Runnable runnable = new Runnable() { |
michael@0 | 1577 | @Override |
michael@0 | 1578 | public void run() { |
michael@0 | 1579 | getDefaultGroupId(); |
michael@0 | 1580 | |
michael@0 | 1581 | // Don't log a user's account if not debug mode. Otherwise, just log a message |
michael@0 | 1582 | // saying that we got an account to use |
michael@0 | 1583 | if (mAccountName == null) { |
michael@0 | 1584 | Log.i(LOGTAG, "No device account selected. Leaving account as null."); |
michael@0 | 1585 | } else if (DEBUG) { |
michael@0 | 1586 | Log.d(LOGTAG, "Using account: " + mAccountName + " (type: " + mAccountType + ")"); |
michael@0 | 1587 | } else { |
michael@0 | 1588 | Log.i(LOGTAG, "Got device account to use for contact operations."); |
michael@0 | 1589 | } |
michael@0 | 1590 | handleMessage.run(); |
michael@0 | 1591 | } |
michael@0 | 1592 | }; |
michael@0 | 1593 | |
michael@0 | 1594 | ThreadUtils.postToBackgroundThread(runnable); |
michael@0 | 1595 | } |
michael@0 | 1596 | |
michael@0 | 1597 | private void getDefaultGroupId() { |
michael@0 | 1598 | Cursor cursor = getAllGroups(); |
michael@0 | 1599 | |
michael@0 | 1600 | cursor.moveToPosition(-1); |
michael@0 | 1601 | while (cursor.moveToNext()) { |
michael@0 | 1602 | // Check if the account name and type for the group match the account name and type of |
michael@0 | 1603 | // the account we're working with |
michael@0 | 1604 | final String groupAccountName = cursor.getString(GROUP_ACCOUNT_NAME); |
michael@0 | 1605 | if (!groupAccountName.equals(mAccountName)) { |
michael@0 | 1606 | continue; |
michael@0 | 1607 | } |
michael@0 | 1608 | |
michael@0 | 1609 | final String groupAccountType = cursor.getString(GROUP_ACCOUNT_TYPE); |
michael@0 | 1610 | if (!groupAccountType.equals(mAccountType)) { |
michael@0 | 1611 | continue; |
michael@0 | 1612 | } |
michael@0 | 1613 | |
michael@0 | 1614 | // For all honeycomb and up, the default group is the first one which has the AUTO_ADD flag set |
michael@0 | 1615 | if (isAutoAddGroup(cursor)) { |
michael@0 | 1616 | mGroupTitle = cursor.getString(GROUP_TITLE); |
michael@0 | 1617 | mGroupId = cursor.getLong(GROUP_ID); |
michael@0 | 1618 | break; |
michael@0 | 1619 | } else if (PRE_HONEYCOMB_DEFAULT_GROUP.equals(cursor.getString(GROUP_TITLE))) { |
michael@0 | 1620 | mGroupId = cursor.getLong(GROUP_ID); |
michael@0 | 1621 | mGroupTitle = PRE_HONEYCOMB_DEFAULT_GROUP; |
michael@0 | 1622 | break; |
michael@0 | 1623 | } |
michael@0 | 1624 | } |
michael@0 | 1625 | cursor.close(); |
michael@0 | 1626 | |
michael@0 | 1627 | if (mGroupId == 0) { |
michael@0 | 1628 | Log.w(LOGTAG, "Default group ID not found. Newly created contacts will not belong to any groups."); |
michael@0 | 1629 | } else if (DEBUG) { |
michael@0 | 1630 | Log.i(LOGTAG, "Using group ID: " + mGroupId + " (" + mGroupTitle + ")"); |
michael@0 | 1631 | } |
michael@0 | 1632 | } |
michael@0 | 1633 | |
michael@0 | 1634 | private static boolean isAutoAddGroup(Cursor cursor) { |
michael@0 | 1635 | // For Honeycomb and up, the default group is the first one which has the AUTO_ADD flag set. |
michael@0 | 1636 | // For everything below Honeycomb, use the default "System Group: My Contacts" group |
michael@0 | 1637 | return (Build.VERSION.SDK_INT >= 11 && !cursor.isNull(GROUP_AUTO_ADD) && |
michael@0 | 1638 | cursor.getInt(GROUP_AUTO_ADD) != 0); |
michael@0 | 1639 | } |
michael@0 | 1640 | |
michael@0 | 1641 | private long getGroupId(String groupName) { |
michael@0 | 1642 | long groupId = -1; |
michael@0 | 1643 | Cursor cursor = getGroups(Groups.TITLE + " = '" + groupName + "'"); |
michael@0 | 1644 | |
michael@0 | 1645 | cursor.moveToPosition(-1); |
michael@0 | 1646 | while (cursor.moveToNext()) { |
michael@0 | 1647 | String groupAccountName = cursor.getString(GROUP_ACCOUNT_NAME); |
michael@0 | 1648 | String groupAccountType = cursor.getString(GROUP_ACCOUNT_TYPE); |
michael@0 | 1649 | |
michael@0 | 1650 | // Check if the account name and type for the group match the account name and type of |
michael@0 | 1651 | // the account we're working with or the default "Phone" account if no account was found |
michael@0 | 1652 | if (groupAccountName.equals(mAccountName) && groupAccountType.equals(mAccountType) || |
michael@0 | 1653 | (mAccountName == null && "Phone".equals(groupAccountType))) { |
michael@0 | 1654 | if (groupName.equals(cursor.getString(GROUP_TITLE))) { |
michael@0 | 1655 | groupId = cursor.getLong(GROUP_ID); |
michael@0 | 1656 | break; |
michael@0 | 1657 | } |
michael@0 | 1658 | } |
michael@0 | 1659 | } |
michael@0 | 1660 | cursor.close(); |
michael@0 | 1661 | |
michael@0 | 1662 | return groupId; |
michael@0 | 1663 | } |
michael@0 | 1664 | |
michael@0 | 1665 | private String getGroupName(long groupId) { |
michael@0 | 1666 | Cursor cursor = getGroups(Groups._ID + " = " + groupId); |
michael@0 | 1667 | |
michael@0 | 1668 | if (cursor.getCount() == 0) { |
michael@0 | 1669 | cursor.close(); |
michael@0 | 1670 | return null; |
michael@0 | 1671 | } |
michael@0 | 1672 | |
michael@0 | 1673 | cursor.moveToPosition(0); |
michael@0 | 1674 | String groupName = cursor.getString(cursor.getColumnIndex(Groups.TITLE)); |
michael@0 | 1675 | cursor.close(); |
michael@0 | 1676 | |
michael@0 | 1677 | return groupName; |
michael@0 | 1678 | } |
michael@0 | 1679 | |
michael@0 | 1680 | private Cursor getAllGroups() { |
michael@0 | 1681 | return getGroups(null); |
michael@0 | 1682 | } |
michael@0 | 1683 | |
michael@0 | 1684 | private Cursor getGroups(String selectArg) { |
michael@0 | 1685 | String[] columns = new String[] { |
michael@0 | 1686 | Groups.ACCOUNT_NAME, |
michael@0 | 1687 | Groups.ACCOUNT_TYPE, |
michael@0 | 1688 | Groups._ID, |
michael@0 | 1689 | Groups.TITLE, |
michael@0 | 1690 | (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? Groups.AUTO_ADD : Groups._ID) |
michael@0 | 1691 | }; |
michael@0 | 1692 | |
michael@0 | 1693 | if (selectArg != null) { |
michael@0 | 1694 | selectArg = "AND " + selectArg; |
michael@0 | 1695 | } else { |
michael@0 | 1696 | selectArg = ""; |
michael@0 | 1697 | } |
michael@0 | 1698 | |
michael@0 | 1699 | return mContentResolver.query(Groups.CONTENT_URI, columns, |
michael@0 | 1700 | Groups.ACCOUNT_TYPE + " NOT NULL AND " + |
michael@0 | 1701 | Groups.ACCOUNT_NAME + " NOT NULL " + selectArg, null, null); |
michael@0 | 1702 | } |
michael@0 | 1703 | |
michael@0 | 1704 | private long createGroup(String groupName) { |
michael@0 | 1705 | if (DEBUG) { |
michael@0 | 1706 | Log.d(LOGTAG, "Creating group: " + groupName); |
michael@0 | 1707 | } |
michael@0 | 1708 | |
michael@0 | 1709 | ArrayList<ContentProviderOperation> newGroupOptions = new ArrayList<ContentProviderOperation>(); |
michael@0 | 1710 | |
michael@0 | 1711 | // Create the group under the account we're using |
michael@0 | 1712 | // If no account is selected, use a default account name/type for the group |
michael@0 | 1713 | newGroupOptions.add(ContentProviderOperation.newInsert(Groups.CONTENT_URI) |
michael@0 | 1714 | .withValue(Groups.ACCOUNT_NAME, (mAccountName == null ? "Phone" : mAccountName)) |
michael@0 | 1715 | .withValue(Groups.ACCOUNT_TYPE, (mAccountType == null ? "Phone" : mAccountType)) |
michael@0 | 1716 | .withValue(Groups.TITLE, groupName) |
michael@0 | 1717 | .withValue(Groups.GROUP_VISIBLE, true) |
michael@0 | 1718 | .build()); |
michael@0 | 1719 | |
michael@0 | 1720 | applyBatch(newGroupOptions); |
michael@0 | 1721 | |
michael@0 | 1722 | // Return the ID of the newly created group |
michael@0 | 1723 | return getGroupId(groupName); |
michael@0 | 1724 | } |
michael@0 | 1725 | |
michael@0 | 1726 | private long[] getAllRawContactIds() { |
michael@0 | 1727 | Cursor cursor = getAllRawContactIdsCursor(); |
michael@0 | 1728 | |
michael@0 | 1729 | // Put the ids into an array |
michael@0 | 1730 | long[] ids = new long[cursor.getCount()]; |
michael@0 | 1731 | int index = 0; |
michael@0 | 1732 | cursor.moveToPosition(-1); |
michael@0 | 1733 | while(cursor.moveToNext()) { |
michael@0 | 1734 | ids[index] = cursor.getLong(cursor.getColumnIndex(RawContacts._ID)); |
michael@0 | 1735 | index++; |
michael@0 | 1736 | } |
michael@0 | 1737 | cursor.close(); |
michael@0 | 1738 | |
michael@0 | 1739 | return ids; |
michael@0 | 1740 | } |
michael@0 | 1741 | |
michael@0 | 1742 | private Cursor getAllRawContactIdsCursor() { |
michael@0 | 1743 | // When a contact is deleted, it actually just sets the deleted field to 1 until the |
michael@0 | 1744 | // sync adapter actually deletes the contact later so ignore any contacts with the deleted |
michael@0 | 1745 | // flag set |
michael@0 | 1746 | String selection = RawContacts.DELETED + "=0"; |
michael@0 | 1747 | String[] selectionArgs = null; |
michael@0 | 1748 | |
michael@0 | 1749 | // Only get contacts from the selected account |
michael@0 | 1750 | if (mAccountName != null) { |
michael@0 | 1751 | selection += " AND " + RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?"; |
michael@0 | 1752 | selectionArgs = new String[] {mAccountName, mAccountType}; |
michael@0 | 1753 | } |
michael@0 | 1754 | |
michael@0 | 1755 | // Get the ID's of all contacts and use the number of contact ID's as |
michael@0 | 1756 | // the total number of contacts |
michael@0 | 1757 | return mContentResolver.query(RawContacts.CONTENT_URI, new String[] {RawContacts._ID}, |
michael@0 | 1758 | selection, selectionArgs, null); |
michael@0 | 1759 | } |
michael@0 | 1760 | |
michael@0 | 1761 | private static Long getRawContactIdFromContentProviderResults(ContentProviderResult[] results) throws NumberFormatException { |
michael@0 | 1762 | for (int i = 0; i < results.length; i++) { |
michael@0 | 1763 | if (results[i].uri == null) { |
michael@0 | 1764 | continue; |
michael@0 | 1765 | } |
michael@0 | 1766 | |
michael@0 | 1767 | String uri = results[i].uri.toString(); |
michael@0 | 1768 | // Check if the uri is from the raw contacts table |
michael@0 | 1769 | if (uri.contains("raw_contacts")) { |
michael@0 | 1770 | // The ID is the after the final forward slash in the URI |
michael@0 | 1771 | return Long.parseLong(uri.substring(uri.lastIndexOf("/") + 1)); |
michael@0 | 1772 | } |
michael@0 | 1773 | } |
michael@0 | 1774 | |
michael@0 | 1775 | return null; |
michael@0 | 1776 | } |
michael@0 | 1777 | |
michael@0 | 1778 | private static boolean checkForPositiveCountInResults(ContentProviderResult[] results) { |
michael@0 | 1779 | for (int i = 0; i < results.length; i++) { |
michael@0 | 1780 | Integer count = results[i].count; |
michael@0 | 1781 | |
michael@0 | 1782 | if (DEBUG) { |
michael@0 | 1783 | Log.d(LOGTAG, "Results count: " + count); |
michael@0 | 1784 | } |
michael@0 | 1785 | |
michael@0 | 1786 | if (count != null && count > 0) { |
michael@0 | 1787 | return true; |
michael@0 | 1788 | } |
michael@0 | 1789 | } |
michael@0 | 1790 | |
michael@0 | 1791 | return false; |
michael@0 | 1792 | } |
michael@0 | 1793 | |
michael@0 | 1794 | private static long[] convertLongListToArray(List<Long> list) { |
michael@0 | 1795 | long[] array = new long[list.size()]; |
michael@0 | 1796 | |
michael@0 | 1797 | for (int i = 0; i < list.size(); i++) { |
michael@0 | 1798 | array[i] = list.get(i); |
michael@0 | 1799 | } |
michael@0 | 1800 | |
michael@0 | 1801 | return array; |
michael@0 | 1802 | } |
michael@0 | 1803 | |
michael@0 | 1804 | private static boolean doesJSONArrayContainString(final JSONArray array, final String value) { |
michael@0 | 1805 | for (int i = 0; i < array.length(); i++) { |
michael@0 | 1806 | if (value.equals(array.optString(i))) { |
michael@0 | 1807 | return true; |
michael@0 | 1808 | } |
michael@0 | 1809 | } |
michael@0 | 1810 | |
michael@0 | 1811 | return false; |
michael@0 | 1812 | } |
michael@0 | 1813 | |
michael@0 | 1814 | private static int max(int... values) { |
michael@0 | 1815 | int max = values[0]; |
michael@0 | 1816 | for (int value : values) { |
michael@0 | 1817 | if (value > max) { |
michael@0 | 1818 | max = value; |
michael@0 | 1819 | } |
michael@0 | 1820 | } |
michael@0 | 1821 | return max; |
michael@0 | 1822 | } |
michael@0 | 1823 | |
michael@0 | 1824 | private static void putPossibleNullValueInJSONObject(final String key, final Object value, JSONObject jsonObject) throws JSONException{ |
michael@0 | 1825 | if (value != null) { |
michael@0 | 1826 | jsonObject.put(key, value); |
michael@0 | 1827 | } else { |
michael@0 | 1828 | jsonObject.put(key, JSONObject.NULL); |
michael@0 | 1829 | } |
michael@0 | 1830 | } |
michael@0 | 1831 | |
michael@0 | 1832 | private static String getKeyFromMapValue(final HashMap<String, Integer> map, Integer value) { |
michael@0 | 1833 | for (Entry<String, Integer> entry : map.entrySet()) { |
michael@0 | 1834 | if (value == entry.getValue()) { |
michael@0 | 1835 | return entry.getKey(); |
michael@0 | 1836 | } |
michael@0 | 1837 | } |
michael@0 | 1838 | return null; |
michael@0 | 1839 | } |
michael@0 | 1840 | |
michael@0 | 1841 | private String getColumnNameConstant(String field) { |
michael@0 | 1842 | initColumnNameConstantsMap(); |
michael@0 | 1843 | return mColumnNameConstantsMap.get(field.toLowerCase()); |
michael@0 | 1844 | } |
michael@0 | 1845 | |
michael@0 | 1846 | private void initColumnNameConstantsMap() { |
michael@0 | 1847 | if (mColumnNameConstantsMap != null) { |
michael@0 | 1848 | return; |
michael@0 | 1849 | } |
michael@0 | 1850 | mColumnNameConstantsMap = new HashMap<String, String>(); |
michael@0 | 1851 | |
michael@0 | 1852 | mColumnNameConstantsMap.put("name", StructuredName.DISPLAY_NAME); |
michael@0 | 1853 | mColumnNameConstantsMap.put("givenname", StructuredName.GIVEN_NAME); |
michael@0 | 1854 | mColumnNameConstantsMap.put("familyname", StructuredName.FAMILY_NAME); |
michael@0 | 1855 | mColumnNameConstantsMap.put("honorificprefix", StructuredName.PREFIX); |
michael@0 | 1856 | mColumnNameConstantsMap.put("honorificsuffix", StructuredName.SUFFIX); |
michael@0 | 1857 | mColumnNameConstantsMap.put("additionalname", CUSTOM_DATA_COLUMN); |
michael@0 | 1858 | mColumnNameConstantsMap.put("nickname", Nickname.NAME); |
michael@0 | 1859 | mColumnNameConstantsMap.put("adr", StructuredPostal.STREET); |
michael@0 | 1860 | mColumnNameConstantsMap.put("email", Email.ADDRESS); |
michael@0 | 1861 | mColumnNameConstantsMap.put("url", Website.URL); |
michael@0 | 1862 | mColumnNameConstantsMap.put("category", GroupMembership.GROUP_ROW_ID); |
michael@0 | 1863 | mColumnNameConstantsMap.put("tel", Phone.NUMBER); |
michael@0 | 1864 | mColumnNameConstantsMap.put("org", Organization.COMPANY); |
michael@0 | 1865 | mColumnNameConstantsMap.put("jobTitle", Organization.TITLE); |
michael@0 | 1866 | mColumnNameConstantsMap.put("note", Note.NOTE); |
michael@0 | 1867 | mColumnNameConstantsMap.put("impp", Im.DATA); |
michael@0 | 1868 | mColumnNameConstantsMap.put("sex", CUSTOM_DATA_COLUMN); |
michael@0 | 1869 | mColumnNameConstantsMap.put("genderidentity", CUSTOM_DATA_COLUMN); |
michael@0 | 1870 | mColumnNameConstantsMap.put("key", CUSTOM_DATA_COLUMN); |
michael@0 | 1871 | } |
michael@0 | 1872 | |
michael@0 | 1873 | private String getMimeTypeOfField(String field) { |
michael@0 | 1874 | initMimeTypeConstantsMap(); |
michael@0 | 1875 | return mMimeTypeConstantsMap.get(field.toLowerCase()); |
michael@0 | 1876 | } |
michael@0 | 1877 | |
michael@0 | 1878 | private void initMimeTypeConstantsMap() { |
michael@0 | 1879 | if (mMimeTypeConstantsMap != null) { |
michael@0 | 1880 | return; |
michael@0 | 1881 | } |
michael@0 | 1882 | mMimeTypeConstantsMap = new HashMap<String, String>(); |
michael@0 | 1883 | |
michael@0 | 1884 | mMimeTypeConstantsMap.put("name", StructuredName.CONTENT_ITEM_TYPE); |
michael@0 | 1885 | mMimeTypeConstantsMap.put("givenname", StructuredName.CONTENT_ITEM_TYPE); |
michael@0 | 1886 | mMimeTypeConstantsMap.put("familyname", StructuredName.CONTENT_ITEM_TYPE); |
michael@0 | 1887 | mMimeTypeConstantsMap.put("honorificprefix", StructuredName.CONTENT_ITEM_TYPE); |
michael@0 | 1888 | mMimeTypeConstantsMap.put("honorificsuffix", StructuredName.CONTENT_ITEM_TYPE); |
michael@0 | 1889 | mMimeTypeConstantsMap.put("additionalname", MIMETYPE_ADDITIONAL_NAME); |
michael@0 | 1890 | mMimeTypeConstantsMap.put("nickname", Nickname.CONTENT_ITEM_TYPE); |
michael@0 | 1891 | mMimeTypeConstantsMap.put("email", Email.CONTENT_ITEM_TYPE); |
michael@0 | 1892 | mMimeTypeConstantsMap.put("url", Website.CONTENT_ITEM_TYPE); |
michael@0 | 1893 | mMimeTypeConstantsMap.put("category", GroupMembership.CONTENT_ITEM_TYPE); |
michael@0 | 1894 | mMimeTypeConstantsMap.put("tel", Phone.CONTENT_ITEM_TYPE); |
michael@0 | 1895 | mMimeTypeConstantsMap.put("org", Organization.CONTENT_ITEM_TYPE); |
michael@0 | 1896 | mMimeTypeConstantsMap.put("jobTitle", Organization.CONTENT_ITEM_TYPE); |
michael@0 | 1897 | mMimeTypeConstantsMap.put("note", Note.CONTENT_ITEM_TYPE); |
michael@0 | 1898 | mMimeTypeConstantsMap.put("impp", Im.CONTENT_ITEM_TYPE); |
michael@0 | 1899 | mMimeTypeConstantsMap.put("sex", MIMETYPE_SEX); |
michael@0 | 1900 | mMimeTypeConstantsMap.put("genderidentity", MIMETYPE_GENDER_IDENTITY); |
michael@0 | 1901 | mMimeTypeConstantsMap.put("key", MIMETYPE_KEY); |
michael@0 | 1902 | } |
michael@0 | 1903 | |
michael@0 | 1904 | private int getAddressType(String addressType) { |
michael@0 | 1905 | initAddressTypesMap(); |
michael@0 | 1906 | Integer type = mAddressTypesMap.get(addressType.toLowerCase()); |
michael@0 | 1907 | return (type != null ? Integer.valueOf(type) : StructuredPostal.TYPE_CUSTOM); |
michael@0 | 1908 | } |
michael@0 | 1909 | |
michael@0 | 1910 | private void initAddressTypesMap() { |
michael@0 | 1911 | if (mAddressTypesMap != null) { |
michael@0 | 1912 | return; |
michael@0 | 1913 | } |
michael@0 | 1914 | mAddressTypesMap = new HashMap<String, Integer>(); |
michael@0 | 1915 | |
michael@0 | 1916 | mAddressTypesMap.put("home", StructuredPostal.TYPE_HOME); |
michael@0 | 1917 | mAddressTypesMap.put("work", StructuredPostal.TYPE_WORK); |
michael@0 | 1918 | } |
michael@0 | 1919 | |
michael@0 | 1920 | private int getPhoneType(String phoneType) { |
michael@0 | 1921 | initPhoneTypesMap(); |
michael@0 | 1922 | Integer type = mPhoneTypesMap.get(phoneType.toLowerCase()); |
michael@0 | 1923 | return (type != null ? Integer.valueOf(type) : Phone.TYPE_CUSTOM); |
michael@0 | 1924 | } |
michael@0 | 1925 | |
michael@0 | 1926 | private void initPhoneTypesMap() { |
michael@0 | 1927 | if (mPhoneTypesMap != null) { |
michael@0 | 1928 | return; |
michael@0 | 1929 | } |
michael@0 | 1930 | mPhoneTypesMap = new HashMap<String, Integer>(); |
michael@0 | 1931 | |
michael@0 | 1932 | mPhoneTypesMap.put("home", Phone.TYPE_HOME); |
michael@0 | 1933 | mPhoneTypesMap.put("mobile", Phone.TYPE_MOBILE); |
michael@0 | 1934 | mPhoneTypesMap.put("work", Phone.TYPE_WORK); |
michael@0 | 1935 | mPhoneTypesMap.put("fax home", Phone.TYPE_FAX_HOME); |
michael@0 | 1936 | mPhoneTypesMap.put("fax work", Phone.TYPE_FAX_WORK); |
michael@0 | 1937 | mPhoneTypesMap.put("pager", Phone.TYPE_PAGER); |
michael@0 | 1938 | mPhoneTypesMap.put("callback", Phone.TYPE_CALLBACK); |
michael@0 | 1939 | mPhoneTypesMap.put("car", Phone.TYPE_CAR); |
michael@0 | 1940 | mPhoneTypesMap.put("company main", Phone.TYPE_COMPANY_MAIN); |
michael@0 | 1941 | mPhoneTypesMap.put("isdn", Phone.TYPE_ISDN); |
michael@0 | 1942 | mPhoneTypesMap.put("main", Phone.TYPE_MAIN); |
michael@0 | 1943 | mPhoneTypesMap.put("fax other", Phone.TYPE_OTHER_FAX); |
michael@0 | 1944 | mPhoneTypesMap.put("other fax", Phone.TYPE_OTHER_FAX); |
michael@0 | 1945 | mPhoneTypesMap.put("radio", Phone.TYPE_RADIO); |
michael@0 | 1946 | mPhoneTypesMap.put("telex", Phone.TYPE_TELEX); |
michael@0 | 1947 | mPhoneTypesMap.put("tty", Phone.TYPE_TTY_TDD); |
michael@0 | 1948 | mPhoneTypesMap.put("ttd", Phone.TYPE_TTY_TDD); |
michael@0 | 1949 | mPhoneTypesMap.put("work mobile", Phone.TYPE_WORK_MOBILE); |
michael@0 | 1950 | mPhoneTypesMap.put("work pager", Phone.TYPE_WORK_PAGER); |
michael@0 | 1951 | mPhoneTypesMap.put("assistant", Phone.TYPE_ASSISTANT); |
michael@0 | 1952 | mPhoneTypesMap.put("mms", Phone.TYPE_MMS); |
michael@0 | 1953 | } |
michael@0 | 1954 | |
michael@0 | 1955 | private int getEmailType(String emailType) { |
michael@0 | 1956 | initEmailTypesMap(); |
michael@0 | 1957 | Integer type = mEmailTypesMap.get(emailType.toLowerCase()); |
michael@0 | 1958 | return (type != null ? Integer.valueOf(type) : Email.TYPE_CUSTOM); |
michael@0 | 1959 | } |
michael@0 | 1960 | |
michael@0 | 1961 | private void initEmailTypesMap() { |
michael@0 | 1962 | if (mEmailTypesMap != null) { |
michael@0 | 1963 | return; |
michael@0 | 1964 | } |
michael@0 | 1965 | mEmailTypesMap = new HashMap<String, Integer>(); |
michael@0 | 1966 | |
michael@0 | 1967 | mEmailTypesMap.put("home", Email.TYPE_HOME); |
michael@0 | 1968 | mEmailTypesMap.put("mobile", Email.TYPE_MOBILE); |
michael@0 | 1969 | mEmailTypesMap.put("work", Email.TYPE_WORK); |
michael@0 | 1970 | } |
michael@0 | 1971 | |
michael@0 | 1972 | private int getWebsiteType(String webisteType) { |
michael@0 | 1973 | initWebsiteTypesMap(); |
michael@0 | 1974 | Integer type = mWebsiteTypesMap.get(webisteType.toLowerCase()); |
michael@0 | 1975 | return (type != null ? Integer.valueOf(type) : Website.TYPE_CUSTOM); |
michael@0 | 1976 | } |
michael@0 | 1977 | |
michael@0 | 1978 | private void initWebsiteTypesMap() { |
michael@0 | 1979 | if (mWebsiteTypesMap != null) { |
michael@0 | 1980 | return; |
michael@0 | 1981 | } |
michael@0 | 1982 | mWebsiteTypesMap = new HashMap<String, Integer>(); |
michael@0 | 1983 | |
michael@0 | 1984 | mWebsiteTypesMap.put("homepage", Website.TYPE_HOMEPAGE); |
michael@0 | 1985 | mWebsiteTypesMap.put("blog", Website.TYPE_BLOG); |
michael@0 | 1986 | mWebsiteTypesMap.put("profile", Website.TYPE_PROFILE); |
michael@0 | 1987 | mWebsiteTypesMap.put("home", Website.TYPE_HOME); |
michael@0 | 1988 | mWebsiteTypesMap.put("work", Website.TYPE_WORK); |
michael@0 | 1989 | mWebsiteTypesMap.put("ftp", Website.TYPE_FTP); |
michael@0 | 1990 | } |
michael@0 | 1991 | |
michael@0 | 1992 | private int getImType(String imType) { |
michael@0 | 1993 | initImTypesMap(); |
michael@0 | 1994 | Integer type = mImTypesMap.get(imType.toLowerCase()); |
michael@0 | 1995 | return (type != null ? Integer.valueOf(type) : Im.TYPE_CUSTOM); |
michael@0 | 1996 | } |
michael@0 | 1997 | |
michael@0 | 1998 | private void initImTypesMap() { |
michael@0 | 1999 | if (mImTypesMap != null) { |
michael@0 | 2000 | return; |
michael@0 | 2001 | } |
michael@0 | 2002 | mImTypesMap = new HashMap<String, Integer>(); |
michael@0 | 2003 | |
michael@0 | 2004 | mImTypesMap.put("home", Im.TYPE_HOME); |
michael@0 | 2005 | mImTypesMap.put("work", Im.TYPE_WORK); |
michael@0 | 2006 | } |
michael@0 | 2007 | |
michael@0 | 2008 | private String[] getAllColumns() { |
michael@0 | 2009 | return new String[] {Entity.DATA_ID, Data.MIMETYPE, Data.IS_SUPER_PRIMARY, |
michael@0 | 2010 | Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4, |
michael@0 | 2011 | Data.DATA5, Data.DATA6, Data.DATA7, Data.DATA8, |
michael@0 | 2012 | Data.DATA9, Data.DATA10, Data.DATA11, Data.DATA12, |
michael@0 | 2013 | Data.DATA13, Data.DATA14, Data.DATA15}; |
michael@0 | 2014 | } |
michael@0 | 2015 | } |