Bug 857730 - Part 3: Add contacts API Javascript to Android. r=cpeterson

This commit is contained in:
Shane Tully 2013-07-18 15:01:00 -07:00
parent 506065e03d
commit 4152d66713
5 changed files with 288 additions and 7 deletions

View File

@ -541,6 +541,7 @@ function ContactManager()
ContactManager.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
_oncontactchange: null,
_cachedContacts: [] ,
set oncontactchange(aCallback) {
if (DEBUG) debug("set oncontactchange");
@ -630,6 +631,13 @@ ContactManager.prototype = {
}
break;
case "Contact:Save:Return:OK":
// If a cached contact was saved and a new contact ID was returned, update the contact's ID
if (this._cachedContacts[msg.requestID]) {
if (msg.contactID) {
this._cachedContacts[msg.requestID].id = msg.contactID;
}
delete this._cachedContacts[msg.requestID];
}
case "Contacts:Clear:Return:OK":
case "Contact:Remove:Return:OK":
req = this.getRequest(msg.requestID);
@ -643,8 +651,12 @@ ContactManager.prototype = {
case "Contacts:GetRevision:Return:KO":
case "Contacts:Count:Return:KO":
req = this.getRequest(msg.requestID);
if (req)
Services.DOMRequest.fireError(req.request, msg.errorMsg);
if (req) {
if (req.request) {
req = req.request;
}
Services.DOMRequest.fireError(req, msg.errorMsg);
}
break;
case "PermissionPromptHelper:AskPermission:OK":
if (DEBUG) debug("id: " + msg.requestID);
@ -747,7 +759,6 @@ ContactManager.prototype = {
},
save: function save(aContact) {
let request;
if (DEBUG) debug("save: " + JSON.stringify(aContact) + " :" + aContact.id);
let newContact = {};
newContact.properties = {
@ -777,12 +788,16 @@ ContactManager.prototype = {
for (let field in newContact.properties) {
newContact.properties[field] = aContact[field];
}
let request = this.createRequest();
let requestID = this.getRequestId({request: request, reason: reason});
let reason;
if (aContact.id == "undefined") {
// for example {25c00f01-90e5-c545-b4d4-21E2ddbab9e0} becomes
// 25c00f0190e5c545b4d421E2ddbab9e0
aContact.id = this._getRandomId().replace('-', '', 'g').replace('{', '').replace('}', '');
// Cache the contact so that its ID may be updated later if necessary
this._cachedContacts[requestID] = aContact;
reason = "create";
} else {
reason = "update";
@ -790,10 +805,9 @@ ContactManager.prototype = {
this._setMetaData(newContact, aContact);
if (DEBUG) debug("send: " + JSON.stringify(newContact));
request = this.createRequest();
let options = { contact: newContact, reason: reason };
let allowCallback = function() {
cpmm.sendAsyncMessage("Contact:Save", {requestID: this.getRequestId({request: request, reason: reason}), options: options});
cpmm.sendAsyncMessage("Contact:Save", {requestID: requestID, options: options});
}.bind(this)
this.askPermission(reason, request, allowCallback);
return request;
@ -920,8 +934,8 @@ ContactManager.prototype = {
"Contact:Remove:Return:OK", "Contact:Remove:Return:KO",
"Contact:Changed",
"PermissionPromptHelper:AskPermission:OK",
"Contacts:GetAll:Next", "Contacts:Revision",
"Contacts:Count"]);
"Contacts:GetAll:Next", "Contacts:Count",
"Contacts:Revision", "Contacts:GetRevision:Return:KO",]);
},
// Called from DOMRequestIpcHelper

View File

@ -757,6 +757,17 @@ pref("dom.payment.provider.0.type", "mozilla/payments/pay/v1");
pref("dom.payment.provider.0.requestMethod", "GET");
#endif
// Contacts API
pref("dom.mozContacts.enabled", true);
pref("dom.navigator-property.disable.mozContacts", false);
pref("dom.global-constructor.disable.mozContact", false);
// Shortnumber matching needed for e.g. Brazil:
// 01187654321 can be found with 87654321
pref("dom.phonenumber.substringmatching.BR", 8);
pref("dom.phonenumber.substringmatching.CO", 10);
pref("dom.phonenumber.substringmatching.VE", 7);
// Support for the mozAudioChannel attribute on media elements is disabled in non-webapps
pref("media.useAudioChannelService", false);

View File

@ -17,6 +17,7 @@ Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/JNI.jsm");
Cu.import('resource://gre/modules/Payment.jsm');
Cu.import("resource://gre/modules/PermissionPromptHelper.jsm");
Cu.import("resource://gre/modules/ContactService.jsm");
#ifdef ACCESSIBILITY
Cu.import("resource://gre/modules/accessibility/AccessFu.jsm");

View File

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

View File

@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_JS_MODULES += [
'ContactService.jsm',
'JNI.jsm',
'LightweightThemeConsumer.jsm',
'OrderedBroadcast.jsm',