You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ni...@apache.org on 2015/05/13 23:12:51 UTC
cordova-plugin-contacts git commit: CB-8987: Support for save and
remove for Windows 10
Repository: cordova-plugin-contacts
Updated Branches:
refs/heads/master 6b69ffa34 -> 959fffad4
CB-8987: Support for save and remove for Windows 10
Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/commit/959fffad
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/tree/959fffad
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/diff/959fffad
Branch: refs/heads/master
Commit: 959fffad4a1e7c27b16478b24cdc628200d718d4
Parents: 6b69ffa
Author: Rob Paveza <Ro...@microsoft.com>
Authored: Fri May 8 13:19:25 2015 -0700
Committer: Rob Paveza <Ro...@microsoft.com>
Committed: Fri May 8 13:19:25 2015 -0700
----------------------------------------------------------------------
README.md | 8 +-
plugin.xml | 3 +
src/windows/ContactProxy.js | 222 +++++++++++++++++++++++++++++++++++++--
tests/tests.js | 52 ++++++++-
4 files changed, 270 insertions(+), 15 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/959fffad/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index c2f6cd0..0c4a81a 100644
--- a/README.md
+++ b/README.md
@@ -69,9 +69,11 @@ __WARNING__: All privileged apps enforce [Content Security Policy](https://devel
### Windows Quirks
-Any contacts returned from `find` and `pickContact` methods are readonly, so your application cannot modify them.
+**Prior to Windows 10:** Any contacts returned from `find` and `pickContact` methods are readonly, so your application cannot modify them.
`find` method available only on Windows Phone 8.1 devices.
+**Windows 10 and above:** Contacts may be saved and will be saved to app-local contacts storage. Contacts may also be deleted.
+
### Windows 8 Quirks
Windows 8 Contacts are readonly. Via the Cordova API Contacts are not queryable/searchable, you should inform the user to pick a contact as a call to contacts.pickContact which will open the 'People' app where the user must choose a contact.
@@ -159,7 +161,7 @@ parameter to control which contact properties must be returned back.
- Firefox OS
- iOS
- Windows Phone 7 and 8
-- Windows (Windows Phone 8.1 devices only)
+- Windows (Windows Phone 8.1 and Windows 10)
### Example
@@ -377,7 +379,7 @@ for details.
- __categories__: Not supported, returning `null`.
-- __remove__: Method is not supported
+- __remove__: Method is only supported in Windows 10 or above.
## ContactAddress
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/959fffad/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index 8fa1fe4..fd40c6f 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -228,6 +228,9 @@
<config-file target="package.phone.appxmanifest" parent="/Package/Capabilities">
<m3:Capability Name="contacts" />
</config-file>
+ <config-file target="package.appxmanifest" parent="/Package/Capabilities" versions=">=10.0.0">
+ <uap:Capability Name="contacts" />
+ </config-file>
</platform>
</plugin>
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/959fffad/src/windows/ContactProxy.js
----------------------------------------------------------------------
diff --git a/src/windows/ContactProxy.js b/src/windows/ContactProxy.js
index dc4ba54..04b6e55 100644
--- a/src/windows/ContactProxy.js
+++ b/src/windows/ContactProxy.js
@@ -31,17 +31,19 @@ function convertToContact(windowsContact) {
var contact = new Contact();
// displayName & nickname
- contact.displayName = windowsContact.name || windowsContact.displayName;
+ contact.displayName = windowsContact.displayName || windowsContact.name;
contact.nickname = windowsContact.name;
+ contact.id = windowsContact.id;
// name
// Additional fields like lastName, middleName etc. available on windows8.1/wp8.1 only
contact.name = new ContactName(
- windowsContact.name || windowsContact.displayName,
+ windowsContact.displayName || windowsContact.name,
windowsContact.lastName,
+ windowsContact.firstName || windowsContact.name,
windowsContact.middleName,
- windowsContact.honorificPrefix,
- windowsContact.honorificSuffix);
+ windowsContact.honorificNamePrefix || windowsContact.honorificPrefix,
+ windowsContact.honorificNameSuffix || windowsContact.honorificSuffix);
// phoneNumbers
contact.phoneNumbers = [];
@@ -116,6 +118,105 @@ function convertToContact(windowsContact) {
// Win API Contacts namespace
var contactsNS = Windows.ApplicationModel.Contacts;
+function cdvContactToWindowsContact(contact) {
+ var result = new contactsNS.Contact();
+
+ // name first
+ if (contact.name) {
+ result.displayNameOverride = contact.name.formatted;
+ result.firstName = contact.name.givenName;
+ result.middleName = contact.name.middleName;
+ result.lastName = contact.name.familyName;
+ result.honorificNamePrefix = contact.name.honorificPrefix;
+ result.honorificNameSuffix = contact.name.honorificSuffix;
+ }
+
+ result.nickname = contact.nickname;
+
+ // phone numbers
+ if (contact.phoneNumbers) {
+ contact.phoneNumbers.forEach(function(contactPhone) {
+ var resultPhone = new contactsNS.ContactPhone();
+ resultPhone.description = contactPhone.type;
+ resultPhone.number = contactPhone.value;
+ result.phones.push(resultPhone);
+ });
+ }
+
+ // emails
+ if (contact.emails) {
+ contact.emails.forEach(function(contactEmail) {
+ var resultEmail = new contactsNS.ContactEmail();
+ resultEmail.description = contactEmail.type;
+ resultEmail.address = contactEmail.value;
+ result.emails.push(resultEmail);
+ });
+ }
+
+ // Addresses
+ if (contact.addresses) {
+ contact.addresses.forEach(function(contactAddress) {
+ var address = new contactsNS.ContactAddress();
+ address.description = contactAddress.type;
+ address.streetAddress = contactAddress.streetAddress;
+ address.locality = contactAddress.locality;
+ address.region = contactAddress.region;
+ address.postalCode = contactAddress.postalCode;
+ address.country = contactAddress.country;
+ result.addresses.push(address);
+ });
+ }
+
+ // IMs
+ if (contact.ims) {
+ contact.ims.forEach(function(contactIM) {
+ var acct = new contactsNS.ContactConnectedServiceAccount();
+ acct.serviceName = contactIM.type;
+ acct.id = contactIM.value;
+ result.connectedServiceAccounts.push(acct);
+ });
+ }
+
+ // JobInfo
+ if (contact.organizations) {
+ contact.organizations.forEach(function(contactOrg) {
+ var job = new contactsNS.ContactJobInfo();
+ job.companyName = contactOrg.name;
+ job.department = contactOrg.department;
+ job.description = contactOrg.type;
+ job.title = contactOrg.title;
+ result.jobInfo.push(job);
+ });
+ }
+
+ result.notes = contact.note;
+
+ if (contact.photos) {
+ var eligiblePhotos = contact.photos.filter(function(photo) {
+ return typeof photo.value !== 'undefined';
+ });
+ if (eligiblePhotos.length > 0) {
+ var supportedPhoto = eligiblePhotos[0];
+ var path = supportedPhoto.value;
+
+ try {
+ var streamRef;
+ if (/^([a-z][a-z0-9+\-.]*):\/\//i.test(path)) {
+ streamRef = Windows.Storage.Streams.RandomAccessStreamReference.createFromUri(new Windows.Foundation.Uri(path));
+ } else {
+ streamRef = Windows.Storage.Streams.RandomAccessStreamReference.createFromFile(path);
+ }
+ result.thumbnail = streamRef;
+ }
+ catch (e) {
+ // incompatible reference to the photo
+ }
+ }
+ }
+
+ return result;
+}
+
module.exports = {
pickContact: function (win, fail, args) {
@@ -164,13 +265,112 @@ module.exports = {
},
save: function (win, fail, args) {
- // Not supported yet since WinJS API do not provide methods to manage contactStore
- // On Windows Phone 8.1 this can be implemented using native class library
- // See Windows.Phone.PersonalInformation namespace
- // http://msdn.microsoft.com/en-us/library/windows/apps/xaml/windows.phone.personalinformation.aspx
-
- //We don't need to create Error object here since it will be created at navigator.contacts.find() method
- fail && fail(ContactError.NOT_SUPPORTED_ERROR);
+ if (typeof contactsNS.ContactList === 'undefined') {
+ // Not supported yet since WinJS API do not provide methods to manage contactStore
+ // On Windows Phone 8.1 this can be implemented using native class library
+ // See Windows.Phone.PersonalInformation namespace
+ // http://msdn.microsoft.com/en-us/library/windows/apps/xaml/windows.phone.personalinformation.aspx
+
+ //We don't need to create Error object here since it will be created at navigator.contacts.find() method
+ fail && fail(ContactError.NOT_SUPPORTED_ERROR);
+ return;
+ }
+
+ var winContact = cdvContactToWindowsContact(args[0]);
+
+ contactsNS.ContactManager.requestStoreAsync(contactsNS.ContactStoreAccessType.appContactsReadWrite).then(function(store) {
+ return store.findContactListsAsync().then(function(lists) {
+ if (lists.length > 0) {
+ return lists[0];
+ } else {
+ return store.createContactListAsync('');
+ }
+ }, function(error) {
+ return store.createContactListAsync('');
+ });
+ }).then(function(list) {
+ return list.saveContactAsync(winContact);
+ }).done(function(result) {
+ win(convertToContact(winContact));
+ }, function(error) {
+ fail(error);
+ });
+ },
+
+ remove: function(win, fail, args) {
+ if (typeof contactsNS.ContactList === 'undefined') {
+ // Not supported yet since WinJS API do not provide methods to manage contactStore
+ // On Windows Phone 8.1 this can be implemented using native class library
+ // See Windows.Phone.PersonalInformation namespace
+ // http://msdn.microsoft.com/en-us/library/windows/apps/xaml/windows.phone.personalinformation.aspx
+
+ //We don't need to create Error object here since it will be created at navigator.contacts.find() method
+ fail && fail(ContactError.NOT_SUPPORTED_ERROR);
+ return;
+ }
+
+ // This is a complicated scenario because in Win10, there is a notion of 'app contacts' vs 'global contacts'.
+ // search() returns all global contacts, which are "aggregate contacts", so the IDs of contacts that Cordova
+ // creates never match the IDs of the contacts returned from search().
+ // In order to work around this, we need to:
+ // - Get two Stores: one that is read-write to the app-contacts list, one which is read-only for global contacts
+ // - Read the app-local store to see if a contact with the passed-in ID matches
+ // - Grab the global aggregate contact manager, then ask it for raw contacts (app-local ACM returns access denied)
+ // - Find my app-list of contacts
+ // - Enumerate the raw contacts and see if there is a raw contact whose parent list matches the app-list
+ // - If so, remove the raw contact from the app-list
+ // - If any of this fails, the operation fails
+ WinJS.Promise.join([contactsNS.ContactManager.requestStoreAsync(contactsNS.ContactStoreAccessType.appContactsReadWrite),
+ contactsNS.ContactManager.requestStoreAsync(contactsNS.ContactStoreAccessType.allContactsReadOnly)]).then(function(stores) {
+ var readOnlyStore = stores[1];
+ var writableStore = stores[0];
+
+ var storeReader = writableStore.getContactReader();
+ return storeReader.readBatchAsync().then(function(batch) {
+ if (batch.status !== contactsNS.ContactBatchStatus.success) {
+ // Couldn't read contacts store
+ throw new ContactError(ContactError.IO_ERROR);
+ }
+
+ var candidates = batch.contacts.filter(function(testContact) {
+ return testContact.id === args[0];
+ });
+
+ if (candidates.length === 0) {
+ // No matching contact from aggregate store
+ throw new ContactError(ContactError.IO_ERROR);
+ }
+
+ return candidates[0];
+ }).then(function(contactToDelete) {
+ return readOnlyStore.aggregateContactManager.findRawContactsAsync(contactToDelete);
+ }).then(function(rawContacts) {
+ return writableStore.findContactListsAsync().then(function(lists) {
+ var deleteList = null;
+ var deleteContact = null;
+ var matched = lists.some(function(list) {
+ for (var i = 0; i < rawContacts.length; i++) {
+ if (rawContacts[i].contactListId === list.id) {
+ deleteList = list;
+ deleteContact = rawContacts[i];
+ return true;
+ }
+ }
+ return false;
+ });
+
+ if (!matched) {
+ throw new ContactError(ContactError.IO_ERROR);
+ }
+
+ return deleteList.deleteContactAsync(deleteContact);
+ });
+ });
+ }).done(function() {
+ win();
+ }, function(error) {
+ fail(error);
+ });
},
search: function (win, fail, options) {
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/959fffad/tests/tests.js
----------------------------------------------------------------------
diff --git a/tests/tests.js b/tests/tests.js
index 46d9c4e..f8b5615 100644
--- a/tests/tests.js
+++ b/tests/tests.js
@@ -515,6 +515,50 @@ exports.defineManualTests = function (contentEl, createActionButton) {
alert(e);
}
}
+
+ function removeDooneyEvans() {
+ var results = document.getElementById('contact_results');
+
+ navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails", "urls", "note"], function(contacts) {
+ var removes = [];
+ contacts.forEach(function(contact) {
+ if (contact.name.formatted.indexOf('Dooney Evans') > -1) {
+ removes.push(contact);
+ }
+ });
+
+ var nextToRemove = undefined;
+ if (removes.length > 0) {
+ nextToRemove = removes.shift();
+ }
+ function removeNext(item) {
+ if (typeof item === 'undefined')
+ return;
+
+ if (removes.length > 0) {
+ nextToRemove = removes.shift();
+ } else {
+ nextToRemove = undefined;
+ }
+
+ item.remove(function removeSucceeded() {
+ results.innerHTML += '<br>Removed contact with ID ' + item.id;
+ removeNext(nextToRemove);
+ }, function removeFailed(e) {
+ results.innerHTML += '<br>Remove failed contact with ID ' + item.id;
+ removeNext(nextToRemove);
+ });
+ }
+ removeNext(nextToRemove);
+ }, function (e) {
+ if (e.code === ContactError.NOT_SUPPORTED_ERROR) {
+ results.innerHTML = 'Searching for contacts is not supported.';
+ }
+ else {
+ results.innerHTML = 'Search failed: error ' + e.code;
+ }
+ })
+ }
/******************************************************************************/
@@ -525,7 +569,9 @@ exports.defineManualTests = function (contentEl, createActionButton) {
'<div id="get_contacts"></div>' +
'Expected result: Status box will show number of contacts and list them. May be empty on a fresh device until you click Add.' +
'</p> <div id="add_contact"></div>' +
- 'Expected result: Will add a new contact. Log will say "Contact saved." or "Saving contacts not supported." if not supported on current platform. Verify by running Get phone contacts again';
+ 'Expected result: Will add a new contact. Log will say "Contact saved." or "Saving contacts not supported." if not supported on current platform. Verify by running Get phone contacts again' +
+ '<div id="remove_dooney_evans"></div>' +
+ '<p>Expected result: Will remove any contacts named "Dooney Evans". Log will output success or failure, plus ID, or fail like getting contacts will fail.</p>';
createActionButton("Get phone's contacts", function () {
getContacts();
@@ -534,4 +580,8 @@ exports.defineManualTests = function (contentEl, createActionButton) {
createActionButton("Add a new contact 'Dooney Evans'", function () {
addContact();
}, 'add_contact');
+
+ createActionButton("Delete all 'Dooney Evans'", function() {
+ removeDooneyEvans();
+ }, 'remove_dooney_evans');
};
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org