/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.plugin.addrbook.macosx; import java.util.*; import java.util.regex.*; import net.java.sip.communicator.plugin.addrbook.*; import net.java.sip.communicator.service.contactsource.*; import net.java.sip.communicator.service.contactsource.ContactDetail.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.util.*; /** * Implements ContactQuery for the Address Book of Mac OS X. * * @author Lyubomir Marinov */ public class MacOSXAddrBookContactQuery extends AbstractAddrBookContactQuery { /** * The Logger used by the MacOSXAddrBookContactQuery class * and its instances for logging output. */ private static final Logger logger = Logger.getLogger(MacOSXAddrBookContactQuery.class); /** * The properties of ABPerson which are to be queried by the * MacOSXAddrBookContactQuery instances. */ public static final long[] ABPERSON_PROPERTIES = new long[] { kABAIMInstantProperty(), kABEmailProperty(), kABFirstNameProperty(), kABFirstNamePhoneticProperty(), kABICQInstantProperty(), kABJabberInstantProperty(), kABLastNameProperty(), kABLastNamePhoneticProperty(), kABMiddleNameProperty(), kABMiddleNamePhoneticProperty(), kABMSNInstantProperty(), kABNicknameProperty(), kABPhoneProperty(), kABYahooInstantProperty(), kABPersonFlags(), kABOrganizationProperty(), kABMaidenNameProperty(), kABBirthdayProperty(), kABJobTitleProperty(), kABHomePageProperty(), kABURLsProperty(), kABCalendarURIsProperty(), kABAddressProperty(), kABOtherDatesProperty(), kABRelatedNamesProperty(), kABDepartmentProperty(), kABNoteProperty(), kABTitleProperty(), kABSuffixProperty() }; /** * The index of the kABAIMInstantProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABAIMInstantProperty = 0; /** * The index of the kABEmailProperty ABPerson property in * {@link #ABPERSON_PROPERTIES}. */ public static final int kABEmailProperty = 1; /** * The index of the kABFirstNameProperty ABPerson property * in {@link #ABPERSON_PROPERTIES}. */ public static final int kABFirstNameProperty = 2; /** * The index of the kABFirstNamePhoneticProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABFirstNamePhoneticProperty = 3; /** * The index of the kABICQInstantProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABICQInstantProperty = 4; /** * The index of the kABJabberInstantProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABJabberInstantProperty = 5; /** * The index of the kABLastNameProperty ABPerson property * in {@link #ABPERSON_PROPERTIES}. */ public static final int kABLastNameProperty = 6; /** * The index of the kABLastNamePhoneticProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABLastNamePhoneticProperty = 7; /** * The index of the kABMiddleNameProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABMiddleNameProperty = 8; /** * The index of the kABMiddleNamePhoneticProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABMiddleNamePhoneticProperty = 9; /** * The index of the kABMSNInstantProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABMSNInstantProperty = 10; /** * The index of the kABNicknameProperty ABPerson property * in {@link #ABPERSON_PROPERTIES}. */ public static final int kABNicknameProperty = 11; /** * The index of the kABOrganizationProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABOrganizationProperty = 15; /** * The index of the kABPersonFlags ABPerson property in * {@link #ABPERSON_PROPERTIES}. */ public static final int kABPersonFlags = 14; /** * The index of the kABPhoneProperty ABPerson property in * {@link #ABPERSON_PROPERTIES}. */ public static final int kABPhoneProperty = 12; /** * The flag which indicates that an ABRecord is to be displayed as * a company. */ public static final long kABShowAsCompany = 1; /** * The mask which extracts the kABShowAsXXX flag from the * personFlags of an ABPerson. */ public static final long kABShowAsMask = 7; /** * The index of the kABYahooInstantProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABYahooInstantProperty = 13; /** * The index of the kABMaidenNameProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABMaidenNameProperty = 16; /** * The index of the kABBirthdayProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABBirthdayProperty = 17; /** * The index of the kABJobTitleProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABJobTitleProperty = 18; /** * The index of the kABHomePageProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABHomePageProperty = 19; /** * The index of the kABURLsProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABURLsProperty = 20; /** * The index of the kABCalendarURIsProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABCalendarURIsProperty = 21; /** * The index of the kABAddressProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABAddressProperty = 22; /** * The index of the kABOtherDatesProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABOtherDatesProperty = 23; /** * The index of the kABRelatedNamesProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABRelatedNamesProperty = 24; /** * The index of the kABDepartmentProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABDepartmentProperty = 25; /** * The index of the kABNoteProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABNoteProperty = 26; /** * The index of the kABTitleProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABTitleProperty = 27; /** * The index of the kABSuffixProperty ABPerson * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABSuffixProperty = 28; /** * The regex which matches the superfluous parts of an ABMultiValue * label. */ private static final Pattern LABEL_PATTERN = Pattern.compile( "kAB|Email|Phone|Label|(\\p{Punct}*)", Pattern.CASE_INSENSITIVE); static { System.loadLibrary("jmacosxaddrbook"); } /** * Initializes a new MacOSXAddrBookContactQuery which is to perform * a specific query in the Address Book of Mac OS X on behalf of a * specific MacOSXAddrBookContactSourceService. * * @param contactSource the MacOSXAddrBookContactSourceService * which is to perform the new ContactQuery instance * @param query the Pattern for which contactSource i.e. * the Address Book of Mac OS X is being queried */ public MacOSXAddrBookContactQuery( MacOSXAddrBookContactSourceService contactSource, Pattern query) { super(contactSource, query); } /** * Gets the imageData of a specific ABPerson instance. * * @param person the pointer to the ABPerson instance to get the * imageData of * @return the imageData of the specified ABPerson * instance */ public static native byte[] ABPerson_imageData(long person); /** * Gets the values of a specific set of ABRecord properties for a * specific ABRecord instance. * * @param record the pointer to the ABRecord to get the property * values of * @param properties the set of ABRecord properties to get the * values of * @return the values of the specified set of ABRecord properties * for the specified ABRecord instance */ public static native Object[] ABRecord_valuesForProperties( long record, long[] properties); /** * Returns the unique id of a record. * @param record the record which id is retrieved. * @return the record id. */ public static native String ABRecord_uniqueId(long record); /** * Sets property for the supplied person id. * @param id the person id * @param property the property to use. * @param subPropety any sub property if available. * @param value the value to set. * @return whether the result was successfully added. */ public static native boolean setProperty( String id, long property, String subPropety, Object value); /** * Remove a property. * @param id the person id. * @param property the property. * @return whether the result was successfully removed. */ public static native boolean removeProperty(String id, long property); /** * Removes a contact from the address book. * * @param id the person id. * * @return whether the contact was successfully removed. */ public static native boolean deleteContact(String id); /** * Creates a new address book contact. * * @return The identifier of the created contact. null if failed. */ public static native String createContact(); /** * Gets the pointer of the given contact. * * @param id the person id. * * @return The pointer of the given contact. Null if failed. */ public static native long getContactPointer(String id); /** * Initializes a new ContactDetail instance which is to reperesent * a specific contact address that is the value of a specific * ABPerson property and, optionally, has a specific label. * * @param property the index in {@link #ABPERSON_PROPERTIES} of the * ABPerson property to be represented by ContactDetail * @param contactAddress the contact address to be represented by the new * ContactDetail instance * @param label an optional label to be added to the set of labels, if any, * determined by property * @param id The id of the detail. * * @return a new ContactDetail instance which represents the * specified contactAddress */ private ContactDetail createContactDetail( int property, String contactAddress, Object label, String additionalProperty, String id) { Category c; SubCategory sc = null; switch (property) { case kABEmailProperty: c = Category.Email; break; case kABPhoneProperty: c = Category.Phone; break; case kABAIMInstantProperty: sc = SubCategory.AIM; c = Category.InstantMessaging; break; case kABICQInstantProperty: sc = SubCategory.ICQ; c = Category.InstantMessaging; break; case kABJabberInstantProperty: sc = SubCategory.Jabber; c = Category.InstantMessaging; break; case kABMSNInstantProperty: sc = SubCategory.Skype; c = Category.InstantMessaging; break; case kABYahooInstantProperty: sc = SubCategory.Yahoo; c = Category.InstantMessaging; break; case kABMaidenNameProperty: case kABFirstNameProperty: sc = SubCategory.Name; c = Category.Personal; break; case kABFirstNamePhoneticProperty: sc = SubCategory.Name; c = Category.Personal; break; case kABLastNameProperty: sc = SubCategory.LastName; c = Category.Personal; break; case kABLastNamePhoneticProperty: sc = SubCategory.LastName; c = Category.Personal; break; case kABMiddleNameProperty: case kABMiddleNamePhoneticProperty: case kABNicknameProperty: sc = SubCategory.Nickname; c = Category.Personal; break; case kABBirthdayProperty: case kABURLsProperty: case kABHomePageProperty: sc = SubCategory.HomePage; c = Category.Personal; break; case kABOtherDatesProperty: case kABRelatedNamesProperty: case kABNoteProperty: case kABTitleProperty: case kABSuffixProperty: c = Category.Personal; break; case kABOrganizationProperty: case kABJobTitleProperty: sc = SubCategory.JobTitle; c = Category.Organization; break; case kABDepartmentProperty: c = Category.Organization; sc = SubCategory.Name; break; case kABAddressProperty: c = Category.Address; break; default: c = null; break; } if (sc == null) { if (label == null) sc = null; else { sc = getSubCategoryFromLabel(label); } } SubCategory[] subCategories; SubCategory additionalSubCategory = null; if(additionalProperty != null) additionalSubCategory = getSubCategoryFromLabel(additionalProperty); if(additionalSubCategory != null) subCategories = new SubCategory[] { sc, additionalSubCategory }; else subCategories = new SubCategory[]{ sc }; return new MacOSXAddrBookContactDetail( property, contactAddress, c, subCategories, additionalProperty, id); } /** * Returns the SubCategory corresponding to the given label. * * @param label the label to match to a SubDirectory * @return the SubDirectory corresponding to the * given label */ private SubCategory getSubCategoryFromLabel(Object label) { String labelString = LABEL_PATTERN.matcher((String) label).replaceAll("").trim(); if (labelString.length() < 1) return null; SubCategory subCategory = null; if (labelString.equalsIgnoreCase("home")) subCategory = SubCategory.Home; else if (labelString.equalsIgnoreCase("work")) subCategory = SubCategory.Work; else if (labelString.equalsIgnoreCase("other")) subCategory = SubCategory.Other; else if (labelString.equalsIgnoreCase("mobile")) subCategory = SubCategory.Mobile; else if (labelString.equalsIgnoreCase("homepage")) subCategory = SubCategory.HomePage; else if (labelString.equalsIgnoreCase("street")) subCategory = SubCategory.Street; else if (labelString.equalsIgnoreCase("state")) subCategory = SubCategory.State; else if (labelString.equalsIgnoreCase("ZIP")) subCategory = SubCategory.PostalCode; else if (labelString.equalsIgnoreCase("country")) subCategory = SubCategory.Country; else if (labelString.equalsIgnoreCase("city")) subCategory = SubCategory.City; else if (labelString.equalsIgnoreCase("InstantMessageUsername")) subCategory = SubCategory.Nickname; else if (labelString.equalsIgnoreCase("workfax")) subCategory = SubCategory.Fax; else if (labelString.equalsIgnoreCase("fax")) subCategory = SubCategory.Fax; return subCategory; } /** * Calls back to a specific PtrCallback for each ABPerson * found in the Address Book of Mac OS X which matches a specific * String query. * * @param query the String for which the Address Book of Mac OS X * is to be queried. Warning: Ignored at the time of this writing. * @param callback the PtrCallback to be notified about the * matching ABPersons */ private static native void foreachPerson( String query, PtrCallback callback); /** * Gets the contactDetails to be set on a SourceContact * which is to represent an ABPerson specified by the values of its * {@link #ABPERSON_PROPERTIES}. * * @param values the values of the ABPERSON_PROPERTIES which * represent the ABPerson to get the contactDetails of * @param id The id of the detail. * * @return the contactDetails to be set on a SourceContact * which is to represent the ABPerson specified by values */ private List getContactDetails(Object[] values, String id) { List contactDetails = new LinkedList(); for (int i = 0; i < ABPERSON_PROPERTIES.length; i++) { int property = i; Object value = values[property]; if (value instanceof String) { String stringValue = (String) value; if (stringValue.length() != 0) { if (kABPhoneProperty == property) stringValue = AddrBookActivator.getPhoneNumberI18nService() .normalize(stringValue); contactDetails.add( setCapabilities( createContactDetail( property, stringValue, null, null, id), property)); } } else if (value instanceof Object[]) { parseMultiDetails(contactDetails, (Object[]) value, property, null, id); } } return contactDetails; } /** * Parses the multi value data resulting it in contact details. * @param contactDetails the result list * @param multiValue the values to parse. * @param property the current property being parsed. * @param id The id of the detail. */ private void parseMultiDetails( List contactDetails, Object[] multiValue, int property, String label, String id) { if(multiValue == null) return; for (int multiValueIndex = 0; multiValueIndex < multiValue.length; multiValueIndex += 2) { Object subValue = multiValue[multiValueIndex]; if (subValue instanceof String) { String stringSubValue = (String) subValue; if (stringSubValue.length() != 0) { if (kABPhoneProperty == property) { stringSubValue = AddrBookActivator.getPhoneNumberI18nService() .normalize(stringSubValue); } Object l = multiValue[multiValueIndex + 1]; contactDetails.add( setCapabilities( createContactDetail( property, stringSubValue, l, label, id), property)); } } else if (subValue instanceof Object[]) { String l = null; Object lObject = multiValue[multiValueIndex + 1]; if(lObject instanceof String) l = (String)lObject; parseMultiDetails(contactDetails, (Object[]) subValue, property, l, id); } } } /** * Gets the displayName to be set on a SourceContact * which is to represent an ABPerson specified by the values of its * {@link #ABPERSON_PROPERTIES}. * * @param values the values of the ABPERSON_PROPERTIES which * represent the ABPerson to get the displayName of * @return the displayName to be set on a SourceContact * which is to represent the ABPerson specified by values */ private static String getDisplayName(Object[] values) { long personFlags = (values[kABPersonFlags] instanceof Long) ? ((Long) values[kABPersonFlags]).longValue() : 0; String displayName; if ((personFlags & kABShowAsMask) == kABShowAsCompany) { displayName = (values[kABOrganizationProperty] instanceof String) ? (String) values[kABOrganizationProperty] : ""; if (displayName.length() != 0) return displayName; } displayName = (values[kABNicknameProperty] instanceof String) ? (String) values[kABNicknameProperty] : ""; if (displayName.length() != 0) return displayName; String firstName = (values[kABFirstNameProperty] instanceof String) ? (String) values[kABFirstNameProperty] : ""; if ((firstName.length() == 0) && (values[kABFirstNamePhoneticProperty] instanceof String)) { firstName = (String) values[kABFirstNamePhoneticProperty]; } String lastName = (values[kABLastNameProperty] instanceof String) ? (String) values[kABLastNameProperty] : ""; if ((lastName.length() == 0) && (values[kABLastNamePhoneticProperty] instanceof String)) lastName = (String) values[kABLastNamePhoneticProperty]; if ((lastName.length() == 0) && (values[kABMiddleNameProperty] instanceof String)) lastName = (String) values[kABMiddleNameProperty]; if ((lastName.length() == 0) && (values[kABMiddleNamePhoneticProperty] instanceof String)) lastName = (String) values[kABMiddleNamePhoneticProperty]; if (firstName.length() == 0) displayName = lastName; else { displayName = (lastName.length() == 0) ? firstName : (firstName + " " + lastName); } if (displayName.length() != 0) return displayName; for (int i = 0; i < ABPERSON_PROPERTIES.length; i++) { Object value = values[i]; if (value instanceof String) { String stringValue = (String) value; if (stringValue.length() != 0) { displayName = stringValue; break; } } else if (value instanceof Object[]) { Object[] multiValue = (Object[]) value; for (int multiValueIndex = 0; multiValueIndex < multiValue.length; multiValueIndex += 2) { Object subValue = multiValue[multiValueIndex]; if (subValue instanceof String) { String stringSubValue = (String) subValue; if (stringSubValue.length() != 0) { displayName = stringSubValue; break; } } } } } return displayName; } /** * Gets the organization name to be set on a SourceContact. * * @param values the values of the ABPERSON_PROPERTIES which * represent the ABPerson to get the organization name of. * * @return The organization name to be set on a SourceContact. */ private static String getOrganization(Object[] values) { String organization = ""; long personFlags = (values[kABPersonFlags] instanceof Long) ? ((Long) values[kABPersonFlags]).longValue() : 0; if ((personFlags & kABShowAsMask) != kABShowAsCompany) { organization = (values[kABOrganizationProperty] instanceof String) ? (String) values[kABOrganizationProperty] : ""; } return organization; } /** * Gets the value of the kABAIMInstantProperty constant. * * @return the value of the kABAIMInstantProperty constant */ public static native long kABAIMInstantProperty(); /** * Gets the value of the kABEmailProperty constant. * * @return the value of the kABEmailProperty constant */ public static native long kABEmailProperty(); /** * Gets the value of the kABFirstNameProperty constant. * * @return the value of the kABFirstNameProperty constant */ public static native long kABFirstNameProperty(); /** * Gets the value of the kABFirstNamePhoneticProperty constant. * * @return the value of the kABFirstNamePhoneticProperty constant */ public static native long kABFirstNamePhoneticProperty(); /** * Gets the value of the kABICQInstantProperty constant. * * @return the value of the kABICQInstantProperty constant */ public static native long kABICQInstantProperty(); /** * Gets the value of the kABJabberInstantProperty constant. * * @return the value of the kABJabberInstantProperty constant */ public static native long kABJabberInstantProperty(); /** * Gets the value of the kABLastNameProperty constant. * * @return the value of the kABLastNameProperty constant */ public static native long kABLastNameProperty(); /** * Gets the value of the kABLastNamePhoneticProperty constant. * * @return the value of the kABLastNamePhoneticProperty constant */ public static native long kABLastNamePhoneticProperty(); /** * Gets the value of the kABMiddleNameProperty constant. * * @return the value of the kABMiddleNameProperty constant */ public static native long kABMiddleNameProperty(); /** * Gets the value of the kABMiddleNamePhoneticProperty constant. * * @return the value of the kABMiddleNamePhoneticProperty constant */ public static native long kABMiddleNamePhoneticProperty(); /** * Gets the value of the kABMSNInstantProperty constant. * * @return the value of the kABMSNInstantProperty constant */ public static native long kABMSNInstantProperty(); /** * Gets the value of the kABNicknameProperty constant. * * @return the value of the kABNicknameProperty constant */ public static native long kABNicknameProperty(); /** * Gets the value of the kABOrganizationProperty constant. * * @return the value of the kABOrganizationProperty constant */ public static native long kABOrganizationProperty(); /** * Gets the value of the kABPersonFlags constant. * * @return the value of the kABPersonFlags constant */ public static native long kABPersonFlags(); /** * Gets the value of the kABPhoneProperty constant. * * @return the value of the kABPhoneProperty constant */ public static native long kABPhoneProperty(); /** * Gets the value of the kABYahooInstantProperty constant. * * @return the value of the kABYahooInstantProperty constant */ public static native long kABYahooInstantProperty(); /** * Gets the value of the kABMaidenNameProperty constant. * * @return the value of the kABMaidenNameProperty constant */ public static native long kABMaidenNameProperty(); /** * Gets the value of the kABBirthdayProperty constant. * * @return the value of the kABBirthdayProperty constant */ public static native long kABBirthdayProperty(); /** * Gets the value of the kABJobTitleProperty constant. * * @return the value of the kABJobTitleProperty constant */ public static native long kABJobTitleProperty(); /** * Gets the value of the kABHomePageProperty constant. * * @return the value of the kABHomePageProperty constant */ public static native long kABHomePageProperty(); /** * Gets the value of the kABURLsProperty constant. * * @return the value of the kABURLsProperty constant */ public static native long kABURLsProperty(); /** * Gets the value of the kABCalendarURIsProperty constant. * * @return the value of the kABCalendarURIsProperty constant */ public static native long kABCalendarURIsProperty(); /** * Gets the value of the kABAddressProperty constant. * * @return the value of the kABAddressProperty constant */ public static native long kABAddressProperty(); /** * Gets the value of the kABOtherDatesProperty constant. * * @return the value of the kABOtherDatesProperty constant */ public static native long kABOtherDatesProperty(); /** * Gets the value of the kABRelatedNamesProperty constant. * * @return the value of the kABRelatedNamesProperty constant */ public static native long kABRelatedNamesProperty(); /** * Gets the value of the kABDepartmentProperty constant. * * @return the value of the kABDepartmentProperty constant */ public static native long kABDepartmentProperty(); /** * Gets the value of the kABInstantMessageProperty constant. * * @return the value of the kABInstantMessageProperty constant */ public static native long kABInstantMessageProperty(); /** * Gets the value of the kABNoteProperty constant. * * @return the value of the kABNoteProperty constant */ public static native long kABNoteProperty(); /** * Gets the value of the kABTitleProperty constant. * * @return the value of the kABTitleProperty constant */ public static native long kABTitleProperty(); /** * Gets the value of the kABSuffixProperty constant. * * @return the value of the kABSuffixProperty constant */ public static native long kABSuffixProperty(); public static native String kABEmailWorkLabel(); public static native String kABEmailHomeLabel(); public static native String kABAddressHomeLabel(); public static native String kABAddressWorkLabel(); public static native String kABPhoneWorkLabel(); public static native String kABPhoneHomeLabel(); public static native String kABPhoneMobileLabel(); public static native String kABPhoneMainLabel(); public static native String kABPhoneWorkFAXLabel(); public static native String kABHomeLabel(); public static native String kABWorkLabel(); public static native String kABOtherLabel(); public static native String kABAddressStreetKey(); public static native String kABAddressCityKey(); public static native String kABAddressStateKey(); public static native String kABAddressZIPKey(); public static native String kABAddressCountryKey(); /** * Determines whether a specific ABPerson property with a specific * value matches the {@link #query} of this * AsyncContactQuery. * * @param property the ABPerson property to check * @param value the value of the property to check * @return true if the specified value of the specified * property matches the query of this * AsyncContactQuery; otherwise, false */ private boolean matches(int property, String value) { return query.matcher(value).find() || ((kABPhoneProperty == property) && phoneNumberMatches(value)); } /** * Determines whether an ABPerson represented by the values of its * {@link #ABPERSON_PROPERTIES} matches {@link #query}. * * @param values the values of the ABPERSON_PROPERTIES which * represent the ABPerson to be determined whether it matches * query * @return true if the ABPerson represented by the * specified values matches query; otherwise, * false */ private boolean matches(Object[] values) { int property = 0; for (Object value : values) { if (value instanceof String) { if (matches(property, (String) value)) return true; } else if (value instanceof Object[]) { Object[] multiValue = (Object[]) value; for (int multiValueIndex = 0; multiValueIndex < multiValue.length; multiValueIndex += 2) { Object subValue = multiValue[multiValueIndex]; if ((subValue instanceof String) && matches(property, (String) subValue)) return true; } } property++; } return false; } /** * Notifies this MacOSXAddrBookContactQuery about a specific * ABPerson. * * @param person a pointer to the ABPerson instance to notify about * @return true if this MacOSXAddrBookContactQuery is to * continue being called; otherwise, false */ private boolean onPerson(long person) { Object[] values = ABRecord_valuesForProperties(person, ABPERSON_PROPERTIES); final String id = ABRecord_uniqueId(person); String displayName = getDisplayName(values); if ((displayName.length() != 0) && (query.matcher(displayName).find() || matches(values))) { List contactDetails = getContactDetails(values, id); if (!contactDetails.isEmpty()) { final MacOSXAddrBookSourceContact sourceContact = new MacOSXAddrBookSourceContact( getContactSource(), displayName, contactDetails); sourceContact.setData(SourceContact.DATA_ID, id); sourceContact.setDisplayDetails(getOrganization(values)); try { byte[] image = ABPerson_imageData(person); if (image != null) sourceContact.setImage(image); } catch (OutOfMemoryError oome) { // Ignore it, the image is not vital. } addQueryResult(sourceContact); } } return (getStatus() == QUERY_IN_PROGRESS); } /** * Performs this AsyncContactQuery in a background Thread. * * @see AsyncContactQuery#run() */ @Override protected void run() { foreachPerson( query.toString(), new PtrCallback() { @Override public boolean callback(long person) { return onPerson(person); } }); } /** * Sets the capabilities of a specific ContactDetail (e.g. * supportedOpSets) depending on the ABPerson property * that it stands for. * * @param contactDetail the ContactDetail to set the capabilities * of * @param property the index in {@link #ABPERSON_PROPERTIES} of the * ABPerson property represented by ContactDetail * @return contactDetail */ private ContactDetail setCapabilities( ContactDetail contactDetail, int property) { List> supportedOpSets = new LinkedList>(); Map, String> preferredProtocols = new HashMap, String>(); // can be added as contacts supportedOpSets.add(OperationSetPersistentPresence.class); switch (property) { case kABAIMInstantProperty: supportedOpSets.add(OperationSetBasicInstantMessaging.class); preferredProtocols.put( OperationSetBasicInstantMessaging.class, ProtocolNames.AIM); break; case kABEmailProperty: supportedOpSets.add(OperationSetBasicTelephony.class); break; case kABICQInstantProperty: supportedOpSets.add(OperationSetBasicInstantMessaging.class); preferredProtocols.put( OperationSetBasicInstantMessaging.class, ProtocolNames.ICQ); break; case kABJabberInstantProperty: supportedOpSets.add(OperationSetBasicInstantMessaging.class); preferredProtocols.put( OperationSetBasicInstantMessaging.class, ProtocolNames.JABBER); supportedOpSets.add(OperationSetBasicTelephony.class); preferredProtocols.put( OperationSetBasicTelephony.class, ProtocolNames.JABBER); break; case kABPhoneProperty: supportedOpSets.add(OperationSetBasicTelephony.class); break; default: break; } contactDetail.setSupportedOpSets(supportedOpSets); if (!preferredProtocols.isEmpty()) contactDetail.setPreferredProtocols(preferredProtocols); return contactDetail; } /** * Callback method when receiving notifications for inserted items. */ public void inserted(long person) { onPerson(person); } /** * Callback method when receiving notifications for updated items. */ public void updated(long person) { SourceContact sourceContact = findSourceContactByID(ABRecord_uniqueId(person)); if(sourceContact != null && sourceContact instanceof MacOSXAddrBookSourceContact) { // let's update the the details Object[] values = ABRecord_valuesForProperties(person, ABPERSON_PROPERTIES); String displayName = getDisplayName(values); final String id = ABRecord_uniqueId(person); MacOSXAddrBookSourceContact editableSourceContact = (MacOSXAddrBookSourceContact)sourceContact; editableSourceContact.setDisplayName(displayName); editableSourceContact.setDisplayDetails(getOrganization(values)); List contactDetails = getContactDetails(values, id); editableSourceContact.setDetails(contactDetails); fireContactChanged(sourceContact); } } /** * Callback method when receiving notifications for deleted items. */ public void deleted(String id) { SourceContact sourceContact = findSourceContactByID(id); if(sourceContact != null) fireContactRemoved(sourceContact); } /** * Find the property from category and subcategories. * * @param category * @param subCategories * @return */ public static int getProperty( Category category, Collection subCategories) { switch(category) { case Personal: if(subCategories.contains(SubCategory.Name)) return kABFirstNameProperty; else if(subCategories.contains(SubCategory.LastName)) return kABLastNameProperty; else if(subCategories.contains(SubCategory.Nickname)) return kABNicknameProperty; else if(subCategories.contains(SubCategory.HomePage)) return kABHomePageProperty; break; case Organization: if(subCategories.contains(SubCategory.JobTitle)) return kABJobTitleProperty; else return kABDepartmentProperty; case Email: return kABEmailProperty; case InstantMessaging: if(subCategories.contains(SubCategory.AIM)) return kABAIMInstantProperty; else if(subCategories.contains(SubCategory.ICQ)) return kABICQInstantProperty; else if(subCategories.contains(SubCategory.Skype)) return kABMSNInstantProperty; else if(subCategories.contains(SubCategory.Jabber)) return kABJabberInstantProperty; else if(subCategories.contains(SubCategory.Yahoo)) return kABYahooInstantProperty; break; case Phone: return kABPhoneProperty; case Address: return kABAddressProperty; default: return -1; } return -1; } /** * Finds the label from category and sub categories. * @param subCategory * @return */ public static String getLabel( int property, SubCategory subCategory, String subProperty) { switch(property) { case kABEmailProperty: if(subCategory == SubCategory.Home) return kABEmailHomeLabel(); if(subCategory == SubCategory.Work) return kABEmailWorkLabel(); break; case kABICQInstantProperty: case kABAIMInstantProperty: case kABYahooInstantProperty: case kABMSNInstantProperty: case kABJabberInstantProperty: return subProperty; case kABPhoneProperty: if(subCategory == SubCategory.Home) return kABPhoneHomeLabel(); if(subCategory == SubCategory.Work) return kABPhoneWorkLabel(); if(subCategory == SubCategory.Fax) return kABPhoneWorkFAXLabel(); if(subCategory == SubCategory.Mobile) return kABPhoneMobileLabel(); if(subCategory == SubCategory.Other) return "other"; break; case kABAddressProperty: if(subCategory == SubCategory.Street) return kABAddressStreetKey(); if(subCategory == SubCategory.City) return kABAddressCityKey(); if(subCategory == SubCategory.State) return kABAddressStateKey(); if(subCategory == SubCategory.Country) return kABAddressCountryKey(); if(subCategory == SubCategory.PostalCode) return kABAddressZIPKey(); break; default: return null; } return null; } /** * Adds a new empty contact, which will be filled in later. * * @param id The ID of the contact to add. */ public void addEmptyContact(String id) { if(id != null) { final MacOSXAddrBookSourceContact sourceContact = new MacOSXAddrBookSourceContact( getContactSource(), null, new LinkedList()); sourceContact.setData(SourceContact.DATA_ID, id); addQueryResult(sourceContact); } } /** * Fires a contact changed event for the given contact. * * @param sourceContact The contact which has changed. */ public void contactChanged(SourceContact sourceContact) { fireContactChanged(sourceContact); } }