You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by dk...@apache.org on 2012/03/19 13:01:23 UTC

[7/8] qt commit: Initial version of contacts support. Search is working

Initial version of contacts support. Search is working


Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-qt/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-qt/commit/f773df7b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-qt/tree/f773df7b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-qt/diff/f773df7b

Branch: refs/heads/master
Commit: f773df7bcebdf04bce7506ca1d842b93c5dbd1d7
Parents: ee8ea6e
Author: Denis Kormalev <dk...@ics.com>
Authored: Thu Mar 1 14:46:56 2012 +0400
Committer: Denis Kormalev <dk...@ics.com>
Committed: Fri Mar 16 19:26:38 2012 +0400

----------------------------------------------------------------------
 cordovaqt.pro            |   11 +-
 src/plugins/contacts.cpp |  273 +++++++++++++++++++++++++++++++++++++++++
 src/plugins/contacts.h   |   57 +++++++++
 www/basic.js             |   10 ++
 www/index.html           |    1 +
 www/index_qt5.html       |    1 +
 www/js/contacts.js       |  237 +++++++++++++++++++++++++++++++++++
 xml/plugins.xml          |    1 +
 8 files changed, 587 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cordova-qt/blob/f773df7b/cordovaqt.pro
----------------------------------------------------------------------
diff --git a/cordovaqt.pro b/cordovaqt.pro
index ff5c6b5..955e0b9 100644
--- a/cordovaqt.pro
+++ b/cordovaqt.pro
@@ -21,7 +21,8 @@ SOURCES += main.cpp \
     src/plugins/accelerometer.cpp \
     src/plugins/events.cpp \
     src/cordova.cpp \
-    src/cplugin.cpp
+    src/cplugin.cpp \
+    src/plugins/contacts.cpp
 HEADERS += \
     src/plugins/notification.h \
     src/plugins/geolocation.h \
@@ -34,7 +35,8 @@ HEADERS += \
     src/plugins/accelerometer.h \
     src/plugins/events.h \
     src/cordova.h \
