michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: const DEBUG = false; michael@0: function debug(s) { dump("-*- Android ContactService component: " + s + "\n"); } michael@0: michael@0: const Cu = Components.utils; michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: michael@0: this.EXPORTED_SYMBOLS = []; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/PhoneNumberUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, "ppmm", "@mozilla.org/parentprocessmessagemanager;1", michael@0: "nsIMessageListenerManager"); michael@0: michael@0: let ContactService = { michael@0: init: function() { michael@0: if (DEBUG) debug("Init"); michael@0: this._requestMessages = {}; michael@0: michael@0: // Add listeners for all messages from ContactManager.js michael@0: let messages = ["Contacts:Clear", "Contacts:Find", "Contacts:GetAll", michael@0: "Contacts:GetAll:SendNow", "Contacts:GetCount", "Contacts:GetRevision", michael@0: "Contact:Remove", "Contact:Save",]; michael@0: messages.forEach(function(msgName) { michael@0: ppmm.addMessageListener(msgName, this); michael@0: }.bind(this)); michael@0: michael@0: // Add listeners for all messages from ContactService.java michael@0: let returnMessages = ["Android:Contacts:Count", michael@0: "Android:Contacts:Clear:Return:OK", "Android:Contacts:Clear:Return:KO", michael@0: "Android:Contacts:Find:Return:OK", "Android:Contacts:Find:Return:KO", michael@0: "Android:Contacts:GetAll:Next", "Android:Contacts:RegisterForMessages", michael@0: "Android:Contact:Remove:Return:OK", "Android:Contact:Remove:Return:KO", michael@0: "Android:Contact:Save:Return:OK", "Android:Contact:Save:Return:KO",]; michael@0: michael@0: returnMessages.forEach(function(msgName) { michael@0: Services.obs.addObserver(this, msgName, false); michael@0: }.bind(this)); michael@0: }, michael@0: michael@0: _sendMessageToJava: function(aMsg) { michael@0: Services.androidBridge.handleGeckoMessage(aMsg); michael@0: }, michael@0: michael@0: _sendReturnMessage: function(aTopic, aRequestID, aResult) { michael@0: this._requestMessages[aRequestID].target.sendAsyncMessage(aTopic, aResult); michael@0: }, michael@0: michael@0: _sendAndDeleteReturnMessage: function(aTopic, aRequestID, aResult) { michael@0: this._sendReturnMessage(aTopic, aRequestID, aResult) michael@0: delete this._requestMessages[aRequestID]; michael@0: }, michael@0: michael@0: observe: function(aSubject, aTopic, aData) { michael@0: if (DEBUG) { michael@0: debug("observe: subject: " + aSubject + " topic: " + aTopic + " data: " + aData); michael@0: } michael@0: michael@0: let message = JSON.parse(aData, function date_reviver(k, v) { michael@0: // The Java service sends dates as strings, so convert them to Dates before michael@0: // sending them back to the child. michael@0: if (v != null && v != "null" && michael@0: ["updated", "published", "anniversary", "bday"].indexOf(k) != -1) { michael@0: return new Date(v); michael@0: } michael@0: return v; michael@0: }); michael@0: let requestID = message.requestID; michael@0: michael@0: // The return message topic is the same as the current topic, but without the "Android:" prefix michael@0: let returnMessageTopic = aTopic.substring(8); michael@0: michael@0: switch (aTopic) { michael@0: case "Android:Contacts:Find:Return:OK": michael@0: this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID, contacts: message.contacts}); michael@0: break; michael@0: michael@0: case "Android:Contacts:Find:Return:KO": michael@0: this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID}); michael@0: break; michael@0: michael@0: case "Android:Contact:Save:Return:OK": michael@0: this._sendReturnMessage(returnMessageTopic, requestID, {requestID: requestID, contactID: message.contactID}); michael@0: this._sendAndDeleteReturnMessage("Contact:Changed", requestID, {contactID: message.contactID, reason: message.reason}); michael@0: break; michael@0: michael@0: case "Android:Contact:Save:Return:KO": michael@0: this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID}); michael@0: break; michael@0: michael@0: case "Android:Contact:Remove:Return:OK": michael@0: this._sendReturnMessage(returnMessageTopic, requestID, {requestID: requestID, contactID: message.contactID}); michael@0: this._sendAndDeleteReturnMessage("Contact:Changed", requestID, {contactID: message.contactID, reason: "remove"}); michael@0: break; michael@0: michael@0: case "Android:Contact:Remove:Return:KO": michael@0: this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID}); michael@0: break; michael@0: michael@0: case "Android:Contacts:Clear:Return:OK": michael@0: this._sendReturnMessage(returnMessageTopic, requestID, {requestID: requestID}); michael@0: this._sendAndDeleteReturnMessage("Contact:Changed", requestID, {reason: "remove"}); michael@0: break; michael@0: michael@0: case "Android:Contact:Clear:Return:KO": michael@0: this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID}); michael@0: break; michael@0: michael@0: case "Android:Contacts:GetAll:Next": michael@0: // GetAll uses a cursor ID instead of a request ID. Translate the request ID back to the cursor ID michael@0: this._sendReturnMessage(returnMessageTopic, requestID, {cursorId: requestID, contacts: message.contacts}); michael@0: michael@0: // Send a message with no contacts to denote the end of contacts returned by the query michael@0: this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {cursorId: requestID}); michael@0: break; michael@0: michael@0: case "Android:Contacts:Count": michael@0: this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID, count: message.count}); michael@0: break; michael@0: michael@0: default: michael@0: throw "Wrong message received: " + aTopic; michael@0: } michael@0: }, michael@0: michael@0: assertPermission: function(aMessage, aPerm) { michael@0: if (!aMessage.target.assertPermission(aPerm)) { michael@0: Cu.reportError("Contacts message " + aMessage.name + michael@0: " from a content process with no" + aPerm + " privileges."); michael@0: return false; michael@0: } michael@0: return true; michael@0: }, michael@0: michael@0: receiveMessage: function(aMessage) { michael@0: if (DEBUG) debug("receiveMessage " + aMessage.name); michael@0: michael@0: // GetAll uses a cursor ID instead of a request ID, but they can be treated the same from here michael@0: if (!aMessage.data.requestID && aMessage.data.cursorId) { michael@0: aMessage.data.requestID = aMessage.data.cursorId; michael@0: } michael@0: let requestID = aMessage.data.requestID; michael@0: michael@0: // Store the message so it the request callback can be called when the Java side is finished michael@0: this._requestMessages[requestID] = aMessage; michael@0: michael@0: switch (aMessage.name) { michael@0: case "Contacts:Find": michael@0: this.findContacts(aMessage); michael@0: break; michael@0: michael@0: case "Contacts:GetAll": michael@0: this.getAllContacts(aMessage); michael@0: break; michael@0: michael@0: case "Contacts:GetAll:SendNow": michael@0: // Send an empty message to denote there are no most contacts for the getAll query michael@0: this._sendAndDeleteReturnMessage("Contacts:GetAll:Next", requestID, {cursorId: requestID}); michael@0: break; michael@0: michael@0: case "Contact:Save": michael@0: this.saveContact(aMessage); michael@0: break; michael@0: michael@0: case "Contact:Remove": michael@0: this.removeContact(aMessage); michael@0: break; michael@0: michael@0: case "Contacts:Clear": michael@0: this.clearContacts(aMessage); michael@0: break; michael@0: michael@0: case "Contacts:GetCount": michael@0: this.getContactsCount(aMessage); michael@0: break; michael@0: michael@0: case "Contacts:GetRevision": michael@0: // Android does not support the get revision function michael@0: this._sendAndDeleteReturnMessage("Contacts:GetRevision:Return:KO", requestID, {requestID: requestID, michael@0: errorMsg: "Android does not support the revision function."}); michael@0: break; michael@0: michael@0: case "Contacts:RegisterForMessages": michael@0: delete this._requestMessages[requestID]; michael@0: break; michael@0: michael@0: default: michael@0: delete this._requestMessages[requestID]; michael@0: throw "Wrong message received: " + aMessage.name; michael@0: } michael@0: }, michael@0: michael@0: findContacts: function(aMessage) { michael@0: if (!this.assertPermission(aMessage, "contacts-read")) { michael@0: return; michael@0: } michael@0: michael@0: let countryName = PhoneNumberUtils.getCountryName(); michael@0: let substringmatchingPref = "dom.phonenumber.substringmatching." + countryName; michael@0: let substringmatchingValue = 0; michael@0: if (Services.prefs.getPrefType(substringmatchingPref) == Ci.nsIPrefBranch.PREF_INT) { michael@0: substringmatchingValue = Services.prefs.getIntPref(substringmatchingPref); michael@0: } michael@0: michael@0: // Add the substring matching value to the find options JSON michael@0: aMessage.data.options.findOptions.substringMatching = substringmatchingValue; michael@0: michael@0: this._sendMessageToJava({type: "Android:Contacts:Find", data: aMessage.data}); michael@0: }, michael@0: michael@0: getAllContacts: function(aMessage) { michael@0: if (!this.assertPermission(aMessage, "contacts-read")) { michael@0: return; michael@0: } michael@0: michael@0: this._sendMessageToJava({type: "Android:Contacts:GetAll", data: aMessage.data}); michael@0: }, michael@0: michael@0: saveContact: function(aMessage) { michael@0: if ((aMessage.data.options.reason === "create" && michael@0: !this.assertPermission(aMessage, "contacts-create")) || michael@0: !this.assertPermission(aMessage, "contacts-write")) { michael@0: return; michael@0: } michael@0: michael@0: this._sendMessageToJava({type: "Android:Contact:Save", data: aMessage.data}); michael@0: }, michael@0: michael@0: removeContact: function(aMessage) { michael@0: if (!this.assertPermission(aMessage, "contacts-write")) { michael@0: return; michael@0: } michael@0: michael@0: this._sendMessageToJava({type: "Android:Contact:Remove", data: aMessage.data}); michael@0: }, michael@0: michael@0: clearContacts: function(aMessage) { michael@0: if (!this.assertPermission(aMessage, "contacts-write")) { michael@0: return; michael@0: } michael@0: michael@0: this._sendMessageToJava({type: "Android:Contacts:Clear", data: aMessage.data}); michael@0: }, michael@0: michael@0: getContactsCount: function(aMessage) { michael@0: if (!this.assertPermission(aMessage, "contacts-read")) { michael@0: return; michael@0: } michael@0: michael@0: this._sendMessageToJava({type: "Android:Contacts:GetCount", data: aMessage.data}); michael@0: }, michael@0: } michael@0: michael@0: ContactService.init();