mobile_template 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (211) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +28 -0
  5. data/Rakefile +3 -0
  6. data/bin/mobile_template +71 -0
  7. data/lib/mobile_template/version.rb +4 -0
  8. data/lib/mobile_template.rb +5 -0
  9. data/mobile_template.gemspec +21 -0
  10. data/templates/assets/Gemfile +4 -0
  11. data/templates/assets/config.rb +96 -0
  12. data/templates/assets/config.ru +4 -0
  13. data/templates/assets/source/images/vendor/ajax-loader.gif +0 -0
  14. data/templates/assets/source/images/vendor/ajax-loader.png +0 -0
  15. data/templates/assets/source/images/vendor/icons-18-black.png +0 -0
  16. data/templates/assets/source/images/vendor/icons-18-white.png +0 -0
  17. data/templates/assets/source/images/vendor/icons-36-black.png +0 -0
  18. data/templates/assets/source/images/vendor/icons-36-white.png +0 -0
  19. data/templates/assets/source/index.html.erb +2 -0
  20. data/templates/assets/source/javascripts/app/index.js.coffee +2 -0
  21. data/templates/assets/source/javascripts/application.js.coffee +5 -0
  22. data/templates/assets/source/javascripts/vendor/cordova.js +4841 -0
  23. data/templates/assets/source/javascripts/vendor/jquery.js +9267 -0
  24. data/templates/assets/source/javascripts/vendor/jquery.mobile.js +7410 -0
  25. data/templates/assets/source/layout.erb +24 -0
  26. data/templates/assets/source/stylesheets/application.css.scss +2 -0
  27. data/templates/assets/source/stylesheets/vendor/jquery.mobile.css.scss +1872 -0
  28. data/templates/cordova_android/.gitignore +18 -0
  29. data/templates/cordova_android/LICENSE +202 -0
  30. data/templates/cordova_android/NOTICE +5 -0
  31. data/templates/cordova_android/README.md +95 -0
  32. data/templates/cordova_android/VERSION +1 -0
  33. data/templates/cordova_android/bin/BOOM +4 -0
  34. data/templates/cordova_android/bin/autotest +2 -0
  35. data/templates/cordova_android/bin/bench +29 -0
  36. data/templates/cordova_android/bin/create +46 -0
  37. data/templates/cordova_android/bin/create.bat +1 -0
  38. data/templates/cordova_android/bin/create.js +88 -0
  39. data/templates/cordova_android/bin/create.xml +79 -0
  40. data/templates/cordova_android/bin/node_modules/.bin/cake +7 -0
  41. data/templates/cordova_android/bin/node_modules/.bin/coffee +7 -0
  42. data/templates/cordova_android/bin/node_modules/.bin/nodeunit +120 -0
  43. data/templates/cordova_android/bin/node_modules/coffee-script/.npmignore +11 -0
  44. data/templates/cordova_android/bin/node_modules/coffee-script/LICENSE +22 -0
  45. data/templates/cordova_android/bin/node_modules/coffee-script/README +48 -0
  46. data/templates/cordova_android/bin/node_modules/coffee-script/Rakefile +78 -0
  47. data/templates/cordova_android/bin/node_modules/coffee-script/bin/cake +7 -0
  48. data/templates/cordova_android/bin/node_modules/coffee-script/bin/coffee +7 -0
  49. data/templates/cordova_android/bin/node_modules/coffee-script/extras/jsl.conf +44 -0
  50. data/templates/cordova_android/bin/node_modules/coffee-script/lib/browser.js +75 -0
  51. data/templates/cordova_android/bin/node_modules/coffee-script/lib/cake.js +76 -0
  52. data/templates/cordova_android/bin/node_modules/coffee-script/lib/coffee-script.js +135 -0
  53. data/templates/cordova_android/bin/node_modules/coffee-script/lib/command.js +301 -0
  54. data/templates/cordova_android/bin/node_modules/coffee-script/lib/grammar.js +591 -0
  55. data/templates/cordova_android/bin/node_modules/coffee-script/lib/helpers.js +66 -0
  56. data/templates/cordova_android/bin/node_modules/coffee-script/lib/index.js +8 -0
  57. data/templates/cordova_android/bin/node_modules/coffee-script/lib/lexer.js +656 -0
  58. data/templates/cordova_android/bin/node_modules/coffee-script/lib/nodes.js +2289 -0
  59. data/templates/cordova_android/bin/node_modules/coffee-script/lib/optparse.js +111 -0
  60. data/templates/cordova_android/bin/node_modules/coffee-script/lib/parser.js +676 -0
  61. data/templates/cordova_android/bin/node_modules/coffee-script/lib/repl.js +123 -0
  62. data/templates/cordova_android/bin/node_modules/coffee-script/lib/rewriter.js +363 -0
  63. data/templates/cordova_android/bin/node_modules/coffee-script/lib/scope.js +120 -0
  64. data/templates/cordova_android/bin/node_modules/coffee-script/package.json +27 -0
  65. data/templates/cordova_android/bin/node_modules/nodeunit/.gitignore +5 -0
  66. data/templates/cordova_android/bin/node_modules/nodeunit/.npmignore +3 -0
  67. data/templates/cordova_android/bin/node_modules/nodeunit/CONTRIBUTORS.md +60 -0
  68. data/templates/cordova_android/bin/node_modules/nodeunit/LICENSE +19 -0
  69. data/templates/cordova_android/bin/node_modules/nodeunit/Makefile +126 -0
  70. data/templates/cordova_android/bin/node_modules/nodeunit/README.md +432 -0
  71. data/templates/cordova_android/bin/node_modules/nodeunit/bin/nodeunit +120 -0
  72. data/templates/cordova_android/bin/node_modules/nodeunit/bin/nodeunit.json +10 -0
  73. data/templates/cordova_android/bin/node_modules/nodeunit/deps/async.js +623 -0
  74. data/templates/cordova_android/bin/node_modules/nodeunit/deps/console.log.js +55 -0
  75. data/templates/cordova_android/bin/node_modules/nodeunit/deps/ejs.js +125 -0
  76. data/templates/cordova_android/bin/node_modules/nodeunit/deps/json2.js +483 -0
  77. data/templates/cordova_android/bin/node_modules/nodeunit/examples/browser/nodeunit.js +1757 -0
  78. data/templates/cordova_android/bin/node_modules/nodeunit/examples/browser/suite1.js +12 -0
  79. data/templates/cordova_android/bin/node_modules/nodeunit/examples/browser/suite2.js +13 -0
  80. data/templates/cordova_android/bin/node_modules/nodeunit/examples/browser/test.html +16 -0
  81. data/templates/cordova_android/bin/node_modules/nodeunit/img/example_fail.png +0 -0
  82. data/templates/cordova_android/bin/node_modules/nodeunit/img/example_pass.png +0 -0
  83. data/templates/cordova_android/bin/node_modules/nodeunit/index.js +3 -0
  84. data/templates/cordova_android/bin/node_modules/nodeunit/lib/assert.js +316 -0
  85. data/templates/cordova_android/bin/node_modules/nodeunit/lib/core.js +260 -0
  86. data/templates/cordova_android/bin/node_modules/nodeunit/lib/nodeunit.js +82 -0
  87. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/browser.js +119 -0
  88. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/default.js +123 -0
  89. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/html.js +107 -0
  90. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/index.js +9 -0
  91. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/junit.js +183 -0
  92. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/minimal.js +112 -0
  93. data/templates/cordova_android/bin/node_modules/nodeunit/lib/reporters/skip_passed.js +105 -0
  94. data/templates/cordova_android/bin/node_modules/nodeunit/lib/track.js +48 -0
  95. data/templates/cordova_android/bin/node_modules/nodeunit/lib/types.js +187 -0
  96. data/templates/cordova_android/bin/node_modules/nodeunit/lib/utils.js +209 -0
  97. data/templates/cordova_android/bin/node_modules/nodeunit/man1/nodeunit.1 +95 -0
  98. data/templates/cordova_android/bin/node_modules/nodeunit/nodelint.cfg +4 -0
  99. data/templates/cordova_android/bin/node_modules/nodeunit/package.json +56 -0
  100. data/templates/cordova_android/bin/node_modules/nodeunit/share/junit.xml.ejs +19 -0
  101. data/templates/cordova_android/bin/node_modules/nodeunit/share/license.js +11 -0
  102. data/templates/cordova_android/bin/node_modules/nodeunit/share/nodeunit.css +70 -0
  103. data/templates/cordova_android/bin/templates/project/.cordova/android/readme.md +1 -0
  104. data/templates/cordova_android/bin/templates/project/.cordova/readme.md +3 -0
  105. data/templates/cordova_android/bin/templates/project/cordova/create +36 -0
  106. data/templates/cordova_android/bin/templates/project/cordova/debug +9 -0
  107. data/templates/cordova_android/bin/templates/project/cordova/emulate +12 -0
  108. data/templates/cordova_android/bin/templates/project/cordova/log +3 -0
  109. data/templates/cordova_android/bin/templates/project/cordova/templates/Activity.java +16 -0
  110. data/templates/cordova_android/bin/templates/project/cordova/templates/project/AndroidManifest.xml +50 -0
  111. data/templates/cordova_android/bin/templates/project/cordova/templates/project/assets/www/index.html +42 -0
  112. data/templates/cordova_android/bin/templates/project/cordova/templates/project/assets/www/main.js +146 -0
  113. data/templates/cordova_android/bin/templates/project/cordova/templates/project/assets/www/master.css +96 -0
  114. data/templates/cordova_android/bin/templates/project/cordova/templates/project/res/drawable/icon.png +0 -0
  115. data/templates/cordova_android/bin/templates/project/cordova/templates/project/res/xml/cordova.xml +5 -0
  116. data/templates/cordova_android/bin/templates/project/cordova/templates/project/res/xml/plugins.xml +19 -0
  117. data/templates/cordova_android/bin/test +26 -0
  118. data/templates/cordova_android/bin/tests/autotest.coffee +4 -0
  119. data/templates/cordova_android/bin/tests/create.coffee +21 -0
  120. data/templates/cordova_android/bin/tests/debug.coffee +0 -0
  121. data/templates/cordova_android/bin/tests/test.coffee +0 -0
  122. data/templates/cordova_android/framework/.classpath +8 -0
  123. data/templates/cordova_android/framework/.project +33 -0
  124. data/templates/cordova_android/framework/AndroidManifest.xml +68 -0
  125. data/templates/cordova_android/framework/ant.properties +34 -0
  126. data/templates/cordova_android/framework/assets/js/accelerometer.js +137 -0
  127. data/templates/cordova_android/framework/assets/js/app.js +89 -0
  128. data/templates/cordova_android/framework/assets/js/battery.js +134 -0
  129. data/templates/cordova_android/framework/assets/js/camera.js +168 -0
  130. data/templates/cordova_android/framework/assets/js/capture.js +203 -0
  131. data/templates/cordova_android/framework/assets/js/compass.js +168 -0
  132. data/templates/cordova_android/framework/assets/js/contact.js +310 -0
  133. data/templates/cordova_android/framework/assets/js/cordova.android.js +4841 -0
  134. data/templates/cordova_android/framework/assets/js/cordova.js.base +924 -0
  135. data/templates/cordova_android/framework/assets/js/crypto.js +54 -0
  136. data/templates/cordova_android/framework/assets/js/device.js +83 -0
  137. data/templates/cordova_android/framework/assets/js/file.js +1082 -0
  138. data/templates/cordova_android/framework/assets/js/filetransfer.js +125 -0
  139. data/templates/cordova_android/framework/assets/js/geolocation.js +209 -0
  140. data/templates/cordova_android/framework/assets/js/header.txt +19 -0
  141. data/templates/cordova_android/framework/assets/js/media.js +233 -0
  142. data/templates/cordova_android/framework/assets/js/network.js +100 -0
  143. data/templates/cordova_android/framework/assets/js/notification.js +133 -0
  144. data/templates/cordova_android/framework/assets/js/position.js +100 -0
  145. data/templates/cordova_android/framework/assets/js/storage.js +439 -0
  146. data/templates/cordova_android/framework/assets/www/index.html +27 -0
  147. data/templates/cordova_android/framework/build.xml +216 -0
  148. data/templates/cordova_android/framework/libs/commons-codec-1.3.jar +0 -0
  149. data/templates/cordova_android/framework/proguard-project.txt +20 -0
  150. data/templates/cordova_android/framework/project.properties +14 -0
  151. data/templates/cordova_android/framework/res/drawable/icon.png +0 -0
  152. data/templates/cordova_android/framework/res/drawable/splash.png +0 -0
  153. data/templates/cordova_android/framework/res/layout/main.xml +29 -0
  154. data/templates/cordova_android/framework/res/values/strings.xml +23 -0
  155. data/templates/cordova_android/framework/res/xml/cordova.xml +37 -0
  156. data/templates/cordova_android/framework/res/xml/plugins.xml +37 -0
  157. data/templates/cordova_android/framework/src/com/phonegap/api/IPlugin.java +27 -0
  158. data/templates/cordova_android/framework/src/com/phonegap/api/LOG.java +28 -0
  159. data/templates/cordova_android/framework/src/com/phonegap/api/PhonegapActivity.java +28 -0
  160. data/templates/cordova_android/framework/src/com/phonegap/api/Plugin.java +27 -0
  161. data/templates/cordova_android/framework/src/com/phonegap/api/PluginManager.java +35 -0
  162. data/templates/cordova_android/framework/src/com/phonegap/api/PluginResult.java +53 -0
  163. data/templates/cordova_android/framework/src/org/apache/cordova/AccelListener.java +311 -0
  164. data/templates/cordova_android/framework/src/org/apache/cordova/App.java +198 -0
  165. data/templates/cordova_android/framework/src/org/apache/cordova/AudioHandler.java +364 -0
  166. data/templates/cordova_android/framework/src/org/apache/cordova/AudioPlayer.java +450 -0
  167. data/templates/cordova_android/framework/src/org/apache/cordova/AuthenticationToken.java +69 -0
  168. data/templates/cordova_android/framework/src/org/apache/cordova/BatteryListener.java +156 -0
  169. data/templates/cordova_android/framework/src/org/apache/cordova/CallbackServer.java +431 -0
  170. data/templates/cordova_android/framework/src/org/apache/cordova/CameraLauncher.java +500 -0
  171. data/templates/cordova_android/framework/src/org/apache/cordova/Capture.java +400 -0
  172. data/templates/cordova_android/framework/src/org/apache/cordova/CompassListener.java +308 -0
  173. data/templates/cordova_android/framework/src/org/apache/cordova/ContactAccessor.java +198 -0
  174. data/templates/cordova_android/framework/src/org/apache/cordova/ContactAccessorSdk5.java +1934 -0
  175. data/templates/cordova_android/framework/src/org/apache/cordova/ContactManager.java +113 -0
  176. data/templates/cordova_android/framework/src/org/apache/cordova/CordovaChromeClient.java +314 -0
  177. data/templates/cordova_android/framework/src/org/apache/cordova/CordovaWebViewClient.java +306 -0
  178. data/templates/cordova_android/framework/src/org/apache/cordova/Device.java +219 -0
  179. data/templates/cordova_android/framework/src/org/apache/cordova/DirectoryManager.java +161 -0
  180. data/templates/cordova_android/framework/src/org/apache/cordova/DroidGap.java +1417 -0
  181. data/templates/cordova_android/framework/src/org/apache/cordova/ExifHelper.java +165 -0
  182. data/templates/cordova_android/framework/src/org/apache/cordova/FileTransfer.java +458 -0
  183. data/templates/cordova_android/framework/src/org/apache/cordova/FileUploadResult.java +63 -0
  184. data/templates/cordova_android/framework/src/org/apache/cordova/FileUtils.java +1048 -0
  185. data/templates/cordova_android/framework/src/org/apache/cordova/GeoBroker.java +165 -0
  186. data/templates/cordova_android/framework/src/org/apache/cordova/GeoListener.java +133 -0
  187. data/templates/cordova_android/framework/src/org/apache/cordova/GpsListener.java +163 -0
  188. data/templates/cordova_android/framework/src/org/apache/cordova/HttpHandler.java +80 -0
  189. data/templates/cordova_android/framework/src/org/apache/cordova/LinearLayoutSoftKeyboardDetect.java +104 -0
  190. data/templates/cordova_android/framework/src/org/apache/cordova/NetworkListener.java +153 -0
  191. data/templates/cordova_android/framework/src/org/apache/cordova/NetworkManager.java +248 -0
  192. data/templates/cordova_android/framework/src/org/apache/cordova/Notification.java +366 -0
  193. data/templates/cordova_android/framework/src/org/apache/cordova/PreferenceNode.java +34 -0
  194. data/templates/cordova_android/framework/src/org/apache/cordova/PreferenceSet.java +62 -0
  195. data/templates/cordova_android/framework/src/org/apache/cordova/StandAlone.java +35 -0
  196. data/templates/cordova_android/framework/src/org/apache/cordova/Storage.java +239 -0
  197. data/templates/cordova_android/framework/src/org/apache/cordova/TempListener.java +112 -0
  198. data/templates/cordova_android/framework/src/org/apache/cordova/api/CordovaInterface.java +145 -0
  199. data/templates/cordova_android/framework/src/org/apache/cordova/api/IPlugin.java +116 -0
  200. data/templates/cordova_android/framework/src/org/apache/cordova/api/LOG.java +234 -0
  201. data/templates/cordova_android/framework/src/org/apache/cordova/api/Plugin.java +210 -0
  202. data/templates/cordova_android/framework/src/org/apache/cordova/api/PluginManager.java +359 -0
  203. data/templates/cordova_android/framework/src/org/apache/cordova/api/PluginResult.java +119 -0
  204. data/templates/cordova_android/framework/src/org/apache/cordova/file/EncodingException.java +28 -0
  205. data/templates/cordova_android/framework/src/org/apache/cordova/file/FileExistsException.java +28 -0
  206. data/templates/cordova_android/framework/src/org/apache/cordova/file/InvalidModificationException.java +29 -0
  207. data/templates/cordova_android/framework/src/org/apache/cordova/file/NoModificationAllowedException.java +28 -0
  208. data/templates/cordova_android/framework/src/org/apache/cordova/file/TypeMismatchException.java +29 -0
  209. data/templates/cordova_android/framework/test/org/apache/cordova/PreferenceNodeTest.java +53 -0
  210. data/templates/cordova_android/framework/test/org/apache/cordova/PreferenceSetTest.java +73 -0
  211. metadata +279 -0
@@ -0,0 +1,1934 @@
1
+ /*
2
+ Licensed to the Apache Software Foundation (ASF) under one
3
+ or more contributor license agreements. See the NOTICE file
4
+ distributed with this work for additional information
5
+ regarding copyright ownership. The ASF licenses this file
6
+ to you under the Apache License, Version 2.0 (the
7
+ "License"); you may not use this file except in compliance
8
+ with the License. You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing,
13
+ software distributed under the License is distributed on an
14
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ KIND, either express or implied. See the License for the
16
+ specific language governing permissions and limitations
17
+ under the License.
18
+ */
19
+
20
+ package org.apache.cordova;
21
+
22
+ import java.io.ByteArrayOutputStream;
23
+ import java.io.FileInputStream;
24
+ import java.io.FileNotFoundException;
25
+ import java.io.IOException;
26
+ import java.io.InputStream;
27
+ import java.net.URL;
28
+ import java.util.ArrayList;
29
+ import java.util.HashMap;
30
+ import java.util.HashSet;
31
+ import java.util.Iterator;
32
+ import java.util.Map;
33
+ import java.util.Set;
34
+
35
+ import org.json.JSONArray;
36
+ import org.json.JSONException;
37
+ import org.json.JSONObject;
38
+
39
+ import android.accounts.Account;
40
+ import android.accounts.AccountManager;
41
+ import android.app.Activity;
42
+ import android.content.ContentProviderOperation;
43
+ import android.content.ContentProviderResult;
44
+ import android.content.ContentUris;
45
+ import android.content.ContentValues;
46
+ import android.content.Context;
47
+ import android.content.OperationApplicationException;
48
+ import android.database.Cursor;
49
+ import android.net.Uri;
50
+ import android.os.RemoteException;
51
+ import android.provider.ContactsContract;
52
+ import android.util.Log;
53
+ import android.webkit.WebView;
54
+
55
+ /**
56
+ * An implementation of {@link ContactAccessor} that uses current Contacts API.
57
+ * This class should be used on Eclair or beyond, but would not work on any earlier
58
+ * release of Android. As a matter of fact, it could not even be loaded.
59
+ * <p>
60
+ * This implementation has several advantages:
61
+ * <ul>
62
+ * <li>It sees contacts from multiple accounts.
63
+ * <li>It works with aggregated contacts. So for example, if the contact is the result
64
+ * of aggregation of two raw contacts from different accounts, it may return the name from
65
+ * one and the phone number from the other.
66
+ * <li>It is efficient because it uses the more efficient current API.
67
+ * <li>Not obvious in this particular example, but it has access to new kinds
68
+ * of data available exclusively through the new APIs. Exercise for the reader: add support
69
+ * for nickname (see {@link android.provider.ContactsContract.CommonDataKinds.Nickname}) or
70
+ * social status updates (see {@link android.provider.ContactsContract.StatusUpdates}).
71
+ * </ul>
72
+ */
73
+ public class ContactAccessorSdk5 extends ContactAccessor {
74
+
75
+ /**
76
+ * Keep the photo size under the 1 MB blog limit.
77
+ */
78
+ private static final long MAX_PHOTO_SIZE = 1048576;
79
+
80
+ private static final String EMAIL_REGEXP = ".+@.+\\.+.+"; /* <anything>@<anything>.<anything>*/
81
+
82
+ /**
83
+ * A static map that converts the JavaScript property name to Android database column name.
84
+ */
85
+ private static final Map<String, String> dbMap = new HashMap<String, String>();
86
+ static {
87
+ dbMap.put("id", ContactsContract.Data.CONTACT_ID);
88
+ dbMap.put("displayName", ContactsContract.Contacts.DISPLAY_NAME);
89
+ dbMap.put("name", ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
90
+ dbMap.put("name.formatted", ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
91
+ dbMap.put("name.familyName", ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME);
92
+ dbMap.put("name.givenName", ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME);
93
+ dbMap.put("name.middleName", ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME);
94
+ dbMap.put("name.honorificPrefix", ContactsContract.CommonDataKinds.StructuredName.PREFIX);
95
+ dbMap.put("name.honorificSuffix", ContactsContract.CommonDataKinds.StructuredName.SUFFIX);
96
+ dbMap.put("nickname", ContactsContract.CommonDataKinds.Nickname.NAME);
97
+ dbMap.put("phoneNumbers", ContactsContract.CommonDataKinds.Phone.NUMBER);
98
+ dbMap.put("phoneNumbers.value", ContactsContract.CommonDataKinds.Phone.NUMBER);
99
+ dbMap.put("emails", ContactsContract.CommonDataKinds.Email.DATA);
100
+ dbMap.put("emails.value", ContactsContract.CommonDataKinds.Email.DATA);
101
+ dbMap.put("addresses", ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS);
102
+ dbMap.put("addresses.formatted", ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS);
103
+ dbMap.put("addresses.streetAddress", ContactsContract.CommonDataKinds.StructuredPostal.STREET);
104
+ dbMap.put("addresses.locality", ContactsContract.CommonDataKinds.StructuredPostal.CITY);
105
+ dbMap.put("addresses.region", ContactsContract.CommonDataKinds.StructuredPostal.REGION);
106
+ dbMap.put("addresses.postalCode", ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE);
107
+ dbMap.put("addresses.country", ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY);
108
+ dbMap.put("ims", ContactsContract.CommonDataKinds.Im.DATA);
109
+ dbMap.put("ims.value", ContactsContract.CommonDataKinds.Im.DATA);
110
+ dbMap.put("organizations", ContactsContract.CommonDataKinds.Organization.COMPANY);
111
+ dbMap.put("organizations.name", ContactsContract.CommonDataKinds.Organization.COMPANY);
112
+ dbMap.put("organizations.department", ContactsContract.CommonDataKinds.Organization.DEPARTMENT);
113
+ dbMap.put("organizations.title", ContactsContract.CommonDataKinds.Organization.TITLE);
114
+ dbMap.put("birthday", ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
115
+ dbMap.put("note", ContactsContract.CommonDataKinds.Note.NOTE);
116
+ dbMap.put("photos.value", ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
117
+ //dbMap.put("categories.value", null);
118
+ dbMap.put("urls", ContactsContract.CommonDataKinds.Website.URL);
119
+ dbMap.put("urls.value", ContactsContract.CommonDataKinds.Website.URL);
120
+ }
121
+
122
+ /**
123
+ * Create an contact accessor.
124
+ */
125
+ public ContactAccessorSdk5(WebView view, Context context) {
126
+ mApp = context;
127
+ mView = view;
128
+ }
129
+
130
+ /**
131
+ * This method takes the fields required and search options in order to produce an
132
+ * array of contacts that matches the criteria provided.
133
+ * @param fields an array of items to be used as search criteria
134
+ * @param options that can be applied to contact searching
135
+ * @return an array of contacts
136
+ */
137
+ @Override
138
+ public JSONArray search(JSONArray fields, JSONObject options) {
139
+ // Get the find options
140
+ String searchTerm = "";
141
+ int limit = Integer.MAX_VALUE;
142
+ boolean multiple = true;
143
+
144
+ if (options != null) {
145
+ searchTerm = options.optString("filter");
146
+ if (searchTerm.length()==0) {
147
+ searchTerm = "%";
148
+ }
149
+ else {
150
+ searchTerm = "%" + searchTerm + "%";
151
+ }
152
+ try {
153
+ multiple = options.getBoolean("multiple");
154
+ if (!multiple) {
155
+ limit = 1;
156
+ }
157
+ } catch (JSONException e) {
158
+ // Multiple was not specified so we assume the default is true.
159
+ }
160
+ }
161
+ else {
162
+ searchTerm = "%";
163
+ }
164
+
165
+ //Log.d(LOG_TAG, "Search Term = " + searchTerm);
166
+ //Log.d(LOG_TAG, "Field Length = " + fields.length());
167
+ //Log.d(LOG_TAG, "Fields = " + fields.toString());
168
+
169
+ // Loop through the fields the user provided to see what data should be returned.
170
+ HashMap<String,Boolean> populate = buildPopulationSet(fields);
171
+
172
+ // Build the ugly where clause and where arguments for one big query.
173
+ WhereOptions whereOptions = buildWhereClause(fields, searchTerm);
174
+
175
+ // Get all the id's where the search term matches the fields passed in.
176
+ Cursor idCursor = mApp.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
177
+ new String[] { ContactsContract.Data.CONTACT_ID },
178
+ whereOptions.getWhere(),
179
+ whereOptions.getWhereArgs(),
180
+ ContactsContract.Data.CONTACT_ID + " ASC");
181
+
182
+ // Create a set of unique ids
183
+ //Log.d(LOG_TAG, "ID cursor query returns = " + idCursor.getCount());
184
+ Set<String> contactIds = new HashSet<String>();
185
+ while (idCursor.moveToNext()) {
186
+ contactIds.add(idCursor.getString(idCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID)));
187
+ }
188
+ idCursor.close();
189
+
190
+ // Build a query that only looks at ids
191
+ WhereOptions idOptions = buildIdClause(contactIds, searchTerm);
192
+
193
+ // Do the id query
194
+ Cursor c = mApp.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
195
+ null,
196
+ idOptions.getWhere(),
197
+ idOptions.getWhereArgs(),
198
+ ContactsContract.Data.CONTACT_ID + " ASC");
199
+
200
+ JSONArray contacts = populateContactArray(limit, populate, c);
201
+ return contacts;
202
+ }
203
+
204
+ /**
205
+ * A special search that finds one contact by id
206
+ *
207
+ * @param id contact to find by id
208
+ * @return a JSONObject representing the contact
209
+ * @throws JSONException
210
+ */
211
+ public JSONObject getContactById(String id) throws JSONException {
212
+ // Do the id query
213
+ Cursor c = mApp.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
214
+ null,
215
+ ContactsContract.Data.CONTACT_ID + " = ? ",
216
+ new String[] { id },
217
+ ContactsContract.Data.CONTACT_ID + " ASC");
218
+
219
+ JSONArray fields = new JSONArray();
220
+ fields.put("*");
221
+
222
+ HashMap<String,Boolean> populate = buildPopulationSet(fields);
223
+
224
+ JSONArray contacts = populateContactArray(1, populate, c);
225
+
226
+ if (contacts.length() == 1) {
227
+ return contacts.getJSONObject(0);
228
+ } else {
229
+ return null;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Creates an array of contacts from the cursor you pass in
235
+ *
236
+ * @param limit max number of contacts for the array
237
+ * @param populate whether or not you should populate a certain value
238
+ * @param c the cursor
239
+ * @return a JSONArray of contacts
240
+ */
241
+ private JSONArray populateContactArray(int limit,
242
+ HashMap<String, Boolean> populate, Cursor c) {
243
+
244
+ String contactId = "";
245
+ String rawId = "";
246
+ String oldContactId = "";
247
+ boolean newContact = true;
248
+ String mimetype = "";
249
+
250
+ JSONArray contacts = new JSONArray();
251
+ JSONObject contact = new JSONObject();
252
+ JSONArray organizations = new JSONArray();
253
+ JSONArray addresses = new JSONArray();
254
+ JSONArray phones = new JSONArray();
255
+ JSONArray emails = new JSONArray();
256
+ JSONArray ims = new JSONArray();
257
+ JSONArray websites = new JSONArray();
258
+ JSONArray photos = new JSONArray();
259
+
260
+ if (c.getCount() > 0) {
261
+ while (c.moveToNext() && (contacts.length() <= (limit-1))) {
262
+ try {
263
+ contactId = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID));
264
+ rawId = c.getString(c.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID));
265
+
266
+ // If we are in the first row set the oldContactId
267
+ if (c.getPosition() == 0) {
268
+ oldContactId = contactId;
269
+ }
270
+
271
+ // When the contact ID changes we need to push the Contact object
272
+ // to the array of contacts and create new objects.
273
+ if (!oldContactId.equals(contactId)) {
274
+ // Populate the Contact object with it's arrays
275
+ // and push the contact into the contacts array
276
+ contacts.put(populateContact(contact, organizations, addresses, phones,
277
+ emails, ims, websites, photos));
278
+
279
+ // Clean up the objects
280
+ contact = new JSONObject();
281
+ organizations = new JSONArray();
282
+ addresses = new JSONArray();
283
+ phones = new JSONArray();
284
+ emails = new JSONArray();
285
+ ims = new JSONArray();
286
+ websites = new JSONArray();
287
+ photos = new JSONArray();
288
+
289
+ // Set newContact to true as we are starting to populate a new contact
290
+ newContact = true;
291
+ }
292
+
293
+ // When we detect a new contact set the ID and display name.
294
+ // These fields are available in every row in the result set returned.
295
+ if (newContact) {
296
+ newContact = false;
297
+ contact.put("id", contactId);
298
+ contact.put("rawId", rawId);
299
+ }
300
+
301
+ // Grab the mimetype of the current row as it will be used in a lot of comparisons
302
+ mimetype = c.getString(c.getColumnIndex(ContactsContract.Data.MIMETYPE));
303
+
304
+ if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) {
305
+ contact.put("displayName", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME)));
306
+ }
307
+ if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
308
+ && isRequired("name",populate)) {
309
+ contact.put("name", nameQuery(c));
310
+ }
311
+ else if (mimetype.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
312
+ && isRequired("phoneNumbers",populate)) {
313
+ phones.put(phoneQuery(c));
314
+ }
315
+ else if (mimetype.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
316
+ && isRequired("emails",populate)) {
317
+ emails.put(emailQuery(c));
318
+ }
319
+ else if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
320
+ && isRequired("addresses",populate)) {
321
+ addresses.put(addressQuery(c));
322
+ }
323
+ else if (mimetype.equals(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
324
+ && isRequired("organizations",populate)) {
325
+ organizations.put(organizationQuery(c));
326
+ }
327
+ else if (mimetype.equals(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
328
+ && isRequired("ims",populate)) {
329
+ ims.put(imQuery(c));
330
+ }
331
+ else if (mimetype.equals(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE)
332
+ && isRequired("note",populate)) {
333
+ contact.put("note",c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Note.NOTE)));
334
+ }
335
+ else if (mimetype.equals(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE)
336
+ && isRequired("nickname",populate)) {
337
+ contact.put("nickname",c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.NAME)));
338
+ }
339
+ else if (mimetype.equals(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE)
340
+ && isRequired("urls",populate)) {
341
+ websites.put(websiteQuery(c));
342
+ }
343
+ else if (mimetype.equals(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)) {
344
+ if (ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY == c.getInt(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE))
345
+ && isRequired("birthday",populate)) {
346
+ contact.put("birthday", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE)));
347
+ }
348
+ }
349
+ else if (mimetype.equals(ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
350
+ && isRequired("photos",populate)) {
351
+ photos.put(photoQuery(c, contactId));
352
+ }
353
+ }
354
+ catch (JSONException e) {
355
+ Log.e(LOG_TAG, e.getMessage(),e);
356
+ }
357
+
358
+ // Set the old contact ID
359
+ oldContactId = contactId;
360
+ }
361
+
362
+ // Push the last contact into the contacts array
363
+ if (contacts.length() < limit) {
364
+ contacts.put(populateContact(contact, organizations, addresses, phones,
365
+ emails, ims, websites, photos));
366
+ }
367
+ }
368
+ c.close();
369
+ return contacts;
370
+ }
371
+
372
+ /**
373
+ * Builds a where clause all all the ids passed into the method
374
+ * @param contactIds a set of unique contact ids
375
+ * @param searchTerm what to search for
376
+ * @return an object containing the selection and selection args
377
+ */
378
+ private WhereOptions buildIdClause(Set<String> contactIds, String searchTerm) {
379
+ WhereOptions options = new WhereOptions();
380
+
381
+ // If the user is searching for every contact then short circuit the method
382
+ // and return a shorter where clause to be searched.
383
+ if (searchTerm.equals("%")) {
384
+ options.setWhere("(" + ContactsContract.Data.CONTACT_ID + " LIKE ? )");
385
+ options.setWhereArgs(new String[] {searchTerm});
386
+ return options;
387
+ }
388
+
389
+ // This clause means that there are specific ID's to be populated
390
+ Iterator<String> it = contactIds.iterator();
391
+ StringBuffer buffer = new StringBuffer("(");
392
+
393
+ while (it.hasNext()) {
394
+ buffer.append("'" + it.next() + "'");
395
+ if (it.hasNext()) {
396
+ buffer.append(",");
397
+ }
398
+ }
399
+ buffer.append(")");
400
+
401
+ options.setWhere(ContactsContract.Data.CONTACT_ID + " IN " + buffer.toString());
402
+ options.setWhereArgs(null);
403
+
404
+ return options;
405
+ }
406
+
407
+ /**
408
+ * Create a new contact using a JSONObject to hold all the data.
409
+ * @param contact
410
+ * @param organizations array of organizations
411
+ * @param addresses array of addresses
412
+ * @param phones array of phones
413
+ * @param emails array of emails
414
+ * @param ims array of instant messenger addresses
415
+ * @param websites array of websites
416
+ * @param photos
417
+ * @return
418
+ */
419
+ private JSONObject populateContact(JSONObject contact, JSONArray organizations,
420
+ JSONArray addresses, JSONArray phones, JSONArray emails,
421
+ JSONArray ims, JSONArray websites, JSONArray photos) {
422
+ try {
423
+ // Only return the array if it has at least one entry
424
+ if (organizations.length() > 0) {
425
+ contact.put("organizations", organizations);
426
+ }
427
+ if (addresses.length() > 0) {
428
+ contact.put("addresses", addresses);
429
+ }
430
+ if (phones.length() > 0) {
431
+ contact.put("phoneNumbers", phones);
432
+ }
433
+ if (emails.length() > 0) {
434
+ contact.put("emails", emails);
435
+ }
436
+ if (ims.length() > 0) {
437
+ contact.put("ims", ims);
438
+ }
439
+ if (websites.length() > 0) {
440
+ contact.put("websites", websites);
441
+ }
442
+ if (photos.length() > 0) {
443
+ contact.put("photos", photos);
444
+ }
445
+ }
446
+ catch (JSONException e) {
447
+ Log.e(LOG_TAG,e.getMessage(),e);
448
+ }
449
+ return contact;
450
+ }
451
+
452
+ /**
453
+ * Take the search criteria passed into the method and create a SQL WHERE clause.
454
+ * @param fields the properties to search against
455
+ * @param searchTerm the string to search for
456
+ * @return an object containing the selection and selection args
457
+ */
458
+ private WhereOptions buildWhereClause(JSONArray fields, String searchTerm) {
459
+
460
+ ArrayList<String> where = new ArrayList<String>();
461
+ ArrayList<String> whereArgs = new ArrayList<String>();
462
+
463
+ WhereOptions options = new WhereOptions();
464
+
465
+ /*
466
+ * Special case where the user wants all fields returned
467
+ */
468
+ if (isWildCardSearch(fields)) {
469
+ // Get all contacts with all properties
470
+ if ("%".equals(searchTerm)) {
471
+ options.setWhere("(" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? )");
472
+ options.setWhereArgs(new String[] {searchTerm});
473
+ return options;
474
+ } else {
475
+ // Get all contacts that match the filter but return all properties
476
+ where.add("(" + dbMap.get("displayName") + " LIKE ? )");
477
+ whereArgs.add(searchTerm);
478
+ where.add("(" + dbMap.get("name") + " LIKE ? AND "
479
+ + ContactsContract.Data.MIMETYPE + " = ? )");
480
+ whereArgs.add(searchTerm);
481
+ whereArgs.add(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
482
+ where.add("(" + dbMap.get("nickname") + " LIKE ? AND "
483
+ + ContactsContract.Data.MIMETYPE + " = ? )");
484
+ whereArgs.add(searchTerm);
485
+ whereArgs.add(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE);
486
+ where.add("(" + dbMap.get("phoneNumbers") + " LIKE ? AND "
487
+ + ContactsContract.Data.MIMETYPE + " = ? )");
488
+ whereArgs.add(searchTerm);
489
+ whereArgs.add(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
490
+ where.add("(" + dbMap.get("emails") + " LIKE ? AND "
491
+ + ContactsContract.Data.MIMETYPE + " = ? )");
492
+ whereArgs.add(searchTerm);
493
+ whereArgs.add(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
494
+ where.add("(" + dbMap.get("addresses") + " LIKE ? AND "
495
+ + ContactsContract.Data.MIMETYPE + " = ? )");
496
+ whereArgs.add(searchTerm);
497
+ whereArgs.add(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
498
+ where.add("(" + dbMap.get("ims") + " LIKE ? AND "
499
+ + ContactsContract.Data.MIMETYPE + " = ? )");
500
+ whereArgs.add(searchTerm);
501
+ whereArgs.add(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
502
+ where.add("(" + dbMap.get("organizations") + " LIKE ? AND "
503
+ + ContactsContract.Data.MIMETYPE + " = ? )");
504
+ whereArgs.add(searchTerm);
505
+ whereArgs.add(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
506
+ where.add("(" + dbMap.get("note") + " LIKE ? AND "
507
+ + ContactsContract.Data.MIMETYPE + " = ? )");
508
+ whereArgs.add(searchTerm);
509
+ whereArgs.add(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE);
510
+ where.add("(" + dbMap.get("urls") + " LIKE ? AND "
511
+ + ContactsContract.Data.MIMETYPE + " = ? )");
512
+ whereArgs.add(searchTerm);
513
+ whereArgs.add(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
514
+ }
515
+ }
516
+
517
+ /*
518
+ * Special case for when the user wants all the contacts but
519
+ */
520
+ if ("%".equals(searchTerm)) {
521
+ options.setWhere("(" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? )");
522
+ options.setWhereArgs(new String[] {searchTerm});
523
+ return options;
524
+ }
525
+
526
+ String key;
527
+ try {
528
+ //Log.d(LOG_TAG, "How many fields do we have = " + fields.length());
529
+ for (int i=0; i<fields.length(); i++) {
530
+ key = fields.getString(i);
531
+
532
+ if (key.equals("id")) {
533
+ where.add("(" + dbMap.get(key) + " = ? )");
534
+ whereArgs.add(searchTerm.substring(1, searchTerm.length()-1));
535
+ }
536
+ else if (key.startsWith("displayName")) {
537
+ where.add("(" + dbMap.get(key) + " LIKE ? )");
538
+ whereArgs.add(searchTerm);
539
+ }
540
+ else if (key.startsWith("name")) {
541
+ where.add("(" + dbMap.get(key) + " LIKE ? AND "
542
+ + ContactsContract.Data.MIMETYPE + " = ? )");
543
+ whereArgs.add(searchTerm);
544
+ whereArgs.add(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
545
+ }
546
+ else if (key.startsWith("nickname")) {
547
+ where.add("(" + dbMap.get(key) + " LIKE ? AND "
548
+ + ContactsContract.Data.MIMETYPE + " = ? )");
549
+ whereArgs.add(searchTerm);
550
+ whereArgs.add(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE);
551
+ }
552
+ else if (key.startsWith("phoneNumbers")) {
553
+ where.add("(" + dbMap.get(key) + " LIKE ? AND "
554
+ + ContactsContract.Data.MIMETYPE + " = ? )");
555
+ whereArgs.add(searchTerm);
556
+ whereArgs.add(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
557
+ }
558
+ else if (key.startsWith("emails")) {
559
+ where.add("(" + dbMap.get(key) + " LIKE ? AND "
560
+ + ContactsContract.Data.MIMETYPE + " = ? )");
561
+ whereArgs.add(searchTerm);
562
+ whereArgs.add(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
563
+ }
564
+ else if (key.startsWith("addresses")) {
565
+ where.add("(" + dbMap.get(key) + " LIKE ? AND "
566
+ + ContactsContract.Data.MIMETYPE + " = ? )");
567
+ whereArgs.add(searchTerm);
568
+ whereArgs.add(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
569
+ }
570
+ else if (key.startsWith("ims")) {
571
+ where.add("(" + dbMap.get(key) + " LIKE ? AND "
572
+ + ContactsContract.Data.MIMETYPE + " = ? )");
573
+ whereArgs.add(searchTerm);
574
+ whereArgs.add(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
575
+ }
576
+ else if (key.startsWith("organizations")) {
577
+ where.add("(" + dbMap.get(key) + " LIKE ? AND "
578
+ + ContactsContract.Data.MIMETYPE + " = ? )");
579
+ whereArgs.add(searchTerm);
580
+ whereArgs.add(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
581
+ }
582
+ // else if (key.startsWith("birthday")) {
583
+ // where.add("(" + dbMap.get(key) + " LIKE ? AND "
584
+ // + ContactsContract.Data.MIMETYPE + " = ? )");
585
+ // }
586
+ else if (key.startsWith("note")) {
587
+ where.add("(" + dbMap.get(key) + " LIKE ? AND "
588
+ + ContactsContract.Data.MIMETYPE + " = ? )");
589
+ whereArgs.add(searchTerm);
590
+ whereArgs.add(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE);
591
+ }
592
+ else if (key.startsWith("urls")) {
593
+ where.add("(" + dbMap.get(key) + " LIKE ? AND "
594
+ + ContactsContract.Data.MIMETYPE + " = ? )");
595
+ whereArgs.add(searchTerm);
596
+ whereArgs.add(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
597
+ }
598
+ }
599
+ }
600
+ catch (JSONException e) {
601
+ Log.e(LOG_TAG, e.getMessage(), e);
602
+ }
603
+
604
+ // Creating the where string
605
+ StringBuffer selection = new StringBuffer();
606
+ for (int i=0; i<where.size(); i++) {
607
+ selection.append(where.get(i));
608
+ if (i != (where.size()-1)) {
609
+ selection.append(" OR ");
610
+ }
611
+ }
612
+ options.setWhere(selection.toString());
613
+
614
+ // Creating the where args array
615
+ String[] selectionArgs = new String[whereArgs.size()];
616
+ for (int i=0; i<whereArgs.size(); i++) {
617
+ selectionArgs[i] = whereArgs.get(i);
618
+ }
619
+ options.setWhereArgs(selectionArgs);
620
+
621
+ return options;
622
+ }
623
+
624
+ /**
625
+ * If the user passes in the '*' wildcard character for search then they want all fields for each contact
626
+ *
627
+ * @param fields
628
+ * @return true if wildcard search requested, false otherwise
629
+ */
630
+ private boolean isWildCardSearch(JSONArray fields) {
631
+ // Only do a wildcard search if we are passed ["*"]
632
+ if (fields.length() == 1) {
633
+ try {
634
+ if ("*".equals(fields.getString(0))) {
635
+ return true;
636
+ }
637
+ } catch (JSONException e) {
638
+ return false;
639
+ }
640
+ }
641
+ return false;
642
+ }
643
+
644
+ /**
645
+ * Create a ContactOrganization JSONObject
646
+ * @param cursor the current database row
647
+ * @return a JSONObject representing a ContactOrganization
648
+ */
649
+ private JSONObject organizationQuery(Cursor cursor) {
650
+ JSONObject organization = new JSONObject();
651
+ try {
652
+ organization.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization._ID)));
653
+ organization.put("pref", false); // Android does not store pref attribute
654
+ organization.put("type", getOrgType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.TYPE))));
655
+ organization.put("department", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.DEPARTMENT)));
656
+ organization.put("name", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.COMPANY)));
657
+ organization.put("title", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.TITLE)));
658
+ } catch (JSONException e) {
659
+ Log.e(LOG_TAG, e.getMessage(), e);
660
+ }
661
+ return organization;
662
+ }
663
+
664
+ /**
665
+ * Create a ContactAddress JSONObject
666
+ * @param cursor the current database row
667
+ * @return a JSONObject representing a ContactAddress
668
+ */
669
+ private JSONObject addressQuery(Cursor cursor) {
670
+ JSONObject address = new JSONObject();
671
+ try {
672
+ address.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal._ID)));
673
+ address.put("pref", false); // Android does not store pref attribute
674
+ address.put("type", getAddressType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.TYPE))));
675
+ address.put("formatted", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS)));
676
+ address.put("streetAddress", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.STREET)));
677
+ address.put("locality", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.CITY)));
678
+ address.put("region", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.REGION)));
679
+ address.put("postalCode", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE)));
680
+ address.put("country", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY)));
681
+ } catch (JSONException e) {
682
+ Log.e(LOG_TAG, e.getMessage(), e);
683
+ }
684
+ return address;
685
+ }
686
+
687
+ /**
688
+ * Create a ContactName JSONObject
689
+ * @param cursor the current database row
690
+ * @return a JSONObject representing a ContactName
691
+ */
692
+ private JSONObject nameQuery(Cursor cursor) {
693
+ JSONObject contactName = new JSONObject();
694
+ try {
695
+ String familyName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
696
+ String givenName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
697
+ String middleName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME));
698
+ String honorificPrefix = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.PREFIX));
699
+ String honorificSuffix = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.SUFFIX));
700
+
701
+ // Create the formatted name
702
+ StringBuffer formatted = new StringBuffer("");
703
+ if (honorificPrefix != null) { formatted.append(honorificPrefix + " "); }
704
+ if (givenName != null) { formatted.append(givenName + " "); }
705
+ if (middleName != null) { formatted.append(middleName + " "); }
706
+ if (familyName != null) { formatted.append(familyName + " "); }
707
+ if (honorificSuffix != null) { formatted.append(honorificSuffix + " "); }
708
+
709
+ contactName.put("familyName", familyName);
710
+ contactName.put("givenName", givenName);
711
+ contactName.put("middleName", middleName);
712
+ contactName.put("honorificPrefix", honorificPrefix);
713
+ contactName.put("honorificSuffix", honorificSuffix);
714
+ contactName.put("formatted", formatted);
715
+ } catch (JSONException e) {
716
+ Log.e(LOG_TAG, e.getMessage(), e);
717
+ }
718
+ return contactName;
719
+ }
720
+
721
+ /**
722
+ * Create a ContactField JSONObject
723
+ * @param cursor the current database row
724
+ * @return a JSONObject representing a ContactField
725
+ */
726
+ private JSONObject phoneQuery(Cursor cursor) {
727
+ JSONObject phoneNumber = new JSONObject();
728
+ try {
729
+ phoneNumber.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID)));
730
+ phoneNumber.put("pref", false); // Android does not store pref attribute
731
+ phoneNumber.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
732
+ phoneNumber.put("type", getPhoneType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE))));
733
+ } catch (JSONException e) {
734
+ Log.e(LOG_TAG, e.getMessage(), e);
735
+ }
736
+ catch (Exception excp) {
737
+ Log.e(LOG_TAG, excp.getMessage(), excp);
738
+ }
739
+ return phoneNumber;
740
+ }
741
+
742
+ /**
743
+ * Create a ContactField JSONObject
744
+ * @param cursor the current database row
745
+ * @return a JSONObject representing a ContactField
746
+ */
747
+ private JSONObject emailQuery(Cursor cursor) {
748
+ JSONObject email = new JSONObject();
749
+ try {
750
+ email.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email._ID)));
751
+ email.put("pref", false); // Android does not store pref attribute
752
+ email.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA)));
753
+ email.put("type", getContactType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE))));
754
+ } catch (JSONException e) {
755
+ Log.e(LOG_TAG, e.getMessage(), e);
756
+ }
757
+ return email;
758
+ }
759
+
760
+ /**
761
+ * Create a ContactField JSONObject
762
+ * @param cursor the current database row
763
+ * @return a JSONObject representing a ContactField
764
+ */
765
+ private JSONObject imQuery(Cursor cursor) {
766
+ JSONObject im = new JSONObject();
767
+ try {
768
+ im.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im._ID)));
769
+ im.put("pref", false); // Android does not store pref attribute
770
+ im.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)));
771
+ im.put("type", getContactType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im.TYPE))));
772
+ } catch (JSONException e) {
773
+ Log.e(LOG_TAG, e.getMessage(), e);
774
+ }
775
+ return im;
776
+ }
777
+
778
+ /**
779
+ * Create a ContactField JSONObject
780
+ * @param cursor the current database row
781
+ * @return a JSONObject representing a ContactField
782
+ */
783
+ private JSONObject websiteQuery(Cursor cursor) {
784
+ JSONObject website = new JSONObject();
785
+ try {
786
+ website.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Website._ID)));
787
+ website.put("pref", false); // Android does not store pref attribute
788
+ website.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Website.URL)));
789
+ website.put("type", getContactType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Website.TYPE))));
790
+ } catch (JSONException e) {
791
+ Log.e(LOG_TAG, e.getMessage(), e);
792
+ }
793
+ return website;
794
+ }
795
+
796
+ /**
797
+ * Create a ContactField JSONObject
798
+ * @param contactId
799
+ * @return a JSONObject representing a ContactField
800
+ */
801
+ private JSONObject photoQuery(Cursor cursor, String contactId) {
802
+ JSONObject photo = new JSONObject();
803
+ try {
804
+ photo.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Photo._ID)));
805
+ photo.put("pref", false);
806
+ photo.put("type", "url");
807
+ Uri person = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, (new Long(contactId)));
808
+ Uri photoUri = Uri.withAppendedPath(person, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
809
+ photo.put("value", photoUri.toString());
810
+ } catch (JSONException e) {
811
+ Log.e(LOG_TAG, e.getMessage(), e);
812
+ }
813
+ return photo;
814
+ }
815
+
816
+ @Override
817
+ /**
818
+ * This method will save a contact object into the devices contacts database.
819
+ *
820
+ * @param contact the contact to be saved.
821
+ * @returns the id if the contact is successfully saved, null otherwise.
822
+ */
823
+ public String save(JSONObject contact) {
824
+ AccountManager mgr = AccountManager.get(mApp);
825
+ Account[] accounts = mgr.getAccounts();
826
+ String accountName = null;
827
+ String accountType = null;
828
+
829
+ if (accounts.length == 1) {
830
+ accountName = accounts[0].name;
831
+ accountType = accounts[0].type;
832
+ }
833
+ else if (accounts.length > 1) {
834
+ for(Account a : accounts) {
835
+ if(a.type.contains("eas")&& a.name.matches(EMAIL_REGEXP)) /*Exchange ActiveSync*/ {
836
+ accountName = a.name;
837
+ accountType = a.type;
838
+ break;
839
+ }
840
+ }
841
+ if(accountName == null){
842
+ for(Account a : accounts){
843
+ if(a.type.contains("com.google") && a.name.matches(EMAIL_REGEXP)) /*Google sync provider*/ {
844
+ accountName = a.name;
845
+ accountType = a.type;
846
+ break;
847
+ }
848
+ }
849
+ }
850
+ if(accountName == null){
851
+ for(Account a : accounts){
852
+ if(a.name.matches(EMAIL_REGEXP)) /*Last resort, just look for an email address...*/ {
853
+ accountName = a.name;
854
+ accountType = a.type;
855
+ break;
856
+ }
857
+ }
858
+ }
859
+ }
860
+
861
+ String id = getJsonString(contact, "id");
862
+ // Create new contact
863
+ if (id == null) {
864
+ return createNewContact(contact, accountType, accountName);
865
+ }
866
+ // Modify existing contact
867
+ else {
868
+ return modifyContact(id, contact, accountType, accountName);
869
+ }
870
+ }
871
+
872
+ /**
873
+ * Creates a new contact and stores it in the database
874
+ *
875
+ * @param id the raw contact id which is required for linking items to the contact
876
+ * @param contact the contact to be saved
877
+ * @param account the account to be saved under
878
+ */
879
+ private String modifyContact(String id, JSONObject contact, String accountType, String accountName) {
880
+ // Get the RAW_CONTACT_ID which is needed to insert new values in an already existing contact.
881
+ // But not needed to update existing values.
882
+ int rawId = (new Integer(getJsonString(contact,"rawId"))).intValue();
883
+
884
+ // Create a list of attributes to add to the contact database
885
+ ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
886
+
887
+ //Add contact type
888
+ ops.add(ContentProviderOperation.newUpdate(ContactsContract.RawContacts.CONTENT_URI)
889
+ .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
890
+ .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
891
+ .build());
892
+
893
+ // Modify name
894
+ JSONObject name;
895
+ try {
896
+ String displayName = getJsonString(contact, "displayName");
897
+ name = contact.getJSONObject("name");
898
+ if (displayName != null || name != null) {
899
+ ContentProviderOperation.Builder builder = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
900
+ .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " +
901
+ ContactsContract.Data.MIMETYPE + "=?",
902
+ new String[]{id, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE});
903
+
904
+ if (displayName != null) {
905
+ builder.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName);
906
+ }
907
+
908
+ String familyName = getJsonString(name, "familyName");
909
+ if (familyName != null) {
910
+ builder.withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, familyName);
911
+ }
912
+ String middleName = getJsonString(name, "middleName");
913
+ if (middleName != null) {
914
+ builder.withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, middleName);
915
+ }
916
+ String givenName = getJsonString(name, "givenName");
917
+ if (givenName != null) {
918
+ builder.withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, givenName);
919
+ }
920
+ String honorificPrefix = getJsonString(name, "honorificPrefix");
921
+ if (honorificPrefix != null) {
922
+ builder.withValue(ContactsContract.CommonDataKinds.StructuredName.PREFIX, honorificPrefix);
923
+ }
924
+ String honorificSuffix = getJsonString(name, "honorificSuffix");
925
+ if (honorificSuffix != null) {
926
+ builder.withValue(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, honorificSuffix);
927
+ }
928
+
929
+ ops.add(builder.build());
930
+ }
931
+ } catch (JSONException e1) {
932
+ Log.d(LOG_TAG, "Could not get name");
933
+ }
934
+
935
+ // Modify phone numbers
936
+ JSONArray phones = null;
937
+ try {
938
+ phones = contact.getJSONArray("phoneNumbers");
939
+ if (phones != null) {
940
+ for (int i=0; i<phones.length(); i++) {
941
+ JSONObject phone = (JSONObject)phones.get(i);
942
+ String phoneId = getJsonString(phone, "id");
943
+ // This is a new phone so do a DB insert
944
+ if (phoneId == null) {
945
+ ContentValues contentValues = new ContentValues();
946
+ contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
947
+ contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
948
+ contentValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value"));
949
+ contentValues.put(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type")));
950
+
951
+ ops.add(ContentProviderOperation.newInsert(
952
+ ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
953
+ }
954
+ // This is an existing phone so do a DB update
955
+ else {
956
+ ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
957
+ .withSelection(ContactsContract.CommonDataKinds.Phone._ID + "=? AND " +
958
+ ContactsContract.Data.MIMETYPE + "=?",
959
+ new String[]{phoneId, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE})
960
+ .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value"))
961
+ .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type")))
962
+ .build());
963
+ }
964
+ }
965
+ }
966
+ }
967
+ catch (JSONException e) {
968
+ Log.d(LOG_TAG, "Could not get phone numbers");
969
+ }
970
+
971
+ // Modify emails
972
+ JSONArray emails = null;
973
+ try {
974
+ emails = contact.getJSONArray("emails");
975
+ if (emails != null) {
976
+ for (int i=0; i<emails.length(); i++) {
977
+ JSONObject email = (JSONObject)emails.get(i);
978
+ String emailId = getJsonString(email, "id");
979
+ // This is a new email so do a DB insert
980
+ if (emailId==null) {
981
+ ContentValues contentValues = new ContentValues();
982
+ contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
983
+ contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
984
+ contentValues.put(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value"));
985
+ contentValues.put(ContactsContract.CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type")));
986
+
987
+ ops.add(ContentProviderOperation.newInsert(
988
+ ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
989
+ }
990
+ // This is an existing email so do a DB update
991
+ else {
992
+ ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
993
+ .withSelection(ContactsContract.CommonDataKinds.Email._ID + "=? AND " +
994
+ ContactsContract.Data.MIMETYPE + "=?",
995
+ new String[]{emailId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE})
996
+ .withValue(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value"))
997
+ .withValue(ContactsContract.CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type")))
998
+ .build());
999
+ }
1000
+ }
1001
+ }
1002
+ }
1003
+ catch (JSONException e) {
1004
+ Log.d(LOG_TAG, "Could not get emails");
1005
+ }
1006
+
1007
+ // Modify addresses
1008
+ JSONArray addresses = null;
1009
+ try {
1010
+ addresses = contact.getJSONArray("addresses");
1011
+ if (addresses != null) {
1012
+ for (int i=0; i<addresses.length(); i++) {
1013
+ JSONObject address = (JSONObject)addresses.get(i);
1014
+ String addressId = getJsonString(address, "id");
1015
+ // This is a new address so do a DB insert
1016
+ if (addressId==null) {
1017
+ ContentValues contentValues = new ContentValues();
1018
+ contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
1019
+ contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
1020
+ contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type")));
1021
+ contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted"));
1022
+ contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress"));
1023
+ contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality"));
1024
+ contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region"));
1025
+ contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode"));
1026
+ contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country"));
1027
+
1028
+ ops.add(ContentProviderOperation.newInsert(
1029
+ ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
1030
+ }
1031
+ // This is an existing address so do a DB update
1032
+ else {
1033
+ ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
1034
+ .withSelection(ContactsContract.CommonDataKinds.StructuredPostal._ID + "=? AND " +
1035
+ ContactsContract.Data.MIMETYPE + "=?",
1036
+ new String[]{addressId, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE})
1037
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type")))
1038
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted"))
1039
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress"))
1040
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality"))
1041
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region"))
1042
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode"))
1043
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country"))
1044
+ .build());
1045
+ }
1046
+ }
1047
+ }
1048
+ }
1049
+ catch (JSONException e) {
1050
+ Log.d(LOG_TAG, "Could not get addresses");
1051
+ }
1052
+
1053
+ // Modify organizations
1054
+ JSONArray organizations = null;
1055
+ try {
1056
+ organizations = contact.getJSONArray("organizations");
1057
+ if (organizations != null) {
1058
+ for (int i=0; i<organizations.length(); i++) {
1059
+ JSONObject org = (JSONObject)organizations.get(i);
1060
+ String orgId = getJsonString(org, "id");
1061
+ // This is a new organization so do a DB insert
1062
+ if (orgId==null) {
1063
+ ContentValues contentValues = new ContentValues();
1064
+ contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
1065
+ contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
1066
+ contentValues.put(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type")));
1067
+ contentValues.put(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department"));
1068
+ contentValues.put(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name"));
1069
+ contentValues.put(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title"));
1070
+
1071
+ ops.add(ContentProviderOperation.newInsert(
1072
+ ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
1073
+ }
1074
+ // This is an existing organization so do a DB update
1075
+ else {
1076
+ ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
1077
+ .withSelection(ContactsContract.CommonDataKinds.Organization._ID + "=? AND " +
1078
+ ContactsContract.Data.MIMETYPE + "=?",
1079
+ new String[]{orgId, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE})
1080
+ .withValue(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type")))
1081
+ .withValue(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department"))
1082
+ .withValue(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name"))
1083
+ .withValue(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title"))
1084
+ .build());
1085
+ }
1086
+ }
1087
+ }
1088
+ }
1089
+ catch (JSONException e) {
1090
+ Log.d(LOG_TAG, "Could not get organizations");
1091
+ }
1092
+
1093
+ // Modify IMs
1094
+ JSONArray ims = null;
1095
+ try {
1096
+ ims = contact.getJSONArray("ims");
1097
+ if (ims != null) {
1098
+ for (int i=0; i<ims.length(); i++) {
1099
+ JSONObject im = (JSONObject)ims.get(i);
1100
+ String imId = getJsonString(im, "id");
1101
+ // This is a new IM so do a DB insert
1102
+ if (imId==null) {
1103
+ ContentValues contentValues = new ContentValues();
1104
+ contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
1105
+ contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
1106
+ contentValues.put(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value"));
1107
+ contentValues.put(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type")));
1108
+
1109
+ ops.add(ContentProviderOperation.newInsert(
1110
+ ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
1111
+ }
1112
+ // This is an existing IM so do a DB update
1113
+ else {
1114
+ ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
1115
+ .withSelection(ContactsContract.CommonDataKinds.Im._ID + "=? AND " +
1116
+ ContactsContract.Data.MIMETYPE + "=?",
1117
+ new String[]{imId, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE})
1118
+ .withValue(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value"))
1119
+ .withValue(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type")))
1120
+ .build());
1121
+ }
1122
+ }
1123
+ }
1124
+ }
1125
+ catch (JSONException e) {
1126
+ Log.d(LOG_TAG, "Could not get emails");
1127
+ }
1128
+
1129
+ // Modify note
1130
+ String note = getJsonString(contact, "note");
1131
+ ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
1132
+ .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " +
1133
+ ContactsContract.Data.MIMETYPE + "=?",
1134
+ new String[]{id,ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE})
1135
+ .withValue(ContactsContract.CommonDataKinds.Note.NOTE, note)
1136
+ .build());
1137
+
1138
+ // Modify nickname
1139
+ String nickname = getJsonString(contact, "nickname");
1140
+ if (nickname != null) {
1141
+ ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
1142
+ .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " +
1143
+ ContactsContract.Data.MIMETYPE + "=?",
1144
+ new String[]{id,ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE})
1145
+ .withValue(ContactsContract.CommonDataKinds.Nickname.NAME, nickname)
1146
+ .build());
1147
+ }
1148
+
1149
+ // Modify urls
1150
+ JSONArray websites = null;
1151
+ try {
1152
+ websites = contact.getJSONArray("websites");
1153
+ if (websites != null) {
1154
+ for (int i=0; i<websites.length(); i++) {
1155
+ JSONObject website = (JSONObject)websites.get(i);
1156
+ String websiteId = getJsonString(website, "id");
1157
+ // This is a new website so do a DB insert
1158
+ if (websiteId==null) {
1159
+ ContentValues contentValues = new ContentValues();
1160
+ contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
1161
+ contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
1162
+ contentValues.put(ContactsContract.CommonDataKinds.Website.DATA, getJsonString(website, "value"));
1163
+ contentValues.put(ContactsContract.CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type")));
1164
+
1165
+ ops.add(ContentProviderOperation.newInsert(
1166
+ ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
1167
+ }
1168
+ // This is an existing website so do a DB update
1169
+ else {
1170
+ ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
1171
+ .withSelection(ContactsContract.CommonDataKinds.Website._ID + "=? AND " +
1172
+ ContactsContract.Data.MIMETYPE + "=?",
1173
+ new String[]{websiteId, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE})
1174
+ .withValue(ContactsContract.CommonDataKinds.Website.DATA, getJsonString(website, "value"))
1175
+ .withValue(ContactsContract.CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type")))
1176
+ .build());
1177
+ }
1178
+ }
1179
+ }
1180
+ }
1181
+ catch (JSONException e) {
1182
+ Log.d(LOG_TAG, "Could not get websites");
1183
+ }
1184
+
1185
+ // Modify birthday
1186
+ String birthday = getJsonString(contact, "birthday");
1187
+ if (birthday != null) {
1188
+ ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
1189
+ .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " +
1190
+ ContactsContract.Data.MIMETYPE + "=? AND " +
1191
+ ContactsContract.CommonDataKinds.Event.TYPE + "=?",
1192
+ new String[]{id,ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, new String(""+ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY)})
1193
+ .withValue(ContactsContract.CommonDataKinds.Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY)
1194
+ .withValue(ContactsContract.CommonDataKinds.Event.START_DATE, birthday)
1195
+ .build());
1196
+ }
1197
+
1198
+ // Modify photos
1199
+ JSONArray photos = null;
1200
+ try {
1201
+ photos = contact.getJSONArray("photos");
1202
+ if (photos != null) {
1203
+ for (int i=0; i<photos.length(); i++) {
1204
+ JSONObject photo = (JSONObject)photos.get(i);
1205
+ String photoId = getJsonString(photo, "id");
1206
+ byte[] bytes = getPhotoBytes(getJsonString(photo, "value"));
1207
+ // This is a new photo so do a DB insert
1208
+ if (photoId==null) {
1209
+ ContentValues contentValues = new ContentValues();
1210
+ contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
1211
+ contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
1212
+ contentValues.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1);
1213
+ contentValues.put(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes);
1214
+
1215
+ ops.add(ContentProviderOperation.newInsert(
1216
+ ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
1217
+ }
1218
+ // This is an existing photo so do a DB update
1219
+ else {
1220
+ ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
1221
+ .withSelection(ContactsContract.CommonDataKinds.Photo._ID + "=? AND " +
1222
+ ContactsContract.Data.MIMETYPE + "=?",
1223
+ new String[]{photoId, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE})
1224
+ .withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1)
1225
+ .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes)
1226
+ .build());
1227
+ }
1228
+ }
1229
+ }
1230
+ }
1231
+ catch (JSONException e) {
1232
+ Log.d(LOG_TAG, "Could not get photos");
1233
+ }
1234
+
1235
+ boolean retVal = true;
1236
+
1237
+ //Modify contact
1238
+ try {
1239
+ mApp.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
1240
+ } catch (RemoteException e) {
1241
+ Log.e(LOG_TAG, e.getMessage(), e);
1242
+ Log.e(LOG_TAG, Log.getStackTraceString(e), e);
1243
+ retVal = false;
1244
+ } catch (OperationApplicationException e) {
1245
+ Log.e(LOG_TAG, e.getMessage(), e);
1246
+ Log.e(LOG_TAG, Log.getStackTraceString(e), e);
1247
+ retVal = false;
1248
+ }
1249
+
1250
+ // if the save was a succes return the contact ID
1251
+ if (retVal) {
1252
+ return id;
1253
+ } else {
1254
+ return null;
1255
+ }
1256
+ }
1257
+
1258
+ /**
1259
+ * Add a website to a list of database actions to be performed
1260
+ *
1261
+ * @param ops the list of database actions
1262
+ * @param website the item to be inserted
1263
+ */
1264
+ private void insertWebsite(ArrayList<ContentProviderOperation> ops,
1265
+ JSONObject website) {
1266
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
1267
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
1268
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE)
1269
+ .withValue(ContactsContract.CommonDataKinds.Website.DATA, getJsonString(website, "value"))
1270
+ .withValue(ContactsContract.CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type")))
1271
+ .build());
1272
+ }
1273
+
1274
+ /**
1275
+ * Add an im to a list of database actions to be performed
1276
+ *
1277
+ * @param ops the list of database actions
1278
+ * @param im the item to be inserted
1279
+ */
1280
+ private void insertIm(ArrayList<ContentProviderOperation> ops, JSONObject im) {
1281
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
1282
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
1283
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
1284
+ .withValue(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value"))
1285
+ .withValue(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type")))
1286
+ .build());
1287
+ }
1288
+
1289
+ /**
1290
+ * Add an organization to a list of database actions to be performed
1291
+ *
1292
+ * @param ops the list of database actions
1293
+ * @param org the item to be inserted
1294
+ */
1295
+ private void insertOrganization(ArrayList<ContentProviderOperation> ops,
1296
+ JSONObject org) {
1297
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
1298
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
1299
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
1300
+ .withValue(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type")))
1301
+ .withValue(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department"))
1302
+ .withValue(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name"))
1303
+ .withValue(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title"))
1304
+ .build());
1305
+ }
1306
+
1307
+ /**
1308
+ * Add an address to a list of database actions to be performed
1309
+ *
1310
+ * @param ops the list of database actions
1311
+ * @param address the item to be inserted
1312
+ */
1313
+ private void insertAddress(ArrayList<ContentProviderOperation> ops,
1314
+ JSONObject address) {
1315
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
1316
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
1317
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
1318
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type")))
1319
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted"))
1320
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress"))
1321
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality"))
1322
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region"))
1323
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode"))
1324
+ .withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country"))
1325
+ .build());
1326
+ }
1327
+
1328
+ /**
1329
+ * Add an email to a list of database actions to be performed
1330
+ *
1331
+ * @param ops the list of database actions
1332
+ * @param email the item to be inserted
1333
+ */
1334
+ private void insertEmail(ArrayList<ContentProviderOperation> ops,
1335
+ JSONObject email) {
1336
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
1337
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
1338
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
1339
+ .withValue(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value"))
1340
+ .withValue(ContactsContract.CommonDataKinds.Email.TYPE, getPhoneType(getJsonString(email, "type")))
1341
+ .build());
1342
+ }
1343
+
1344
+ /**
1345
+ * Add a phone to a list of database actions to be performed
1346
+ *
1347
+ * @param ops the list of database actions
1348
+ * @param phone the item to be inserted
1349
+ */
1350
+ private void insertPhone(ArrayList<ContentProviderOperation> ops,
1351
+ JSONObject phone) {
1352
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
1353
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
1354
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
1355
+ .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value"))
1356
+ .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type")))
1357
+ .build());
1358
+ }
1359
+
1360
+ /**
1361
+ * Add a phone to a list of database actions to be performed
1362
+ *
1363
+ * @param ops the list of database actions
1364
+ * @param phone the item to be inserted
1365
+ */
1366
+ private void insertPhoto(ArrayList<ContentProviderOperation> ops,
1367
+ JSONObject photo) {
1368
+ byte[] bytes = getPhotoBytes(getJsonString(photo, "value"));
1369
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
1370
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
1371
+ .withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1)
1372
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
1373
+ .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes)
1374
+ .build());
1375
+ }
1376
+
1377
+ /**
1378
+ * Gets the raw bytes from the supplied filename
1379
+ *
1380
+ * @param filename the file to read the bytes from
1381
+ * @return a byte array
1382
+ * @throws IOException
1383
+ */
1384
+ private byte[] getPhotoBytes(String filename) {
1385
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
1386
+ try {
1387
+ int bytesRead = 0;
1388
+ long totalBytesRead = 0;
1389
+ byte[] data = new byte[8192];
1390
+ InputStream in = getPathFromUri(filename);
1391
+
1392
+ while ((bytesRead = in.read(data, 0, data.length)) != -1 && totalBytesRead <= MAX_PHOTO_SIZE) {
1393
+ buffer.write(data, 0, bytesRead);
1394
+ totalBytesRead += bytesRead;
1395
+ }
1396
+
1397
+ in.close();
1398
+ buffer.flush();
1399
+ } catch (FileNotFoundException e) {
1400
+ Log.e(LOG_TAG, e.getMessage(), e);
1401
+ } catch (IOException e) {
1402
+ Log.e(LOG_TAG, e.getMessage(), e);
1403
+ }
1404
+ return buffer.toByteArray();
1405
+ }
1406
+ /**
1407
+ * Get an input stream based on file path or uri content://, http://, file://
1408
+ *
1409
+ * @param path
1410
+ * @return an input stream
1411
+ * @throws IOException
1412
+ */
1413
+ private InputStream getPathFromUri(String path) throws IOException {
1414
+ if (path.startsWith("content:")) {
1415
+ Uri uri = Uri.parse(path);
1416
+ return mApp.getContentResolver().openInputStream(uri);
1417
+ }
1418
+ if (path.startsWith("http:") || path.startsWith("file:")) {
1419
+ URL url = new URL(path);
1420
+ return url.openStream();
1421
+ }
1422
+ else {
1423
+ return new FileInputStream(path);
1424
+ }
1425
+ }
1426
+
1427
+ /**
1428
+ * Creates a new contact and stores it in the database
1429
+ *
1430
+ * @param contact the contact to be saved
1431
+ * @param account the account to be saved under
1432
+ */
1433
+ private String createNewContact(JSONObject contact, String accountType, String accountName) {
1434
+ // Create a list of attributes to add to the contact database
1435
+ ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
1436
+
1437
+ //Add contact type
1438
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
1439
+ .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
1440
+ .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
1441
+ .build());
1442
+
1443
+ // Add name
1444
+ try {
1445
+ JSONObject name = contact.optJSONObject("name");
1446
+ String displayName = contact.getString("displayName");
1447
+ if (displayName != null || name != null) {
1448
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
1449
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
1450
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
1451
+ .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName)
1452
+ .withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, getJsonString(name, "familyName"))
1453
+ .withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, getJsonString(name, "middleName"))
1454
+ .withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, getJsonString(name, "givenName"))
1455
+ .withValue(ContactsContract.CommonDataKinds.StructuredName.PREFIX, getJsonString(name, "honorificPrefix"))
1456
+ .withValue(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, getJsonString(name, "honorificSuffix"))
1457
+ .build());
1458
+ }
1459
+ }
1460
+ catch (JSONException e) {
1461
+ Log.d(LOG_TAG, "Could not get name object");
1462
+ }
1463
+
1464
+ //Add phone numbers
1465
+ JSONArray phones = null;
1466
+ try {
1467
+ phones = contact.getJSONArray("phoneNumbers");
1468
+ if (phones != null) {
1469
+ for (int i=0; i<phones.length(); i++) {
1470
+ JSONObject phone = (JSONObject)phones.get(i);
1471
+ insertPhone(ops, phone);
1472
+ }
1473
+ }
1474
+ }
1475
+ catch (JSONException e) {
1476
+ Log.d(LOG_TAG, "Could not get phone numbers");
1477
+ }
1478
+
1479
+ // Add emails
1480
+ JSONArray emails = null;
1481
+ try {
1482
+ emails = contact.getJSONArray("emails");
1483
+ if (emails != null) {
1484
+ for (int i=0; i<emails.length(); i++) {
1485
+ JSONObject email = (JSONObject)emails.get(i);
1486
+ insertEmail(ops, email);
1487
+ }
1488
+ }
1489
+ }
1490
+ catch (JSONException e) {
1491
+ Log.d(LOG_TAG, "Could not get emails");
1492
+ }
1493
+
1494
+ // Add addresses
1495
+ JSONArray addresses = null;
1496
+ try {
1497
+ addresses = contact.getJSONArray("addresses");
1498
+ if (addresses != null) {
1499
+ for (int i=0; i<addresses.length(); i++) {
1500
+ JSONObject address = (JSONObject)addresses.get(i);
1501
+ insertAddress(ops, address);
1502
+ }
1503
+ }
1504
+ }
1505
+ catch (JSONException e) {
1506
+ Log.d(LOG_TAG, "Could not get addresses");
1507
+ }
1508
+
1509
+ // Add organizations
1510
+ JSONArray organizations = null;
1511
+ try {
1512
+ organizations = contact.getJSONArray("organizations");
1513
+ if (organizations != null) {
1514
+ for (int i=0; i<organizations.length(); i++) {
1515
+ JSONObject org = (JSONObject)organizations.get(i);
1516
+ insertOrganization(ops, org);
1517
+ }
1518
+ }
1519
+ }
1520
+ catch (JSONException e) {
1521
+ Log.d(LOG_TAG, "Could not get organizations");
1522
+ }
1523
+
1524
+ // Add IMs
1525
+ JSONArray ims = null;
1526
+ try {
1527
+ ims = contact.getJSONArray("ims");
1528
+ if (ims != null) {
1529
+ for (int i=0; i<ims.length(); i++) {
1530
+ JSONObject im = (JSONObject)ims.get(i);
1531
+ insertIm(ops, im);
1532
+ }
1533
+ }
1534
+ }
1535
+ catch (JSONException e) {
1536
+ Log.d(LOG_TAG, "Could not get emails");
1537
+ }
1538
+
1539
+ // Add note
1540
+ String note = getJsonString(contact, "note");
1541
+ if (note != null) {
1542
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
1543
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
1544
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE)
1545
+ .withValue(ContactsContract.CommonDataKinds.Note.NOTE, note)
1546
+ .build());
1547
+ }
1548
+
1549
+ // Add nickname
1550
+ String nickname = getJsonString(contact, "nickname");
1551
+ if (nickname != null) {
1552
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
1553
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
1554
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE)
1555
+ .withValue(ContactsContract.CommonDataKinds.Nickname.NAME, nickname)
1556
+ .build());
1557
+ }
1558
+
1559
+ // Add urls
1560
+ JSONArray websites = null;
1561
+ try {
1562
+ websites = contact.getJSONArray("websites");
1563
+ if (websites != null) {
1564
+ for (int i=0; i<websites.length(); i++) {
1565
+ JSONObject website = (JSONObject)websites.get(i);
1566
+ insertWebsite(ops, website);
1567
+ }
1568
+ }
1569
+ }
1570
+ catch (JSONException e) {
1571
+ Log.d(LOG_TAG, "Could not get websites");
1572
+ }
1573
+
1574
+ // Add birthday
1575
+ String birthday = getJsonString(contact, "birthday");
1576
+ if (birthday != null) {
1577
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
1578
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
1579
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)
1580
+ .withValue(ContactsContract.CommonDataKinds.Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY)
1581
+ .withValue(ContactsContract.CommonDataKinds.Event.START_DATE, birthday)
1582
+ .build());
1583
+ }
1584
+
1585
+ // Add photos
1586
+ JSONArray photos = null;
1587
+ try {
1588
+ photos = contact.getJSONArray("photos");
1589
+ if (photos != null) {
1590
+ for (int i=0; i<photos.length(); i++) {
1591
+ JSONObject photo = (JSONObject)photos.get(i);
1592
+ insertPhoto(ops, photo);
1593
+ }
1594
+ }
1595
+ }
1596
+ catch (JSONException e) {
1597
+ Log.d(LOG_TAG, "Could not get photos");
1598
+ }
1599
+
1600
+ String newId = null;
1601
+ //Add contact
1602
+ try {
1603
+ ContentProviderResult[] cpResults = mApp.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
1604
+ if (cpResults.length >= 0) {
1605
+ newId = cpResults[0].uri.getLastPathSegment();
1606
+ }
1607
+ } catch (RemoteException e) {
1608
+ Log.e(LOG_TAG, e.getMessage(), e);
1609
+ } catch (OperationApplicationException e) {
1610
+ Log.e(LOG_TAG, e.getMessage(), e);
1611
+ }
1612
+ return newId;
1613
+ }
1614
+
1615
+ @Override
1616
+ /**
1617
+ * This method will remove a Contact from the database based on ID.
1618
+ * @param id the unique ID of the contact to remove
1619
+ */
1620
+ public boolean remove(String id) {
1621
+ int result = 0;
1622
+ Cursor cursor = mApp.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
1623
+ null,
1624
+ ContactsContract.Contacts._ID + " = ?",
1625
+ new String[] {id}, null);
1626
+ if(cursor.getCount() == 1) {
1627
+ cursor.moveToFirst();
1628
+ String lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
1629
+ Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey);
1630
+ result = mApp.getContentResolver().delete(uri, null, null);
1631
+ } else {
1632
+ Log.d(LOG_TAG, "Could not find contact with ID");
1633
+ }
1634
+
1635
+ return (result > 0) ? true : false;
1636
+ }
1637
+
1638
+ /**************************************************************************
1639
+ *
1640
+ * All methods below this comment are used to convert from JavaScript
1641
+ * text types to Android integer types and vice versa.
1642
+ *
1643
+ *************************************************************************/
1644
+
1645
+ /**
1646
+ * Converts a string from the W3C Contact API to it's Android int value.
1647
+ * @param string
1648
+ * @return Android int value
1649
+ */
1650
+ private int getPhoneType(String string) {
1651
+ int type = ContactsContract.CommonDataKinds.Phone.TYPE_OTHER;
1652
+ if ("home".equals(string.toLowerCase())) {
1653
+ return ContactsContract.CommonDataKinds.Phone.TYPE_HOME;
1654
+ }
1655
+ else if ("mobile".equals(string.toLowerCase())) {
1656
+ return ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE;
1657
+ }
1658
+ else if ("work".equals(string.toLowerCase())) {
1659
+ return ContactsContract.CommonDataKinds.Phone.TYPE_WORK;
1660
+ }
1661
+ else if ("work fax".equals(string.toLowerCase())) {
1662
+ return ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK;
1663
+ }
1664
+ else if ("home fax".equals(string.toLowerCase())) {
1665
+ return ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME;
1666
+ }
1667
+ else if ("fax".equals(string.toLowerCase())) {
1668
+ return ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK;
1669
+ }
1670
+ else if ("pager".equals(string.toLowerCase())) {
1671
+ return ContactsContract.CommonDataKinds.Phone.TYPE_PAGER;
1672
+ }
1673
+ else if ("other".equals(string.toLowerCase())) {
1674
+ return ContactsContract.CommonDataKinds.Phone.TYPE_OTHER;
1675
+ }
1676
+ else if ("car".equals(string.toLowerCase())) {
1677
+ return ContactsContract.CommonDataKinds.Phone.TYPE_CAR;
1678
+ }
1679
+ else if ("company main".equals(string.toLowerCase())) {
1680
+ return ContactsContract.CommonDataKinds.Phone.TYPE_COMPANY_MAIN;
1681
+ }
1682
+ else if ("isdn".equals(string.toLowerCase())) {
1683
+ return ContactsContract.CommonDataKinds.Phone.TYPE_ISDN;
1684
+ }
1685
+ else if ("main".equals(string.toLowerCase())) {
1686
+ return ContactsContract.CommonDataKinds.Phone.TYPE_MAIN;
1687
+ }
1688
+ else if ("other fax".equals(string.toLowerCase())) {
1689
+ return ContactsContract.CommonDataKinds.Phone.TYPE_OTHER_FAX;
1690
+ }
1691
+ else if ("radio".equals(string.toLowerCase())) {
1692
+ return ContactsContract.CommonDataKinds.Phone.TYPE_RADIO;
1693
+ }
1694
+ else if ("telex".equals(string.toLowerCase())) {
1695
+ return ContactsContract.CommonDataKinds.Phone.TYPE_TELEX;
1696
+ }
1697
+ else if ("work mobile".equals(string.toLowerCase())) {
1698
+ return ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE;
1699
+ }
1700
+ else if ("work pager".equals(string.toLowerCase())) {
1701
+ return ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER;
1702
+ }
1703
+ else if ("assistant".equals(string.toLowerCase())) {
1704
+ return ContactsContract.CommonDataKinds.Phone.TYPE_ASSISTANT;
1705
+ }
1706
+ else if ("mms".equals(string.toLowerCase())) {
1707
+ return ContactsContract.CommonDataKinds.Phone.TYPE_MMS;
1708
+ }
1709
+ else if ("callback".equals(string.toLowerCase())) {
1710
+ return ContactsContract.CommonDataKinds.Phone.TYPE_CALLBACK;
1711
+ }
1712
+ else if ("tty ttd".equals(string.toLowerCase())) {
1713
+ return ContactsContract.CommonDataKinds.Phone.TYPE_TTY_TDD;
1714
+ }
1715
+ else if ("custom".equals(string.toLowerCase())) {
1716
+ return ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM;
1717
+ }
1718
+ return type;
1719
+ }
1720
+
1721
+ /**
1722
+ * getPhoneType converts an Android phone type into a string
1723
+ * @param type
1724
+ * @return phone type as string.
1725
+ */
1726
+ private String getPhoneType(int type) {
1727
+ String stringType;
1728
+ switch (type) {
1729
+ case ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM:
1730
+ stringType = "custom";
1731
+ break;
1732
+ case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME:
1733
+ stringType = "home fax";
1734
+ break;
1735
+ case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK:
1736
+ stringType = "work fax";
1737
+ break;
1738
+ case ContactsContract.CommonDataKinds.Phone.TYPE_HOME:
1739
+ stringType = "home";
1740
+ break;
1741
+ case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE:
1742
+ stringType = "mobile";
1743
+ break;
1744
+ case ContactsContract.CommonDataKinds.Phone.TYPE_PAGER:
1745
+ stringType = "pager";
1746
+ break;
1747
+ case ContactsContract.CommonDataKinds.Phone.TYPE_WORK:
1748
+ stringType = "work";
1749
+ break;
1750
+ case ContactsContract.CommonDataKinds.Phone.TYPE_CALLBACK:
1751
+ stringType = "callback";
1752
+ break;
1753
+ case ContactsContract.CommonDataKinds.Phone.TYPE_CAR:
1754
+ stringType = "car";
1755
+ break;
1756
+ case ContactsContract.CommonDataKinds.Phone.TYPE_COMPANY_MAIN:
1757
+ stringType = "company main";
1758
+ break;
1759
+ case ContactsContract.CommonDataKinds.Phone.TYPE_OTHER_FAX:
1760
+ stringType = "other fax";
1761
+ break;
1762
+ case ContactsContract.CommonDataKinds.Phone.TYPE_RADIO:
1763
+ stringType = "radio";
1764
+ break;
1765
+ case ContactsContract.CommonDataKinds.Phone.TYPE_TELEX:
1766
+ stringType = "telex";
1767
+ break;
1768
+ case ContactsContract.CommonDataKinds.Phone.TYPE_TTY_TDD:
1769
+ stringType = "tty tdd";
1770
+ break;
1771
+ case ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE:
1772
+ stringType = "work mobile";
1773
+ break;
1774
+ case ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER:
1775
+ stringType = "work pager";
1776
+ break;
1777
+ case ContactsContract.CommonDataKinds.Phone.TYPE_ASSISTANT:
1778
+ stringType = "assistant";
1779
+ break;
1780
+ case ContactsContract.CommonDataKinds.Phone.TYPE_MMS:
1781
+ stringType = "mms";
1782
+ break;
1783
+ case ContactsContract.CommonDataKinds.Phone.TYPE_ISDN:
1784
+ stringType = "isdn";
1785
+ break;
1786
+ case ContactsContract.CommonDataKinds.Phone.TYPE_OTHER:
1787
+ default:
1788
+ stringType = "other";
1789
+ break;
1790
+ }
1791
+ return stringType;
1792
+ }
1793
+
1794
+ /**
1795
+ * Converts a string from the W3C Contact API to it's Android int value.
1796
+ * @param string
1797
+ * @return Android int value
1798
+ */
1799
+ private int getContactType(String string) {
1800
+ int type = ContactsContract.CommonDataKinds.Email.TYPE_OTHER;
1801
+ if (string!=null) {
1802
+ if ("home".equals(string.toLowerCase())) {
1803
+ return ContactsContract.CommonDataKinds.Email.TYPE_HOME;
1804
+ }
1805
+ else if ("work".equals(string.toLowerCase())) {
1806
+ return ContactsContract.CommonDataKinds.Email.TYPE_WORK;
1807
+ }
1808
+ else if ("other".equals(string.toLowerCase())) {
1809
+ return ContactsContract.CommonDataKinds.Email.TYPE_OTHER;
1810
+ }
1811
+ else if ("mobile".equals(string.toLowerCase())) {
1812
+ return ContactsContract.CommonDataKinds.Email.TYPE_MOBILE;
1813
+ }
1814
+ else if ("custom".equals(string.toLowerCase())) {
1815
+ return ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM;
1816
+ }
1817
+ }
1818
+ return type;
1819
+ }
1820
+
1821
+ /**
1822
+ * getPhoneType converts an Android phone type into a string
1823
+ * @param type
1824
+ * @return phone type as string.
1825
+ */
1826
+ private String getContactType(int type) {
1827
+ String stringType;
1828
+ switch (type) {
1829
+ case ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM:
1830
+ stringType = "custom";
1831
+ break;
1832
+ case ContactsContract.CommonDataKinds.Email.TYPE_HOME:
1833
+ stringType = "home";
1834
+ break;
1835
+ case ContactsContract.CommonDataKinds.Email.TYPE_WORK:
1836
+ stringType = "work";
1837
+ break;
1838
+ case ContactsContract.CommonDataKinds.Email.TYPE_MOBILE:
1839
+ stringType = "mobile";
1840
+ break;
1841
+ case ContactsContract.CommonDataKinds.Email.TYPE_OTHER:
1842
+ default:
1843
+ stringType = "other";
1844
+ break;
1845
+ }
1846
+ return stringType;
1847
+ }
1848
+
1849
+ /**
1850
+ * Converts a string from the W3C Contact API to it's Android int value.
1851
+ * @param string
1852
+ * @return Android int value
1853
+ */
1854
+ private int getOrgType(String string) {
1855
+ int type = ContactsContract.CommonDataKinds.Organization.TYPE_OTHER;
1856
+ if (string!=null) {
1857
+ if ("work".equals(string.toLowerCase())) {
1858
+ return ContactsContract.CommonDataKinds.Organization.TYPE_WORK;
1859
+ }
1860
+ else if ("other".equals(string.toLowerCase())) {
1861
+ return ContactsContract.CommonDataKinds.Organization.TYPE_OTHER;
1862
+ }
1863
+ else if ("custom".equals(string.toLowerCase())) {
1864
+ return ContactsContract.CommonDataKinds.Organization.TYPE_CUSTOM;
1865
+ }
1866
+ }
1867
+ return type;
1868
+ }
1869
+
1870
+ /**
1871
+ * getPhoneType converts an Android phone type into a string
1872
+ * @param type
1873
+ * @return phone type as string.
1874
+ */
1875
+ private String getOrgType(int type) {
1876
+ String stringType;
1877
+ switch (type) {
1878
+ case ContactsContract.CommonDataKinds.Organization.TYPE_CUSTOM:
1879
+ stringType = "custom";
1880
+ break;
1881
+ case ContactsContract.CommonDataKinds.Organization.TYPE_WORK:
1882
+ stringType = "work";
1883
+ break;
1884
+ case ContactsContract.CommonDataKinds.Organization.TYPE_OTHER:
1885
+ default:
1886
+ stringType = "other";
1887
+ break;
1888
+ }
1889
+ return stringType;
1890
+ }
1891
+
1892
+ /**
1893
+ * Converts a string from the W3C Contact API to it's Android int value.
1894
+ * @param string
1895
+ * @return Android int value
1896
+ */
1897
+ private int getAddressType(String string) {
1898
+ int type = ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER;
1899
+ if (string!=null) {
1900
+ if ("work".equals(string.toLowerCase())) {
1901
+ return ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK;
1902
+ }
1903
+ else if ("other".equals(string.toLowerCase())) {
1904
+ return ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER;
1905
+ }
1906
+ else if ("home".equals(string.toLowerCase())) {
1907
+ return ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME;
1908
+ }
1909
+ }
1910
+ return type;
1911
+ }
1912
+
1913
+ /**
1914
+ * getPhoneType converts an Android phone type into a string
1915
+ * @param type
1916
+ * @return phone type as string.
1917
+ */
1918
+ private String getAddressType(int type) {
1919
+ String stringType;
1920
+ switch (type) {
1921
+ case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME:
1922
+ stringType = "home";
1923
+ break;
1924
+ case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK:
1925
+ stringType = "work";
1926
+ break;
1927
+ case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER:
1928
+ default:
1929
+ stringType = "other";
1930
+ break;
1931
+ }
1932
+ return stringType;
1933
+ }
1934
+ }