You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by be...@apache.org on 2013/06/24 23:30:16 UTC
git commit: [CB-3684] first pass wp8 support
Updated Branches:
refs/heads/master 0c4f9e6fe -> 88b5792fd
[CB-3684] first pass wp8 support
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/88b5792f
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/tree/88b5792f
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/diff/88b5792f
Branch: refs/heads/master
Commit: 88b5792fd205810b48c2367f351e451b7c51f6e4
Parents: 0c4f9e6
Author: Benn Mapes <be...@gmail.com>
Authored: Mon Jun 24 14:30:03 2013 -0700
Committer: Benn Mapes <be...@gmail.com>
Committed: Mon Jun 24 14:30:03 2013 -0700
----------------------------------------------------------------------
plugin.xml | 17 +-
src/wp8/Contacts.cs | 664 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 680 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/88b5792f/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index 341a717..24f33ae 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -112,7 +112,22 @@ id="org.apache.cordova.core.contacts">
<Capability Name="ID_CAP_CONTACTS" />
</config-file>
- <source-file src="src/wp7/Contacts.cs" />
+ <source-file src="src/wp7/Contacts.cs" />
+ </platform>
+
+ <!-- wp8 -->
+ <platform name="wp8">
+ <config-file target="config.xml" parent="/*">
+ <feature name="Contacts">
+ <param name="wp-package" value="Contacts"/>
+ </feature>
+ </config-file>
+
+ <config-file target="Properties/WMAppManifest.xml" parent="/Deployment/App/Capabilities">
+ <Capability Name="ID_CAP_CONTACTS" />
+ </config-file>
+
+ <source-file src="src/wp8/Contacts.cs" />
</platform>
</plugin>
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/88b5792f/src/wp8/Contacts.cs
----------------------------------------------------------------------
diff --git a/src/wp8/Contacts.cs b/src/wp8/Contacts.cs
new file mode 100644
index 0000000..af78942
--- /dev/null
+++ b/src/wp8/Contacts.cs
@@ -0,0 +1,664 @@
+/*
+ Licensed 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.
+*/
+
+using Microsoft.Phone.Tasks;
+using Microsoft.Phone.UserData;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Windows;
+using DeviceContacts = Microsoft.Phone.UserData.Contacts;
+
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+ [DataContract]
+ public class SearchOptions
+ {
+ [DataMember]
+ public string filter { get; set; }
+ [DataMember]
+ public bool multiple { get; set; }
+ }
+
+ [DataContract]
+ public class ContactSearchParams
+ {
+ [DataMember]
+ public string[] fields { get; set; }
+ [DataMember]
+ public SearchOptions options { get; set; }
+ }
+
+ [DataContract]
+ public class JSONContactAddress
+ {
+ [DataMember]
+ public string formatted { get; set; }
+ [DataMember]
+ public string type { get; set; }
+ [DataMember]
+ public string streetAddress { get; set; }
+ [DataMember]
+ public string locality { get; set; }
+ [DataMember]
+ public string region { get; set; }
+ [DataMember]
+ public string postalCode { get; set; }
+ [DataMember]
+ public string country { get; set; }
+ [DataMember]
+ public bool pref { get; set; }
+ }
+
+ [DataContract]
+ public class JSONContactName
+ {
+ [DataMember]
+ public string formatted { get; set; }
+ [DataMember]
+ public string familyName { get; set; }
+ [DataMember]
+ public string givenName { get; set; }
+ [DataMember]
+ public string middleName { get; set; }
+ [DataMember]
+ public string honorificPrefix { get; set; }
+ [DataMember]
+ public string honorificSuffix { get; set; }
+ }
+
+ [DataContract]
+ public class JSONContactField
+ {
+ [DataMember]
+ public string type { get; set; }
+ [DataMember]
+ public string value { get; set; }
+ [DataMember]
+ public bool pref { get; set; }
+ }
+
+ [DataContract]
+ public class JSONContactOrganization
+ {
+ [DataMember]
+ public string type { get; set; }
+ [DataMember]
+ public string name { get; set; }
+ [DataMember]
+ public bool pref { get; set; }
+ [DataMember]
+ public string department { get; set; }
+ [DataMember]
+ public string title { get; set; }
+ }
+
+ [DataContract]
+ public class JSONContact
+ {
+ [DataMember]
+ public string id { get; set; }
+ [DataMember]
+ public string rawId { get; set; }
+ [DataMember]
+ public string displayName { get; set; }
+ [DataMember]
+ public string nickname { get; set; }
+ [DataMember]
+ public string note { get; set; }
+
+ [DataMember]
+ public JSONContactName name { get; set; }
+
+ [DataMember]
+ public JSONContactField[] emails { get; set; }
+
+ [DataMember]
+ public JSONContactField[] phoneNumbers { get; set; }
+
+ [DataMember]
+ public JSONContactField[] ims { get; set; }
+
+ [DataMember]
+ public JSONContactField[] photos { get; set; }
+
+ [DataMember]
+ public JSONContactField[] categories { get; set; }
+
+ [DataMember]
+ public JSONContactField[] urls { get; set; }
+
+ [DataMember]
+ public JSONContactOrganization[] organizations { get; set; }
+
+ [DataMember]
+ public JSONContactAddress[] addresses { get; set; }
+ }
+
+
+ public class Contacts : BaseCommand
+ {
+
+ public const int UNKNOWN_ERROR = 0;
+ public const int INVALID_ARGUMENT_ERROR = 1;
+ public const int TIMEOUT_ERROR = 2;
+ public const int PENDING_OPERATION_ERROR = 3;
+ public const int IO_ERROR = 4;
+ public const int NOT_SUPPORTED_ERROR = 5;
+ public const int PERMISSION_DENIED_ERROR = 20;
+ public const int SYNTAX_ERR = 8;
+
+ public Contacts()
+ {
+
+ }
+
+ // refer here for contact properties we can access: http://msdn.microsoft.com/en-us/library/microsoft.phone.tasks.savecontacttask_members%28v=VS.92%29.aspx
+ public void save(string jsonContact)
+ {
+
+ // jsonContact is actually an array of 1 {contact}
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(jsonContact);
+
+
+ JSONContact contact = JSON.JsonHelper.Deserialize<JSONContact>(args[0]);
+
+ SaveContactTask contactTask = new SaveContactTask();
+
+ if (contact.nickname != null)
+ {
+ contactTask.Nickname = contact.nickname;
+ }
+ if (contact.urls != null && contact.urls.Length > 0)
+ {
+ contactTask.Website = contact.urls[0].value;
+ }
+ if (contact.note != null)
+ {
+ contactTask.Notes = contact.note;
+ }
+
+ #region contact.name
+ if (contact.name != null)
+ {
+ if (contact.name.givenName != null)
+ contactTask.FirstName = contact.name.givenName;
+ if (contact.name.familyName != null)
+ contactTask.LastName = contact.name.familyName;
+ if (contact.name.middleName != null)
+ contactTask.MiddleName = contact.name.middleName;
+ if (contact.name.honorificSuffix != null)
+ contactTask.Suffix = contact.name.honorificSuffix;
+ if (contact.name.honorificPrefix != null)
+ contactTask.Title = contact.name.honorificPrefix;
+ }
+ #endregion
+
+ #region contact.org
+ if (contact.organizations != null && contact.organizations.Count() > 0)
+ {
+ contactTask.Company = contact.organizations[0].name;
+ contactTask.JobTitle = contact.organizations[0].title;
+ }
+ #endregion
+
+ #region contact.phoneNumbers
+ if (contact.phoneNumbers != null && contact.phoneNumbers.Length > 0)
+ {
+ foreach (JSONContactField field in contact.phoneNumbers)
+ {
+ string fieldType = field.type.ToLower();
+ if (fieldType == "work")
+ {
+ contactTask.WorkPhone = field.value;
+ }
+ else if (fieldType == "home")
+ {
+ contactTask.HomePhone = field.value;
+ }
+ else if (fieldType == "mobile")
+ {
+ contactTask.MobilePhone = field.value;
+ }
+ }
+ }
+ #endregion
+
+ #region contact.emails
+
+ if (contact.emails != null && contact.emails.Length > 0)
+ {
+
+ // set up different email types if they are not explicitly defined
+ foreach (string type in new string[] { "personal", "work", "other" })
+ {
+ foreach (JSONContactField field in contact.emails)
+ {
+ if (field != null && String.IsNullOrEmpty(field.type))
+ {
+ field.type = type;
+ break;
+ }
+ }
+ }
+
+ foreach (JSONContactField field in contact.emails)
+ {
+ if (field != null)
+ {
+ if (field.type != null && field.type != "other")
+ {
+ string fieldType = field.type.ToLower();
+ if (fieldType == "work")
+ {
+ contactTask.WorkEmail = field.value;
+ }
+ else if (fieldType == "home" || fieldType == "personal")
+ {
+ contactTask.PersonalEmail = field.value;
+ }
+ }
+ else
+ {
+ contactTask.OtherEmail = field.value;
+ }
+ }
+
+ }
+ }
+ #endregion
+
+ if (contact.note != null && contact.note.Length > 0)
+ {
+ contactTask.Notes = contact.note;
+ }
+
+ #region contact.addresses
+ if (contact.addresses != null && contact.addresses.Length > 0)
+ {
+ foreach (JSONContactAddress address in contact.addresses)
+ {
+ if (address.type == null)
+ {
+ address.type = "home"; // set a default
+ }
+ string fieldType = address.type.ToLower();
+ if (fieldType == "work")
+ {
+ contactTask.WorkAddressCity = address.locality;
+ contactTask.WorkAddressCountry = address.country;
+ contactTask.WorkAddressState = address.region;
+ contactTask.WorkAddressStreet = address.streetAddress;
+ contactTask.WorkAddressZipCode = address.postalCode;
+ }
+ else if (fieldType == "home" || fieldType == "personal")
+ {
+ contactTask.HomeAddressCity = address.locality;
+ contactTask.HomeAddressCountry = address.country;
+ contactTask.HomeAddressState = address.region;
+ contactTask.HomeAddressStreet = address.streetAddress;
+ contactTask.HomeAddressZipCode = address.postalCode;
+ }
+ else
+ {
+ // no other address fields available ...
+ Debug.WriteLine("Creating contact with unsupported address type :: " + address.type);
+ }
+ }
+ }
+ #endregion
+
+
+ contactTask.Completed += new EventHandler<SaveContactResult>(ContactSaveTaskCompleted);
+ contactTask.Show();
+ }
+
+ void ContactSaveTaskCompleted(object sender, SaveContactResult e)
+ {
+ SaveContactTask task = sender as SaveContactTask;
+
+ if (e.TaskResult == TaskResult.OK)
+ {
+
+ Deployment.Current.Dispatcher.BeginInvoke(() =>
+ {
+ DeviceContacts deviceContacts = new DeviceContacts();
+ deviceContacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(postAdd_SearchCompleted);
+
+ string displayName = String.Format("{0}{2}{1}", task.FirstName, task.LastName, String.IsNullOrEmpty(task.FirstName) ? "" : " ");
+
+ deviceContacts.SearchAsync(displayName, FilterKind.DisplayName, task);
+ });
+
+
+ }
+ else if (e.TaskResult == TaskResult.Cancel)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Operation cancelled."));
+ }
+ }
+
+ void postAdd_SearchCompleted(object sender, ContactsSearchEventArgs e)
+ {
+ if (e.Results.Count() > 0)
+ {
+ List<Contact> foundContacts = new List<Contact>();
+
+ int n = (from Contact contact in e.Results select contact.GetHashCode()).Max();
+ Contact newContact = (from Contact contact in e.Results
+ where contact.GetHashCode() == n
+ select contact).First();
+
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK, FormatJSONContact(newContact, null)));
+ }
+ else
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
+ }
+ }
+
+
+
+ public void remove(string id)
+ {
+ // note id is wrapped in [] and always has exactly one string ...
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "{\"code\":" + NOT_SUPPORTED_ERROR + "}"));
+ }
+
+ public void search(string searchCriteria)
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(searchCriteria);
+
+ ContactSearchParams searchParams = new ContactSearchParams();
+ try
+ {
+ searchParams.fields = JSON.JsonHelper.Deserialize<string[]>(args[0]);
+ searchParams.options = JSON.JsonHelper.Deserialize<SearchOptions>(args[1]);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_ARGUMENT_ERROR));
+ return;
+ }
+
+ if (searchParams.options == null)
+ {
+ searchParams.options = new SearchOptions();
+ searchParams.options.filter = "";
+ searchParams.options.multiple = true;
+ }
+
+ DeviceContacts deviceContacts = new DeviceContacts();
+ deviceContacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(contacts_SearchCompleted);
+
+ // default is to search all fields
+ FilterKind filterKind = FilterKind.None;
+ // if only one field is specified, we will try the 3 available DeviceContact search filters
+ if (searchParams.fields.Count() == 1)
+ {
+ if (searchParams.fields.Contains("name"))
+ {
+ filterKind = FilterKind.DisplayName;
+ }
+ else if (searchParams.fields.Contains("emails"))
+ {
+ filterKind = FilterKind.EmailAddress;
+ }
+ else if (searchParams.fields.Contains("phoneNumbers"))
+ {
+ filterKind = FilterKind.PhoneNumber;
+ }
+ }
+
+ try
+ {
+
+ deviceContacts.SearchAsync(searchParams.options.filter, filterKind, searchParams);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("search contacts exception :: " + ex.Message);
+ }
+ }
+
+ private void contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
+ {
+ ContactSearchParams searchParams = (ContactSearchParams)e.State;
+
+ List<Contact> foundContacts = null;
+
+ // if we have multiple search fields
+ if (searchParams.options.filter.Length > 0 && searchParams.fields.Count() > 1)
+ {
+ foundContacts = new List<Contact>();
+ if (searchParams.fields.Contains("emails"))
+ {
+ foundContacts.AddRange(from Contact con in e.Results
+ from ContactEmailAddress a in con.EmailAddresses
+ where a.EmailAddress.Contains(searchParams.options.filter)
+ select con);
+ }
+ if (searchParams.fields.Contains("displayName"))
+ {
+ foundContacts.AddRange(from Contact con in e.Results
+ where con.DisplayName.Contains(searchParams.options.filter)
+ select con);
+ }
+ if (searchParams.fields.Contains("name"))
+ {
+ foundContacts.AddRange(from Contact con in e.Results
+ where con.CompleteName != null && con.CompleteName.ToString().Contains(searchParams.options.filter)
+ select con);
+ }
+ if (searchParams.fields.Contains("phoneNumbers"))
+ {
+ foundContacts.AddRange(from Contact con in e.Results
+ from ContactPhoneNumber a in con.PhoneNumbers
+ where a.PhoneNumber.Contains(searchParams.options.filter)
+ select con);
+ }
+ if (searchParams.fields.Contains("urls"))
+ {
+ foundContacts.AddRange(from Contact con in e.Results
+ from string a in con.Websites
+ where a.Contains(searchParams.options.filter)
+ select con);
+ }
+ }
+ else
+ {
+ foundContacts = new List<Contact>(e.Results);
+ }
+
+ //List<string> contactList = new List<string>();
+
+ string strResult = "";
+
+ IEnumerable<Contact> distinctContacts = foundContacts.Distinct();
+
+ foreach (Contact contact in distinctContacts)
+ {
+ strResult += FormatJSONContact(contact, null) + ",";
+ //contactList.Add(FormatJSONContact(contact, null));
+ if (!searchParams.options.multiple)
+ {
+ break; // just return the first item
+ }
+ }
+ PluginResult result = new PluginResult(PluginResult.Status.OK);
+ result.Message = "[" + strResult.TrimEnd(',') + "]";
+ DispatchCommandResult(result);
+
+ }
+
+ private string FormatJSONPhoneNumbers(Contact con)
+ {
+ string retVal = "";
+ string contactFieldFormat = "\"type\":\"{0}\",\"value\":\"{1}\",\"pref\":\"false\"";
+ foreach (ContactPhoneNumber number in con.PhoneNumbers)
+ {
+
+ string contactField = string.Format(contactFieldFormat,
+ number.Kind.ToString(),
+ number.PhoneNumber);
+
+ retVal += "{" + contactField + "},";
+ }
+ return retVal.TrimEnd(',');
+ }
+
+ private string FormatJSONEmails(Contact con)
+ {
+ string retVal = "";
+ string contactFieldFormat = "\"type\":\"{0}\",\"value\":\"{1}\",\"pref\":\"false\"";
+ foreach (ContactEmailAddress address in con.EmailAddresses)
+ {
+ string contactField = string.Format(contactFieldFormat,
+ address.Kind.ToString(),
+ address.EmailAddress);
+
+ retVal += "{" + contactField + "},";
+ }
+ return retVal.TrimEnd(',');
+ }
+
+ private string getFormattedJSONAddress(ContactAddress address, bool isPrefered)
+ {
+
+ string addressFormatString = "\"pref\":{0}," + // bool
+ "\"type\":\"{1}\"," +
+ "\"formatted\":\"{2}\"," +
+ "\"streetAddress\":\"{3}\"," +
+ "\"locality\":\"{4}\"," +
+ "\"region\":\"{5}\"," +
+ "\"postalCode\":\"{6}\"," +
+ "\"country\":\"{7}\"";
+
+ string formattedAddress = address.PhysicalAddress.AddressLine1 + " "
+ + address.PhysicalAddress.AddressLine2 + " "
+ + address.PhysicalAddress.City + " "
+ + address.PhysicalAddress.StateProvince + " "
+ + address.PhysicalAddress.CountryRegion + " "
+ + address.PhysicalAddress.PostalCode;
+
+ string jsonAddress = string.Format(addressFormatString,
+ isPrefered ? "\"true\"" : "\"false\"",
+ address.Kind.ToString(),
+ formattedAddress,
+ address.PhysicalAddress.AddressLine1 + " " + address.PhysicalAddress.AddressLine2,
+ address.PhysicalAddress.City,
+ address.PhysicalAddress.StateProvince,
+ address.PhysicalAddress.PostalCode,
+ address.PhysicalAddress.CountryRegion);
+
+ //Debug.WriteLine("getFormattedJSONAddress returning :: " + jsonAddress);
+
+ return "{" + jsonAddress + "}";
+ }
+
+ private string FormatJSONAddresses(Contact con)
+ {
+ string retVal = "";
+ foreach (ContactAddress address in con.Addresses)
+ {
+ retVal += this.getFormattedJSONAddress(address, false) + ",";
+ }
+
+ //Debug.WriteLine("FormatJSONAddresses returning :: " + retVal);
+ return retVal.TrimEnd(',');
+ }
+
+ private string FormatJSONWebsites(Contact con)
+ {
+ string retVal = "";
+ foreach (string website in con.Websites)
+ {
+ retVal += "\"" + website + "\",";
+ }
+ return retVal.TrimEnd(',');
+ }
+
+ /*
+ * formatted: The complete name of the contact. (DOMString)
+ familyName: The contacts family name. (DOMString)
+ givenName: The contacts given name. (DOMString)
+ middleName: The contacts middle name. (DOMString)
+ honorificPrefix: The contacts prefix (example Mr. or Dr.) (DOMString)
+ honorificSuffix: The contacts suffix (example Esq.). (DOMString)
+ */
+ private string FormatJSONName(Contact con)
+ {
+ string retVal = "";
+ string formatStr = "\"formatted\":\"{0}\"," +
+ "\"familyName\":\"{1}\"," +
+ "\"givenName\":\"{2}\"," +
+ "\"middleName\":\"{3}\"," +
+ "\"honorificPrefix\":\"{4}\"," +
+ "\"honorificSuffix\":\"{5}\"";
+
+ if (con.CompleteName != null)
+ {
+ retVal = string.Format(formatStr,
+ con.CompleteName.FirstName + " " + con.CompleteName.LastName, // TODO: does this need suffix? middlename?
+ con.CompleteName.LastName,
+ con.CompleteName.FirstName,
+ con.CompleteName.MiddleName,
+ con.CompleteName.Title,
+ con.CompleteName.Suffix);
+ }
+ else
+ {
+ retVal = string.Format(formatStr,"","","","","","");
+ }
+
+ return "{" + retVal + "}";
+ }
+
+ private string FormatJSONContact(Contact con, string[] fields)
+ {
+
+ string contactFormatStr = "\"id\":\"{0}\"," +
+ "\"displayName\":\"{1}\"," +
+ "\"nickname\":\"{2}\"," +
+ "\"phoneNumbers\":[{3}]," +
+ "\"emails\":[{4}]," +
+ "\"addresses\":[{5}]," +
+ "\"urls\":[{6}]," +
+ "\"name\":{7}," +
+ "\"note\":\"{8}\"," +
+ "\"birthday\":\"{9}\"";
+
+
+ string jsonContact = String.Format(contactFormatStr,
+ con.GetHashCode(),
+ con.DisplayName,
+ con.CompleteName != null ? con.CompleteName.Nickname : "",
+ FormatJSONPhoneNumbers(con),
+ FormatJSONEmails(con),
+ FormatJSONAddresses(con),
+ FormatJSONWebsites(con),
+ FormatJSONName(con),
+ con.Notes.FirstOrDefault(),
+ con.Birthdays.FirstOrDefault());
+
+ //Debug.WriteLine("jsonContact = " + jsonContact);
+ // JSON requires new line characters be escaped
+ return "{" + jsonContact.Replace("\n", "\\n") + "}";
+ }
+ }
+}