Mon 21 Jul 22:43:21 CEST 2025
This commit is contained in:
parent
8fedf43478
commit
85b399034d
423
js/ui/cordova/plugins/cordova-plugin-contacts/src/windows/ContactProxy.js
vendored
Normal file
423
js/ui/cordova/plugins/cordova-plugin-contacts/src/windows/ContactProxy.js
vendored
Normal file
|
@ -0,0 +1,423 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
var ContactField = require('./ContactField'),
|
||||||
|
ContactAddress = require('./ContactAddress'),
|
||||||
|
ContactOrganization = require('./ContactOrganization'),
|
||||||
|
ContactName = require('./ContactName'),
|
||||||
|
ContactError = require('./ContactError'),
|
||||||
|
Contact = require('./Contact');
|
||||||
|
|
||||||
|
|
||||||
|
function convertToContact(windowsContact) {
|
||||||
|
var contact = new Contact();
|
||||||
|
|
||||||
|
// displayName & nickname
|
||||||
|
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.displayName || windowsContact.name,
|
||||||
|
windowsContact.lastName,
|
||||||
|
windowsContact.firstName || windowsContact.name,
|
||||||
|
windowsContact.middleName,
|
||||||
|
windowsContact.honorificNamePrefix || windowsContact.honorificPrefix,
|
||||||
|
windowsContact.honorificNameSuffix || windowsContact.honorificSuffix);
|
||||||
|
|
||||||
|
// phoneNumbers
|
||||||
|
contact.phoneNumbers = [];
|
||||||
|
var phoneSource = windowsContact.phoneNumbers || windowsContact.phones;
|
||||||
|
for (var i = 0; i < phoneSource.size; i++) {
|
||||||
|
var rawPhone = phoneSource[i];
|
||||||
|
var phone = new ContactField(rawPhone.category || rawPhone.kind, rawPhone.value || rawPhone.number);
|
||||||
|
contact.phoneNumbers.push(phone);
|
||||||
|
}
|
||||||
|
|
||||||
|
// emails
|
||||||
|
contact.emails = [];
|
||||||
|
var emailSource = windowsContact.emails;
|
||||||
|
for (var i = 0; i < emailSource.size; i++) {
|
||||||
|
var rawEmail = emailSource[i];
|
||||||
|
var email = new ContactField(rawEmail.category || rawEmail.kind, rawEmail.value || rawEmail.address);
|
||||||
|
contact.emails.push(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
// addressres
|
||||||
|
contact.addresses = [];
|
||||||
|
var addressSource = windowsContact.locations || windowsContact.addresses;
|
||||||
|
for (var i = 0; i < addressSource.size; i++) {
|
||||||
|
var rawAddress = addressSource[i];
|
||||||
|
var address = new ContactAddress(
|
||||||
|
null,
|
||||||
|
rawAddress.category || rawAddress.kind,
|
||||||
|
rawAddress.unstructuredAddress,
|
||||||
|
rawAddress.street || rawAddress.streetAddress,
|
||||||
|
rawAddress.city || rawAddress.locality,
|
||||||
|
rawAddress.region,
|
||||||
|
rawAddress.postalCode,
|
||||||
|
rawAddress.country);
|
||||||
|
contact.addresses.push(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ims
|
||||||
|
contact.ims = [];
|
||||||
|
var imSource = windowsContact.instantMessages || windowsContact.connectedServiceAccounts;
|
||||||
|
for (var i = 0; i < imSource.size; i++) {
|
||||||
|
var rawIm = imSource[i];
|
||||||
|
var im = new ContactField(rawIm.category || rawIm.serviceName, rawIm.userName || rawIm.id);
|
||||||
|
contact.ims.push(im);
|
||||||
|
}
|
||||||
|
|
||||||
|
// jobInfo field available on Windows 8.1/WP8.1 only
|
||||||
|
var jobInfo = windowsContact.jobInfo;
|
||||||
|
if (jobInfo) {
|
||||||
|
contact.organizations = [];
|
||||||
|
for (var j = 0; j < jobInfo.size; j++) {
|
||||||
|
var rawJob = jobInfo[i];
|
||||||
|
contact.organizations.push(new ContactOrganization(false, null,
|
||||||
|
rawJob.companyName, rawJob.department, rawJob.title));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// note field available on Windows 8.1/WP8.1 only
|
||||||
|
var contactNotes = windowsContact.notes;
|
||||||
|
if (contactNotes) {
|
||||||
|
contact.note = contactNotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returned is a file, a blob url can be made
|
||||||
|
var contactPhoto = windowsContact.thumbnail;
|
||||||
|
if (contactPhoto && contactPhoto.path) {
|
||||||
|
contact.photos = [new ContactField('url', URL.createObjectURL(contactPhoto) , false)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return contact;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
|
||||||
|
// ContactPicker class works differently on Windows8/8.1 and Windows Phone 8.1
|
||||||
|
// so we need to detect when we are running on phone
|
||||||
|
var runningOnPhone = navigator.userAgent.indexOf('Windows Phone') !== -1;
|
||||||
|
|
||||||
|
var picker = new contactsNS.ContactPicker();
|
||||||
|
if (runningOnPhone) {
|
||||||
|
// TODO: Windows Phone 8.1 requires this specification. This should be noted in quirks
|
||||||
|
// See http://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.contacts.contactpicker.desiredfieldswithcontactfieldtype.aspx for details
|
||||||
|
// Multiple ContactFieldType items, appended to array causes `Request not suported` error.
|
||||||
|
picker.desiredFieldsWithContactFieldType.append(Windows.ApplicationModel.Contacts.ContactFieldType.phoneNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pickContactAsync is available on Windows 8.1 or later, instead of
|
||||||
|
// pickSingleContactAsync, which is deprecated in Windows 8.1,
|
||||||
|
// so try to use newer method, if available.
|
||||||
|
// see http://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.contacts.contactpicker.picksinglecontactasync.aspx
|
||||||
|
|
||||||
|
var pickRequest = picker.pickContactAsync ? picker.pickContactAsync() : picker.pickSingleContactAsync();
|
||||||
|
pickRequest.done(function (contact) {
|
||||||
|
// if contact was not picked
|
||||||
|
if (!contact) {
|
||||||
|
var cancelledError = new ContactError(ContactError.OPERATION_CANCELLED_ERROR);
|
||||||
|
cancelledError.message = "User did not pick a contact.";
|
||||||
|
fail(cancelledError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If we are on desktop, just send em back
|
||||||
|
if (!runningOnPhone) {
|
||||||
|
win(convertToContact(contact));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// On WP8.1 fields set in resulted Contact object depends on desiredFieldsWithContactFieldType property of contact picker
|
||||||
|
// so we retrieve full contact by its' Id
|
||||||
|
contactsNS.ContactManager.requestStoreAsync().done(function (contactStore) {
|
||||||
|
contactStore.getContactAsync(contact.id).done(function(con) {
|
||||||
|
win(convertToContact(con));
|
||||||
|
}, function() {
|
||||||
|
fail(new ContactError(ContactError.UNKNOWN_ERROR));
|
||||||
|
});
|
||||||
|
}, function () {
|
||||||
|
fail(new ContactError(ContactError.UNKNOWN_ERROR));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
save: 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
// searchFields is not supported yet due to WP8.1 API limitations.
|
||||||
|
// findContactsAsync(String) method will attempt to match the name, email address, or phone number of a contact.
|
||||||
|
// see http://msdn.microsoft.com/en-us/library/windows/apps/dn624861.aspx for details
|
||||||
|
var searchFields = options[0],
|
||||||
|
searchOptions = options[1],
|
||||||
|
searchFilter = searchOptions.filter,
|
||||||
|
searchMultiple = searchOptions && searchOptions.multiple;
|
||||||
|
|
||||||
|
// Check if necessary API is available.
|
||||||
|
// If not available, we are running on desktop, which doesn't support searching for contacts
|
||||||
|
if (!(contactsNS.ContactManager && contactsNS.ContactManager.requestStoreAsync)) {
|
||||||
|
fail(new ContactError(ContactError.NOT_SUPPORTED_ERROR));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve contact store instance
|
||||||
|
var contactStoreRequest = contactsNS.ContactManager.requestStoreAsync();
|
||||||
|
|
||||||
|
// When contact store retrieved
|
||||||
|
contactStoreRequest.done(function (contactStore) {
|
||||||
|
// determine, which function we use depending on whether searchOptions.filter specified or not
|
||||||
|
var contactsRequest = searchFilter ? contactStore.findContactsAsync(searchFilter) : contactStore.findContactsAsync();
|
||||||
|
// request contacts and resolve either with success or error callback
|
||||||
|
contactsRequest.done(function (contacts) {
|
||||||
|
var result = [];
|
||||||
|
if (contacts.size !== 0) {
|
||||||
|
// Depending on searchOptions we should return all contacts found or only first
|
||||||
|
var outputContactsArray = searchMultiple ? contacts : [contacts[0]];
|
||||||
|
outputContactsArray.forEach(function (contact) {
|
||||||
|
// Convert windows contacts to plugin's contact objects
|
||||||
|
result.push(convertToContact(contact));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
win(result);
|
||||||
|
}, function() {
|
||||||
|
fail(new ContactError(ContactError.UNKNOWN_ERROR));
|
||||||
|
});
|
||||||
|
}, function() {
|
||||||
|
fail(new ContactError(ContactError.UNKNOWN_ERROR));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
require("cordova/exec/proxy").add("Contacts", module.exports);
|
Loading…
Reference in New Issue
Block a user