Return-Path: X-Original-To: apmail-cordova-commits-archive@www.apache.org Delivered-To: apmail-cordova-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 7B0411169D for ; Fri, 2 May 2014 18:32:26 +0000 (UTC) Received: (qmail 47633 invoked by uid 500); 2 May 2014 18:31:21 -0000 Delivered-To: apmail-cordova-commits-archive@cordova.apache.org Received: (qmail 46932 invoked by uid 500); 2 May 2014 18:30:57 -0000 Mailing-List: contact commits-help@cordova.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cordova.apache.org Delivered-To: mailing list commits@cordova.apache.org Received: (qmail 45076 invoked by uid 99); 2 May 2014 18:30:09 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 02 May 2014 18:30:09 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id ECE8B45EFC; Fri, 2 May 2014 18:30:07 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: kamrik@apache.org To: commits@cordova.apache.org Date: Fri, 02 May 2014 18:30:24 -0000 Message-Id: <5921efb75cab4a1a8b49d2c38f7d43fd@git.apache.org> In-Reply-To: <050c3a3a1ef946b884993d2093241785@git.apache.org> References: <050c3a3a1ef946b884993d2093241785@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [18/70] Split out cordova-lib: move cordova-plugman files http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/spec/plugins/Contacts/src/ios/CDVContact.m ---------------------------------------------------------------------- diff --git a/spec/plugins/Contacts/src/ios/CDVContact.m b/spec/plugins/Contacts/src/ios/CDVContact.m deleted file mode 100644 index 82704ea..0000000 --- a/spec/plugins/Contacts/src/ios/CDVContact.m +++ /dev/null @@ -1,1752 +0,0 @@ -/* - 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. - */ - -#import "CDVContact.h" -#import - -#define DATE_OR_NULL(dateObj) ((aDate != nil) ? (id)([aDate descriptionWithLocale:[NSLocale currentLocale]]) : (id)([NSNull null])) -#define IS_VALID_VALUE(value) ((value != nil) && (![value isKindOfClass:[NSNull class]])) - -static NSDictionary* org_apache_cordova_contacts_W3CtoAB = nil; -static NSDictionary* org_apache_cordova_contacts_ABtoW3C = nil; -static NSSet* org_apache_cordova_contacts_W3CtoNull = nil; -static NSDictionary* org_apache_cordova_contacts_objectAndProperties = nil; -static NSDictionary* org_apache_cordova_contacts_defaultFields = nil; - -@implementation CDVContact : NSObject - - @synthesize returnFields; - -- (id)init -{ - if ((self = [super init]) != nil) { - ABRecordRef rec = ABPersonCreate(); - self.record = rec; - if (rec) { - CFRelease(rec); - } - } - return self; -} - -- (id)initFromABRecord:(ABRecordRef)aRecord -{ - if ((self = [super init]) != nil) { - self.record = aRecord; - } - return self; -} - -/* synthesize 'record' ourselves to have retain properties for CF types */ - -- (void)setRecord:(ABRecordRef)aRecord -{ - if (record != NULL) { - CFRelease(record); - } - if (aRecord != NULL) { - record = CFRetain(aRecord); - } -} - -- (ABRecordRef)record -{ - return record; -} - -/* Rather than creating getters and setters for each AddressBook (AB) Property, generic methods are used to deal with - * simple properties, MultiValue properties( phone numbers and emails) and MultiValueDictionary properties (Ims and addresses). - * The dictionaries below are used to translate between the W3C identifiers and the AB properties. Using the dictionaries, - * allows looping through sets of properties to extract from or set into the W3C dictionary to/from the ABRecord. - */ - -/* The two following dictionaries translate between W3C properties and AB properties. It currently mixes both - * Properties (kABPersonAddressProperty for example) and Strings (kABPersonAddressStreetKey) so users should be aware of - * what types of values are expected. - * a bit. -*/ -+ (NSDictionary*)defaultABtoW3C -{ - if (org_apache_cordova_contacts_ABtoW3C == nil) { - org_apache_cordova_contacts_ABtoW3C = [NSDictionary dictionaryWithObjectsAndKeys: - kW3ContactNickname, [NSNumber numberWithInt:kABPersonNicknameProperty], - kW3ContactGivenName, [NSNumber numberWithInt:kABPersonFirstNameProperty], - kW3ContactFamilyName, [NSNumber numberWithInt:kABPersonLastNameProperty], - kW3ContactMiddleName, [NSNumber numberWithInt:kABPersonMiddleNameProperty], - kW3ContactHonorificPrefix, [NSNumber numberWithInt:kABPersonPrefixProperty], - kW3ContactHonorificSuffix, [NSNumber numberWithInt:kABPersonSuffixProperty], - kW3ContactPhoneNumbers, [NSNumber numberWithInt:kABPersonPhoneProperty], - kW3ContactAddresses, [NSNumber numberWithInt:kABPersonAddressProperty], - kW3ContactStreetAddress, kABPersonAddressStreetKey, - kW3ContactLocality, kABPersonAddressCityKey, - kW3ContactRegion, kABPersonAddressStateKey, - kW3ContactPostalCode, kABPersonAddressZIPKey, - kW3ContactCountry, kABPersonAddressCountryKey, - kW3ContactEmails, [NSNumber numberWithInt:kABPersonEmailProperty], - kW3ContactIms, [NSNumber numberWithInt:kABPersonInstantMessageProperty], - kW3ContactOrganizations, [NSNumber numberWithInt:kABPersonOrganizationProperty], - kW3ContactOrganizationName, [NSNumber numberWithInt:kABPersonOrganizationProperty], - kW3ContactTitle, [NSNumber numberWithInt:kABPersonJobTitleProperty], - kW3ContactDepartment, [NSNumber numberWithInt:kABPersonDepartmentProperty], - kW3ContactBirthday, [NSNumber numberWithInt:kABPersonBirthdayProperty], - kW3ContactUrls, [NSNumber numberWithInt:kABPersonURLProperty], - kW3ContactNote, [NSNumber numberWithInt:kABPersonNoteProperty], - nil]; - } - - return org_apache_cordova_contacts_ABtoW3C; -} - -+ (NSDictionary*)defaultW3CtoAB -{ - if (org_apache_cordova_contacts_W3CtoAB == nil) { - org_apache_cordova_contacts_W3CtoAB = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInt:kABPersonNicknameProperty], kW3ContactNickname, - [NSNumber numberWithInt:kABPersonFirstNameProperty], kW3ContactGivenName, - [NSNumber numberWithInt:kABPersonLastNameProperty], kW3ContactFamilyName, - [NSNumber numberWithInt:kABPersonMiddleNameProperty], kW3ContactMiddleName, - [NSNumber numberWithInt:kABPersonPrefixProperty], kW3ContactHonorificPrefix, - [NSNumber numberWithInt:kABPersonSuffixProperty], kW3ContactHonorificSuffix, - [NSNumber numberWithInt:kABPersonPhoneProperty], kW3ContactPhoneNumbers, - [NSNumber numberWithInt:kABPersonAddressProperty], kW3ContactAddresses, - kABPersonAddressStreetKey, kW3ContactStreetAddress, - kABPersonAddressCityKey, kW3ContactLocality, - kABPersonAddressStateKey, kW3ContactRegion, - kABPersonAddressZIPKey, kW3ContactPostalCode, - kABPersonAddressCountryKey, kW3ContactCountry, - [NSNumber numberWithInt:kABPersonEmailProperty], kW3ContactEmails, - [NSNumber numberWithInt:kABPersonInstantMessageProperty], kW3ContactIms, - [NSNumber numberWithInt:kABPersonOrganizationProperty], kW3ContactOrganizations, - [NSNumber numberWithInt:kABPersonJobTitleProperty], kW3ContactTitle, - [NSNumber numberWithInt:kABPersonDepartmentProperty], kW3ContactDepartment, - [NSNumber numberWithInt:kABPersonBirthdayProperty], kW3ContactBirthday, - [NSNumber numberWithInt:kABPersonNoteProperty], kW3ContactNote, - [NSNumber numberWithInt:kABPersonURLProperty], kW3ContactUrls, - kABPersonInstantMessageUsernameKey, kW3ContactImValue, - kABPersonInstantMessageServiceKey, kW3ContactImType, - [NSNull null], kW3ContactFieldType, /* include entries in dictionary to indicate ContactField properties */ - [NSNull null], kW3ContactFieldValue, - [NSNull null], kW3ContactFieldPrimary, - [NSNull null], kW3ContactFieldId, - [NSNumber numberWithInt:kABPersonOrganizationProperty], kW3ContactOrganizationName, /* careful, name is used multiple times*/ - nil]; - } - return org_apache_cordova_contacts_W3CtoAB; -} - -+ (NSSet*)defaultW3CtoNull -{ - // these are values that have no AddressBook Equivalent OR have not been implemented yet - if (org_apache_cordova_contacts_W3CtoNull == nil) { - org_apache_cordova_contacts_W3CtoNull = [NSSet setWithObjects:kW3ContactDisplayName, - kW3ContactCategories, kW3ContactFormattedName, nil]; - } - return org_apache_cordova_contacts_W3CtoNull; -} - -/* - * The objectAndProperties dictionary contains the all of the properties of the W3C Contact Objects specified by the key - * Used in calcReturnFields, and various extract methods - */ -+ (NSDictionary*)defaultObjectAndProperties -{ - if (org_apache_cordova_contacts_objectAndProperties == nil) { - org_apache_cordova_contacts_objectAndProperties = [NSDictionary dictionaryWithObjectsAndKeys: - [NSArray arrayWithObjects:kW3ContactGivenName, kW3ContactFamilyName, - kW3ContactMiddleName, kW3ContactHonorificPrefix, kW3ContactHonorificSuffix, kW3ContactFormattedName, nil], kW3ContactName, - [NSArray arrayWithObjects:kW3ContactStreetAddress, kW3ContactLocality, kW3ContactRegion, - kW3ContactPostalCode, kW3ContactCountry, /*kW3ContactAddressFormatted,*/ nil], kW3ContactAddresses, - [NSArray arrayWithObjects:kW3ContactOrganizationName, kW3ContactTitle, kW3ContactDepartment, nil], kW3ContactOrganizations, - [NSArray arrayWithObjects:kW3ContactFieldType, kW3ContactFieldValue, kW3ContactFieldPrimary, nil], kW3ContactPhoneNumbers, - [NSArray arrayWithObjects:kW3ContactFieldType, kW3ContactFieldValue, kW3ContactFieldPrimary, nil], kW3ContactEmails, - [NSArray arrayWithObjects:kW3ContactFieldType, kW3ContactFieldValue, kW3ContactFieldPrimary, nil], kW3ContactPhotos, - [NSArray arrayWithObjects:kW3ContactFieldType, kW3ContactFieldValue, kW3ContactFieldPrimary, nil], kW3ContactUrls, - [NSArray arrayWithObjects:kW3ContactImValue, kW3ContactImType, nil], kW3ContactIms, - nil]; - } - return org_apache_cordova_contacts_objectAndProperties; -} - -+ (NSDictionary*)defaultFields -{ - if (org_apache_cordova_contacts_defaultFields == nil) { - org_apache_cordova_contacts_defaultFields = [NSDictionary dictionaryWithObjectsAndKeys: - [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactName], kW3ContactName, - [NSNull null], kW3ContactNickname, - [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactAddresses], kW3ContactAddresses, - [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactOrganizations], kW3ContactOrganizations, - [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactPhoneNumbers], kW3ContactPhoneNumbers, - [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactEmails], kW3ContactEmails, - [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactIms], kW3ContactIms, - [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactPhotos], kW3ContactPhotos, - [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactUrls], kW3ContactUrls, - [NSNull null], kW3ContactBirthday, - [NSNull null], kW3ContactNote, - nil]; - } - return org_apache_cordova_contacts_defaultFields; -} - -/* Translate W3C Contact data into ABRecordRef - * - * New contact information comes in as a NSMutableDictionary. All Null entries in Contact object are set - * as [NSNull null] in the dictionary when translating from the JSON input string of Contact data. However, if - * user did not set a value within a Contact object or sub-object (by not using the object constructor) some data - * may not exist. - * bUpdate = YES indicates this is a save of an existing record - */ -- (bool)setFromContactDict:(NSDictionary*)aContact asUpdate:(BOOL)bUpdate -{ - if (![aContact isKindOfClass:[NSDictionary class]]) { - return FALSE; // can't do anything if no dictionary! - } - - ABRecordRef person = self.record; - bool bSuccess = TRUE; - CFErrorRef error; - - // set name info - // iOS doesn't have displayName - might have to pull parts from it to create name - bool bName = false; - NSDictionary* dict = [aContact valueForKey:kW3ContactName]; - if ([dict isKindOfClass:[NSDictionary class]]) { - bName = true; - NSArray* propArray = [[CDVContact defaultObjectAndProperties] objectForKey:kW3ContactName]; - - for (id i in propArray) { - if (![(NSString*)i isEqualToString : kW3ContactFormattedName]) { // kW3ContactFormattedName is generated from ABRecordCopyCompositeName() and can't be set - [self setValue:[dict valueForKey:i] forProperty:(ABPropertyID)[(NSNumber*)[[CDVContact defaultW3CtoAB] objectForKey:i] intValue] - inRecord:person asUpdate:bUpdate]; - } - } - } - - id nn = [aContact valueForKey:kW3ContactNickname]; - if (![nn isKindOfClass:[NSNull class]]) { - bName = true; - [self setValue:nn forProperty:kABPersonNicknameProperty inRecord:person asUpdate:bUpdate]; - } - if (!bName) { - // if no name or nickname - try and use displayName as W3Contact must have displayName or ContactName - [self setValue:[aContact valueForKey:kW3ContactDisplayName] forProperty:kABPersonNicknameProperty - inRecord:person asUpdate:bUpdate]; - } - - // set phoneNumbers - // NSLog(@"setting phoneNumbers"); - NSArray* array = [aContact valueForKey:kW3ContactPhoneNumbers]; - if ([array isKindOfClass:[NSArray class]]) { - [self setMultiValueStrings:array forProperty:kABPersonPhoneProperty inRecord:person asUpdate:bUpdate]; - } - // set Emails - // NSLog(@"setting emails"); - array = [aContact valueForKey:kW3ContactEmails]; - if ([array isKindOfClass:[NSArray class]]) { - [self setMultiValueStrings:array forProperty:kABPersonEmailProperty inRecord:person asUpdate:bUpdate]; - } - // set Urls - // NSLog(@"setting urls"); - array = [aContact valueForKey:kW3ContactUrls]; - if ([array isKindOfClass:[NSArray class]]) { - [self setMultiValueStrings:array forProperty:kABPersonURLProperty inRecord:person asUpdate:bUpdate]; - } - - // set multivalue dictionary properties - // set addresses: streetAddress, locality, region, postalCode, country - // set ims: value = username, type = servicetype - // iOS addresses and im are a MultiValue Properties with label, value=dictionary of info, and id - // NSLog(@"setting addresses"); - error = nil; - array = [aContact valueForKey:kW3ContactAddresses]; - if ([array isKindOfClass:[NSArray class]]) { - [self setMultiValueDictionary:array forProperty:kABPersonAddressProperty inRecord:person asUpdate:bUpdate]; - } - // ims - // NSLog(@"setting ims"); - array = [aContact valueForKey:kW3ContactIms]; - if ([array isKindOfClass:[NSArray class]]) { - [self setMultiValueDictionary:array forProperty:kABPersonInstantMessageProperty inRecord:person asUpdate:bUpdate]; - } - - // organizations - // W3C ContactOrganization has pref, type, name, title, department - // iOS only supports name, title, department - // NSLog(@"setting organizations"); - // TODO this may need work - should Organization information be removed when array is empty?? - array = [aContact valueForKey:kW3ContactOrganizations]; // iOS only supports one organization - use first one - if ([array isKindOfClass:[NSArray class]]) { - BOOL bRemove = NO; - NSDictionary* dict = nil; - if ([array count] > 0) { - dict = [array objectAtIndex:0]; - } else { - // remove the organization info entirely - bRemove = YES; - } - if ([dict isKindOfClass:[NSDictionary class]] || (bRemove == YES)) { - [self setValue:(bRemove ? @"" : [dict valueForKey:@"name"]) forProperty:kABPersonOrganizationProperty inRecord:person asUpdate:bUpdate]; - [self setValue:(bRemove ? @"" : [dict valueForKey:kW3ContactTitle]) forProperty:kABPersonJobTitleProperty inRecord:person asUpdate:bUpdate]; - [self setValue:(bRemove ? @"" : [dict valueForKey:kW3ContactDepartment]) forProperty:kABPersonDepartmentProperty inRecord:person asUpdate:bUpdate]; - } - } - // add dates - // Dates come in as milliseconds in NSNumber Object - id ms = [aContact valueForKey:kW3ContactBirthday]; - NSDate* aDate = nil; - if (ms && [ms isKindOfClass:[NSNumber class]]) { - double msValue = [ms doubleValue]; - msValue = msValue / 1000; - aDate = [NSDate dateWithTimeIntervalSince1970:msValue]; - } - if ((aDate != nil) || [ms isKindOfClass:[NSString class]]) { - [self setValue:aDate != nil ? aDate:ms forProperty:kABPersonBirthdayProperty inRecord:person asUpdate:bUpdate]; - } - // don't update creation date - // modification date will get updated when save - // anniversary is removed from W3C Contact api Dec 9, 2010 spec - don't waste time on it yet - - // kABPersonDateProperty - - // kABPersonAnniversaryLabel - - // iOS doesn't have gender - ignore - // note - [self setValue:[aContact valueForKey:kW3ContactNote] forProperty:kABPersonNoteProperty inRecord:person asUpdate:bUpdate]; - - // iOS doesn't have preferredName- ignore - - // photo - array = [aContact valueForKey:kW3ContactPhotos]; - if ([array isKindOfClass:[NSArray class]]) { - if (bUpdate && ([array count] == 0)) { - // remove photo - bSuccess = ABPersonRemoveImageData(person, &error); - } else if ([array count] > 0) { - NSDictionary* dict = [array objectAtIndex:0]; // currently only support one photo - if ([dict isKindOfClass:[NSDictionary class]]) { - id value = [dict objectForKey:kW3ContactFieldValue]; - if ([value isKindOfClass:[NSString class]]) { - if (bUpdate && ([value length] == 0)) { - // remove the current image - bSuccess = ABPersonRemoveImageData(person, &error); - } else { - // use this image - // don't know if string is encoded or not so first unencode it then encode it again - NSString* cleanPath = [value stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - NSURL* photoUrl = [NSURL URLWithString:[cleanPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; - // caller is responsible for checking for a connection, if no connection this will fail - NSError* err = nil; - NSData* data = nil; - if (photoUrl) { - data = [NSData dataWithContentsOfURL:photoUrl options:NSDataReadingUncached error:&err]; - } - if (data && ([data length] > 0)) { - bSuccess = ABPersonSetImageData(person, (__bridge CFDataRef)data, &error); - } - if (!data || !bSuccess) { - NSLog(@"error setting contact image: %@", (err != nil ? [err localizedDescription] : @"")); - } - } - } - } - } - } - - // TODO WebURLs - - // TODO timezone - - return bSuccess; -} - -/* Set item into an AddressBook Record for the specified property. - * aValue - the value to set into the address book (code checks for null or [NSNull null] - * aProperty - AddressBook property ID - * aRecord - the record to update - * bUpdate - whether this is a possible update vs a new entry - * RETURN - * true - property was set (or input value as null) - * false - property was not set - */ -- (bool)setValue:(id)aValue forProperty:(ABPropertyID)aProperty inRecord:(ABRecordRef)aRecord asUpdate:(BOOL)bUpdate -{ - bool bSuccess = true; // if property was null, just ignore and return success - CFErrorRef error; - - if (aValue && ![aValue isKindOfClass:[NSNull class]]) { - if (bUpdate && ([aValue isKindOfClass:[NSString class]] && ([aValue length] == 0))) { // if updating, empty string means to delete - aValue = NULL; - } // really only need to set if different - more efficient to just update value or compare and only set if necessary??? - bSuccess = ABRecordSetValue(aRecord, aProperty, (__bridge CFTypeRef)aValue, &error); - if (!bSuccess) { - NSLog(@"error setting %d property", aProperty); - } - } - - return bSuccess; -} - -- (bool)removeProperty:(ABPropertyID)aProperty inRecord:(ABRecordRef)aRecord -{ - CFErrorRef err; - bool bSuccess = ABRecordRemoveValue(aRecord, aProperty, &err); - - if (!bSuccess) { - CFStringRef errDescription = CFErrorCopyDescription(err); - NSLog(@"Unable to remove property %d: %@", aProperty, errDescription); - CFRelease(errDescription); - } - return bSuccess; -} - -- (bool)addToMultiValue:(ABMultiValueRef)multi fromDictionary:dict -{ - bool bSuccess = FALSE; - id value = [dict valueForKey:kW3ContactFieldValue]; - - if (IS_VALID_VALUE(value)) { - CFStringRef label = [CDVContact convertContactTypeToPropertyLabel:[dict valueForKey:kW3ContactFieldType]]; - bSuccess = ABMultiValueAddValueAndLabel(multi, (__bridge CFTypeRef)value, label, NULL); - if (!bSuccess) { - NSLog(@"Error setting Value: %@ and label: %@", value, label); - } - } - return bSuccess; -} - -- (ABMultiValueRef)allocStringMultiValueFromArray:array -{ - ABMutableMultiValueRef multi = ABMultiValueCreateMutable(kABMultiStringPropertyType); - - for (NSDictionary* dict in array) { - [self addToMultiValue:multi fromDictionary:dict]; - } - - return multi; // caller is responsible for releasing multi -} - -- (bool)setValue:(CFTypeRef)value forProperty:(ABPropertyID)prop inRecord:(ABRecordRef)person -{ - CFErrorRef error; - bool bSuccess = ABRecordSetValue(person, prop, value, &error); - - if (!bSuccess) { - NSLog(@"Error setting value for property: %d", prop); - } - return bSuccess; -} - -/* Set MultiValue string properties into Address Book Record. - * NSArray* fieldArray - array of dictionaries containing W3C properties to be set into record - * ABPropertyID prop - the property to be set (generally used for phones and emails) - * ABRecordRef person - the record to set values into - * BOOL bUpdate - whether or not to update date or set as new. - * When updating: - * empty array indicates to remove entire property - * empty string indicates to remove - * [NSNull null] do not modify (keep existing record value) - * RETURNS - * bool false indicates error - * - * used for phones and emails - */ -- (bool)setMultiValueStrings:(NSArray*)fieldArray forProperty:(ABPropertyID)prop inRecord:(ABRecordRef)person asUpdate:(BOOL)bUpdate -{ - bool bSuccess = TRUE; - ABMutableMultiValueRef multi = nil; - - if (!bUpdate) { - multi = [self allocStringMultiValueFromArray:fieldArray]; - bSuccess = [self setValue:multi forProperty:prop inRecord:person]; - } else if (bUpdate && ([fieldArray count] == 0)) { - // remove entire property - bSuccess = [self removeProperty:prop inRecord:person]; - } else { // check for and apply changes - ABMultiValueRef copy = ABRecordCopyValue(person, prop); - if (copy != nil) { - multi = ABMultiValueCreateMutableCopy(copy); - CFRelease(copy); - - for (NSDictionary* dict in fieldArray) { - id val; - NSString* label = nil; - val = [dict valueForKey:kW3ContactFieldValue]; - label = (__bridge NSString*)[CDVContact convertContactTypeToPropertyLabel:[dict valueForKey:kW3ContactFieldType]]; - if (IS_VALID_VALUE(val)) { - // is an update, find index of entry with matching id, if values are different, update. - id idValue = [dict valueForKey:kW3ContactFieldId]; - int identifier = [idValue isKindOfClass:[NSNumber class]] ? [idValue intValue] : -1; - CFIndex i = identifier >= 0 ? ABMultiValueGetIndexForIdentifier(multi, identifier) : kCFNotFound; - if (i != kCFNotFound) { - if ([val length] == 0) { - // remove both value and label - ABMultiValueRemoveValueAndLabelAtIndex(multi, i); - } else { - NSString* valueAB = (__bridge_transfer NSString*)ABMultiValueCopyValueAtIndex(multi, i); - NSString* labelAB = (__bridge_transfer NSString*)ABMultiValueCopyLabelAtIndex(multi, i); - if ((valueAB == nil) || ![val isEqualToString:valueAB]) { - ABMultiValueReplaceValueAtIndex(multi, (__bridge CFTypeRef)val, i); - } - if ((labelAB == nil) || ![label isEqualToString:labelAB]) { - ABMultiValueReplaceLabelAtIndex(multi, (__bridge CFStringRef)label, i); - } - } - } else { - // is a new value - insert - [self addToMultiValue:multi fromDictionary:dict]; - } - } // end of if value - } // end of for - } else { // adding all new value(s) - multi = [self allocStringMultiValueFromArray:fieldArray]; - } - // set the (updated) copy as the new value - bSuccess = [self setValue:multi forProperty:prop inRecord:person]; - } - - if (multi) { - CFRelease(multi); - } - - return bSuccess; -} - -// used for ims and addresses -- (ABMultiValueRef)allocDictMultiValueFromArray:array forProperty:(ABPropertyID)prop -{ - ABMutableMultiValueRef multi = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType); - NSMutableDictionary* newDict; - NSMutableDictionary* addDict; - - for (NSDictionary* dict in array) { - newDict = [self translateW3Dict:dict forProperty:prop]; - addDict = [NSMutableDictionary dictionaryWithCapacity:2]; - if (newDict) { // create a new dictionary with a Label and Value, value is the dictionary previously created - // June, 2011 W3C Contact spec adds type into ContactAddress book - // get the type out of the original dictionary for address - NSString* addrType = (NSString*)[dict valueForKey:kW3ContactFieldType]; - if (!addrType) { - addrType = (NSString*)kABOtherLabel; - } - NSObject* typeValue = ((prop == kABPersonInstantMessageProperty) ? (NSObject*)kABOtherLabel : addrType); - // NSLog(@"typeValue: %@", typeValue); - [addDict setObject:typeValue forKey:kW3ContactFieldType]; // im labels will be set as Other and address labels as type from dictionary - [addDict setObject:newDict forKey:kW3ContactFieldValue]; - [self addToMultiValue:multi fromDictionary:addDict]; - } - } - - return multi; // caller is responsible for releasing -} - -// used for ims and addresses to convert W3 dictionary of values to AB Dictionary -// got messier when June, 2011 W3C Contact spec added type field into ContactAddress -- (NSMutableDictionary*)translateW3Dict:(NSDictionary*)dict forProperty:(ABPropertyID)prop -{ - NSArray* propArray = [[CDVContact defaultObjectAndProperties] valueForKey:[[CDVContact defaultABtoW3C] objectForKey:[NSNumber numberWithInt:prop]]]; - - NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithCapacity:1]; - id value; - - for (NSString* key in propArray) { // for each W3 Contact key get the value - if (((value = [dict valueForKey:key]) != nil) && ![value isKindOfClass:[NSNull class]]) { - // if necessary convert the W3 value to AB Property label - NSString* setValue = value; - if ([CDVContact needsConversion:key]) { // IM types must be converted - setValue = (NSString*)[CDVContact convertContactTypeToPropertyLabel:value]; - // IMs must have a valid AB value! - if ((prop == kABPersonInstantMessageProperty) && [setValue isEqualToString:(NSString*)kABOtherLabel]) { - setValue = @""; // try empty string - } - } - // set the AB value into the dictionary - [newDict setObject:setValue forKey:(NSString*)[[CDVContact defaultW3CtoAB] valueForKey:(NSString*)key]]; - } - } - - if ([newDict count] == 0) { - newDict = nil; // no items added - } - return newDict; -} - -/* set multivalue dictionary properties into an AddressBook Record - * NSArray* array - array of dictionaries containing the W3C properties to set into the record - * ABPropertyID prop - the property id for the multivalue dictionary (addresses and ims) - * ABRecordRef person - the record to set the values into - * BOOL bUpdate - YES if this is an update to an existing record - * When updating: - * empty array indicates to remove entire property - * value/label == "" indicates to remove - * value/label == [NSNull null] do not modify (keep existing record value) - * RETURN - * bool false indicates fatal error - * - * iOS addresses and im are a MultiValue Properties with label, value=dictionary of info, and id - * set addresses: streetAddress, locality, region, postalCode, country - * set ims: value = username, type = servicetype - * there are some special cases in here for ims - needs cleanup / simplification - * - */ -- (bool)setMultiValueDictionary:(NSArray*)array forProperty:(ABPropertyID)prop inRecord:(ABRecordRef)person asUpdate:(BOOL)bUpdate -{ - bool bSuccess = FALSE; - ABMutableMultiValueRef multi = nil; - - if (!bUpdate) { - multi = [self allocDictMultiValueFromArray:array forProperty:prop]; - bSuccess = [self setValue:multi forProperty:prop inRecord:person]; - } else if (bUpdate && ([array count] == 0)) { - // remove property - bSuccess = [self removeProperty:prop inRecord:person]; - } else { // check for and apply changes - ABMultiValueRef copy = ABRecordCopyValue(person, prop); - if (copy) { - multi = ABMultiValueCreateMutableCopy(copy); - CFRelease(copy); - // get the W3C values for this property - NSArray* propArray = [[CDVContact defaultObjectAndProperties] valueForKey:[[CDVContact defaultABtoW3C] objectForKey:[NSNumber numberWithInt:prop]]]; - id value; - id valueAB; - - for (NSDictionary* field in array) { - NSMutableDictionary* dict; - // find the index for the current property - id idValue = [field valueForKey:kW3ContactFieldId]; - int identifier = [idValue isKindOfClass:[NSNumber class]] ? [idValue intValue] : -1; - CFIndex idx = identifier >= 0 ? ABMultiValueGetIndexForIdentifier(multi, identifier) : kCFNotFound; - BOOL bUpdateLabel = NO; - if (idx != kCFNotFound) { - dict = [NSMutableDictionary dictionaryWithCapacity:1]; - // NSDictionary* existingDictionary = (NSDictionary*)ABMultiValueCopyValueAtIndex(multi, idx); - CFTypeRef existingDictionary = ABMultiValueCopyValueAtIndex(multi, idx); - NSString* existingABLabel = (__bridge_transfer NSString*)ABMultiValueCopyLabelAtIndex(multi, idx); - NSString* testLabel = [field valueForKey:kW3ContactFieldType]; - // fixes cb-143 where setting empty label could cause address to not be removed - // (because empty label would become 'other' in convertContactTypeToPropertyLabel - // which may not have matched existing label thus resulting in an incorrect updating of the label - // and the address not getting removed at the end of the for loop) - if (testLabel && [testLabel isKindOfClass:[NSString class]] && ([testLabel length] > 0)) { - CFStringRef w3cLabel = [CDVContact convertContactTypeToPropertyLabel:testLabel]; - if (w3cLabel && ![existingABLabel isEqualToString:(__bridge NSString*)w3cLabel]) { - // replace the label - ABMultiValueReplaceLabelAtIndex(multi, w3cLabel, idx); - bUpdateLabel = YES; - } - } // else was invalid or empty label string so do not update - - for (id k in propArray) { - value = [field valueForKey:k]; - bool bSet = (value != nil && ![value isKindOfClass:[NSNull class]] && ([value isKindOfClass:[NSString class]] && [value length] > 0)); - // if there is a contact value, put it into dictionary - if (bSet) { - NSString* setValue = [CDVContact needsConversion:(NSString*)k] ? (NSString*)[CDVContact convertContactTypeToPropertyLabel:value] : value; - [dict setObject:setValue forKey:(NSString*)[[CDVContact defaultW3CtoAB] valueForKey:(NSString*)k]]; - } else if ((value == nil) || ([value isKindOfClass:[NSString class]] && ([value length] != 0))) { - // value not provided in contact dictionary - if prop exists in AB dictionary, preserve it - valueAB = [(__bridge NSDictionary*)existingDictionary valueForKey : [[CDVContact defaultW3CtoAB] valueForKey:k]]; - if (valueAB != nil) { - [dict setValue:valueAB forKey:[[CDVContact defaultW3CtoAB] valueForKey:k]]; - } - } // else if value == "" it will not be added into updated dict and thus removed - } // end of for loop (moving here fixes cb-143, need to end for loop before replacing or removing multivalue) - - if ([dict count] > 0) { - // something was added into new dict, - ABMultiValueReplaceValueAtIndex(multi, (__bridge CFTypeRef)dict, idx); - } else if (!bUpdateLabel) { - // nothing added into new dict and no label change so remove this property entry - ABMultiValueRemoveValueAndLabelAtIndex(multi, idx); - } - - CFRelease(existingDictionary); - } else { - // not found in multivalue so add it - dict = [self translateW3Dict:field forProperty:prop]; - if (dict) { - NSMutableDictionary* addDict = [NSMutableDictionary dictionaryWithCapacity:2]; - // get the type out of the original dictionary for address - NSObject* typeValue = ((prop == kABPersonInstantMessageProperty) ? (NSObject*)kABOtherLabel : (NSString*)[field valueForKey:kW3ContactFieldType]); - // NSLog(@"typeValue: %@", typeValue); - [addDict setObject:typeValue forKey:kW3ContactFieldType]; // im labels will be set as Other and address labels as type from dictionary - [addDict setObject:dict forKey:kW3ContactFieldValue]; - [self addToMultiValue:multi fromDictionary:addDict]; - } - } - } // end of looping through dictionaries - - // set the (updated) copy as the new value - bSuccess = [self setValue:multi forProperty:prop inRecord:person]; - } - } // end of copy and apply changes - if (multi) { - CFRelease(multi); - } - - return bSuccess; -} - -/* Determine which W3C labels need to be converted - */ -+ (BOOL)needsConversion:(NSString*)W3Label -{ - BOOL bConvert = NO; - - if ([W3Label isEqualToString:kW3ContactFieldType] || [W3Label isEqualToString:kW3ContactImType]) { - bConvert = YES; - } - return bConvert; -} - -/* Translation of property type labels contact API ---> iPhone - * - * phone: work, home, other, mobile, fax, pager --> - * kABWorkLabel, kABHomeLabel, kABOtherLabel, kABPersonPhoneMobileLabel, kABPersonHomeFAXLabel || kABPersonHomeFAXLabel, kABPersonPhonePagerLabel - * emails: work, home, other ---> kABWorkLabel, kABHomeLabel, kABOtherLabel - * ims: aim, gtalk, icq, xmpp, msn, skype, qq, yahoo --> kABPersonInstantMessageService + (AIM, ICG, MSN, Yahoo). No support for gtalk, xmpp, skype, qq - * addresses: work, home, other --> kABWorkLabel, kABHomeLabel, kABOtherLabel - * - * - */ -+ (CFStringRef)convertContactTypeToPropertyLabel:(NSString*)label -{ - CFStringRef type; - - if ([label isKindOfClass:[NSNull class]] || ![label isKindOfClass:[NSString class]]) { - type = NULL; // no label - } else if ([label caseInsensitiveCompare:kW3ContactWorkLabel] == NSOrderedSame) { - type = kABWorkLabel; - } else if ([label caseInsensitiveCompare:kW3ContactHomeLabel] == NSOrderedSame) { - type = kABHomeLabel; - } else if ([label caseInsensitiveCompare:kW3ContactOtherLabel] == NSOrderedSame) { - type = kABOtherLabel; - } else if ([label caseInsensitiveCompare:kW3ContactPhoneMobileLabel] == NSOrderedSame) { - type = kABPersonPhoneMobileLabel; - } else if ([label caseInsensitiveCompare:kW3ContactPhonePagerLabel] == NSOrderedSame) { - type = kABPersonPhonePagerLabel; - } else if ([label caseInsensitiveCompare:kW3ContactImAIMLabel] == NSOrderedSame) { - type = kABPersonInstantMessageServiceAIM; - } else if ([label caseInsensitiveCompare:kW3ContactImICQLabel] == NSOrderedSame) { - type = kABPersonInstantMessageServiceICQ; - } else if ([label caseInsensitiveCompare:kW3ContactImMSNLabel] == NSOrderedSame) { - type = kABPersonInstantMessageServiceMSN; - } else if ([label caseInsensitiveCompare:kW3ContactImYahooLabel] == NSOrderedSame) { - type = kABPersonInstantMessageServiceYahoo; - } else if ([label caseInsensitiveCompare:kW3ContactUrlProfile] == NSOrderedSame) { - type = kABPersonHomePageLabel; - } else { - type = kABOtherLabel; - } - - return type; -} - -+ (NSString*)convertPropertyLabelToContactType:(NSString*)label -{ - NSString* type = nil; - - if (label != nil) { // improve efficiency...... - if ([label isEqualToString:(NSString*)kABPersonPhoneMobileLabel]) { - type = kW3ContactPhoneMobileLabel; - } else if ([label isEqualToString:(NSString*)kABPersonPhoneHomeFAXLabel] || - [label isEqualToString:(NSString*)kABPersonPhoneWorkFAXLabel]) { - type = kW3ContactPhoneFaxLabel; - } else if ([label isEqualToString:(NSString*)kABPersonPhonePagerLabel]) { - type = kW3ContactPhonePagerLabel; - } else if ([label isEqualToString:(NSString*)kABHomeLabel]) { - type = kW3ContactHomeLabel; - } else if ([label isEqualToString:(NSString*)kABWorkLabel]) { - type = kW3ContactWorkLabel; - } else if ([label isEqualToString:(NSString*)kABOtherLabel]) { - type = kW3ContactOtherLabel; - } else if ([label isEqualToString:(NSString*)kABPersonInstantMessageServiceAIM]) { - type = kW3ContactImAIMLabel; - } else if ([label isEqualToString:(NSString*)kABPersonInstantMessageServiceICQ]) { - type = kW3ContactImICQLabel; - } else if ([label isEqualToString:(NSString*)kABPersonInstantMessageServiceJabber]) { - type = kW3ContactOtherLabel; - } else if ([label isEqualToString:(NSString*)kABPersonInstantMessageServiceMSN]) { - type = kW3ContactImMSNLabel; - } else if ([label isEqualToString:(NSString*)kABPersonInstantMessageServiceYahoo]) { - type = kW3ContactImYahooLabel; - } else if ([label isEqualToString:(NSString*)kABPersonHomePageLabel]) { - type = kW3ContactUrlProfile; - } else { - type = kW3ContactOtherLabel; - } - } - return type; -} - -/* Check if the input label is a valid W3C ContactField.type. This is used when searching, - * only search field types if the search string is a valid type. If we converted any search - * string to a ABPropertyLabel it could convert to kABOtherLabel which is probably not want - * the user wanted to search for and could skew the results. - */ -+ (BOOL)isValidW3ContactType:(NSString*)label -{ - BOOL isValid = NO; - - if ([label isKindOfClass:[NSNull class]] || ![label isKindOfClass:[NSString class]]) { - isValid = NO; // no label - } else if ([label caseInsensitiveCompare:kW3ContactWorkLabel] == NSOrderedSame) { - isValid = YES; - } else if ([label caseInsensitiveCompare:kW3ContactHomeLabel] == NSOrderedSame) { - isValid = YES; - } else if ([label caseInsensitiveCompare:kW3ContactOtherLabel] == NSOrderedSame) { - isValid = YES; - } else if ([label caseInsensitiveCompare:kW3ContactPhoneMobileLabel] == NSOrderedSame) { - isValid = YES; - } else if ([label caseInsensitiveCompare:kW3ContactPhonePagerLabel] == NSOrderedSame) { - isValid = YES; - } else if ([label caseInsensitiveCompare:kW3ContactImAIMLabel] == NSOrderedSame) { - isValid = YES; - } else if ([label caseInsensitiveCompare:kW3ContactImICQLabel] == NSOrderedSame) { - isValid = YES; - } else if ([label caseInsensitiveCompare:kW3ContactImMSNLabel] == NSOrderedSame) { - isValid = YES; - } else if ([label caseInsensitiveCompare:kW3ContactImYahooLabel] == NSOrderedSame) { - isValid = YES; - } else { - isValid = NO; - } - - return isValid; -} - -/* Create a new Contact Dictionary object from an ABRecordRef that contains information in a format such that - * it can be returned to JavaScript callback as JSON object string. - * Uses: - * ABRecordRef set into Contact Object - * NSDictionary withFields indicates which fields to return from the AddressBook Record - * - * JavaScript Contact: - * @param {DOMString} id unique identifier - * @param {DOMString} displayName - * @param {ContactName} name - * @param {DOMString} nickname - * @param {ContactField[]} phoneNumbers array of phone numbers - * @param {ContactField[]} emails array of email addresses - * @param {ContactAddress[]} addresses array of addresses - * @param {ContactField[]} ims instant messaging user ids - * @param {ContactOrganization[]} organizations - * @param {DOMString} published date contact was first created - * @param {DOMString} updated date contact was last updated - * @param {DOMString} birthday contact's birthday - * @param (DOMString} anniversary contact's anniversary - * @param {DOMString} gender contact's gender - * @param {DOMString} note user notes about contact - * @param {DOMString} preferredUsername - * @param {ContactField[]} photos - * @param {ContactField[]} tags - * @param {ContactField[]} relationships - * @param {ContactField[]} urls contact's web sites - * @param {ContactAccounts[]} accounts contact's online accounts - * @param {DOMString} timezone UTC time zone offset - * @param {DOMString} connected - */ - -- (NSDictionary*)toDictionary:(NSDictionary*)withFields -{ - // if not a person type record bail out for now - if (ABRecordGetRecordType(self.record) != kABPersonType) { - return NULL; - } - id value = nil; - self.returnFields = withFields; - - NSMutableDictionary* nc = [NSMutableDictionary dictionaryWithCapacity:1]; // new contact dictionary to fill in from ABRecordRef - // id - [nc setObject:[NSNumber numberWithInt:ABRecordGetRecordID(self.record)] forKey:kW3ContactId]; - if (self.returnFields == nil) { - // if no returnFields specified, W3C says to return empty contact (but Cordova will at least return id) - return nc; - } - if ([self.returnFields objectForKey:kW3ContactDisplayName]) { - // displayname requested - iOS doesn't have so return null - [nc setObject:[NSNull null] forKey:kW3ContactDisplayName]; - // may overwrite below if requested ContactName and there are no values - } - // nickname - if ([self.returnFields valueForKey:kW3ContactNickname]) { - value = (__bridge_transfer NSString*)ABRecordCopyValue(self.record, kABPersonNicknameProperty); - [nc setObject:(value != nil) ? value:[NSNull null] forKey:kW3ContactNickname]; - } - - // name dictionary - // NSLog(@"getting name info"); - NSObject* data = [self extractName]; - if (data != nil) { - [nc setObject:data forKey:kW3ContactName]; - } - if ([self.returnFields objectForKey:kW3ContactDisplayName] && ((data == nil) || ([(NSDictionary*)data objectForKey : kW3ContactFormattedName] == [NSNull null]))) { - // user asked for displayName which iOS doesn't support but there is no other name data being returned - // try and use Composite Name so some name is returned - id tryName = (__bridge_transfer NSString*)ABRecordCopyCompositeName(self.record); - if (tryName != nil) { - [nc setObject:tryName forKey:kW3ContactDisplayName]; - } else { - // use nickname or empty string - value = (__bridge_transfer NSString*)ABRecordCopyValue(self.record, kABPersonNicknameProperty); - [nc setObject:(value != nil) ? value:@"" forKey:kW3ContactDisplayName]; - } - } - // phoneNumbers array - // NSLog(@"getting phoneNumbers"); - value = [self extractMultiValue:kW3ContactPhoneNumbers]; - if (value != nil) { - [nc setObject:value forKey:kW3ContactPhoneNumbers]; - } - // emails array - // NSLog(@"getting emails"); - value = [self extractMultiValue:kW3ContactEmails]; - if (value != nil) { - [nc setObject:value forKey:kW3ContactEmails]; - } - // urls array - value = [self extractMultiValue:kW3ContactUrls]; - if (value != nil) { - [nc setObject:value forKey:kW3ContactUrls]; - } - // addresses array - // NSLog(@"getting addresses"); - value = [self extractAddresses]; - if (value != nil) { - [nc setObject:value forKey:kW3ContactAddresses]; - } - // im array - // NSLog(@"getting ims"); - value = [self extractIms]; - if (value != nil) { - [nc setObject:value forKey:kW3ContactIms]; - } - // organization array (only info for one organization in iOS) - // NSLog(@"getting organizations"); - value = [self extractOrganizations]; - if (value != nil) { - [nc setObject:value forKey:kW3ContactOrganizations]; - } - - // for simple properties, could make this a bit more efficient by storing all simple properties in a single - // array in the returnFields dictionary and setting them via a for loop through the array - - // add dates - // NSLog(@"getting dates"); - NSNumber* ms; - - /** Contact Revision field removed from June 16, 2011 version of specification - - if ([self.returnFields valueForKey:kW3ContactUpdated]){ - ms = [self getDateAsNumber: kABPersonModificationDateProperty]; - if (!ms){ - // try and get published date - ms = [self getDateAsNumber: kABPersonCreationDateProperty]; - } - if (ms){ - [nc setObject: ms forKey:kW3ContactUpdated]; - } - - } - */ - - if ([self.returnFields valueForKey:kW3ContactBirthday]) { - ms = [self getDateAsNumber:kABPersonBirthdayProperty]; - if (ms) { - [nc setObject:ms forKey:kW3ContactBirthday]; - } - } - - /* Anniversary removed from 12-09-2010 W3C Contacts api spec - if ([self.returnFields valueForKey:kW3ContactAnniversary]){ - // Anniversary date is stored in a multivalue property - ABMultiValueRef multi = ABRecordCopyValue(self.record, kABPersonDateProperty); - if (multi){ - CFStringRef label = nil; - CFIndex count = ABMultiValueGetCount(multi); - // see if contains an Anniversary date - for(CFIndex i=0; i 0) { // ?? this will always be true since we set id,label,primary field?? - [(NSMutableArray*)addresses addObject : newAddress]; - } - CFRelease(dict); - } // end of loop through addresses - } else { - addresses = [NSNull null]; - } - if (multi) { - CFRelease(multi); - } - - return addresses; -} - -/* Create array of Dictionaries to match JavaScript ContactField object for ims - * type one of [aim, gtalk, icq, xmpp, msn, skype, qq, yahoo] needs other as well - * value - * (bool) primary - * id - * - * iOS IMs are a MultiValue Properties with label, value=dictionary of IM details (service, username), and id - */ -- (NSObject*)extractIms -{ - NSArray* fields = [self.returnFields objectForKey:kW3ContactIms]; - - if (fields == nil) { // no name fields requested - return nil; - } - NSObject* imArray; - ABMultiValueRef multi = ABRecordCopyValue(self.record, kABPersonInstantMessageProperty); - CFIndex count = multi ? ABMultiValueGetCount(multi) : 0; - if (count) { - imArray = [NSMutableArray arrayWithCapacity:count]; - - for (CFIndex i = 0; i < ABMultiValueGetCount(multi); i++) { - NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithCapacity:3]; - // iOS has label property (work, home, other) for each IM but W3C contact API doesn't use - CFDictionaryRef dict = (CFDictionaryRef)ABMultiValueCopyValueAtIndex(multi, i); - CFStringRef value; // all values should be CFStringRefs / NSString* - bool bFound; - if ([fields containsObject:kW3ContactFieldValue]) { - // value = user name - bFound = CFDictionaryGetValueIfPresent(dict, kABPersonInstantMessageUsernameKey, (void*)&value); - if (bFound && (value != NULL)) { - CFRetain(value); - [newDict setObject:(__bridge id)value forKey:kW3ContactFieldValue]; - CFRelease(value); - } else { - [newDict setObject:[NSNull null] forKey:kW3ContactFieldValue]; - } - } - if ([fields containsObject:kW3ContactFieldType]) { - bFound = CFDictionaryGetValueIfPresent(dict, kABPersonInstantMessageServiceKey, (void*)&value); - if (bFound && (value != NULL)) { - CFRetain(value); - [newDict setObject:(id)[[CDVContact class] convertPropertyLabelToContactType : (__bridge NSString*)value] forKey:kW3ContactFieldType]; - CFRelease(value); - } else { - [newDict setObject:[NSNull null] forKey:kW3ContactFieldType]; - } - } - // always set ID - id identifier = [NSNumber numberWithUnsignedInt:ABMultiValueGetIdentifierAtIndex(multi, i)]; - [newDict setObject:(identifier != nil) ? identifier:[NSNull null] forKey:kW3ContactFieldId]; - - [(NSMutableArray*)imArray addObject : newDict]; - CFRelease(dict); - } - } else { - imArray = [NSNull null]; - } - - if (multi) { - CFRelease(multi); - } - return imArray; -} - -/* Create array of Dictionaries to match JavaScript ContactOrganization object - * pref - not supported in iOS - * type - not supported in iOS - * name - * department - * title - */ - -- (NSObject*)extractOrganizations -{ - NSArray* fields = [self.returnFields objectForKey:kW3ContactOrganizations]; - - if (fields == nil) { // no name fields requested - return nil; - } - NSObject* array = nil; - NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithCapacity:5]; - id value; - int validValueCount = 0; - - for (id i in fields) { - id key = [[CDVContact defaultW3CtoAB] valueForKey:i]; - if (key && [key isKindOfClass:[NSNumber class]]) { - value = (__bridge_transfer NSString*)ABRecordCopyValue(self.record, (ABPropertyID)[[[CDVContact defaultW3CtoAB] valueForKey:i] intValue]); - if (value != nil) { - // if there are no organization values we should return null for organization - // this counter keeps indicates if any organization values have been set - validValueCount++; - } - [newDict setObject:(value != nil) ? value:[NSNull null] forKey:i]; - } else { // not a key iOS supports, set to null - [newDict setObject:[NSNull null] forKey:i]; - } - } - - if (([newDict count] > 0) && (validValueCount > 0)) { - // add pref and type - // they are not supported by iOS and thus these values never change - [newDict setObject:@"false" forKey:kW3ContactFieldPrimary]; - [newDict setObject:[NSNull null] forKey:kW3ContactFieldType]; - array = [NSMutableArray arrayWithCapacity:1]; - [(NSMutableArray*)array addObject : newDict]; - } else { - array = [NSNull null]; - } - return array; -} - -// W3C Contacts expects an array of photos. Can return photos in more than one format, currently -// just returning the default format -// Save the photo data into tmp directory and return FileURI - temp directory is deleted upon application exit -- (NSObject*)extractPhotos -{ - NSMutableArray* photos = nil; - - if (ABPersonHasImageData(self.record)) { - CFDataRef photoData = ABPersonCopyImageData(self.record); - NSData* data = (__bridge NSData*)photoData; - // write to temp directory and store URI in photos array - // get the temp directory path - NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath]; - NSError* err = nil; - NSString* filePath = [NSString stringWithFormat:@"%@/photo_XXXXX", docsPath]; - char template[filePath.length + 1]; - strcpy(template, [filePath cStringUsingEncoding:NSASCIIStringEncoding]); - mkstemp(template); - filePath = [[NSFileManager defaultManager] - stringWithFileSystemRepresentation:template - length:strlen(template)]; - - // save file - if ([data writeToFile:filePath options:NSAtomicWrite error:&err]) { - photos = [NSMutableArray arrayWithCapacity:1]; - NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithCapacity:2]; - [newDict setObject:filePath forKey:kW3ContactFieldValue]; - [newDict setObject:@"url" forKey:kW3ContactFieldType]; - [newDict setObject:@"false" forKey:kW3ContactFieldPrimary]; - [photos addObject:newDict]; - } - - CFRelease(photoData); - } - return photos; -} - -/** - * given an array of W3C Contact field names, create a dictionary of field names to extract - * if field name represents an object, return all properties for that object: "name" - returns all properties in ContactName - * if field name is an explicit property, return only those properties: "name.givenName - returns a ContactName with only ContactName.givenName - * if field contains ONLY ["*"] return all fields - * dictionary format: - * key is W3Contact #define - * value is NSMutableArray* for complex keys: name,addresses,organizations, phone, emails, ims - * value is [NSNull null] for simple keys -*/ -+ (NSDictionary*)calcReturnFields:(NSArray*)fieldsArray // NSLog(@"getting self.returnFields"); -{ - NSMutableDictionary* d = [NSMutableDictionary dictionaryWithCapacity:1]; - - if ((fieldsArray != nil) && [fieldsArray isKindOfClass:[NSArray class]]) { - if (([fieldsArray count] == 1) && [[fieldsArray objectAtIndex:0] isEqualToString:@"*"]) { - return [CDVContact defaultFields]; // return all fields - } - - for (id i in fieldsArray) { - NSMutableArray* keys = nil; - NSString* fieldStr = nil; - if ([i isKindOfClass:[NSNumber class]]) { - fieldStr = [i stringValue]; - } else { - fieldStr = i; - } - - // see if this is specific property request in object - object.property - NSArray* parts = [fieldStr componentsSeparatedByString:@"."]; // returns original string if no separator found - NSString* name = [parts objectAtIndex:0]; - NSString* property = nil; - if ([parts count] > 1) { - property = [parts objectAtIndex:1]; - } - // see if this is a complex field by looking for its array of properties in objectAndProperties dictionary - id fields = [[CDVContact defaultObjectAndProperties] objectForKey:name]; - - // if find complex name (name,addresses,organizations, phone, emails, ims) in fields, add name as key - // with array of associated properties as the value - if ((fields != nil) && (property == nil)) { // request was for full object - keys = [NSMutableArray arrayWithArray:fields]; - if (keys != nil) { - [d setObject:keys forKey:name]; // will replace if prop array already exists - } - } else if ((fields != nil) && (property != nil)) { - // found an individual property request in form of name.property - // verify is real property name by using it as key in W3CtoAB - id abEquiv = [[CDVContact defaultW3CtoAB] objectForKey:property]; - if (abEquiv || [[CDVContact defaultW3CtoNull] containsObject:property]) { - // if existing array add to it - if ((keys = [d objectForKey:name]) != nil) { - [keys addObject:property]; - } else { - keys = [NSMutableArray arrayWithObject:property]; - [d setObject:keys forKey:name]; - } - } else { - NSLog(@"Contacts.find -- request for invalid property ignored: %@.%@", name, property); - } - } else { // is an individual property, verify is real property name by using it as key in W3CtoAB - id valid = [[CDVContact defaultW3CtoAB] objectForKey:name]; - if (valid || [[CDVContact defaultW3CtoNull] containsObject:name]) { - [d setObject:[NSNull null] forKey:name]; - } - } - } - } - if ([d count] == 0) { - // no array or nothing in the array. W3C spec says to return nothing - return nil; // [Contact defaultFields]; - } - return d; -} - -/* - * Search for the specified value in each of the fields specified in the searchFields dictionary. - * NSString* value - the string value to search for (need clarification from W3C on how to search for dates) - * NSDictionary* searchFields - a dictionary created via calcReturnFields where the key is the top level W3C - * object and the object is the array of specific fields within that object or null if it is a single property - * RETURNS - * YES as soon as a match is found in any of the fields - * NO - the specified value does not exist in any of the fields in this contact - * - * Note: I'm not a fan of returning in the middle of methods but have done it some in this method in order to - * keep the code simpler. bgibson - */ -- (BOOL)foundValue:(NSString*)testValue inFields:(NSDictionary*)searchFields -{ - BOOL bFound = NO; - - if ((testValue == nil) || ![testValue isKindOfClass:[NSString class]] || ([testValue length] == 0)) { - // nothing to find so return NO - return NO; - } - NSInteger valueAsInt = [testValue integerValue]; - - // per W3C spec, always include id in search - int recordId = ABRecordGetRecordID(self.record); - if (valueAsInt && (recordId == valueAsInt)) { - return YES; - } - - if (searchFields == nil) { - // no fields to search - return NO; - } - - if ([searchFields valueForKey:kW3ContactNickname]) { - bFound = [self testStringValue:testValue forW3CProperty:kW3ContactNickname]; - if (bFound == YES) { - return bFound; - } - } - - if ([searchFields valueForKeyIsArray:kW3ContactName]) { - // test name fields. All are string properties obtained via ABRecordCopyValue except kW3ContactFormattedName - NSArray* fields = [searchFields valueForKey:kW3ContactName]; - - for (NSString* testItem in fields) { - if ([testItem isEqualToString:kW3ContactFormattedName]) { - NSString* propValue = (__bridge_transfer NSString*)ABRecordCopyCompositeName(self.record); - if ((propValue != nil) && ([propValue length] > 0)) { - NSRange range = [propValue rangeOfString:testValue options:NSCaseInsensitiveSearch]; - bFound = (range.location != NSNotFound); - propValue = nil; - } - } else { - bFound = [self testStringValue:testValue forW3CProperty:testItem]; - } - - if (bFound) { - break; - } - } - } - if (!bFound && [searchFields valueForKeyIsArray:kW3ContactPhoneNumbers]) { - bFound = [self searchContactFields:(NSArray*)[searchFields valueForKey:kW3ContactPhoneNumbers] - forMVStringProperty:kABPersonPhoneProperty withValue:testValue]; - } - if (!bFound && [searchFields valueForKeyIsArray:kW3ContactEmails]) { - bFound = [self searchContactFields:(NSArray*)[searchFields valueForKey:kW3ContactEmails] - forMVStringProperty:kABPersonEmailProperty withValue:testValue]; - } - - if (!bFound && [searchFields valueForKeyIsArray:kW3ContactAddresses]) { - bFound = [self searchContactFields:[searchFields valueForKey:kW3ContactAddresses] - forMVDictionaryProperty:kABPersonAddressProperty withValue:testValue]; - } - - if (!bFound && [searchFields valueForKeyIsArray:kW3ContactIms]) { - bFound = [self searchContactFields:[searchFields valueForKey:kW3ContactIms] - forMVDictionaryProperty:kABPersonInstantMessageProperty withValue:testValue]; - } - - if (!bFound && [searchFields valueForKeyIsArray:kW3ContactOrganizations]) { - NSArray* fields = [searchFields valueForKey:kW3ContactOrganizations]; - - for (NSString* testItem in fields) { - bFound = [self testStringValue:testValue forW3CProperty:testItem]; - if (bFound == YES) { - break; - } - } - } - if (!bFound && [searchFields valueForKey:kW3ContactNote]) { - bFound = [self testStringValue:testValue forW3CProperty:kW3ContactNote]; - } - - // if searching for a date field is requested, get the date field as a localized string then look for match against testValue in date string - // searching for photos is not supported - if (!bFound && [searchFields valueForKey:kW3ContactBirthday]) { - bFound = [self testDateValue:testValue forW3CProperty:kW3ContactBirthday]; - } - if (!bFound && [searchFields valueForKeyIsArray:kW3ContactUrls]) { - bFound = [self searchContactFields:(NSArray*)[searchFields valueForKey:kW3ContactUrls] - forMVStringProperty:kABPersonURLProperty withValue:testValue]; - } - - return bFound; -} - -/* - * Test for the existence of a given string within the value of a ABPersonRecord string property based on the W3c property name. - * - * IN: - * NSString* testValue - the value to find - search is case insensitive - * NSString* property - the W3c property string - * OUT: - * BOOL YES if the given string was found within the property value - * NO if the testValue was not found, W3C property string was invalid or the AddressBook property was not a string - */ -- (BOOL)testStringValue:(NSString*)testValue forW3CProperty:(NSString*)property -{ - BOOL bFound = NO; - - if ([[CDVContact defaultW3CtoAB] valueForKeyIsNumber:property]) { - ABPropertyID propId = [[[CDVContact defaultW3CtoAB] objectForKey:property] intValue]; - if (ABPersonGetTypeOfProperty(propId) == kABStringPropertyType) { - NSString* propValue = (__bridge_transfer NSString*)ABRecordCopyValue(self.record, propId); - if ((propValue != nil) && ([propValue length] > 0)) { - NSPredicate* containPred = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", testValue]; - bFound = [containPred evaluateWithObject:propValue]; - // NSRange range = [propValue rangeOfString:testValue options: NSCaseInsensitiveSearch]; - // bFound = (range.location != NSNotFound); - } - } - } - return bFound; -} - -/* - * Test for the existence of a given Date string within the value of a ABPersonRecord datetime property based on the W3c property name. - * - * IN: - * NSString* testValue - the value to find - search is case insensitive - * NSString* property - the W3c property string - * OUT: - * BOOL YES if the given string was found within the localized date string value - * NO if the testValue was not found, W3C property string was invalid or the AddressBook property was not a DateTime - */ -- (BOOL)testDateValue:(NSString*)testValue forW3CProperty:(NSString*)property -{ - BOOL bFound = NO; - - if ([[CDVContact defaultW3CtoAB] valueForKeyIsNumber:property]) { - ABPropertyID propId = [[[CDVContact defaultW3CtoAB] objectForKey:property] intValue]; - if (ABPersonGetTypeOfProperty(propId) == kABDateTimePropertyType) { - NSDate* date = (__bridge_transfer NSDate*)ABRecordCopyValue(self.record, propId); - if (date != nil) { - NSString* dateString = [date descriptionWithLocale:[NSLocale currentLocale]]; - NSPredicate* containPred = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", testValue]; - bFound = [containPred evaluateWithObject:dateString]; - } - } - } - return bFound; -} - -/* - * Search the specified fields within an AddressBook multivalue string property for the specified test value. - * Used for phoneNumbers, emails and urls. - * IN: - * NSArray* fields - the fields to search for within the multistring property (value and/or type) - * ABPropertyID - the property to search - * NSString* testValue - the value to search for. Will convert between W3C types and AB types. Will only - * search for types if the testValue is a valid ContactField type. - * OUT: - * YES if the test value was found in one of the specified fields - * NO if the test value was not found - */ -- (BOOL)searchContactFields:(NSArray*)fields forMVStringProperty:(ABPropertyID)propId withValue:testValue -{ - BOOL bFound = NO; - - for (NSString* type in fields) { - NSString* testString = nil; - if ([type isEqualToString:kW3ContactFieldType]) { - if ([CDVContact isValidW3ContactType:testValue]) { - // only search types if the filter string is a valid ContactField.type - testString = (NSString*)[CDVContact convertContactTypeToPropertyLabel:testValue]; - } - } else { - testString = testValue; - } - - if (testString != nil) { - bFound = [self testMultiValueStrings:testString forProperty:propId ofType:type]; - } - if (bFound == YES) { - break; - } - } - - return bFound; -} - -/* - * Searches a multiString value of the specified type for the specified test value. - * - * IN: - * NSString* testValue - the value to test for - * ABPropertyID propId - the property id of the multivalue property to search - * NSString* type - the W3C contact type to search for (value or type) - * OUT: - * YES is the test value was found - * NO if the test value was not found - */ -- (BOOL)testMultiValueStrings:(NSString*)testValue forProperty:(ABPropertyID)propId ofType:(NSString*)type -{ - BOOL bFound = NO; - - if (ABPersonGetTypeOfProperty(propId) == kABMultiStringPropertyType) { - NSArray* valueArray = nil; - if ([type isEqualToString:kW3ContactFieldType]) { - valueArray = [self labelsForProperty:propId inRecord:self.record]; - } else if ([type isEqualToString:kW3ContactFieldValue]) { - valueArray = [self valuesForProperty:propId inRecord:self.record]; - } - if (valueArray) { - NSString* valuesAsString = [valueArray componentsJoinedByString:@" "]; - NSPredicate* containPred = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", testValue]; - bFound = [containPred evaluateWithObject:valuesAsString]; - } - } - return bFound; -} - -/* - * Returns the array of values for a multivalue string property of the specified property id - */ -- (__autoreleasing NSArray*)valuesForProperty:(ABPropertyID)propId inRecord:(ABRecordRef)aRecord -{ - ABMultiValueRef multi = ABRecordCopyValue(aRecord, propId); - NSArray* values = (__bridge_transfer NSArray*)ABMultiValueCopyArrayOfAllValues(multi); - - CFRelease(multi); - return values; -} - -/* - * Returns the array of labels for a multivalue string property of the specified property id - */ -- (NSArray*)labelsForProperty:(ABPropertyID)propId inRecord:(ABRecordRef)aRecord -{ - ABMultiValueRef multi = ABRecordCopyValue(aRecord, propId); - CFIndex count = ABMultiValueGetCount(multi); - NSMutableArray* labels = [NSMutableArray arrayWithCapacity:count]; - - for (int i = 0; i < count; i++) { - NSString* label = (__bridge_transfer NSString*)ABMultiValueCopyLabelAtIndex(multi, i); - if (label) { - [labels addObject:label]; - } - } - - CFRelease(multi); - return labels; -} - -/* search for values within MultiValue Dictionary properties Address or IM property - * IN: - * (NSArray*) fields - the array of W3C field names to search within - * (ABPropertyID) propId - the AddressBook property that returns a multivalue dictionary - * (NSString*) testValue - the string to search for within the specified fields - * - */ -- (BOOL)searchContactFields:(NSArray*)fields forMVDictionaryProperty:(ABPropertyID)propId withValue:(NSString*)testValue -{ - BOOL bFound = NO; - - NSArray* values = [self valuesForProperty:propId inRecord:self.record]; // array of dictionaries (as CFDictionaryRef) - int dictCount = [values count]; - - // for ims dictionary contains with service (w3C type) and username (W3c value) - // for addresses dictionary contains street, city, state, zip, country - for (int i = 0; i < dictCount; i++) { - CFDictionaryRef dict = (__bridge CFDictionaryRef)[values objectAtIndex:i]; - - for (NSString* member in fields) { - NSString* abKey = [[CDVContact defaultW3CtoAB] valueForKey:member]; // im and address fields are all strings - CFStringRef abValue = nil; - if (abKey) { - NSString* testString = nil; - if ([member isEqualToString:kW3ContactImType]) { - if ([CDVContact isValidW3ContactType:testValue]) { - // only search service/types if the filter string is a valid ContactField.type - testString = (NSString*)[CDVContact convertContactTypeToPropertyLabel:testValue]; - } - } else { - testString = testValue; - } - if (testString != nil) { - BOOL bExists = CFDictionaryGetValueIfPresent(dict, (__bridge const void*)abKey, (void*)&abValue); - if (bExists) { - CFRetain(abValue); - NSPredicate* containPred = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", testString]; - bFound = [containPred evaluateWithObject:(__bridge id)abValue]; - CFRelease(abValue); - } - } - } - if (bFound == YES) { - break; - } - } // end of for each member in fields - - if (bFound == YES) { - break; - } - } // end of for each dictionary - - return bFound; -} - -@end http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/0318d8cd/spec/plugins/Contacts/src/ios/CDVContacts.h ---------------------------------------------------------------------- diff --git a/spec/plugins/Contacts/src/ios/CDVContacts.h b/spec/plugins/Contacts/src/ios/CDVContacts.h deleted file mode 100644 index e3deb21..0000000 --- a/spec/plugins/Contacts/src/ios/CDVContacts.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - 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. - */ - -#import -#import -#import -#import -#import "CDVContact.h" - -@interface CDVContacts : CDVPlugin -{ - ABAddressBookRef addressBook; -} - -/* - * newContact - create a new contact via the GUI - * - * arguments: - * 1: successCallback: this is the javascript function that will be called with the newly created contactId - */ -- (void)newContact:(CDVInvokedUrlCommand*)command; - -/* - * displayContact - IN PROGRESS - * - * arguments: - * 1: recordID of the contact to display in the iPhone contact display - * 2: successCallback - currently not used - * 3: error callback - * options: - * allowsEditing: set to true to allow the user to edit the contact - currently not supported - */ -- (void)displayContact:(CDVInvokedUrlCommand*)command; - -/* - * chooseContact - * - * arguments: - * 1: this is the javascript function that will be called with the contact data as a JSON object (as the first param) - * options: - * allowsEditing: set to true to not choose the contact, but to edit it in the iPhone contact editor - */ -- (void)chooseContact:(CDVInvokedUrlCommand*)command; - -- (void)newPersonViewController:(ABNewPersonViewController*)newPersonViewController didCompleteWithNewPerson:(ABRecordRef)person; -- (BOOL)personViewController:(ABPersonViewController*)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person - property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifierForValue; - -/* - * search - searches for contacts. Only person records are currently supported. - * - * arguments: - * 1: successcallback - this is the javascript function that will be called with the array of found contacts - * 2: errorCallback - optional javascript function to be called in the event of an error with an error code. - * options: dictionary containing ContactFields and ContactFindOptions - * fields - ContactFields array - * findOptions - ContactFindOptions object as dictionary - * - */ -- (void)search:(CDVInvokedUrlCommand*)command; - -/* - * save - saves a new contact or updates and existing contact - * - * arguments: - * 1: success callback - this is the javascript function that will be called with the JSON representation of the saved contact - * search calls a fixed navigator.service.contacts._findCallback which then calls the success callback stored before making the call into obj-c - */ -- (void)save:(CDVInvokedUrlCommand*)command; - -/* - * remove - removes a contact from the address book - * - * arguments: - * 1: 1: successcallback - this is the javascript function that will be called with a (now) empty contact object - * - * options: dictionary containing Contact object to remove - * contact - Contact object as dictionary - */ -- (void)remove:(CDVInvokedUrlCommand*)command; - -// - (void) dealloc; - -@end - -@interface CDVContactsPicker : ABPeoplePickerNavigationController -{ - BOOL allowsEditing; - NSString* callbackId; - NSDictionary* options; - NSDictionary* pickedContactDictionary; -} - -@property BOOL allowsEditing; -@property (copy) NSString* callbackId; -@property (nonatomic, strong) NSDictionary* options; -@property (nonatomic, strong) NSDictionary* pickedContactDictionary; - -@end - -@interface CDVNewContactsController : ABNewPersonViewController -{ - NSString* callbackId; -} -@property (copy) NSString* callbackId; -@end - -/* ABPersonViewController does not have any UI to dismiss. Adding navigationItems to it does not work properly, the navigationItems are lost when the app goes into the background. - The solution was to create an empty NavController in front of the ABPersonViewController. This - causes the ABPersonViewController to have a back button. By subclassing the ABPersonViewController, - we can override viewWillDisappear and take down the entire NavigationController at that time. - */ -@interface CDVDisplayContactViewController : ABPersonViewController -{} -@property (nonatomic, strong) CDVPlugin* contactsPlugin; - -@end -@interface CDVAddressBookAccessError : NSObject -{} -@property (assign) CDVContactError errorCode; -- (CDVAddressBookAccessError*)initWithCode:(CDVContactError)code; -@end - -typedef void (^ CDVAddressBookWorkerBlock)( - ABAddressBookRef addressBook, - CDVAddressBookAccessError* error - ); -@interface CDVAddressBookHelper : NSObject -{} - -- (void)createAddressBook:(CDVAddressBookWorkerBlock)workerBlock; -@end