mobile_template 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }