You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by pu...@apache.org on 2014/05/28 00:47:17 UTC
[2/5] git commit: Add pickContact functionality to cordova contacts
plugin
Add pickContact functionality to cordova contacts plugin
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/d656191c
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/tree/d656191c
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/diff/d656191c
Branch: refs/heads/master
Commit: d656191c4072cbef0bf5b3b5f5eb4dfe4817d25b
Parents: 6ed8b90
Author: Vladimir Kotikov <vl...@akvelon.com>
Authored: Wed Apr 2 10:38:12 2014 +0400
Committer: Vladimir Kotikov <vl...@akvelon.com>
Committed: Wed Apr 2 10:58:01 2014 +0400
----------------------------------------------------------------------
doc/index.md | 56 ++++--
plugin.xml | 23 ++-
src/android/ContactAccessor.java | 67 ++++---
src/android/ContactAccessorSdk5.java | 81 ++++----
src/android/ContactInfoDTO.java | 43 +++++
src/android/ContactManager.java | 62 +++++-
src/ios/CDVContacts.h | 11 ++
src/ios/CDVContacts.m | 36 +++-
src/windows8/ContactProxy.js | 173 +++++++----------
src/wp/ContactPicker.xaml | 58 ++++++
src/wp/ContactPicker.xaml.cs | 110 +++++++++++
src/wp/ContactPickerTask.cs | 107 +++++++++++
src/wp/Contacts.cs | 308 ++++++++++--------------------
src/wp/ContactsHelper.cs | 277 +++++++++++++++++++++++++++
www/ContactFieldType.js | 38 ++++
www/ContactFindOptions.js | 4 +-
www/contacts.js | 28 ++-
17 files changed, 1077 insertions(+), 405 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/d656191c/doc/index.md
----------------------------------------------------------------------
diff --git a/doc/index.md b/doc/index.md
index 2f41254..b13966b 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -58,12 +58,18 @@ __WARNING__: All privileged apps enforce [Content Security Policy](https://devel
}
}
+### 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.
+Any contacts returned are readonly, so your application cannot modify them.
+
## navigator.contacts
### Methods
- navigator.contacts.create
- navigator.contacts.find
+- navigator.contacts.pickContact
### Objects
@@ -74,6 +80,7 @@ __WARNING__: All privileged apps enforce [Content Security Policy](https://devel
- ContactOrganization
- ContactFindOptions
- ContactError
+- ContactFieldType
## navigator.contacts.create
@@ -89,9 +96,6 @@ database, for which you need to invoke the `Contact.save` method.
- Firefox OS
- iOS
- Windows Phone 7 and 8
-- Windows 8 ( Note: 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.find will open the 'People' app where the user must choose a contact.
-Any contacts returned are readonly, so your application cannot modify them. )
### Example
@@ -105,9 +109,7 @@ The resulting objects are passed to the `contactSuccess` callback
function specified by the __contactSuccess__ parameter.
The __contactFields__ parameter specifies the fields to be used as a
-search qualifier, and only those results are passed to the
-__contactSuccess__ callback function. A zero-length __contactFields__
-parameter is invalid and results in
+search qualifier. A zero-length __contactFields__ parameter is invalid and results in
`ContactError.INVALID_ARGUMENT_ERROR`. A __contactFields__ value of
`"*"` returns all contact fields.
@@ -115,22 +117,25 @@ The __contactFindOptions.filter__ string can be used as a search
filter when querying the contacts database. If provided, a
case-insensitive, partial value match is applied to each field
specified in the __contactFields__ parameter. If there's a match for
-_any_ of the specified fields, the contact is returned.
+_any_ of the specified fields, the contact is returned. Use __contactFindOptions.desiredFields__
+parameter to control which contact properties must be returned back.
### Parameters
-- __contactFields__: Contact fields to use as a search qualifier. The resulting `Contact` object only features values for these fields. _(DOMString[])_ [Required]
-
- __contactSuccess__: Success callback function invoked with the array of Contact objects returned from the database. [Required]
- __contactError__: Error callback function, invoked when an error occurs. [Optional]
+- __contactFields__: Contact fields to use as a search qualifier. _(DOMString[])_ [Required]
+
- __contactFindOptions__: Search options to filter navigator.contacts. [Optional] Keys include:
- __filter__: The search string used to find navigator.contacts. _(DOMString)_ (Default: `""`)
- __multiple__: Determines if the find operation returns multiple navigator.contacts. _(Boolean)_ (Default: `false`)
+ - __desiredFields__: Contact fields to be returned back. If specified, the resulting `Contact` object only features values for these fields. _(DOMString[])_ [Optional]
+
### Supported Platforms
- Android
@@ -138,7 +143,6 @@ _any_ of the specified fields, the contact is returned.
- Firefox OS
- iOS
- Windows Phone 7 and 8
-- Windows 8 ( read-only support, search requires user interaction, contactFields are ignored, only contactFindOptions.multiple is used to enable the user to select 1 or many contacts. )
### Example
@@ -154,9 +158,36 @@ _any_ of the specified fields, the contact is returned.
var options = new ContactFindOptions();
options.filter = "Bob";
options.multiple = true;
- var fields = ["displayName", "name"];
- navigator.contacts.find(fields, onSuccess, onError, options);
+ options.desiredFields = [navigator.contacts.fieldType.id];
+ var fields = [navigator.contacts.fieldType.displayName, navigator.contacts.fieldType.name];
+ navigator.contacts.find(onSuccess, onError, fields, options);
+## navigator.contacts.pickContact
+
+The `navigator.contacts.pickContact` method launches the Contact Picker to select a single contact.
+The resulting object is passed to the `contactSuccess` callback
+function specified by the __contactSuccess__ parameter.
+
+### Parameters
+
+- __contactSuccess__: Success callback function invoked with the single Contact object. [Required]
+
+- __contactError__: Error callback function, invoked when an error occurs. [Optional]
+
+### Supported Platforms
+
+- Android
+- iOS
+- Windows Phone 8
+- Windows 8
+
+### Example
+
+ navigator.contacts.pickContact(function(contact){
+ console.log('The following contact has been selected:' + JSON.stringify(contact));
+ },function(err){
+ console.log('Error: ' + err);
+ });
## Contact
@@ -216,6 +247,7 @@ for details.
- Firefox OS
- iOS
- Windows Phone 7 and 8
+- Windows 8
### Save Example
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/d656191c/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index acfc080..bdaec08 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -45,8 +45,9 @@
<clobbers target="ContactOrganization" />
</js-module>
-
-
+ <js-module src="www/ContactFieldType.js" name="ContactFieldType">
+ <merge target="" />
+ </js-module>
<!-- android -->
<platform name="android">
@@ -65,6 +66,7 @@
<source-file src="src/android/ContactAccessor.java" target-dir="src/org/apache/cordova/contacts" />
<source-file src="src/android/ContactAccessorSdk5.java" target-dir="src/org/apache/cordova/contacts" />
<source-file src="src/android/ContactManager.java" target-dir="src/org/apache/cordova/contacts" />
+ <source-file src="src/android/ContactInfoDTO.java" target-dir="src/org/apache/cordova/contacts" />
</platform>
<!-- amazon-fireos -->
@@ -160,6 +162,7 @@
</config-file>
<source-file src="src/wp/Contacts.cs" />
+ <source-file src="src/wp/ContactsHelper.cs" />
</platform>
<!-- wp8 -->
@@ -175,6 +178,10 @@
</config-file>
<source-file src="src/wp/Contacts.cs" />
+ <source-file src="src/wp/ContactsHelper.cs" />
+ <source-file src="src/wp/ContactPicker.xaml" />
+ <source-file src="src/wp/ContactPicker.xaml.cs" />
+ <source-file src="src/wp/ContactPickerTask.cs" />
</platform>
<!-- firefoxos -->
@@ -187,11 +194,13 @@
<js-module src="src/firefoxos/ContactsProxy.js" name="ContactsProxy">
<runs />
</js-module>
- </platform>
+ </platform>
- <!-- wp8 -->
- <js-module src="src/windows8/ContactProxy.js" name="ContactProxy">
- <merges target="" />
- </js-module>
+ <!-- Windows 8 -->
+ <platform name="windows8">
+ <js-module src="src/windows8/ContactProxy.js" name="ContactProxy">
+ <merges target="" />
+ </js-module>
+ </platform>
</plugin>
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/d656191c/src/android/ContactAccessor.java
----------------------------------------------------------------------
diff --git a/src/android/ContactAccessor.java b/src/android/ContactAccessor.java
index bac243e..879fe68 100644
--- a/src/android/ContactAccessor.java
+++ b/src/android/ContactAccessor.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2009 The Android Open Source Project
+ * Copyright (c) Microsoft Open Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +20,6 @@ package org.apache.cordova.contacts;
import java.util.HashMap;
import android.util.Log;
-
import org.apache.cordova.CordovaInterface;
import org.json.JSONArray;
import org.json.JSONException;
@@ -54,12 +54,16 @@ public abstract class ContactAccessor {
* @param fields the list of fields to populate
* @return the hash map of required data
*/
- protected HashMap<String,Boolean> buildPopulationSet(JSONArray fields) {
- HashMap<String,Boolean> map = new HashMap<String,Boolean>();
+ protected HashMap<String, Boolean> buildPopulationSet(JSONObject options) {
+ HashMap<String, Boolean> map = new HashMap<String, Boolean>();
String key;
try {
- if (fields.length() == 1 && fields.getString(0).equals("*")) {
+ JSONArray desiredFields = null;
+ if (options!=null && options.has("desiredFields")) {
+ desiredFields = options.getJSONArray("desiredFields");
+ }
+ if (desiredFields == null || desiredFields.length() == 0) {
map.put("displayName", true);
map.put("name", true);
map.put("nickname", true);
@@ -73,54 +77,40 @@ public abstract class ContactAccessor {
map.put("urls", true);
map.put("photos", true);
map.put("categories", true);
- }
- else {
- for (int i=0; i<fields.length(); i++) {
- key = fields.getString(i);
+ } else {
+ for (int i = 0; i < desiredFields.length(); i++) {
+ key = desiredFields.getString(i);
if (key.startsWith("displayName")) {
map.put("displayName", true);
- }
- else if (key.startsWith("name")) {
+ } else if (key.startsWith("name")) {
map.put("displayName", true);
map.put("name", true);
- }
- else if (key.startsWith("nickname")) {
+ } else if (key.startsWith("nickname")) {
map.put("nickname", true);
- }
- else if (key.startsWith("phoneNumbers")) {
+ } else if (key.startsWith("phoneNumbers")) {
map.put("phoneNumbers", true);
- }
- else if (key.startsWith("emails")) {
+ } else if (key.startsWith("emails")) {
map.put("emails", true);
- }
- else if (key.startsWith("addresses")) {
+ } else if (key.startsWith("addresses")) {
map.put("addresses", true);
- }
- else if (key.startsWith("ims")) {
+ } else if (key.startsWith("ims")) {
map.put("ims", true);
- }
- else if (key.startsWith("organizations")) {
+ } else if (key.startsWith("organizations")) {
map.put("organizations", true);
- }
- else if (key.startsWith("birthday")) {
+ } else if (key.startsWith("birthday")) {
map.put("birthday", true);
- }
- else if (key.startsWith("note")) {
+ } else if (key.startsWith("note")) {
map.put("note", true);
- }
- else if (key.startsWith("urls")) {
+ } else if (key.startsWith("urls")) {
map.put("urls", true);
- }
- else if (key.startsWith("photos")) {
+ } else if (key.startsWith("photos")) {
map.put("photos", true);
- }
- else if (key.startsWith("categories")) {
+ } else if (key.startsWith("categories")) {
map.put("categories", true);
}
}
}
- }
- catch (JSONException e) {
+ } catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
return map;
@@ -168,12 +158,19 @@ public abstract class ContactAccessor {
* @throws JSONException
*/
public abstract JSONObject getContactById(String id) throws JSONException;
+
+ /**
+ * Handles searching through SDK-specific contacts API.
+ * @param desiredFields fields that will filled. All fields will be filled if null
+ * @throws JSONException
+ */
+ public abstract JSONObject getContactById(String id, JSONArray desiredFields) throws JSONException;
/**
* Handles removing a contact from the database.
*/
public abstract boolean remove(String id);
-
+
/**
* A class that represents the where clause to be used in the database query
*/
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/d656191c/src/android/ContactAccessorSdk5.java
----------------------------------------------------------------------
diff --git a/src/android/ContactAccessorSdk5.java b/src/android/ContactAccessorSdk5.java
index d8b9592..9178b38 100644
--- a/src/android/ContactAccessorSdk5.java
+++ b/src/android/ContactAccessorSdk5.java
@@ -15,28 +15,12 @@
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-*/
+ Copyright (c) Microsoft Open Technologies, Inc.
+*/
+
package org.apache.cordova.contacts;
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.OperationApplicationException;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.util.Log;
-
-import org.apache.cordova.CordovaInterface;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -49,8 +33,24 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
-//import android.app.Activity;
-//import android.content.Context;
+
+import org.apache.cordova.CordovaInterface;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.OperationApplicationException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.util.Log;
/**
* An implementation of {@link ContactAccessor} that uses current Contacts API.
@@ -84,6 +84,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
* A static map that converts the JavaScript property name to Android database column name.
*/
private static final Map<String, String> dbMap = new HashMap<String, String>();
+
static {
dbMap.put("id", ContactsContract.Data.CONTACT_ID);
dbMap.put("displayName", ContactsContract.Contacts.DISPLAY_NAME);
@@ -126,7 +127,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
public ContactAccessorSdk5(CordovaInterface context) {
mApp = context;
}
-
+
/**
* This method takes the fields required and search options in order to produce an
* array of contacts that matches the criteria provided.
@@ -169,7 +170,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
//Log.d(LOG_TAG, "Fields = " + fields.toString());
// Loop through the fields the user provided to see what data should be returned.
- HashMap<String, Boolean> populate = buildPopulationSet(fields);
+ HashMap<String, Boolean> populate = buildPopulationSet(options);
// Build the ugly where clause and where arguments for one big query.
WhereOptions whereOptions = buildWhereClause(fields, searchTerm);
@@ -268,7 +269,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
idOptions.getWhere(),
idOptions.getWhereArgs(),
ContactsContract.Data.CONTACT_ID + " ASC");
-
+
JSONArray contacts = populateContactArray(limit, populate, c);
return contacts;
}
@@ -281,17 +282,23 @@ public class ContactAccessorSdk5 extends ContactAccessor {
* @throws JSONException
*/
public JSONObject getContactById(String id) throws JSONException {
+ // Call overloaded version with no desiredFields
+ return getContactById(id, null);
+ }
+
+ @Override
+ public JSONObject getContactById(String id, JSONArray desiredFields) throws JSONException {
// Do the id query
- Cursor c = mApp.getActivity().getContentResolver().query(ContactsContract.Data.CONTENT_URI,
+ Cursor c = mApp.getActivity().getContentResolver().query(
+ ContactsContract.Data.CONTENT_URI,
null,
ContactsContract.Data.RAW_CONTACT_ID + " = ? ",
new String[] { id },
ContactsContract.Data.RAW_CONTACT_ID + " ASC");
- JSONArray fields = new JSONArray();
- fields.put("*");
-
- HashMap<String, Boolean> populate = buildPopulationSet(fields);
+ HashMap<String, Boolean> populate = buildPopulationSet(
+ new JSONObject().put("desiredFields", desiredFields)
+ );
JSONArray contacts = populateContactArray(1, populate, c);
@@ -383,7 +390,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
// Grab the mimetype of the current row as it will be used in a lot of comparisons
mimetype = c.getString(colMimetype);
- if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) {
+ if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) && isRequired("name", populate)) {
contact.put("displayName", c.getString(colDisplayName));
}
@@ -894,7 +901,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
photo.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Photo._ID)));
photo.put("pref", false);
photo.put("type", "url");
- Uri person = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, (new Long(contactId)));
+ Uri person = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, (Long.valueOf(contactId)));
Uri photoUri = Uri.withAppendedPath(person, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
photo.put("value", photoUri.toString());
} catch (JSONException e) {
@@ -968,7 +975,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
private String modifyContact(String id, JSONObject contact, String accountType, String accountName) {
// Get the RAW_CONTACT_ID which is needed to insert new values in an already existing contact.
// But not needed to update existing values.
- int rawId = (new Integer(getJsonString(contact, "rawId"))).intValue();
+ int rawId = (Integer.valueOf(getJsonString(contact, "rawId"))).intValue();
// Create a list of attributes to add to the contact database
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
@@ -1098,8 +1105,8 @@ public class ContactAccessorSdk5 extends ContactAccessor {
}
// This is an existing email so do a DB update
else {
- String emailValue=getJsonString(email, "value");
- if(!emailValue.isEmpty()) {
+ String emailValue=getJsonString(email, "value");
+ if(!emailValue.isEmpty()) {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Email._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
@@ -1107,13 +1114,13 @@ public class ContactAccessorSdk5 extends ContactAccessor {
.withValue(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value"))
.withValue(ContactsContract.CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type")))
.build());
- } else {
+ } else {
ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Email._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { emailId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE })
.build());
- }
+ }
}
}
}
@@ -2177,5 +2184,5 @@ public class ContactAccessorSdk5 extends ContactAccessor {
}
return stringType;
}
-}
+}
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/d656191c/src/android/ContactInfoDTO.java
----------------------------------------------------------------------
diff --git a/src/android/ContactInfoDTO.java b/src/android/ContactInfoDTO.java
new file mode 100644
index 0000000..52a066f
--- /dev/null
+++ b/src/android/ContactInfoDTO.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) Microsoft Open Technologies, Inc. Licensed under the Apache License, Version 2.0 (the "License").
+ */
+package org.apache.cordova.contacts;
+
+import java.util.HashMap;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+public class ContactInfoDTO {
+
+ String displayName;
+ JSONObject name;
+ JSONArray organizations;
+ JSONArray addresses;
+ JSONArray phones;
+ JSONArray emails;
+ JSONArray ims;
+ JSONArray websites;
+ JSONArray photos;
+ String note;
+ String nickname;
+ String birthday;
+ HashMap<String, Object> desiredFieldsWithVals;
+
+ public ContactInfoDTO() {
+
+ displayName = "";
+ name = new JSONObject();
+ organizations = new JSONArray();
+ addresses = new JSONArray();
+ phones = new JSONArray();
+ emails = new JSONArray();
+ ims = new JSONArray();
+ websites = new JSONArray();
+ photos = new JSONArray();
+ note = "";
+ nickname = "";
+ desiredFieldsWithVals = new HashMap<String, Object>();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/d656191c/src/android/ContactManager.java
----------------------------------------------------------------------
diff --git a/src/android/ContactManager.java b/src/android/ContactManager.java
index a50d799..7e88893 100644
--- a/src/android/ContactManager.java
+++ b/src/android/ContactManager.java
@@ -15,6 +15,8 @@
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
+
+ Copyright (c) Microsoft Open Technologies, Inc.
*/
package org.apache.cordova.contacts;
@@ -24,11 +26,18 @@ import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.provider.ContactsContract.Contacts;
import android.util.Log;
public class ContactManager extends CordovaPlugin {
private ContactAccessor contactAccessor;
+ private CallbackContext callbackContext; // The callback context from which we were invoked.
+ private JSONArray executeArgs;
+
private static final String LOG_TAG = "Contact Query";
public static final int UNKNOWN_ERROR = 0;
@@ -38,6 +47,7 @@ public class ContactManager extends CordovaPlugin {
public static final int IO_ERROR = 4;
public static final int NOT_SUPPORTED_ERROR = 5;
public static final int PERMISSION_DENIED_ERROR = 20;
+ private static final int CONTACT_PICKER_RESULT = 1000;
/**
* Constructor.
@@ -54,6 +64,10 @@ public class ContactManager extends CordovaPlugin {
* @return True if the action was valid, false otherwise.
*/
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
+
+ this.callbackContext = callbackContext;
+ this.executeArgs = args;
+
/**
* Check to see if we are on an Android 1.X device. If we are return an error as we
* do not support this as of Cordova 1.0.
@@ -73,7 +87,7 @@ public class ContactManager extends CordovaPlugin {
if (action.equals("search")) {
final JSONArray filter = args.getJSONArray(0);
- final JSONObject options = args.getJSONObject(1);
+ final JSONObject options = args.get(1) == null ? null : args.getJSONObject(1);
this.cordova.getThreadPool().execute(new Runnable() {
public void run() {
JSONArray res = contactAccessor.search(filter, options);
@@ -83,7 +97,7 @@ public class ContactManager extends CordovaPlugin {
}
else if (action.equals("save")) {
final JSONObject contact = args.getJSONObject(0);
- this.cordova.getThreadPool().execute(new Runnable() {
+ this.cordova.getThreadPool().execute(new Runnable(){
public void run() {
JSONObject res = null;
String id = contactAccessor.save(contact);
@@ -114,9 +128,53 @@ public class ContactManager extends CordovaPlugin {
}
});
}
+ else if (action.equals("pickContact")) {
+ pickContactAsync();
+ }
else {
return false;
}
return true;
}
+
+ /**
+ * Launches the Contact Picker to select a single contact.
+ */
+ private void pickContactAsync() {
+ final CordovaPlugin plugin = (CordovaPlugin) this;
+ Runnable worker = new Runnable() {
+ public void run() {
+ Intent contactPickerIntent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
+ plugin.cordova.startActivityForResult(plugin, contactPickerIntent, CONTACT_PICKER_RESULT);
+ }
+ };
+ this.cordova.getThreadPool().execute(worker);
+ }
+
+ /**
+ * Called when user picks contact.
+ * @param requestCode The request code originally supplied to startActivityForResult(),
+ * allowing you to identify who this result came from.
+ * @param resultCode The integer result code returned by the child activity through its setResult().
+ * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
+ * @throws JSONException
+ */
+ public void onActivityResult(int requestCode, int resultCode, final Intent intent) {
+ if (requestCode == CONTACT_PICKER_RESULT) {
+ if (resultCode == Activity.RESULT_OK) {
+ String contactId = intent.getData().getLastPathSegment();
+ try {
+ JSONObject contact = contactAccessor.getContactById(contactId);
+ this.callbackContext.success(contact);
+ return;
+ } catch (JSONException e) {
+ Log.e(LOG_TAG, "JSON fail.", e);
+ }
+ } else if (resultCode == Activity.RESULT_CANCELED){
+ this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.NO_RESULT, UNKNOWN_ERROR));
+ return;
+ }
+ this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR));
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/d656191c/src/ios/CDVContacts.h
----------------------------------------------------------------------
diff --git a/src/ios/CDVContacts.h b/src/ios/CDVContacts.h
index e3deb21..294c48e 100644
--- a/src/ios/CDVContacts.h
+++ b/src/ios/CDVContacts.h
@@ -15,6 +15,8 @@
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
+
+ Copyright (c) Microsoft Open Technologies, Inc.
*/
#import <Foundation/Foundation.h>
@@ -64,6 +66,15 @@
- (void)newPersonViewController:(ABNewPersonViewController*)newPersonViewController didCompleteWithNewPerson:(ABRecordRef)person;
- (BOOL)personViewController:(ABPersonViewController*)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person
property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifierForValue;
+/*
+ * Launches the Contact Picker to select a single contact.
+ *
+ * arguments:
+ * 1: this is the javascript function that will be called with the contact data as a JSON object (as the first param)
+ * options:
+ * desiredFields: ContactFields array to be returned back
+ */
+- (void)pickContact:(CDVInvokedUrlCommand*)command;
/*
* search - searches for contacts. Only person records are currently supported.
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/d656191c/src/ios/CDVContacts.m
----------------------------------------------------------------------
diff --git a/src/ios/CDVContacts.m b/src/ios/CDVContacts.m
index aa0c9c7..b8513bf 100644
--- a/src/ios/CDVContacts.m
+++ b/src/ios/CDVContacts.m
@@ -15,6 +15,8 @@
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
+
+ Copyright (c) Microsoft Open Technologies, Inc.
*/
#import "CDVContacts.h"
@@ -211,6 +213,29 @@
}
}
+- (void)pickContact:(CDVInvokedUrlCommand *)command
+{
+ // mimic chooseContact method call with required for us parameters
+ NSArray* desiredFields = [command.arguments objectAtIndex:0 withDefault:[NSNull null]];
+ if (desiredFields == nil || desiredFields.count == 0) {
+ desiredFields = [NSArray arrayWithObjects:@"*", nil];
+ }
+ NSMutableDictionary* options = [NSMutableDictionary dictionaryWithCapacity:2];
+
+ [options setObject: desiredFields forKey:@"fields"];
+ [options setObject: [NSNumber numberWithBool: FALSE] forKey:@"allowsEditing"];
+
+ NSArray* args = [NSArray arrayWithObjects:options, nil];
+
+ CDVInvokedUrlCommand* newCommand = [[CDVInvokedUrlCommand alloc] initWithArguments:args
+ callbackId:command.callbackId
+ className:command.className
+ methodName:command.methodName];
+
+ [self chooseContact:newCommand];
+
+}
+
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
@@ -313,6 +338,7 @@
// get the findOptions values
BOOL multiple = NO; // default is false
NSString* filter = nil;
+ NSArray* desiredFields = nil;
if (![findOptions isKindOfClass:[NSNull class]]) {
id value = nil;
filter = (NSString*)[findOptions objectForKey:@"filter"];
@@ -322,9 +348,15 @@
multiple = [(NSNumber*)value boolValue];
// NSLog(@"multiple is: %d", multiple);
}
+ desiredFields = [findOptions objectForKey:@"desiredFields"];
+ // return all fields if desired fields are not explicitly defined
+ if (desiredFields == nil || desiredFields.count == 0) {
+ desiredFields = [NSArray arrayWithObjects:@"*", nil];
+ }
}
- NSDictionary* returnFields = [[CDVContact class] calcReturnFields:fields];
+ NSDictionary* searchFields = [[CDVContact class] calcReturnFields:fields];
+ NSDictionary* returnFields = [[CDVContact class] calcReturnFields:desiredFields];
NSMutableArray* matches = nil;
if (!filter || [filter isEqualToString:@""]) {
@@ -351,7 +383,7 @@
for (int j = 0; j < testCount; j++) {
CDVContact* testContact = [[CDVContact alloc] initFromABRecord:(__bridge ABRecordRef)[foundRecords objectAtIndex:j]];
if (testContact) {
- bFound = [testContact foundValue:filter inFields:returnFields];
+ bFound = [testContact foundValue:filter inFields:searchFields];
if (bFound) {
[matches addObject:testContact];
}
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/d656191c/src/windows8/ContactProxy.js
----------------------------------------------------------------------
diff --git a/src/windows8/ContactProxy.js b/src/windows8/ContactProxy.js
index 10f9402..960ae5c 100644
--- a/src/windows8/ContactProxy.js
+++ b/src/windows8/ContactProxy.js
@@ -18,121 +18,88 @@
* specific language governing permissions and limitations
* under the License.
*
+ * Copyright (c) Microsoft Open Technologies, Inc.
*/
+var ContactField = require('./ContactField'),
+ ContactAddress = require('./ContactAddress'),
+ ContactName = require('./ContactName'),
+ Contact = require('./Contact');
-module.exports = {
- search:function(win,fail,args){
- var fields = args[0]; // ignored, always returns entire object
- var options = args[1];
- var filter = options.filter; // ignored
- var multiple = true;//options.multiple;
+function convertToContact(windowsContact) {
+ var contact = new Contact();
+
+ // displayName & nickname
+ contact.displayName = windowsContact.name;
+ contact.nickname = windowsContact.name;
+
+ // name
+ contact.name = new ContactName(windowsContact.name);
+
+ // phoneNumbers
+ contact.phoneNumbers = [];
+ for (var i = 0; i < windowsContact.phoneNumbers.size; i++) {
+ var phone = new ContactField(windowsContact.phoneNumbers[i].category, windowsContact.phoneNumbers[i].value);
+ contact.phoneNumbers.push(phone);
+ }
+
+ // emails
+ contact.emails = [];
+ for (var i = 0; i < windowsContact.emails.size; i++) {
+ var email = new ContactField(windowsContact.emails[i].category, windowsContact.emails[i].value);
+ contact.emails.push(email);
+ }
+
+ // addressres
+ contact.addresses = [];
+ for (var i = 0; i < windowsContact.locations.size; i++) {
+ var address = new ContactAddress(null, windowsContact.locations[i].category,
+ windowsContact.locations[i].unstructuredAddress, windowsContact.locations[i].street,
+ null, windowsContact.locations[i].region, windowsContact.locations[i].postalCode,
+ windowsContact.locations[i].country);
+ contact.addresses.push(address);
+ }
+
+ // ims
+ contact.ims = [];
+ for (var i = 0; i < windowsContact.instantMessages.size; i++) {
+ var im = new ContactField(windowsContact.instantMessages[i].category, windowsContact.instantMessages[i].userName);
+ contact.ims.push(im);
+ }
+
+ return contact;
+};
+
+module.exports = {
+ pickContact: function(win, fail, args) {
var picker = new Windows.ApplicationModel.Contacts.ContactPicker();
- picker.selectionMode = Windows.ApplicationModel.Contacts.ContactSelectionMode.contacts; // select entire contact
+ picker.selectionMode = Windows.ApplicationModel.Contacts.ContactSelectionMode.contacts; // select entire contact
+
+ // pickContactAsync is available on Windows 8.1 or later, instead of
+ // pickSingleContactAsync, which is deprecated after Windows 8,
+ // so try to use newer method, if available.
+ // see http://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.contacts.contactpicker.picksinglecontactasync.aspx
if (picker.pickContactAsync) {
// TODO: 8.1 has better contact support via the 'Contact' object
- }
- else {
- // 8.0 use the ContactInformation class
- // decide which function we will call
- var pickerFunkName = multiple ? 'pickMultipleContactsAsync' : 'pickSingleContactAsync';
- picker[pickerFunkName]().done(function (res) {
- if (!res) {
- fail && setTimeout(function () {
+ } else {
+
+ function success(con) {
+ // if contact was not picked
+ if (!con) {
+ fail && setTimeout(function() {
fail(new Error("User did not pick a contact."));
}, 0);
return;
}
- var contactResults = [];
-
- for (var i = 0; i < res.length; i++) {
-
-
- var index,
- contactResult = res[i],
- contact = {
- id: "",
- name: { formatted: contactResult.name }, // ContactName
- displayName: contactResult.name, // DOMString
- nickname: contactResult.name, // DOMString
- phoneNumbers: contactResult.phoneNumbers, // ContactField[]
- addresses: contactResult.locations, // ContactAddress[]
- emails: [], // ContactField
- ims: contactResult.instantMessages, // ContactField[]
- organizations: [], // ContactOrganization[]
- birthday: null, // Date
- note: "", // DOMString
- photos: [], // ContactField[]
- categories: [], // ContactField[]
- urls: [] // ContactField[]
- };
-
- // Win8-ContactField is {category, name, type, value};
- // Cordova ContactField is {type,value, pref:bool };
- // Win8 type means 'email' cordova type means 'work|home|...' so we convert them
- if (contact.emails && contact.emails.length) {
- contact.emails[0].pref = true; // add a preferred prop
- for (index = 0; index < contacts.emails.length; index++) {
- contact.emails[index].type = contact.emails[index].category;
- }
- }
-
- if (contact.phoneNumbers && contact.phoneNumbers.length) {
- contact.phoneNumbers[0].pref = true; // cordova contact field needs a 'prefered' property on a contact
- // change the meaning of type from 'telephonenumber' to 'work|home|...'
- for (index = 0; index < contact.phoneNumbers.length; index++) {
- contact.phoneNumbers[index].type = contact.phoneNumbers[index].category;
- }
- }
-
- if (contact.addresses && contact.addresses.length) {
-
- // convert addresses/locations to Cordova.ContactAddresses
- // constr: ContactAddress(pref, type, formatted, streetAddress, locality, region, postalCode, country)
- var address, formatted;
- for (index = 0; index < contact.addresses.length; index++) {
- address = contact.addresses[index]; // make an alias
- var formattedArray = [];
- // get rid of the empty fields.
- var fields = [address.street, address.city, address.region, address.country, address.postalCode];
- for (var n = 0; n < fields.length; n++) {
- if (fields[n].length > 0) {
- formattedArray.push(fields[n]);
- }
- }
- formattedAddress = formattedArray.join(", ");
- console.log(contact.name.formatted + " formatted looks like " + formattedAddress);
- contact.addresses[index] = new ContactAddress(false,
- address.name,
- formattedAddress,
- address.street,
- address.city,
- address.region,
- address.postalCode,
- address.country);
- }
-
- }
-
- // convert ims to ContactField
- if (contact.ims && contact.ims.length) {
- // MS ContactInstantMessageField has : displayText, launchUri, service, userName, category, type
- contact.ims[0].pref = true;
- for (index = 0; index < contact.ims.length; index++) {
- contact.ims[index] = new ContactField(contact.ims[index].type, contact.ims[index].value, false);
- }
- }
-
- contactResults.push(contact);
-
- }
// send em back
- win(contactResults);
+ win(convertToContact(con));
+
+ }
- });
+ picker.pickSingleContactAsync().done(success, fail);
}
},
@@ -141,7 +108,13 @@ module.exports = {
fail && setTimeout(function () {
fail(new Error("Contact create/save not supported on Windows 8"));
}, 0);
+ },
+ search: function(win, fail, args) {
+ console && console.error && console.error("Error : Windows 8 does not support searching contacts");
+ fail && setTimeout(function() {
+ fail(new Error("Contact search not supported on Windows 8"));
+ }, 0);
}
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/d656191c/src/wp/ContactPicker.xaml
----------------------------------------------------------------------
diff --git a/src/wp/ContactPicker.xaml b/src/wp/ContactPicker.xaml
new file mode 100644
index 0000000..4f95823
--- /dev/null
+++ b/src/wp/ContactPicker.xaml
@@ -0,0 +1,58 @@
+<!--
+ Copyright (c) Microsoft Open Technologies, Inc. Licensed under the Apache License, Version 2.0 (the "License").
+-->
+<phone:PhoneApplicationPage
+ x:Class="WPCordovaClassLib.Cordova.Commands.ContactPicker"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+ xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+ FontFamily="{StaticResource PhoneFontFamilyNormal}"
+ FontSize="{StaticResource PhoneFontSizeLarge}"
+ Foreground="{StaticResource PhoneForegroundBrush}"
+ SupportedOrientations="Portrait" Orientation="Portrait"
+ shell:SystemTray.IsVisible="False">
+
+ <Grid x:Name="ContentRoot" Background="Transparent">
+
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"></RowDefinition>
+ <RowDefinition Height="*"></RowDefinition>
+ </Grid.RowDefinitions>
+
+ <TextBlock Name="HeaderBlock"
+ Text="CHOOSE A CONTACT"
+ Margin="20,50,0,50"
+ FontSize="{StaticResource PhoneFontSizeMedium}"
+ FontFamily="{StaticResource PhoneFontFamilySemiBold}"
+ Grid.Row="0"
+ />
+
+ <TextBlock Name="NoContactsBlock"
+ Margin="20,0,0,0"
+ Text="Sorry, we couldn't find any contacts."
+ TextWrapping="Wrap"
+ FontSize="{StaticResource PhoneFontSizeLarge}"
+ FontFamily="{StaticResource PhoneFontFamilyLight}"
+ Visibility="Collapsed"
+ Grid.Row="1"
+ />
+
+ <phone:LongListSelector Name="lstContacts"
+ SelectionChanged="ContactsListSelectionChanged"
+ Grid.Row="1">
+ <phone:LongListSelector.ItemTemplate>
+ <DataTemplate>
+ <StackPanel Orientation="Horizontal" Margin="16,0,0,0">
+ <StackPanel Margin="0,20,0,10">
+ <TextBlock Text="{Binding DisplayName}"
+ TextWrapping="Wrap"
+ Style="{StaticResource PhoneTextExtraLargeStyle}" />
+ </StackPanel>
+ </StackPanel>
+ </DataTemplate>
+ </phone:LongListSelector.ItemTemplate>
+ </phone:LongListSelector>
+
+ </Grid>
+</phone:PhoneApplicationPage>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/d656191c/src/wp/ContactPicker.xaml.cs
----------------------------------------------------------------------
diff --git a/src/wp/ContactPicker.xaml.cs b/src/wp/ContactPicker.xaml.cs
new file mode 100644
index 0000000..90484fd
--- /dev/null
+++ b/src/wp/ContactPicker.xaml.cs
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) Microsoft Open Technologies, Inc. Licensed under the Apache License, Version 2.0 (the "License").
+ */
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+ using System;
+ using System.Linq;
+ using System.Windows;
+ using System.Windows.Controls;
+ using System.Windows.Navigation;
+ using Microsoft.Phone.Tasks;
+ using Microsoft.Phone.UserData;
+ using DeviceContacts = Microsoft.Phone.UserData.Contacts;
+
+ /// <summary>
+ /// Custom implemented class for picking single contact
+ /// </summary>
+ public partial class ContactPicker
+ {
+ #region Fields
+
+ /// <summary>
+ /// Result of ContactPicker call, represent contact returned.
+ /// </summary>
+ private ContactPickerTask.PickResult result;
+
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ContactPicker"/> class.
+ /// </summary>
+ public ContactPicker()
+ {
+ InitializeComponent();
+ var cons = new DeviceContacts();
+ cons.SearchCompleted += this.OnSearchCompleted;
+ cons.SearchAsync(string.Empty, FilterKind.None, string.Empty);
+ }
+
+ #endregion
+
+ #region Callbacks
+
+ /// <summary>
+ /// Occurs when contact is selected or pick operation cancelled.
+ /// </summary>
+ public event EventHandler<ContactPickerTask.PickResult> Completed;
+
+ #endregion
+
+ /// <summary>
+ /// The on navigated from.
+ /// </summary>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ protected override void OnNavigatedFrom(NavigationEventArgs e)
+ {
+ if (this.result == null)
+ {
+ this.Completed(this, new ContactPickerTask.PickResult(TaskResult.Cancel));
+ }
+
+ base.OnNavigatedFrom(e);
+ }
+
+ /// <summary>
+ /// Called when contacts retrieval completed.
+ /// </summary>
+ /// <param name="sender">The sender.</param>
+ /// <param name="e">The <see cref="ContactsSearchEventArgs"/> instance containing the event data.</param>
+ private void OnSearchCompleted(object sender, ContactsSearchEventArgs e)
+ {
+ if (e.Results.Count() != 0)
+ {
+ lstContacts.ItemsSource = e.Results.ToList();
+ lstContacts.Visibility = Visibility.Visible;
+ NoContactsBlock.Visibility = Visibility.Collapsed;
+ }
+ else
+ {
+ lstContacts.Visibility = Visibility.Collapsed;
+ NoContactsBlock.Visibility = Visibility.Visible;
+ }
+ }
+
+ /// <summary>
+ /// Called when any contact is selected.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void ContactsListSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ this.result = new ContactPickerTask.PickResult(TaskResult.OK) { Contact = e.AddedItems[0] as Contact };
+ this.Completed(this, this.result);
+
+ if (NavigationService.CanGoBack)
+ {
+ NavigationService.GoBack();
+ }
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/d656191c/src/wp/ContactPickerTask.cs
----------------------------------------------------------------------
diff --git a/src/wp/ContactPickerTask.cs b/src/wp/ContactPickerTask.cs
new file mode 100644
index 0000000..1228cc0
--- /dev/null
+++ b/src/wp/ContactPickerTask.cs
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) Microsoft Open Technologies, Inc. Licensed under the Apache License, Version 2.0 (the "License").
+ */
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+ using System;
+ using System.Windows;
+ using Microsoft.Phone.Controls;
+ using Microsoft.Phone.Tasks;
+ using Microsoft.Phone.UserData;
+
+ /// <summary>
+ /// Allows an application to pick contact.
+ /// Use this to allow users to pick contact from your application.
+ /// </summary>
+ public class ContactPickerTask
+ {
+ /// <summary>
+ /// Occurs when a Pick task is completed.
+ /// </summary>
+ public event EventHandler<PickResult> Completed;
+
+ /// <summary>
+ /// Shows Contact pick application
+ /// </summary>
+ public void Show()
+ {
+ Deployment.Current.Dispatcher.BeginInvoke(() =>
+ {
+ var root = Application.Current.RootVisual as PhoneApplicationFrame;
+
+ string baseUrl = "/";
+
+ if (root != null)
+ {
+ root.Navigated += this.OnNavigate;
+
+ // dummy parameter is used to always open a fresh version
+ root.Navigate(
+ new Uri(
+ baseUrl + "Plugins/org.apache.cordova.contacts/ContactPicker.xaml?dummy="
+ + Guid.NewGuid(),
+ UriKind.Relative));
+ }
+ });
+ }
+
+ /// <summary>
+ /// Performs additional configuration of the picker application.
+ /// </summary>
+ /// <param name="sender">The source of the event.</param>
+ /// <param name="e">The <see cref="System.Windows.Navigation.NavigationEventArgs"/> instance containing the event data.</param>
+ private void OnNavigate(object sender, System.Windows.Navigation.NavigationEventArgs e)
+ {
+ if (!(e.Content is ContactPicker))
+ {
+ return;
+ }
+
+ var phoneApplicationFrame = Application.Current.RootVisual as PhoneApplicationFrame;
+ if (phoneApplicationFrame != null)
+ {
+ phoneApplicationFrame.Navigated -= this.OnNavigate;
+ }
+
+ ContactPicker contactPicker = (ContactPicker)e.Content;
+
+ if (contactPicker != null)
+ {
+ contactPicker.Completed += this.Completed;
+ }
+ else if (this.Completed != null)
+ {
+ this.Completed(this, new PickResult(TaskResult.Cancel));
+ }
+ }
+
+ /// <summary>
+ /// Represents contact returned
+ /// </summary>
+ public class PickResult : TaskEventArgs
+ {
+ /// <summary>
+ /// Initializes a new instance of the PickResult class.
+ /// </summary>
+ public PickResult()
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the PickResult class
+ /// with the specified Microsoft.Phone.Tasks.TaskResult.
+ /// </summary>
+ /// <param name="taskResult">Associated Microsoft.Phone.Tasks.TaskResult</param>
+ public PickResult(TaskResult taskResult)
+ : base(taskResult)
+ {
+ }
+
+ /// <summary>
+ /// Gets the contact.
+ /// </summary>
+ public Contact Contact { get; internal set; }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts/blob/d656191c/src/wp/Contacts.cs
----------------------------------------------------------------------
diff --git a/src/wp/Contacts.cs b/src/wp/Contacts.cs
index 5ae2144..0059c4e 100644
--- a/src/wp/Contacts.cs
+++ b/src/wp/Contacts.cs
@@ -10,12 +10,13 @@
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.
+
+ Copyright (c) Microsoft Open Technologies, Inc.
*/
using Microsoft.Phone.Tasks;
using Microsoft.Phone.UserData;
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
@@ -32,8 +33,12 @@ namespace WPCordovaClassLib.Cordova.Commands
{
[DataMember]
public string filter { get; set; }
+
[DataMember]
public bool multiple { get; set; }
+
+ [DataMember]
+ public string[] desiredFields { get; set; }
}
[DataContract]
@@ -41,6 +46,7 @@ namespace WPCordovaClassLib.Cordova.Commands
{
[DataMember]
public string[] fields { get; set; }
+
[DataMember]
public SearchOptions options { get; set; }
}
@@ -50,18 +56,25 @@ namespace WPCordovaClassLib.Cordova.Commands
{
[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; }
}
@@ -71,14 +84,19 @@ namespace WPCordovaClassLib.Cordova.Commands
{
[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; }
}
@@ -88,8 +106,10 @@ namespace WPCordovaClassLib.Cordova.Commands
{
[DataMember]
public string type { get; set; }
+
[DataMember]
public string value { get; set; }
+
[DataMember]
public bool pref { get; set; }
}
@@ -99,12 +119,16 @@ namespace WPCordovaClassLib.Cordova.Commands
{
[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; }
}
@@ -114,12 +138,16 @@ namespace WPCordovaClassLib.Cordova.Commands
{
[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; }
@@ -154,7 +182,6 @@ namespace WPCordovaClassLib.Cordova.Commands
public class Contacts : BaseCommand
{
-
public const int UNKNOWN_ERROR = 0;
public const int INVALID_ARGUMENT_ERROR = 1;
public const int TIMEOUT_ERROR = 2;
@@ -164,15 +191,9 @@ namespace WPCordovaClassLib.Cordova.Commands
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);
@@ -195,6 +216,7 @@ namespace WPCordovaClassLib.Cordova.Commands
}
#region contact.name
+
if (contact.name != null)
{
if (contact.name.givenName != null)
@@ -208,17 +230,21 @@ namespace WPCordovaClassLib.Cordova.Commands
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)
@@ -238,15 +264,15 @@ namespace WPCordovaClassLib.Cordova.Commands
}
}
}
+
#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 (string type in new[] {"personal", "work", "other"})
{
foreach (JSONContactField field in contact.emails)
{
@@ -279,9 +305,9 @@ namespace WPCordovaClassLib.Cordova.Commands
contactTask.OtherEmail = field.value;
}
}
-
}
}
+
#endregion
if (contact.note != null && contact.note.Length > 0)
@@ -290,6 +316,7 @@ namespace WPCordovaClassLib.Cordova.Commands
}
#region contact.addresses
+
if (contact.addresses != null && contact.addresses.Length > 0)
{
foreach (JSONContactAddress address in contact.addresses)
@@ -322,31 +349,33 @@ namespace WPCordovaClassLib.Cordova.Commands
}
}
}
- #endregion
+ #endregion
- contactTask.Completed += new EventHandler<SaveContactResult>(ContactSaveTaskCompleted);
+ contactTask.Completed += ContactSaveTaskCompleted;
contactTask.Show();
}
- void ContactSaveTaskCompleted(object sender, SaveContactResult e)
+ private 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);
- });
+ {
+ var deviceContacts = new DeviceContacts();
+ deviceContacts.SearchCompleted +=
+ postAdd_SearchCompleted;
+ if (task != null)
+ {
+ 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)
{
@@ -354,18 +383,18 @@ namespace WPCordovaClassLib.Cordova.Commands
}
}
- void postAdd_SearchCompleted(object sender, ContactsSearchEventArgs e)
+ private void postAdd_SearchCompleted(object sender, ContactsSearchEventArgs e)
{
- if (e.Results.Count() > 0)
+ if (e.Results.Any())
{
- List<Contact> foundContacts = new List<Contact>();
+ 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)));
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK, newContact.ToJson(null)));
}
else
{
@@ -374,13 +403,41 @@ namespace WPCordovaClassLib.Cordova.Commands
}
-
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 pickContact(string arguments)
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(arguments);
+
+ // Use custom contact picker because WP8 api doesn't provide its' own
+ // contact picker, only PhoneNumberChooser or EmailAddressChooserTask
+ var task = new ContactPickerTask();
+ var desiredFields = JSON.JsonHelper.Deserialize<string[]>(args[0]);
+
+ task.Completed += delegate(Object sender, ContactPickerTask.PickResult e)
+ {
+ if (e.TaskResult == TaskResult.OK)
+ {
+ string strResult = e.Contact.ToJson(desiredFields);
+ var result = new PluginResult(PluginResult.Status.OK)
+ {
+ Message = strResult
+ };
+ DispatchCommandResult(result);
+ }
+ if (e.TaskResult == TaskResult.Cancel)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Operation cancelled."));
+ }
+ };
+
+ task.Show();
+ }
+
public void search(string searchCriteria)
{
string[] args = JSON.JsonHelper.Deserialize<string[]>(searchCriteria);
@@ -409,7 +466,7 @@ namespace WPCordovaClassLib.Cordova.Commands
}
DeviceContacts deviceContacts = new DeviceContacts();
- deviceContacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(contacts_SearchCompleted);
+ deviceContacts.SearchCompleted += contacts_SearchCompleted;
// default is to search all fields
FilterKind filterKind = FilterKind.None;
@@ -432,7 +489,6 @@ namespace WPCordovaClassLib.Cordova.Commands
try
{
-
deviceContacts.SearchAsync(searchParams.options.filter, filterKind, searchParams);
}
catch (Exception ex)
@@ -443,7 +499,7 @@ namespace WPCordovaClassLib.Cordova.Commands
private void contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
{
- ContactSearchParams searchParams = (ContactSearchParams)e.State;
+ var searchParams = (ContactSearchParams) e.State;
List<Contact> foundContacts = null;
// used for comparing strings, "" instantiates with InvariantCulture
@@ -460,13 +516,17 @@ namespace WPCordovaClassLib.Cordova.Commands
{
foundContacts.AddRange(from Contact con in e.Results
from ContactEmailAddress a in con.EmailAddresses
- where culture.CompareInfo.IndexOf(a.EmailAddress, searchParams.options.filter, compare_option) >= 0
+ where
+ culture.CompareInfo.IndexOf(a.EmailAddress, searchParams.options.filter,
+ compare_option) >= 0
select con);
}
if (searchParams.fields.Contains("displayName"))
{
foundContacts.AddRange(from Contact con in e.Results
- where culture.CompareInfo.IndexOf(con.DisplayName, searchParams.options.filter, compare_option) >= 0
+ where
+ culture.CompareInfo.IndexOf(con.DisplayName, searchParams.options.filter,
+ compare_option) >= 0
select con);
}
if (searchParams.fields.Contains("name"))
@@ -488,14 +548,18 @@ namespace WPCordovaClassLib.Cordova.Commands
{
foundContacts.AddRange(from Contact con in e.Results
from ContactPhoneNumber a in con.PhoneNumbers
- where culture.CompareInfo.IndexOf(a.PhoneNumber, searchParams.options.filter, compare_option) >= 0
+ where
+ culture.CompareInfo.IndexOf(a.PhoneNumber, searchParams.options.filter,
+ compare_option) >= 0
select con);
}
if (searchParams.fields.Contains("urls"))
{
foundContacts.AddRange(from Contact con in e.Results
from string a in con.Websites
- where culture.CompareInfo.IndexOf(a, searchParams.options.filter, compare_option) >= 0
+ where
+ culture.CompareInfo.IndexOf(a, searchParams.options.filter,
+ compare_option) >= 0
select con);
}
}
@@ -504,16 +568,14 @@ namespace WPCordovaClassLib.Cordova.Commands
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));
+ strResult += contact.ToJson(searchParams.options.desiredFields) + ",";
+
if (!searchParams.options.multiple)
{
break; // just return the first item
@@ -522,172 +584,6 @@ namespace WPCordovaClassLib.Cordova.Commands
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(),
- EscapeJson(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 = EscapeJson(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,
- EscapeJson(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 += "\"" + EscapeJson(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,
- EscapeJson(con.CompleteName.FirstName + " " + con.CompleteName.LastName), // TODO: does this need suffix? middlename?
- EscapeJson(con.CompleteName.LastName),
- EscapeJson(con.CompleteName.FirstName),
- EscapeJson(con.CompleteName.MiddleName),
- EscapeJson(con.CompleteName.Title),
- EscapeJson(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(),
- EscapeJson(con.DisplayName),
- EscapeJson(con.CompleteName != null ? con.CompleteName.Nickname : ""),
- FormatJSONPhoneNumbers(con),
- FormatJSONEmails(con),
- FormatJSONAddresses(con),
- FormatJSONWebsites(con),
- FormatJSONName(con),
- EscapeJson(con.Notes.FirstOrDefault()),
- EscapeJson(Convert.ToString(con.Birthdays.FirstOrDefault())));
-
- return "{" + jsonContact + "}";
-
- }
-
-
- private static string EscapeJson(string str)
- {
- if (String.IsNullOrEmpty(str))
- {
- return str;
- }
-
- return str.Replace("\n", "\\n").Replace("\r", "\\r").Replace("\t", "\\t").Replace("\"", "\\\"").Replace("&", "\\&");
}
}
-}
+}
\ No newline at end of file