Bug 578691 - Add contact support to form autocomplete (Maemo) [r=vingtetun]

This commit is contained in:
Mark Finkle 2010-07-30 10:40:26 -04:00
Родитель 30e69f866d
Коммит 3148d188a0
11 изменённых файлов: 564 добавлений и 2 удалений

Просмотреть файл

@ -43,7 +43,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS = chrome locales components themes app
DIRS = chrome locales components modules themes app
ifndef LIBXUL_SDK
PARALLEL_DIRS += $(DEPTH)/xulrunner/tools/redit

Просмотреть файл

@ -49,6 +49,11 @@ XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
return PlacesUtils;
});
XPCOMUtils.defineLazyGetter(this, "Contacts", function() {
Cu.import("resource:///modules/contacts.jsm");
return Contacts;
});
const TOOLBARSTATE_LOADING = 1;
const TOOLBARSTATE_LOADED = 2;
@ -400,6 +405,8 @@ var BrowserUI = {
FormHelperUI.init();
FindHelperUI.init();
Contacts.init();
},
uninit: function() {
@ -1679,7 +1686,7 @@ var FormHelperUI = {
let suggestions = [];
let autocompleteService = Cc["@mozilla.org/satchel/form-autocomplete;1"].getService(Ci.nsIFormAutoComplete);
let results = autocompleteService.autoCompleteSearch(aElement.name, aElement.value, aElement, null);
let results = autocompleteService.autoCompleteSearch(aElement.name, aElement.value, null, null);
if (results.matchCount > 0) {
for (let i = 0; i < results.matchCount; i++) {
let value = results.getValueAt(i);

Просмотреть файл

@ -61,6 +61,7 @@ _BROWSER_FILES = \
browser_navigation.js \
browser_preferences_basic.js \
browser_sessionstore.js \
browser_contacts.js \
browser_blank_01.html \
browser_blank_02.html \
browser_select.html \

Просмотреть файл

@ -0,0 +1,54 @@
// pull in the Contacts service
Components.utils.import("resource:///modules/contacts.jsm");
function test() {
ok(Contacts, "Contacts class exists");
for (var fname in tests) {
tests[fname]();
}
}
let MockContactsProvider = {
getContacts: function() {
let contacts = [
{
fullName: "-Billy One",
emails: [],
phoneNumbers: ["999-888-7777"]
},
{
fullName: "-Billy Two",
emails: ["billy.two@fake.com", "btwo@work.com"],
phoneNumbers: ["111-222-3333", "123-123-1234"]
},
{
fullName: "-Joe Schmo",
emails: ["joeschmo@foo.com"],
phoneNumbers: ["555-555-5555"]
}
];
return contacts;
}
};
Contacts.addProvider(MockContactsProvider);
let fac = Cc["@mozilla.org/satchel/form-autocomplete;1"].getService(Ci.nsIFormAutoComplete);
let tests = {
testBasicMatch: function() {
let results = fac.autoCompleteSearch("email", "-Billy", null, null);
ok(results.matchCount == 2, "Found 2 emails '-Billy'");
results = fac.autoCompleteSearch("tel", "-Billy", null, null);
ok(results.matchCount == 3, "Found 3 phone numbers '-Billy'");
results = fac.autoCompleteSearch("skip", "-Billy", null, null);
ok(results.matchCount == 0, "Found nothing for a non-contact field");
results = fac.autoCompleteSearch("phone", "-Jo", null, null);
ok(results.matchCount == 1, "Found 1 phone number '-Jo'");
}
};

Просмотреть файл

@ -0,0 +1,171 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Form Autocomplete Plus.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mark Finkle <mfinkle@mozilla.com>
* Dan Mills <thunder@mozilla.com>
* Justin Dolske <dolske@mozilla.com>
* Michael Hanson <mhanson@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
function LOG() {
return; // comment out for verbose debugging
let msg = Array.join(arguments, " ");
dump(msg + "\n");
Cu.reportError(msg);
}
// Lazily get the base Form AutoComplete Search
XPCOMUtils.defineLazyGetter(this, "FAC", function() {
return Components.classesByID["{c11c21b2-71c9-4f87-a0f8-5e13f50495fd}"]
.getService(Ci.nsIFormAutoComplete);
});
XPCOMUtils.defineLazyGetter(this, "Contacts", function() {
Cu.import("resource:///modules/contacts.jsm");
return Contacts;
});
function FormAutoComplete() {
LOG("new FAC");
}
FormAutoComplete.prototype = {
classDescription: "Form AutoComplete Plus",
classID: Components.ID("{cccd414c-3ec2-4cc5-9dc4-36c87cc3c4fe}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormAutoComplete]),
// Specify the html5 types that we want and some values to guess
contactTypes: {
email: /^(?:.*(?:e-?mail|recipients?).*|(send_)?to(_b?cc)?)$/i,
tel: /^(?:tel(?:ephone)?|.*phone.*)$/i
},
checkQueryType: function checkQueryType(aName, aField) {
// If we have an input field with the desired html5 type, take it!
if (aField != null) {
let type = aField.getAttribute("type");
if (this.contactTypes[type] != null)
return type;
}
// Grab attributes to check for contact inputs
let props = [aName];
if (aField != null) {
let attributes = ["class", "id", "rel"];
attributes.forEach(function(attr) {
if (aField.hasAttribute(attr))
props.push(aField.getAttribute(attr));
});
}
// Check the gathered properties for contact-like values
for (let [type, regex] in Iterator(this.contactTypes)) {
if (props.some(function(prop) prop.search(regex) != -1))
return type;
}
return null;
},
findContact: function findContact(aQuery, aType, aResult, aDupCheck) {
// Match the name and show the email for now..
Contacts.find({ fullName: aQuery }).forEach(function(contact) {
// Might not have an email for some reason... ?
try {
LOG("findContact", "Contact " + contact.fullName);
let suggestions;
switch (aType) {
case "email":
suggestions = contact.emails;
break;
case "tel":
suggestions = contact.phoneNumbers;
break;
default:
LOG("unknown type!", aType);
return;
}
for each (let suggestion in suggestions) {
if (aDupCheck[suggestion])
continue;
aDupCheck[suggestion] = true;
let data = contact.fullName + " <" + suggestion + ">";
aResult.appendMatch(suggestion, data, null, "contact");
}
}
catch(ex) {
LOG("findContact error", ex);
}
});
},
autoCompleteSearch: function autoCompleteSearch(aName, aQuery, aField, aPrev) {
LOG("autocomplete search", Array.slice(arguments));
let result = Cc["@mozilla.org/autocomplete/simple-result;1"].createInstance(Ci.nsIAutoCompleteSimpleResult);
result.setSearchString(aQuery);
// Don't allow duplicates get merged into the final results
let dupCheck = {};
// Use the base form autocomplete for non-contact searches
let normal = FAC.autoCompleteSearch(aName, aQuery, aField, aPrev);
if (normal.matchCount > 0) {
for (let i = 0; i < normal.matchCount; i++) {
dupCheck[normal.getValueAt(i)] = true;
result.appendMatch(normal.getValueAt(i), normal.getCommentAt(i), normal.getImageAt(i), normal.getStyleAt(i));
}
}
// Do searches for certain input fields
let type = this.checkQueryType(aName, aField);
if (type != null)
this.findContact(aQuery, type, result, dupCheck);
let resultCode = result.matchCount ? "RESULT_SUCCESS" : "RESULT_NOMATCH";
result.setSearchResult(Ci.nsIAutoCompleteResult[resultCode]);
return result;
}
};
let components = [FormAutoComplete];
const NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

Просмотреть файл

@ -70,6 +70,7 @@ EXTRA_COMPONENTS = \
ContentDispatchChooser.js \
AutoCompleteCache.js \
AddonUpdateService.js \
FormAutoComplete.js \
$(NULL)
DIRS = phone \

Просмотреть файл

@ -82,3 +82,7 @@ category bookmark-observers BrowserBookmarkObserver @mozilla.org/browser/autocom
component {93c8824c-9b87-45ae-bc90-5b82a1e4d877} AddonUpdateService.js
contract @mozilla.org/browser/addon-update-service;1 {93c8824c-9b87-45ae-bc90-5b82a1e4d877}
category update-timer AddonUpdateService @mozilla.org/browser/addon-update-service;1,getService,auto-addon-background-update-timer,extensions.autoupdate.interval,86400
# FormAutoComplete.js
component {cccd414c-3ec2-4cc5-9dc4-36c87cc3c4fe} FormAutoComplete.js
contract @mozilla.org/satchel/form-autocomplete;1 {cccd414c-3ec2-4cc5-9dc4-36c87cc3c4fe}

Просмотреть файл

@ -47,6 +47,7 @@ mobile/chrome/Makefile
mobile/chrome/tests/Makefile
mobile/components/Makefile
mobile/components/phone/Makefile
mobile/modules/Makefile
mobile/installer/Makefile
mobile/locales/Makefile
mobile/Makefile

Просмотреть файл

@ -0,0 +1,53 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla Mobile Browser.
#
# The Initial Developer of the Original Code is
# the Mozilla Foundation <http://www.mozilla.org/>.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Mark Finkle <mfinkle@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
EXTRA_JS_MODULES = \
linuxTypes.jsm \
$(NULL)
EXTRA_PP_JS_MODULES = \
contacts.jsm \
$(NULL)
include $(topsrcdir)/config/rules.mk

154
mobile/modules/contacts.jsm Normal file
Просмотреть файл

@ -0,0 +1,154 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Mobile Browser.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mark Finkle <mfinkle@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let EXPORTED_SYMBOLS = ["Contacts"];
const Cu = Components.utils;
Cu.import("resource://gre/modules/ctypes.jsm");
let Contacts = {
_providers: [],
_contacts: [],
_load: function _load() {
this._contacts = [];
this._providers.forEach(function(provider) {
this._contacts = this._contacts.concat(provider.getContacts());
}, this)
},
init: function init() {
// Not much to do for now
this._load();
},
refresh: function refresh() {
// Pretty simple for now
this._load();
},
addProvider: function(aProvider) {
this._providers.push(aProvider);
this.refresh();
},
find: function find(aMatch) {
let results = [];
if (!this._contacts.length)
return results;
for (let field in aMatch) {
// Simple string-only partial matching
let match = aMatch[field];
this._contacts.forEach(function(aContact) {
if (field in aContact && aContact[field].indexOf(match) != -1)
results.push(aContact);
});
}
return results;
}
};
#ifdef XP_UNIX
Cu.import("resource:///modules/linuxTypes.jsm");
function EBookProvider() {
EBook.init();
}
EBookProvider.prototype = {
getContacts: function() {
if (!EBook.lib) {
Cu.reportError("EBook not loaded")
return [];
}
let gError = new GLib.GError.ptr;
let book = EBook.openSystem(gError.address());
if (!book) {
Cu.reportError("EBook.openSystem: " + gError.contents.message.readString())
return [];
}
if (!EBook.openBook(book, false, gError.address())) {
Cu.reportError("EBook.openBook: " + gError.contents.message.readString())
return [];
}
let query = EBook.queryAnyFieldContains("");
if (query) {
let gList = new GLib.GList.ptr();
if (!EBook.getContacts(book, query, gList.address(), gError.address())) {
Cu.reportError("EBook.getContacts: " + gError.contents.message.readString())
return [];
}
let contacts = [];
while (gList && !gList.isNull()) {
let fullName = EBook.getContactField(gList.contents.data, EBook.E_CONTACT_FULL_NAME);
if (!fullName.isNull()) {
let contact = {};
contact.fullName = fullName.readString();
contact.emails = [];
contact.phoneNumbers = [];
for (let emailIndex=EBook.E_CONTACT_EMAIL_FIRST; emailIndex<=EBook.E_CONTACT_EMAIL_LAST; emailIndex++) {
let email = EBook.getContactField(gList.contents.data, emailIndex);
if (!email.isNull())
contact.emails.push(email.readString());
}
for (let phoneIndex=EBook.E_CONTACT_PHONE_FIRST; phoneIndex<=EBook.E_CONTACT_PHONE_LAST; phoneIndex++) {
let phone = EBook.getContactField(gList.contents.data, phoneIndex);
if (!phone.isNull())
contact.phoneNumbers.push(phone.readString());
}
contacts.push(contact);
}
gList = ctypes.cast(gList.contents.next, GLib.GList.ptr);
}
return contacts;
}
return [];
}
};
Contacts.addProvider(new EBookProvider);
#endif

Просмотреть файл

@ -0,0 +1,116 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Mobile Browser.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mark Finkle <mfinkle@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let EXPORTED_SYMBOLS = ["GLib", "EBook"];
const Cu = Components.utils;
Cu.import("resource://gre/modules/ctypes.jsm");
let GLib = {
lib: null,
init: function glib_init() {
if (this.lib)
return;
this.lib = true; // TODO hook up the real glib
this.GError = new ctypes.StructType("GError", [
{"domain": ctypes.int32_t},
{"code": ctypes.int32_t},
{"message": ctypes.char.ptr}
]);
this.GList = new ctypes.StructType("GList", [
{"data": ctypes.voidptr_t},
{"next": ctypes.voidptr_t},
{"prev": ctypes.voidptr_t}
]);
}
};
let EBook = {
lib: null,
E_CONTACT_FULL_NAME: 4,
E_CONTACT_EMAIL_1: 8,
E_CONTACT_EMAIL_2: 9,
E_CONTACT_EMAIL_3: 10,
E_CONTACT_EMAIL_4: 11,
E_CONTACT_PHONE_BUSINESS: 17,
E_CONTACT_PHONE_BUSINESS_2: 18,
E_CONTACT_PHONE_HOME: 23,
E_CONTACT_PHONE_HOME_2: 24,
E_CONTACT_PHONE_MOBILE: 27,
E_CONTACT_EMAIL_FIRST: 8,
E_CONTACT_EMAIL_LAST: 11,
E_CONTACT_PHONE_FIRST: 16,
E_CONTACT_PHONE_LAST: 34,
init: function ebook_init() {
if (this.lib)
return;
GLib.init();
try {
// Shipping on N900
this.lib = ctypes.open("libebook-1.2.so.5");
} catch (e) {
try {
// Shipping on Ubuntu
this.lib = ctypes.open("libebook-1.2.so.9");
} catch (e) {
Cu.reportError("EBook: couldn't load libebook:\n" + e)
this.lib = null;
return;
}
}
this.EBook = new ctypes.StructType("EBook");
this.EQuery = new ctypes.StructType("EQuery");
this.openSystem = this.lib.declare("e_book_new_system_addressbook", ctypes.default_abi, EBook.EBook.ptr, GLib.GError.ptr.ptr);
this.openBook = this.lib.declare("e_book_open", ctypes.default_abi, ctypes.bool, EBook.EBook.ptr, ctypes.bool, GLib.GError.ptr.ptr);
this.queryAnyFieldContains = this.lib.declare("e_book_query_any_field_contains", ctypes.default_abi, EBook.EQuery.ptr, ctypes.char.ptr);
this.getContacts = this.lib.declare("e_book_get_contacts", ctypes.default_abi, ctypes.bool, EBook.EBook.ptr, EBook.EQuery.ptr, GLib.GList.ptr.ptr, GLib.GError.ptr.ptr);
this.getContactField = this.lib.declare("e_contact_get_const", ctypes.default_abi, ctypes.char.ptr, ctypes.voidptr_t, ctypes.uint32_t);
}
};