-    src/cplugin.h
+    src/cplugin.h \
+    src/plugins/contacts.h
 
 greaterThan(QT_MAJOR_VERSION, 4) {
     message("Qt5 build")
@@ -43,6 +45,7 @@ greaterThan(QT_MAJOR_VERSION, 4) {
     QT += sensors
     QT += feedback
     QT += systeminfo
+    QT += contacts
     QT += quick declarative
 
     OTHER_FILES += qml/main_qt5.qml \
@@ -59,7 +62,7 @@ greaterThan(QT_MAJOR_VERSION, 4) {
     QT += declarative
     QT += webkit
     CONFIG += mobility qdeclarative-boostable
-    MOBILITY += feedback location systeminfo sensors multimedia
+    MOBILITY += feedback location systeminfo sensors multimedia contacts
 } else {
     message("Qt4 build")
     message("Non-harmattan build")
@@ -74,7 +77,7 @@ greaterThan(QT_MAJOR_VERSION, 4) {
     QT += declarative
     QT += webkit
     CONFIG += mobility
-    MOBILITY += feedback location systeminfo sensors multimedia
+    MOBILITY += feedback location systeminfo sensors multimedia contacts
 }
 
 # Please do not modify the following two lines. Required for deployment.

http://git-wip-us.apache.org/repos/asf/incubator-cordova-qt/blob/f773df7b/src/plugins/contacts.cpp
----------------------------------------------------------------------
diff --git a/src/plugins/contacts.cpp b/src/plugins/contacts.cpp
new file mode 100644
index 0000000..1274332
--- /dev/null
+++ b/src/plugins/contacts.cpp
@@ -0,0 +1,273 @@
+#include "contacts.h"
+#include "../pluginregistry.h"
+
+#include <QMap>
+#include <QContactGuid>
+#include <QContactDisplayLabel>
+#include <QContactName>
+#include <QContactNickname>
+#include <QContactPhoneNumber>
+#include <QContactEmailAddress>
+#include <QContactAddress>
+#include <QContactOnlineAccount>
+#include <QContactOrganization>
+#include <QContactBirthday>
+#include <QContactNote>
+#include <QContactAvatar>
+#include <QContactUrl>
+#include <QContact>
+#include <QContactManager>
+#include <QContactUnionFilter>
+#include <QContactDetailFilter>
+
+#include <QDebug>
+
+#ifdef QTM_USE_NAMESPACE
+QTM_USE_NAMESPACE
+#elif defined QTCONTACTS_USE_NAMESPACE
+QTCONTACTS_USE_NAMESPACE
+#endif
+
+
+
+Contacts *Contacts::m_contacts = new Contacts();
+
+Contacts::Contacts() :
+    CPlugin()
+{
+    PluginRegistry::getRegistry()->registerPlugin( "com.cordova.Contacts", this );
+}
+
+void Contacts::init()
+{
+    m_fieldNamePairs.clear();
+    m_fieldNamePairs["id"] = (QLatin1String)QContactGuid::DefinitionName;
+    m_fieldNamePairs["displayName"] = (QLatin1String)QContactDisplayLabel::DefinitionName;
+    m_fieldNamePairs["name"] = (QLatin1String)QContactName::DefinitionName;
+    m_fieldNamePairs["nickname"] = (QLatin1String)QContactNickname::DefinitionName;
+    m_fieldNamePairs["phoneNumbers"] = (QLatin1String)QContactPhoneNumber::DefinitionName;
+    m_fieldNamePairs["emails"] = (QLatin1String)QContactEmailAddress::DefinitionName;
+    m_fieldNamePairs["addresses"] = (QLatin1String)QContactAddress::DefinitionName;
+    m_fieldNamePairs["ims"] = (QLatin1String)QContactOnlineAccount::DefinitionName;
+    m_fieldNamePairs["organizations"] = (QLatin1String)QContactOrganization::DefinitionName;
+    m_fieldNamePairs["birthday"] = (QLatin1String)QContactBirthday::DefinitionName;
+    m_fieldNamePairs["note"] = (QLatin1String)QContactNote::DefinitionName;
+    m_fieldNamePairs["photos"] = (QLatin1String)QContactAvatar::DefinitionName;
+    m_fieldNamePairs["urls"] = (QLatin1String)QContactUrl::DefinitionName;
+
+    m_notSupportedFields.clear();
+    m_notSupportedFields << "categories";
+    m_manager = new QContactManager();
+}
+
+void Contacts::saveContact(int scId, int ecId, const QVariantMap &params)
+{
+    Q_UNUSED(scId);
+    Q_UNUSED(ecId);
+
+    QContact *result = new QContact();
+    QList<QContactDetail *> detailsToDelete;
+    foreach (const QString& paramName, params.keys()) {
+        QString mobilityDetailName = cordovaFieldNameToQtDefinitionName(paramName);
+        if (mobilityDetailName.isEmpty())
+            continue;
+        QContactDetail *detail = new QContactDetail(mobilityDetailName);
+//        detail->setValue(params[paramName]);
+        detailsToDelete << detail;
+        result->saveDetail(detail);
+    }
+    m_manager->saveContact(result);
+    delete result;
+    qDeleteAll(detailsToDelete);
+}
+
+void Contacts::findContacts(int scId, int ecId, const QStringList &fields, const QString &filter, bool multiple)
+{
+    qDebug() << Q_FUNC_INFO << filter << fields << multiple;
+    Q_UNUSED(ecId);
+
+    QContactUnionFilter unionFilter;
+
+    QMap<QString, QStringList> fieldNames;
+    fieldNames[QContactDisplayLabel::DefinitionName] << QContactDisplayLabel::FieldLabel;
+    fieldNames[QContactName::DefinitionName] << QContactName::FieldFirstName << QContactName::FieldLastName << QContactName::FieldMiddleName << QContactName::FieldPrefix << QContactName::FieldSuffix;
+    fieldNames[QContactNickname::DefinitionName] << QContactNickname::FieldNickname;
+    fieldNames[QContactPhoneNumber::DefinitionName] << QContactPhoneNumber::FieldNumber;
+    fieldNames[QContactEmailAddress::DefinitionName] << QContactEmailAddress::FieldEmailAddress;
+    fieldNames[QContactAddress::DefinitionName] << QContactAddress::FieldCountry << QContactAddress::FieldLocality << QContactAddress::FieldPostcode << QContactAddress::FieldPostOfficeBox << QContactAddress::FieldRegion << QContactAddress::FieldStreet;
+    fieldNames[QContactOnlineAccount::DefinitionName] << QContactOnlineAccount::FieldAccountUri;
+    fieldNames[QContactOrganization::DefinitionName] << QContactOrganization::FieldAssistantName << QContactOrganization::FieldDepartment << QContactOrganization::FieldLocation << QContactOrganization::FieldName << QContactOrganization::FieldRole << QContactOrganization::FieldTitle;
+    fieldNames[QContactBirthday::DefinitionName] << QContactBirthday::FieldBirthday;
+    fieldNames[QContactNote::DefinitionName] << QContactNote::FieldNote;
+    fieldNames[QContactUrl::DefinitionName] << QContactUrl::FieldUrl;
+
+    foreach (const QString &defName, fieldNames.keys()) {
+        foreach(const QString &fieldName, fieldNames[defName]) {
+            QContactDetailFilter subFilter;
+            subFilter.setDetailDefinitionName(defName, fieldName);
+            subFilter.setValue(filter);
+            subFilter.setMatchFlags(QContactFilter::MatchContains);
+            unionFilter.append(subFilter);
+        }
+    }
+
+    QList<QContact> contacts = m_manager->contacts(unionFilter);
+    if (contacts.empty()) {
+        callback(scId, "[]");
+    } else {
+        QStringList stringifiedContacts;
+        foreach (const QContact &contact, contacts) {
+            stringifiedContacts << jsonedContact(contact, fields);
+            if (!multiple)
+                break;
+        }
+        callback(scId, QString("[%1]").arg(stringifiedContacts.join(", ")));
+    }
+}
+
+QString Contacts::cordovaFieldNameToQtDefinitionName(const QString &cordovaFieldName) const
+{
+    if (m_fieldNamePairs.contains(cordovaFieldName))
+        return m_fieldNamePairs[cordovaFieldName];
+    return "";
+}
+
+QString Contacts::jsonedContact(const QContact &contact, const QStringList &fields) const
+{
+    QStringList resultingFields = fields;
+    if (resultingFields.empty())
+        resultingFields.append(m_fieldNamePairs.keys());
+    QStringList fieldValuesList;
+    foreach (const QString &field, resultingFields) {
+        QString qtDefinitionName = cordovaFieldNameToQtDefinitionName(field);
+        if (field == "id") {
+            QContactGuid detail = contact.detail(qtDefinitionName);
+            fieldValuesList << QString("%1: \"%2\"")
+                               .arg(field)
+                               .arg(detail.guid());
+        } else if (field == "displayName") {
+            QContactDisplayLabel detail = contact.detail(qtDefinitionName);
+            fieldValuesList << QString("%1: \"%2\"")
+                               .arg(field)
+                               .arg(detail.label());
+        } else if (field == "nickname") {
+            QContactNickname detail = contact.detail(qtDefinitionName);
+            fieldValuesList << QString("%1: \"%2\"")
+                               .arg(field)
+                               .arg(detail.nickname());
+        } else if (field == "note") {
+            QContactNote detail = contact.detail(qtDefinitionName);
+            fieldValuesList << QString("%1: \"%2\"")
+                               .arg(field)
+                               .arg(detail.note());
+        } else if (field == "phoneNumbers") {
+            QStringList fieldValues;
+            QList<QContactDetail> details = contact.details(qtDefinitionName);
+            foreach (const QContactDetail &detail, details) {
+                QContactPhoneNumber castedDetail = detail;
+                QStringList subTypes = castedDetail.subTypes();
+                if (subTypes.isEmpty())
+                    subTypes << "phone";
+                foreach(const QString &subType, subTypes) {
+                    fieldValues << QString("{type: \"%1\", value: \"%2\", pref: %3}")
+                                   .arg(subType)
+                                   .arg(castedDetail.number())
+                                   .arg("false");
+                }
+            }
+            fieldValuesList << QString("%1: [%2]")
+                               .arg(field)
+                               .arg(fieldValues.join(", "));
+        } else if (field == "emails") {
+            QStringList fieldValues;
+            QList<QContactDetail> details = contact.details(qtDefinitionName);
+            foreach (const QContactDetail &detail, details) {
+                QContactEmailAddress castedDetail = detail;
+                fieldValues << QString("{type: \"%1\", value: \"%2\", pref: %3}")
+                               .arg("email")
+                               .arg(castedDetail.emailAddress())
+                               .arg("false");
+            }
+            fieldValuesList << QString("%1: [%2]")
+                               .arg(field)
+                               .arg(fieldValues.join(", "));
+        } else if (field == "ims") {
+            QStringList fieldValues;
+            QList<QContactDetail> details = contact.details(qtDefinitionName);
+            foreach (const QContactDetail &detail, details) {
+                QContactOnlineAccount castedDetail = detail;
+                QStringList subTypes = castedDetail.subTypes();
+                if (subTypes.isEmpty())
+                    subTypes << "IM";
+                foreach(const QString &subType, subTypes) {
+                    fieldValues << QString("{type: \"%1\", value: \"%2\", pref: %3}")
+                                   .arg(subType)
+                                   .arg(castedDetail.accountUri())
+                                   .arg("false");
+                }
+            }
+            fieldValuesList << QString("%1: [%2]")
+                               .arg(field)
+                               .arg(fieldValues.join(", "));
+        } else if (field == "photos") {
+            QStringList fieldValues;
+            QList<QContactDetail> details = contact.details(qtDefinitionName);
+            foreach (const QContactDetail &detail, details) {
+                QContactAvatar castedDetail = detail;
+                fieldValues << QString("{type: \"%1\", value: \"%2\", pref: %3}")
+                               .arg("url")
+                               .arg(castedDetail.imageUrl().toString())
+                               .arg("false");
+            }
+            fieldValuesList << QString("%1: [%2]")
+                               .arg(field)
+                               .arg(fieldValues.join(", "));
+        } else if (field == "urls") {
+            QStringList fieldValues;
+            QList<QContactDetail> details = contact.details(qtDefinitionName);
+            foreach (const QContactDetail &detail, details) {
+                QContactUrl castedDetail = detail;
+                fieldValues << QString("{type: \"%1\", value: \"%2\", pref: %3}")
+                               .arg(castedDetail.subType())
+                               .arg(castedDetail.url())
+                               .arg("false");
+            }
+            fieldValuesList << QString("%1: [%2]")
+                               .arg(field)
+                               .arg(fieldValues.join(", "));
+        } else if (field == "birthday") {
+            QContactBirthday detail = contact.detail(qtDefinitionName);
+            fieldValuesList << QString("%1: new Date(%2)")
+                               .arg(field)
+                               .arg(detail.dateTime().toTime_t()*1000);
+        } else if (field == "organizations") {
+            QStringList fieldValues;
+            QList<QContactDetail> details = contact.details(qtDefinitionName);
+            foreach (const QContactDetail &detail, details) {
+                QContactOrganization castedDetail = detail;
+                fieldValues << QString("{type: \"%1\", name: \"%2\", department: \"%3\", title: \"%4\", pref: %5}")
+                               .arg("organization")
+                               .arg(castedDetail.name())
+                               .arg(castedDetail.department().join(" "))
+                               .arg(castedDetail.role())
+                               .arg("false");
+            }
+            fieldValuesList << QString("%1: [%2]")
+                               .arg(field)
+                               .arg(fieldValues.join(", "));
+        } else if (field == "name") {
+            QContactName detail = contact.detail(qtDefinitionName);
+            fieldValuesList <<  QString("%1: {familyName: \"%2\", givenName: \"%3\", middleName: \"%4\", honorificPrefix: \"%5\", honorificSuffix: \"%6\"}")
+                                .arg(field)
+                                .arg(detail.lastName())
+                                .arg(detail.firstName())
+                                .arg(detail.middleName())
+                                .arg(detail.prefix())
+                                .arg(detail.suffix());
+        }
+
+
+    }
+    QString result = QString("Contact.create({%1})").arg(fieldValuesList.join(", "));
+    return result;
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-qt/blob/f773df7b/src/plugins/contacts.h
----------------------------------------------------------------------
diff --git a/src/plugins/contacts.h b/src/plugins/contacts.h
new file mode 100644
index 0000000..e4c23d6
--- /dev/null
+++ b/src/plugins/contacts.h
@@ -0,0 +1,57 @@
+#ifndef CONTACTS_H
+#define CONTACTS_H
+
+#include "../cplugin.h"
+
+#include <QHash>
+#include <QSet>
+#include <QVariantMap>
+#include <QStringList>
+#if QT_VERSION < 0x050000
+# include <qmobilityglobal.h>
+#else
+# include <qcontactsglobal.h>
+#endif
+
+#ifdef QTM_NAMESPACE
+QTM_BEGIN_NAMESPACE
+#elif defined QTCONTACTS_USE_NAMESPACE
+QTCONTACTS_BEGIN_NAMESPACE
+#endif
+class QContact;
+class QContactManager;
+#ifdef QTM_NAMESPACE
+QTM_END_NAMESPACE
+QTM_USE_NAMESPACE
+#elif defined QTCONTACTS_USE_NAMESPACE
+QTCONTACTS_END_NAMESPACE
+QTCONTACTS_USE_NAMESPACE
+#endif
+
+
+class Contacts : public CPlugin
+{
+    Q_OBJECT
+public:
+    explicit Contacts();
+
+    void init();
+
+signals:
+
+public slots:
+    void saveContact(int scId, int ecId, const QVariantMap &params);
+    void findContacts(int scId, int ecId, const QStringList &fields, const QString &filter, bool multiple);
+
+private:
+    static Contacts *m_contacts;
+
+    QString cordovaFieldNameToQtDefinitionName(const QString &cordovaFieldName) const;
+    QString jsonedContact(const QContact &contact, const QStringList &fields = QStringList()) const;
+
+    QHash<QString, QString> m_fieldNamePairs;
+    QSet<QString> m_notSupportedFields;
+    QContactManager *m_manager;
+};
+
+#endif // CONTACTS_H

http://git-wip-us.apache.org/repos/asf/incubator-cordova-qt/blob/f773df7b/www/basic.js
----------------------------------------------------------------------
diff --git a/www/basic.js b/www/basic.js
index 0122382..80c7f76 100644
--- a/www/basic.js
+++ b/www/basic.js
@@ -158,6 +158,16 @@ function fileError( p_fileError ) {
 document.addEventListener( "deviceready", function() {
                                               console.log("basicjs.deviceReady")
                                               get( "debug_output" ).innerHTML = "Device Ready!<br/>";
+
+                                              navigator.contacts.find(["name", "phoneNumbers", "nickname", "displayName", "emails"],
+                                                                      function(contacts){
+                                                                          var result = ""
+                                                                          for (var contact in contacts) {
+                                                                              result += contacts[contact].name.formatted + ": " + contacts[contact].phoneNumbers[0].value + "\n"
+                                                                          }
+
+                                                                          console.log(result)
+                                                                      }, 0, {filter:"mar", multiple: true})
                                           }, false );
 
 document.addEventListener( "resume", function() {

http://git-wip-us.apache.org/repos/asf/incubator-cordova-qt/blob/f773df7b/www/index.html
----------------------------------------------------------------------
diff --git a/www/index.html b/www/index.html
index 902b9de..c38846d 100644
--- a/www/index.html
+++ b/www/index.html
@@ -14,6 +14,7 @@
         <script language="javascript" type="text/javascript" src="js/notification.js"></script>
         <script language="javascript" type="text/javascript" src="js/compass.js"></script>
         <script language="javascript" type="text/javascript" src="js/accelerometer.js"></script>
+        <script language="javascript" type="text/javascript" src="js/contacts.js"></script>
 
         <script language="javascript" type="text/javascript" src="basic.js"></script>
 

http://git-wip-us.apache.org/repos/asf/incubator-cordova-qt/blob/f773df7b/www/index_qt5.html
----------------------------------------------------------------------
diff --git a/www/index_qt5.html b/www/index_qt5.html
index 3756453..aaf1d83 100644
--- a/www/index_qt5.html
+++ b/www/index_qt5.html
@@ -14,6 +14,7 @@
         <script language="javascript" type="text/javascript" src="js/notification.js"></script>
         <script language="javascript" type="text/javascript" src="js/compass.js"></script>
         <script language="javascript" type="text/javascript" src="js/accelerometer.js"></script>
+        <script language="javascript" type="text/javascript" src="js/contacts.js"></script>
 
         <script language="javascript" type="text/javascript" src="basic.js"></script>
 

http://git-wip-us.apache.org/repos/asf/incubator-cordova-qt/blob/f773df7b/www/js/contacts.js
----------------------------------------------------------------------
diff --git a/www/js/contacts.js b/www/js/contacts.js
new file mode 100644
index 0000000..79c5e66
--- /dev/null
+++ b/www/js/contacts.js
@@ -0,0 +1,237 @@
+function ContactAddress() {
+}
+
+ContactAddress.create = function(obj) {
+            var result = new ContactAddress()
+            result.pref = obj.pref
+            result.type = obj.type
+            result.formatted = obj.formatted
+            result.streetAddress = obj.streetAddress
+            result.locality = obj.streetLocality
+            result.region = obj.region
+            result.postalCode = obj.postalCode
+            result.country = obj.country
+            return result
+        }
+
+ContactAddress.prototype.pref = false
+ContactAddress.prototype.type = ""
+ContactAddress.prototype.formatted = ""
+ContactAddress.prototype.streetAddress = ""
+ContactAddress.prototype.locality = ""
+ContactAddress.prototype.region = ""
+ContactAddress.prototype.postalCode = ""
+ContactAddress.prototype.country = ""
+
+
+function ContactField() {
+}
+
+ContactField.create = function(obj) {
+            var result = new ContactField()
+            result.type = obj.type
+            result.value = obj.value
+            result.pref = obj.pref
+            return result
+        }
+
+ContactField.prototype.type = ""
+ContactField.prototype.value = ""
+ContactField.prototype.pref = false
+
+
+function ContactFindOptions() {
+}
+
+ContactFindOptions.create = function(obj) {
+            var result = new ContactFindOptions()
+            result.filter = obj.filter
+            result.multiple = obj.multiple
+            return result
+        }
+
+ContactFindOptions.prototype.filter = ""
+ContactFindOptions.prototype.multiple = false
+
+
+function ContactName() {
+}
+
+ContactName.create = function(obj) {
+            var result = new ContactName()
+            result.familyName = obj.familyName
+            result.givenName = obj.givenName
+            result.middleName = obj.middleName
+            result.honorificPrefix = obj.honorificPrefix
+            result.honorificSuffix = obj.honorificSuffix
+            var formattedArr = []
+            if (typeof result.honorificPrefix == 'undefined')
+                result.honorificPrefix = ""
+            else if (result.honorificPrefix != "")
+                formattedArr.push(result.honorificPrefix)
+            if (typeof result.givenName == 'undefined')
+                result.givenName = ""
+            else if (result.givenName != "")
+                formattedArr.push(result.givenName)
+            if (typeof result.middleName == 'undefined')
+                result.middleName = ""
+            else if (result.middleName != "")
+                formattedArr.push(result.middleName)
+            if (typeof result.familyName == 'undefined')
+                result.familyName = ""
+            else if (result.familyName != "")
+                formattedArr.push(result.familyName)
+            if (typeof result.honorificSuffix == 'undefined')
+                result.honorificSuffix = ""
+            else if (result.honorificSuffix != "")
+                formattedArr.push(result.honorificSuffix)
+
+            result.formatted = formattedArr.join(" ")
+
+            return result
+        }
+
+ContactName.prototype.formatted = ""
+ContactName.prototype.familyName = ""
+ContactName.prototype.givenName = ""
+ContactName.prototype.middleName = ""
+ContactName.prototype.honorificPrefix = ""
+ContactName.prototype.honorificSuffix = ""
+
+
+function ContactOrganization() {
+}
+
+ContactOrganization.create = function(obj) {
+            var result = new ContactOrganization()
+            result.pref = obj.pref
+            result.type = obj.type
+            result.name = obj.name
+            result.department = obj.department
+            result.title = obj.title
+            return result
+        }
+
+ContactOrganization.prototype.pref = false
+ContactOrganization.prototype.type = ""
+ContactOrganization.prototype.name = ""
+ContactOrganization.prototype.department = ""
+ContactOrganization.prototype.title = ""
+
+
+function ContactError() {
+}
+
+ContactError.UNKNOWN_ERROR = 0
+ContactError.INVALID_ARGUMENT_ERROR = 1
+ContactError.TIMEOUT_ERROR = 2
+ContactError.PENDING_OPERATION_ERROR = 3
+ContactError.IO_ERROR = 4
+ContactError.NOT_SUPPORTED_ERROR = 5
+ContactError.PERMISSION_DENIED_ERROR = 6
+
+ContactError.prototype.code = ContactError.UNKNOWN_ERROR
+
+
+function Contact() {
+    this.name = new ContactName()
+    this.phoneNumbers = []
+    this.emails = []
+    this.addresses = []
+    this.ims = []
+    this.organizations = []
+    this.photos = []
+    this.categories = []
+    this.urls = []
+}
+
+Contact.create = function(obj) {
+            var result = new Contact()
+            result.id = obj.id
+            result.displayName = obj.displayName
+            result.name = ContactName.create(obj.name)
+            result.nickname = obj.nickname
+            var subObj
+            for (subObj in obj.phoneNumbers)
+                result.phoneNumbers.push(ContactField.create(obj.phoneNumbers[subObj]))
+            for (subObj in obj.emails)
+                result.emails.push(ContactField.create(obj.emails[subObj]))
+            for (subObj in obj.addresses)
+                result.addresses.push(ContactAddress.create(obj.addresses[subObj]))
+            for (subObj in obj.ims)
+                result.ims.push(ContactField.create(obj.ims[subObj]))
+            for (subObj in obj.organizations)
+                result.organizations.push(ContactOrganization.create(obj.organizations[subObj]))
+            result.birthday = obj.birthday
+            result.note = obj.note
+            for (subObj in obj.photos)
+                result.photos.push(ContactField.create(obj.photos[subObj]))
+            for (subObj in obj.categories)
+                result.categories.push(ContactField.create(obj.categories[subObj]))
+            for (subObj in obj.urls)
+                result.urls.push(ContactField.create(obj.urls[subObj]))
+            return result
+        }
+
+Contact.prototype.id = ""
+Contact.prototype.displayName = ""
+Contact.prototype.name = new ContactName()
+Contact.prototype.nickname = ""
+Contact.prototype.phoneNumbers = []
+Contact.prototype.emails = []
+Contact.prototype.addresses = []
+Contact.prototype.ims = []
+Contact.prototype.organizations = []
+Contact.prototype.birthday = new Date()
+Contact.prototype.note = ""
+Contact.prototype.photos = []
+Contact.prototype.categories = []
+Contact.prototype.urls = []
+
+Contact.prototype.clone = function() {
+
+        }
+
+Contact.prototype.remove = function(onSaveSuccess,onSaveError) {
+
+        }
+
+Contact.prototype.save = function(onSaveSuccess,onSaveError) {
+
+        }
+
+
+
+function ContactsManager() {
+
+}
+
+ContactsManager.prototype.create = function(properties) {
+            var result = new Contact()
+            for (var property in properties) {
+                if (typeof result[property] != 'undefined')
+                    result[property] = properties[property]
+            }
+            return result
+        }
+
+ContactsManager.prototype.find = function(contactFields, contactSuccess, contactError, contactFindOptions) {
+            // Check the callbacks
+            console.log("contacts.find 1")
+            if( typeof contactSuccess !== "function" ) return
+            console.log("contacts.find 2")
+            if( typeof contactError !== "function" ) contactError = function() {}
+            console.log("contacts.find 3")
+
+            Cordova.exec( function( contactsList ) {
+                              contactSuccess( contactsList )
+                          }, contactError, "com.cordova.Contacts", "findContacts", [ contactFields, contactFindOptions.filter, contactFindOptions.multiple ] )
+            console.log("contacts.find 4")
+        }
+
+/**
+ * Add the ContactsManager object to the navigator
+ */
+Cordova.addConstructor( "com.cordova.Contacts", function () {
+                            navigator.contacts = new ContactsManager()
+                        } );

http://git-wip-us.apache.org/repos/asf/incubator-cordova-qt/blob/f773df7b/xml/plugins.xml
----------------------------------------------------------------------
diff --git a/xml/plugins.xml b/xml/plugins.xml
index 0b1cc45..c6da722 100644
--- a/xml/plugins.xml
+++ b/xml/plugins.xml
@@ -9,6 +9,7 @@
     <plugin name="Console" value="com.cordova.Console"/>
     <plugin name="Connection" value="com.cordova.Connection"/>
     <plugin name="Compass" value="com.cordova.Compass"/>
+    <plugin name="Contacts" value="com.cordova.Contacts"/>
 
 <!--    <plugin name="Compass" value="com.cordova.CompassListener"/>
     <plugin name="Media" value="com.cordova.AudioHandler"/>