|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 const DEBUG = false; |
|
8 function debug(s) { dump("-*- Android ContactService component: " + s + "\n"); } |
|
9 |
|
10 const Cu = Components.utils; |
|
11 const Cc = Components.classes; |
|
12 const Ci = Components.interfaces; |
|
13 |
|
14 this.EXPORTED_SYMBOLS = []; |
|
15 |
|
16 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
17 Cu.import("resource://gre/modules/Services.jsm"); |
|
18 Cu.import("resource://gre/modules/PhoneNumberUtils.jsm"); |
|
19 |
|
20 XPCOMUtils.defineLazyServiceGetter(this, "ppmm", "@mozilla.org/parentprocessmessagemanager;1", |
|
21 "nsIMessageListenerManager"); |
|
22 |
|
23 let ContactService = { |
|
24 init: function() { |
|
25 if (DEBUG) debug("Init"); |
|
26 this._requestMessages = {}; |
|
27 |
|
28 // Add listeners for all messages from ContactManager.js |
|
29 let messages = ["Contacts:Clear", "Contacts:Find", "Contacts:GetAll", |
|
30 "Contacts:GetAll:SendNow", "Contacts:GetCount", "Contacts:GetRevision", |
|
31 "Contact:Remove", "Contact:Save",]; |
|
32 messages.forEach(function(msgName) { |
|
33 ppmm.addMessageListener(msgName, this); |
|
34 }.bind(this)); |
|
35 |
|
36 // Add listeners for all messages from ContactService.java |
|
37 let returnMessages = ["Android:Contacts:Count", |
|
38 "Android:Contacts:Clear:Return:OK", "Android:Contacts:Clear:Return:KO", |
|
39 "Android:Contacts:Find:Return:OK", "Android:Contacts:Find:Return:KO", |
|
40 "Android:Contacts:GetAll:Next", "Android:Contacts:RegisterForMessages", |
|
41 "Android:Contact:Remove:Return:OK", "Android:Contact:Remove:Return:KO", |
|
42 "Android:Contact:Save:Return:OK", "Android:Contact:Save:Return:KO",]; |
|
43 |
|
44 returnMessages.forEach(function(msgName) { |
|
45 Services.obs.addObserver(this, msgName, false); |
|
46 }.bind(this)); |
|
47 }, |
|
48 |
|
49 _sendMessageToJava: function(aMsg) { |
|
50 Services.androidBridge.handleGeckoMessage(aMsg); |
|
51 }, |
|
52 |
|
53 _sendReturnMessage: function(aTopic, aRequestID, aResult) { |
|
54 this._requestMessages[aRequestID].target.sendAsyncMessage(aTopic, aResult); |
|
55 }, |
|
56 |
|
57 _sendAndDeleteReturnMessage: function(aTopic, aRequestID, aResult) { |
|
58 this._sendReturnMessage(aTopic, aRequestID, aResult) |
|
59 delete this._requestMessages[aRequestID]; |
|
60 }, |
|
61 |
|
62 observe: function(aSubject, aTopic, aData) { |
|
63 if (DEBUG) { |
|
64 debug("observe: subject: " + aSubject + " topic: " + aTopic + " data: " + aData); |
|
65 } |
|
66 |
|
67 let message = JSON.parse(aData, function date_reviver(k, v) { |
|
68 // The Java service sends dates as strings, so convert them to Dates before |
|
69 // sending them back to the child. |
|
70 if (v != null && v != "null" && |
|
71 ["updated", "published", "anniversary", "bday"].indexOf(k) != -1) { |
|
72 return new Date(v); |
|
73 } |
|
74 return v; |
|
75 }); |
|
76 let requestID = message.requestID; |
|
77 |
|
78 // The return message topic is the same as the current topic, but without the "Android:" prefix |
|
79 let returnMessageTopic = aTopic.substring(8); |
|
80 |
|
81 switch (aTopic) { |
|
82 case "Android:Contacts:Find:Return:OK": |
|
83 this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID, contacts: message.contacts}); |
|
84 break; |
|
85 |
|
86 case "Android:Contacts:Find:Return:KO": |
|
87 this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID}); |
|
88 break; |
|
89 |
|
90 case "Android:Contact:Save:Return:OK": |
|
91 this._sendReturnMessage(returnMessageTopic, requestID, {requestID: requestID, contactID: message.contactID}); |
|
92 this._sendAndDeleteReturnMessage("Contact:Changed", requestID, {contactID: message.contactID, reason: message.reason}); |
|
93 break; |
|
94 |
|
95 case "Android:Contact:Save:Return:KO": |
|
96 this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID}); |
|
97 break; |
|
98 |
|
99 case "Android:Contact:Remove:Return:OK": |
|
100 this._sendReturnMessage(returnMessageTopic, requestID, {requestID: requestID, contactID: message.contactID}); |
|
101 this._sendAndDeleteReturnMessage("Contact:Changed", requestID, {contactID: message.contactID, reason: "remove"}); |
|
102 break; |
|
103 |
|
104 case "Android:Contact:Remove:Return:KO": |
|
105 this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID}); |
|
106 break; |
|
107 |
|
108 case "Android:Contacts:Clear:Return:OK": |
|
109 this._sendReturnMessage(returnMessageTopic, requestID, {requestID: requestID}); |
|
110 this._sendAndDeleteReturnMessage("Contact:Changed", requestID, {reason: "remove"}); |
|
111 break; |
|
112 |
|
113 case "Android:Contact:Clear:Return:KO": |
|
114 this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID}); |
|
115 break; |
|
116 |
|
117 case "Android:Contacts:GetAll:Next": |
|
118 // GetAll uses a cursor ID instead of a request ID. Translate the request ID back to the cursor ID |
|
119 this._sendReturnMessage(returnMessageTopic, requestID, {cursorId: requestID, contacts: message.contacts}); |
|
120 |
|
121 // Send a message with no contacts to denote the end of contacts returned by the query |
|
122 this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {cursorId: requestID}); |
|
123 break; |
|
124 |
|
125 case "Android:Contacts:Count": |
|
126 this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID, count: message.count}); |
|
127 break; |
|
128 |
|
129 default: |
|
130 throw "Wrong message received: " + aTopic; |
|
131 } |
|
132 }, |
|
133 |
|
134 assertPermission: function(aMessage, aPerm) { |
|
135 if (!aMessage.target.assertPermission(aPerm)) { |
|
136 Cu.reportError("Contacts message " + aMessage.name + |
|
137 " from a content process with no" + aPerm + " privileges."); |
|
138 return false; |
|
139 } |
|
140 return true; |
|
141 }, |
|
142 |
|
143 receiveMessage: function(aMessage) { |
|
144 if (DEBUG) debug("receiveMessage " + aMessage.name); |
|
145 |
|
146 // GetAll uses a cursor ID instead of a request ID, but they can be treated the same from here |
|
147 if (!aMessage.data.requestID && aMessage.data.cursorId) { |
|
148 aMessage.data.requestID = aMessage.data.cursorId; |
|
149 } |
|
150 let requestID = aMessage.data.requestID; |
|
151 |
|
152 // Store the message so it the request callback can be called when the Java side is finished |
|
153 this._requestMessages[requestID] = aMessage; |
|
154 |
|
155 switch (aMessage.name) { |
|
156 case "Contacts:Find": |
|
157 this.findContacts(aMessage); |
|
158 break; |
|
159 |
|
160 case "Contacts:GetAll": |
|
161 this.getAllContacts(aMessage); |
|
162 break; |
|
163 |
|
164 case "Contacts:GetAll:SendNow": |
|
165 // Send an empty message to denote there are no most contacts for the getAll query |
|
166 this._sendAndDeleteReturnMessage("Contacts:GetAll:Next", requestID, {cursorId: requestID}); |
|
167 break; |
|
168 |
|
169 case "Contact:Save": |
|
170 this.saveContact(aMessage); |
|
171 break; |
|
172 |
|
173 case "Contact:Remove": |
|
174 this.removeContact(aMessage); |
|
175 break; |
|
176 |
|
177 case "Contacts:Clear": |
|
178 this.clearContacts(aMessage); |
|
179 break; |
|
180 |
|
181 case "Contacts:GetCount": |
|
182 this.getContactsCount(aMessage); |
|
183 break; |
|
184 |
|
185 case "Contacts:GetRevision": |
|
186 // Android does not support the get revision function |
|
187 this._sendAndDeleteReturnMessage("Contacts:GetRevision:Return:KO", requestID, {requestID: requestID, |
|
188 errorMsg: "Android does not support the revision function."}); |
|
189 break; |
|
190 |
|
191 case "Contacts:RegisterForMessages": |
|
192 delete this._requestMessages[requestID]; |
|
193 break; |
|
194 |
|
195 default: |
|
196 delete this._requestMessages[requestID]; |
|
197 throw "Wrong message received: " + aMessage.name; |
|
198 } |
|
199 }, |
|
200 |
|
201 findContacts: function(aMessage) { |
|
202 if (!this.assertPermission(aMessage, "contacts-read")) { |
|
203 return; |
|
204 } |
|
205 |
|
206 let countryName = PhoneNumberUtils.getCountryName(); |
|
207 let substringmatchingPref = "dom.phonenumber.substringmatching." + countryName; |
|
208 let substringmatchingValue = 0; |
|
209 if (Services.prefs.getPrefType(substringmatchingPref) == Ci.nsIPrefBranch.PREF_INT) { |
|
210 substringmatchingValue = Services.prefs.getIntPref(substringmatchingPref); |
|
211 } |
|
212 |
|
213 // Add the substring matching value to the find options JSON |
|
214 aMessage.data.options.findOptions.substringMatching = substringmatchingValue; |
|
215 |
|
216 this._sendMessageToJava({type: "Android:Contacts:Find", data: aMessage.data}); |
|
217 }, |
|
218 |
|
219 getAllContacts: function(aMessage) { |
|
220 if (!this.assertPermission(aMessage, "contacts-read")) { |
|
221 return; |
|
222 } |
|
223 |
|
224 this._sendMessageToJava({type: "Android:Contacts:GetAll", data: aMessage.data}); |
|
225 }, |
|
226 |
|
227 saveContact: function(aMessage) { |
|
228 if ((aMessage.data.options.reason === "create" && |
|
229 !this.assertPermission(aMessage, "contacts-create")) || |
|
230 !this.assertPermission(aMessage, "contacts-write")) { |
|
231 return; |
|
232 } |
|
233 |
|
234 this._sendMessageToJava({type: "Android:Contact:Save", data: aMessage.data}); |
|
235 }, |
|
236 |
|
237 removeContact: function(aMessage) { |
|
238 if (!this.assertPermission(aMessage, "contacts-write")) { |
|
239 return; |
|
240 } |
|
241 |
|
242 this._sendMessageToJava({type: "Android:Contact:Remove", data: aMessage.data}); |
|
243 }, |
|
244 |
|
245 clearContacts: function(aMessage) { |
|
246 if (!this.assertPermission(aMessage, "contacts-write")) { |
|
247 return; |
|
248 } |
|
249 |
|
250 this._sendMessageToJava({type: "Android:Contacts:Clear", data: aMessage.data}); |
|
251 }, |
|
252 |
|
253 getContactsCount: function(aMessage) { |
|
254 if (!this.assertPermission(aMessage, "contacts-read")) { |
|
255 return; |
|
256 } |
|
257 |
|
258 this._sendMessageToJava({type: "Android:Contacts:GetCount", data: aMessage.data}); |
|
259 }, |
|
260 } |
|
261 |
|
262 ContactService.init(); |