ContactOperations.java 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. package third_parties.ezvcard_android;
  2. import android.content.ContentProviderOperation;
  3. import android.content.ContentValues;
  4. import android.content.Context;
  5. import android.content.OperationApplicationException;
  6. import android.graphics.Bitmap;
  7. import android.os.RemoteException;
  8. import android.provider.ContactsContract;
  9. import com.owncloud.android.utils.DisplayUtils;
  10. import java.io.ByteArrayOutputStream;
  11. import java.text.DateFormat;
  12. import java.text.SimpleDateFormat;
  13. import java.util.ArrayList;
  14. import java.util.Date;
  15. import java.util.HashMap;
  16. import java.util.List;
  17. import java.util.Map;
  18. import ezvcard.VCard;
  19. import ezvcard.parameter.ImageType;
  20. import ezvcard.property.Address;
  21. import ezvcard.property.Birthday;
  22. import ezvcard.property.Email;
  23. import ezvcard.property.FormattedName;
  24. import ezvcard.property.Impp;
  25. import ezvcard.property.Nickname;
  26. import ezvcard.property.Note;
  27. import ezvcard.property.Organization;
  28. import ezvcard.property.Photo;
  29. import ezvcard.property.RawProperty;
  30. import ezvcard.property.StructuredName;
  31. import ezvcard.property.Telephone;
  32. import ezvcard.property.Title;
  33. import ezvcard.property.Url;
  34. import ezvcard.property.VCardProperty;
  35. import ezvcard.util.TelUri;
  36. import static android.text.TextUtils.isEmpty;
  37. /*
  38. Copyright (c) 2014-2015, Michael Angstadt
  39. All rights reserved.
  40. Redistribution and use in source and binary forms, with or without
  41. modification, are permitted provided that the following conditions are met:
  42. 1. Redistributions of source code must retain the above copyright notice, this
  43. list of conditions and the following disclaimer.
  44. 2. Redistributions in binary form must reproduce the above copyright notice,
  45. this list of conditions and the following disclaimer in the documentation
  46. and/or other materials provided with the distribution.
  47. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  48. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  49. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  50. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  51. ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  52. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  53. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  54. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  55. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  56. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  57. The views and conclusions contained in the software and documentation are those
  58. of the authors and should not be interpreted as representing official policies,
  59. either expressed or implied, of the FreeBSD Project.
  60. */
  61. /**
  62. * Inserts a {@link VCard} into an Android database.
  63. *
  64. * @author Pratyush
  65. * @author Michael Angstadt
  66. */
  67. public class ContactOperations {
  68. private static final int rawContactID = 0;
  69. private final Context context;
  70. private final NonEmptyContentValues account;
  71. public ContactOperations(Context context) {
  72. this(context, null, null);
  73. }
  74. public ContactOperations(Context context, String accountName, String accountType) {
  75. this.context = context;
  76. account = new NonEmptyContentValues();
  77. account.put(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType);
  78. account.put(ContactsContract.RawContacts.ACCOUNT_NAME, accountName);
  79. }
  80. public void insertContact(VCard vcard) throws RemoteException, OperationApplicationException {
  81. // TODO handle Raw properties - Raw properties include various extension which start with "X-" like X-ASSISTANT, X-AIM, X-SPOUSE
  82. List<NonEmptyContentValues> contentValues = new ArrayList<NonEmptyContentValues>();
  83. convertName(contentValues, vcard);
  84. convertNickname(contentValues, vcard);
  85. convertPhones(contentValues, vcard);
  86. convertEmails(contentValues, vcard);
  87. convertAddresses(contentValues, vcard);
  88. convertIms(contentValues, vcard);
  89. // handle Android Custom fields..This is only valid for Android generated Vcards. As the Android would
  90. // generate NickName, ContactEvents other than Birthday and RelationShip with this "X-ANDROID-CUSTOM" name
  91. convertCustomFields(contentValues, vcard);
  92. // handle Iphone kinda of group properties. which are grouped together.
  93. convertGroupedProperties(contentValues, vcard);
  94. convertBirthdays(contentValues, vcard);
  95. convertWebsites(contentValues, vcard);
  96. convertNotes(contentValues, vcard);
  97. convertPhotos(contentValues, vcard);
  98. convertOrganization(contentValues, vcard);
  99. ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(contentValues.size());
  100. ContentValues cv = account.getContentValues();
  101. //ContactsContract.RawContact.CONTENT_URI needed to add account, backReference is also not needed
  102. ContentProviderOperation operation =
  103. ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
  104. .withValues(cv)
  105. .build();
  106. operations.add(operation);
  107. for (NonEmptyContentValues values : contentValues) {
  108. cv = values.getContentValues();
  109. if (cv.size() == 0) {
  110. continue;
  111. }
  112. //@formatter:off
  113. operation =
  114. ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
  115. .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactID)
  116. .withValues(cv)
  117. .build();
  118. //@formatter:on
  119. operations.add(operation);
  120. }
  121. // Executing all the insert operations as a single database transaction
  122. context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
  123. }
  124. public void updateContact(VCard vcard, Long key) throws RemoteException, OperationApplicationException {
  125. List<NonEmptyContentValues> contentValues = new ArrayList<NonEmptyContentValues>();
  126. convertName(contentValues, vcard);
  127. convertNickname(contentValues, vcard);
  128. convertPhones(contentValues, vcard);
  129. convertEmails(contentValues, vcard);
  130. convertAddresses(contentValues, vcard);
  131. convertIms(contentValues, vcard);
  132. // handle Android Custom fields..This is only valid for Android generated Vcards. As the Android would
  133. // generate NickName, ContactEvents other than Birthday and RelationShip with this "X-ANDROID-CUSTOM" name
  134. convertCustomFields(contentValues, vcard);
  135. // handle Iphone kinda of group properties. which are grouped together.
  136. convertGroupedProperties(contentValues, vcard);
  137. convertBirthdays(contentValues, vcard);
  138. convertWebsites(contentValues, vcard);
  139. convertNotes(contentValues, vcard);
  140. convertPhotos(contentValues, vcard);
  141. convertOrganization(contentValues, vcard);
  142. ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(contentValues.size());
  143. ContentValues cv = account.getContentValues();
  144. //ContactsContract.RawContact.CONTENT_URI needed to add account, backReference is also not needed
  145. long contactID = key;
  146. ContentProviderOperation operation;
  147. for (NonEmptyContentValues values : contentValues) {
  148. cv = values.getContentValues();
  149. if (cv.size() == 0) {
  150. continue;
  151. }
  152. String mimeType = cv.getAsString("mimetype");
  153. cv.remove("mimetype");
  154. //@formatter:off
  155. operation =
  156. ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
  157. .withSelection(ContactsContract.Data.RAW_CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ? ", new String[]{"" + contactID, "" + mimeType})
  158. .withValues(cv)
  159. .build();
  160. //@formatter:on
  161. operations.add(operation);
  162. }
  163. // Executing all the insert operations as a single database transaction
  164. context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
  165. }
  166. private void convertName(List<NonEmptyContentValues> contentValues, VCard vcard) {
  167. NonEmptyContentValues values = new NonEmptyContentValues(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
  168. String firstName = null, lastName = null, namePrefix = null, nameSuffix = null;
  169. StructuredName n = vcard.getStructuredName();
  170. if (n != null) {
  171. firstName = n.getGiven();
  172. values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, firstName);
  173. lastName = n.getFamily();
  174. values.put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, lastName);
  175. List<String> prefixes = n.getPrefixes();
  176. if (!prefixes.isEmpty()) {
  177. namePrefix = prefixes.get(0);
  178. values.put(ContactsContract.CommonDataKinds.StructuredName.PREFIX, namePrefix);
  179. }
  180. List<String> suffixes = n.getSuffixes();
  181. if (!suffixes.isEmpty()) {
  182. nameSuffix = suffixes.get(0);
  183. values.put(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, nameSuffix);
  184. }
  185. }
  186. FormattedName fn = vcard.getFormattedName();
  187. String formattedName = (fn == null) ? null : fn.getValue();
  188. String displayName;
  189. if (isEmpty(formattedName)) {
  190. StringBuilder sb = new StringBuilder();
  191. if (!isEmpty(namePrefix)){
  192. sb.append(namePrefix).append(' ');
  193. }
  194. if (!isEmpty(firstName)){
  195. sb.append(firstName).append(' ');
  196. }
  197. if (!isEmpty(lastName)){
  198. sb.append(lastName).append(' ');
  199. }
  200. if (!isEmpty(nameSuffix)){
  201. if (sb.length() > 0){
  202. sb.deleteCharAt(sb.length()-1); //delete space character
  203. sb.append(", ");
  204. }
  205. sb.append(nameSuffix);
  206. }
  207. displayName = sb.toString().trim();
  208. } else {
  209. displayName = formattedName;
  210. }
  211. values.put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName);
  212. RawProperty xPhoneticFirstName = vcard.getExtendedProperty("X-PHONETIC-FIRST-NAME");
  213. String firstPhoneticName = (xPhoneticFirstName == null) ? null : xPhoneticFirstName.getValue();
  214. values.put(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_GIVEN_NAME, firstPhoneticName);
  215. RawProperty xPhoneticLastName = vcard.getExtendedProperty("X-PHONETIC-LAST-NAME");
  216. String lastPhoneticName = (xPhoneticLastName == null) ? null : xPhoneticLastName.getValue();
  217. values.put(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_FAMILY_NAME, lastPhoneticName);
  218. contentValues.add(values);
  219. }
  220. private void convertNickname(List<NonEmptyContentValues> contentValues, VCard vcard) {
  221. for (Nickname nickname : vcard.getNicknames()) {
  222. List<String> nicknameValues = nickname.getValues();
  223. if (nicknameValues.isEmpty()) {
  224. continue;
  225. }
  226. for (String nicknameValue : nicknameValues) {
  227. NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE);
  228. cv.put(ContactsContract.CommonDataKinds.Nickname.NAME, nicknameValue);
  229. contentValues.add(cv);
  230. }
  231. }
  232. }
  233. private void convertPhones(List<NonEmptyContentValues> contentValues, VCard vcard) {
  234. for (Telephone telephone : vcard.getTelephoneNumbers()) {
  235. String value = telephone.getText();
  236. TelUri uri = telephone.getUri();
  237. if (isEmpty(value)) {
  238. if (uri == null) {
  239. continue;
  240. }
  241. value = uri.toString();
  242. }
  243. int phoneKind = DataMappings.getPhoneType(telephone);
  244. NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
  245. cv.put(ContactsContract.CommonDataKinds.Phone.NUMBER, value);
  246. cv.put(ContactsContract.CommonDataKinds.Phone.TYPE, phoneKind);
  247. contentValues.add(cv);
  248. }
  249. }
  250. private void convertEmails(List<NonEmptyContentValues> contentValues, VCard vcard) {
  251. for (Email email : vcard.getEmails()) {
  252. String value = email.getValue();
  253. if (isEmpty(value)) {
  254. continue;
  255. }
  256. int emailKind = DataMappings.getEmailType(email);
  257. NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
  258. cv.put(ContactsContract.CommonDataKinds.Email.ADDRESS, value);
  259. cv.put(ContactsContract.CommonDataKinds.Email.TYPE, emailKind);
  260. contentValues.add(cv);
  261. }
  262. }
  263. private void convertAddresses(List<NonEmptyContentValues> contentValues, VCard vcard) {
  264. for (Address address : vcard.getAddresses()) {
  265. NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
  266. String street = address.getStreetAddress();
  267. cv.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, street);
  268. String poBox = address.getPoBox();
  269. cv.put(ContactsContract.CommonDataKinds.StructuredPostal.POBOX, poBox);
  270. String city = address.getLocality();
  271. cv.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, city);
  272. String state = address.getRegion();
  273. cv.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, state);
  274. String zipCode = address.getPostalCode();
  275. cv.put(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, zipCode);
  276. String country = address.getCountry();
  277. cv.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, country);
  278. String label = address.getLabel();
  279. cv.put(ContactsContract.CommonDataKinds.StructuredPostal.LABEL, label);
  280. int addressKind = DataMappings.getAddressType(address);
  281. cv.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, addressKind);
  282. contentValues.add(cv);
  283. }
  284. }
  285. private void convertIms(List<NonEmptyContentValues> contentValues, VCard vcard) {
  286. //handle extended properties
  287. for (Map.Entry<String, Integer> entry : DataMappings.getImPropertyNameMappings().entrySet()) {
  288. String propertyName = entry.getKey();
  289. Integer protocolType = entry.getValue();
  290. List<RawProperty> rawProperties = vcard.getExtendedProperties(propertyName);
  291. for (RawProperty rawProperty : rawProperties) {
  292. NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
  293. String value = rawProperty.getValue();
  294. cv.put(ContactsContract.CommonDataKinds.Im.DATA, value);
  295. cv.put(ContactsContract.CommonDataKinds.Im.PROTOCOL, protocolType);
  296. contentValues.add(cv);
  297. }
  298. }
  299. //handle IMPP properties
  300. for (Impp impp : vcard.getImpps()) {
  301. NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
  302. String immpAddress = impp.getHandle();
  303. cv.put(ContactsContract.CommonDataKinds.Im.DATA, immpAddress);
  304. int immpProtocolType = DataMappings.getIMTypeFromProtocol(impp.getProtocol());
  305. cv.put(ContactsContract.CommonDataKinds.Im.PROTOCOL, immpProtocolType);
  306. contentValues.add(cv);
  307. }
  308. }
  309. private void convertCustomFields(List<NonEmptyContentValues> contentValues, VCard vcard) {
  310. for (AndroidCustomField customField : vcard.getProperties(AndroidCustomField.class)) {
  311. List<String> values = customField.getValues();
  312. if (values.isEmpty()) {
  313. continue;
  314. }
  315. NonEmptyContentValues cv;
  316. if (customField.isNickname()) {
  317. cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE);
  318. cv.put(ContactsContract.CommonDataKinds.Nickname.NAME, values.get(0));
  319. } else if (customField.isContactEvent()) {
  320. cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
  321. cv.put(ContactsContract.CommonDataKinds.Event.START_DATE, values.get(0));
  322. cv.put(ContactsContract.CommonDataKinds.Event.TYPE, values.get(1));
  323. } else if (customField.isRelation()) {
  324. cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Relation.CONTENT_ITEM_TYPE);
  325. cv.put(ContactsContract.CommonDataKinds.Relation.NAME, values.get(0));
  326. cv.put(ContactsContract.CommonDataKinds.Relation.TYPE, values.get(1));
  327. } else {
  328. continue;
  329. }
  330. contentValues.add(cv);
  331. }
  332. }
  333. private void convertGroupedProperties(List<NonEmptyContentValues> contentValues, VCard vcard) {
  334. List<RawProperty> extendedProperties = vcard.getExtendedProperties();
  335. Map<String, List<RawProperty>> orderedByGroup = orderPropertiesByGroup(extendedProperties);
  336. final int ABDATE = 1, ABRELATEDNAMES = 2;
  337. for (List<RawProperty> properties : orderedByGroup.values()) {
  338. if (properties.size() == 1) {
  339. continue;
  340. }
  341. String label = null;
  342. String val = null;
  343. int mime = 0;
  344. for (RawProperty property : properties) {
  345. String name = property.getPropertyName();
  346. if ("X-ABDATE".equalsIgnoreCase(name)) {
  347. label = property.getValue(); //date
  348. mime = ABDATE;
  349. continue;
  350. }
  351. if ("X-ABRELATEDNAMES".equalsIgnoreCase(name)) {
  352. label = property.getValue(); //name
  353. mime = ABRELATEDNAMES;
  354. continue;
  355. }
  356. if ("X-ABLABEL".equalsIgnoreCase(name)) {
  357. val = property.getValue(); // type of value ..Birthday,anniversary
  358. continue;
  359. }
  360. }
  361. NonEmptyContentValues cv = null;
  362. switch (mime) {
  363. case ABDATE:
  364. cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
  365. cv.put(ContactsContract.CommonDataKinds.Event.START_DATE, label);
  366. int type = DataMappings.getDateType(val);
  367. cv.put(ContactsContract.CommonDataKinds.Event.TYPE, type);
  368. break;
  369. case ABRELATEDNAMES:
  370. if (val != null) {
  371. cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE);
  372. cv.put(ContactsContract.CommonDataKinds.Nickname.NAME, label);
  373. if (!"Nickname".equals(val)) {
  374. type = DataMappings.getNameType(val);
  375. cv.put(ContactsContract.CommonDataKinds.Relation.TYPE, type);
  376. }
  377. }
  378. break;
  379. default:
  380. continue;
  381. }
  382. contentValues.add(cv);
  383. }
  384. }
  385. private void convertBirthdays(List<NonEmptyContentValues> contentValues, VCard vcard) {
  386. DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
  387. for (Birthday birthday : vcard.getBirthdays()) {
  388. Date date = birthday.getDate();
  389. if (date == null) {
  390. continue;
  391. }
  392. NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
  393. cv.put(ContactsContract.CommonDataKinds.Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY);
  394. cv.put(ContactsContract.CommonDataKinds.Event.START_DATE, df.format(date));
  395. contentValues.add(cv);
  396. }
  397. }
  398. private void convertWebsites(List<NonEmptyContentValues> contentValues, VCard vcard) {
  399. for (Url url : vcard.getUrls()) {
  400. String urlValue = url.getValue();
  401. if (isEmpty(urlValue)) {
  402. continue;
  403. }
  404. int type = DataMappings.getWebSiteType(url.getType());
  405. NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
  406. cv.put(ContactsContract.CommonDataKinds.Website.URL, urlValue);
  407. cv.put(ContactsContract.CommonDataKinds.Website.TYPE, type);
  408. contentValues.add(cv);
  409. }
  410. }
  411. private void convertNotes(List<NonEmptyContentValues> contentValues, VCard vcard) {
  412. for (Note note : vcard.getNotes()) {
  413. String noteValue = note.getValue();
  414. NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE);
  415. cv.put(ContactsContract.CommonDataKinds.Note.NOTE, noteValue);
  416. contentValues.add(cv);
  417. }
  418. }
  419. private void convertPhotos(List<NonEmptyContentValues> contentValues, VCard vcard) {
  420. for (Photo photo : vcard.getPhotos()) {
  421. if (photo.getUrl() != null) {
  422. downloadPhoto(photo);
  423. }
  424. byte[] data = photo.getData();
  425. NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
  426. cv.put(ContactsContract.CommonDataKinds.Photo.PHOTO, data);
  427. contentValues.add(cv);
  428. }
  429. }
  430. private void downloadPhoto(Photo photo) {
  431. String url = photo.getUrl();
  432. Bitmap bitmap = DisplayUtils.downloadImageSynchronous(context, url);
  433. if (bitmap != null) {
  434. ByteArrayOutputStream stream = new ByteArrayOutputStream();
  435. bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
  436. byte[] bitmapdata = stream.toByteArray();
  437. photo.setData(bitmapdata, ImageType.find(null, null,
  438. url.substring(url.lastIndexOf(".") + 1)));
  439. }
  440. }
  441. private void convertOrganization(List<NonEmptyContentValues> contentValues, VCard vcard) {
  442. NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
  443. Organization organization = vcard.getOrganization();
  444. if (organization != null) {
  445. List<String> values = organization.getValues();
  446. String keys[] = { ContactsContract.CommonDataKinds.Organization.COMPANY, ContactsContract.CommonDataKinds.Organization.DEPARTMENT, ContactsContract.CommonDataKinds.Organization.OFFICE_LOCATION };
  447. for (int i = 0; i < values.size(); i++) {
  448. String key = keys[i];
  449. String value = values.get(i);
  450. cv.put(key, value);
  451. }
  452. }
  453. List<Title> titleList = vcard.getTitles();
  454. if (!titleList.isEmpty()) {
  455. cv.put(ContactsContract.CommonDataKinds.Organization.TITLE, titleList.get(0).getValue());
  456. }
  457. contentValues.add(cv);
  458. }
  459. /**
  460. * Groups properties by their group name.
  461. * @param properties the properties to group
  462. * @return a map where the key is the group name (null for no group) and the
  463. * value is the list of properties that belong to that group
  464. */
  465. private <T extends VCardProperty> Map<String, List<T>> orderPropertiesByGroup(List<T> properties) {
  466. Map<String, List<T>> groupedProperties = new HashMap<String, List<T>>();
  467. for (T property : properties) {
  468. String group = property.getGroup();
  469. if (isEmpty(group)) {
  470. continue;
  471. }
  472. List<T> groupPropertiesList = groupedProperties.get(group);
  473. if (groupPropertiesList == null) {
  474. groupPropertiesList = new ArrayList<T>();
  475. groupedProperties.put(group, groupPropertiesList);
  476. }
  477. groupPropertiesList.add(property);
  478. }
  479. return groupedProperties;
  480. }
  481. /**
  482. * A wrapper for {@link ContentValues} that only adds values which are
  483. * non-null and non-empty (in the case of Strings).
  484. */
  485. private static class NonEmptyContentValues {
  486. private final ContentValues contentValues = new ContentValues();
  487. private final String contentItemType;
  488. public NonEmptyContentValues() {
  489. this(null);
  490. }
  491. /**
  492. * @param contentItemType the MIME type (value of
  493. * {@link ContactsContract.Contacts.Data#MIMETYPE})
  494. */
  495. public NonEmptyContentValues(String contentItemType) {
  496. this.contentItemType = contentItemType;
  497. }
  498. public void put(String key, String value) {
  499. if (isEmpty(value)) {
  500. return;
  501. }
  502. contentValues.put(key, value);
  503. }
  504. public void put(String key, int value) {
  505. contentValues.put(key, value);
  506. }
  507. public void put(String key, byte[] value) {
  508. if (value == null) {
  509. return;
  510. }
  511. contentValues.put(key, value);
  512. }
  513. /**
  514. * Gets the wrapped {@link ContentValues} object, adding the MIME type
  515. * entry if the values map is not empty.
  516. * @return the wrapped {@link ContentValues} object
  517. */
  518. public ContentValues getContentValues() {
  519. if (contentValues.size() > 0 && contentItemType != null) {
  520. put(ContactsContract.Contacts.Data.MIMETYPE, contentItemType);
  521. }
  522. return contentValues;
  523. }
  524. }
  525. }