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