/* * 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 <tt>ContactQuery</tt> for the Address Book of Mac OS X. * * @author Lyubomir Marinov */ public class MacOSXAddrBookContactQuery extends AbstractAddrBookContactQuery<MacOSXAddrBookContactSourceService> { /** * The <tt>Logger</tt> used by the <tt>MacOSXAddrBookContactQuery</tt> class * and its instances for logging output. */ private static final Logger logger = Logger.getLogger(MacOSXAddrBookContactQuery.class); /** * The properties of <tt>ABPerson</tt> which are to be queried by the * <tt>MacOSXAddrBookContactQuery</tt> 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 <tt>kABAIMInstantProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABAIMInstantProperty = 0; /** * The index of the <tt>kABEmailProperty</tt> <tt>ABPerson</tt> property in * {@link #ABPERSON_PROPERTIES}. */ public static final int kABEmailProperty = 1; /** * The index of the <tt>kABFirstNameProperty</tt> <tt>ABPerson</tt> property * in {@link #ABPERSON_PROPERTIES}. */ public static final int kABFirstNameProperty = 2; /** * The index of the <tt>kABFirstNamePhoneticProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABFirstNamePhoneticProperty = 3; /** * The index of the <tt>kABICQInstantProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABICQInstantProperty = 4; /** * The index of the <tt>kABJabberInstantProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABJabberInstantProperty = 5; /** * The index of the <tt>kABLastNameProperty</tt> <tt>ABPerson</tt> property * in {@link #ABPERSON_PROPERTIES}. */ public static final int kABLastNameProperty = 6; /** * The index of the <tt>kABLastNamePhoneticProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABLastNamePhoneticProperty = 7; /** * The index of the <tt>kABMiddleNameProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABMiddleNameProperty = 8; /** * The index of the <tt>kABMiddleNamePhoneticProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABMiddleNamePhoneticProperty = 9; /** * The index of the <tt>kABMSNInstantProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABMSNInstantProperty = 10; /** * The index of the <tt>kABNicknameProperty</tt> <tt>ABPerson</tt> property * in {@link #ABPERSON_PROPERTIES}. */ public static final int kABNicknameProperty = 11; /** * The index of the <tt>kABOrganizationProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABOrganizationProperty = 15; /** * The index of the <tt>kABPersonFlags</tt> <tt>ABPerson</tt> property in * {@link #ABPERSON_PROPERTIES}. */ public static final int kABPersonFlags = 14; /** * The index of the <tt>kABPhoneProperty</tt> <tt>ABPerson</tt> property in * {@link #ABPERSON_PROPERTIES}. */ public static final int kABPhoneProperty = 12; /** * The flag which indicates that an <tt>ABRecord</tt> is to be displayed as * a company. */ public static final long kABShowAsCompany = 1; /** * The mask which extracts the <tt>kABShowAsXXX</tt> flag from the * <tt>personFlags</tt> of an <tt>ABPerson</tt>. */ public static final long kABShowAsMask = 7; /** * The index of the <tt>kABYahooInstantProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABYahooInstantProperty = 13; /** * The index of the <tt>kABMaidenNameProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABMaidenNameProperty = 16; /** * The index of the <tt>kABBirthdayProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABBirthdayProperty = 17; /** * The index of the <tt>kABJobTitleProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABJobTitleProperty = 18; /** * The index of the <tt>kABHomePageProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABHomePageProperty = 19; /** * The index of the <tt>kABURLsProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABURLsProperty = 20; /** * The index of the <tt>kABCalendarURIsProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABCalendarURIsProperty = 21; /** * The index of the <tt>kABAddressProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABAddressProperty = 22; /** * The index of the <tt>kABOtherDatesProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABOtherDatesProperty = 23; /** * The index of the <tt>kABRelatedNamesProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABRelatedNamesProperty = 24; /** * The index of the <tt>kABDepartmentProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABDepartmentProperty = 25; /** * The index of the <tt>kABNoteProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABNoteProperty = 26; /** * The index of the <tt>kABTitleProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABTitleProperty = 27; /** * The index of the <tt>kABSuffixProperty</tt> <tt>ABPerson</tt> * property in {@link #ABPERSON_PROPERTIES}. */ public static final int kABSuffixProperty = 28; /** * The regex which matches the superfluous parts of an <tt>ABMultiValue</tt> * 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 <tt>MacOSXAddrBookContactQuery</tt> which is to perform * a specific <tt>query</tt> in the Address Book of Mac OS X on behalf of a * specific <tt>MacOSXAddrBookContactSourceService</tt>. * * @param contactSource the <tt>MacOSXAddrBookContactSourceService</tt> * which is to perform the new <tt>ContactQuery</tt> instance * @param query the <tt>Pattern</tt> for which <tt>contactSource</tt> i.e. * the Address Book of Mac OS X is being queried */ public MacOSXAddrBookContactQuery( MacOSXAddrBookContactSourceService contactSource, Pattern query) { super(contactSource, query); } /** * Gets the <tt>imageData</tt> of a specific <tt>ABPerson</tt> instance. * * @param person the pointer to the <tt>ABPerson</tt> instance to get the * <tt>imageData</tt> of * @return the <tt>imageData</tt> of the specified <tt>ABPerson</tt> * instance */ public static native byte[] ABPerson_imageData(long person); /** * Gets the values of a specific set of <tt>ABRecord</tt> properties for a * specific <tt>ABRecord</tt> instance. * * @param record the pointer to the <tt>ABRecord</tt> to get the property * values of * @param properties the set of <tt>ABRecord</tt> properties to get the * values of * @return the values of the specified set of <tt>ABRecord</tt> properties * for the specified <tt>ABRecord</tt> 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 <tt>ContactDetail</tt> instance which is to reperesent * a specific contact address that is the value of a specific * <tt>ABPerson</tt> property and, optionally, has a specific label. * * @param property the index in {@link #ABPERSON_PROPERTIES} of the * <tt>ABPerson</tt> property to be represented by <tt>ContactDetail</tt> * @param contactAddress the contact address to be represented by the new * <tt>ContactDetail</tt> instance * @param label an optional label to be added to the set of labels, if any, * determined by <tt>property</tt> * @param id The id of the detail. * * @return a new <tt>ContactDetail</tt> instance which represents the * specified <tt>contactAddress</tt> */ 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 <tt>SubDirectory</tt> * @return the <tt>SubDirectory</tt> 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 <tt>PtrCallback</tt> for each <tt>ABPerson</tt> * found in the Address Book of Mac OS X which matches a specific * <tt>String</tt> query. * * @param query the <tt>String</tt> for which the Address Book of Mac OS X * is to be queried. <b>Warning</b>: Ignored at the time of this writing. * @param callback the <tt>PtrCallback</tt> to be notified about the * matching <tt>ABPerson</tt>s */ private static native void foreachPerson( String query, PtrCallback callback); /** * Gets the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt> * which is to represent an <tt>ABPerson</tt> specified by the values of its * {@link #ABPERSON_PROPERTIES}. * * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which * represent the <tt>ABPerson</tt> to get the <tt>contactDetails</tt> of * @param id The id of the detail. * * @return the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt> * which is to represent the <tt>ABPerson</tt> specified by <tt>values</tt> */ private List<ContactDetail> getContactDetails(Object[] values, String id) { List<ContactDetail> contactDetails = new LinkedList<ContactDetail>(); 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<ContactDetail> 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 <tt>displayName</tt> to be set on a <tt>SourceContact</tt> * which is to represent an <tt>ABPerson</tt> specified by the values of its * {@link #ABPERSON_PROPERTIES}. * * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which * represent the <tt>ABPerson</tt> to get the <tt>displayName</tt> of * @return the <tt>displayName</tt> to be set on a <tt>SourceContact</tt> * which is to represent the <tt>ABPerson</tt> specified by <tt>values</tt> */ 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 <tt>SourceContact</tt>. * * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which * represent the <tt>ABPerson</tt> to get the organization name of. * * @return The organization name to be set on a <tt>SourceContact</tt>. */ 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 <tt>kABAIMInstantProperty</tt> constant. * * @return the value of the <tt>kABAIMInstantProperty</tt> constant */ public static native long kABAIMInstantProperty(); /** * Gets the value of the <tt>kABEmailProperty</tt> constant. * * @return the value of the <tt>kABEmailProperty</tt> constant */ public static native long kABEmailProperty(); /** * Gets the value of the <tt>kABFirstNameProperty</tt> constant. * * @return the value of the <tt>kABFirstNameProperty</tt> constant */ public static native long kABFirstNameProperty(); /** * Gets the value of the <tt>kABFirstNamePhoneticProperty</tt> constant. * * @return the value of the <tt>kABFirstNamePhoneticProperty</tt> constant */ public static native long kABFirstNamePhoneticProperty(); /** * Gets the value of the <tt>kABICQInstantProperty</tt> constant. * * @return the value of the <tt>kABICQInstantProperty</tt> constant */ public static native long kABICQInstantProperty(); /** * Gets the value of the <tt>kABJabberInstantProperty</tt> constant. * * @return the value of the <tt>kABJabberInstantProperty</tt> constant */ public static native long kABJabberInstantProperty(); /** * Gets the value of the <tt>kABLastNameProperty</tt> constant. * * @return the value of the <tt>kABLastNameProperty</tt> constant */ public static native long kABLastNameProperty(); /** * Gets the value of the <tt>kABLastNamePhoneticProperty</tt> constant. * * @return the value of the <tt>kABLastNamePhoneticProperty</tt> constant */ public static native long kABLastNamePhoneticProperty(); /** * Gets the value of the <tt>kABMiddleNameProperty</tt> constant. * * @return the value of the <tt>kABMiddleNameProperty</tt> constant */ public static native long kABMiddleNameProperty(); /** * Gets the value of the <tt>kABMiddleNamePhoneticProperty</tt> constant. * * @return the value of the <tt>kABMiddleNamePhoneticProperty</tt> constant */ public static native long kABMiddleNamePhoneticProperty(); /** * Gets the value of the <tt>kABMSNInstantProperty</tt> constant. * * @return the value of the <tt>kABMSNInstantProperty</tt> constant */ public static native long kABMSNInstantProperty(); /** * Gets the value of the <tt>kABNicknameProperty</tt> constant. * * @return the value of the <tt>kABNicknameProperty</tt> constant */ public static native long kABNicknameProperty(); /** * Gets the value of the <tt>kABOrganizationProperty</tt> constant. * * @return the value of the <tt>kABOrganizationProperty</tt> constant */ public static native long kABOrganizationProperty(); /** * Gets the value of the <tt>kABPersonFlags</tt> constant. * * @return the value of the <tt>kABPersonFlags</tt> constant */ public static native long kABPersonFlags(); /** * Gets the value of the <tt>kABPhoneProperty</tt> constant. * * @return the value of the <tt>kABPhoneProperty</tt> constant */ public static native long kABPhoneProperty(); /** * Gets the value of the <tt>kABYahooInstantProperty</tt> constant. * * @return the value of the <tt>kABYahooInstantProperty</tt> constant */ public static native long kABYahooInstantProperty(); /** * Gets the value of the <tt>kABMaidenNameProperty</tt> constant. * * @return the value of the <tt>kABMaidenNameProperty</tt> constant */ public static native long kABMaidenNameProperty(); /** * Gets the value of the <tt>kABBirthdayProperty</tt> constant. * * @return the value of the <tt>kABBirthdayProperty</tt> constant */ public static native long kABBirthdayProperty(); /** * Gets the value of the <tt>kABJobTitleProperty</tt> constant. * * @return the value of the <tt>kABJobTitleProperty</tt> constant */ public static native long kABJobTitleProperty(); /** * Gets the value of the <tt>kABHomePageProperty</tt> constant. * * @return the value of the <tt>kABHomePageProperty</tt> constant */ public static native long kABHomePageProperty(); /** * Gets the value of the <tt>kABURLsProperty</tt> constant. * * @return the value of the <tt>kABURLsProperty</tt> constant */ public static native long kABURLsProperty(); /** * Gets the value of the <tt>kABCalendarURIsProperty</tt> constant. * * @return the value of the <tt>kABCalendarURIsProperty</tt> constant */ public static native long kABCalendarURIsProperty(); /** * Gets the value of the <tt>kABAddressProperty</tt> constant. * * @return the value of the <tt>kABAddressProperty</tt> constant */ public static native long kABAddressProperty(); /** * Gets the value of the <tt>kABOtherDatesProperty</tt> constant. * * @return the value of the <tt>kABOtherDatesProperty</tt> constant */ public static native long kABOtherDatesProperty(); /** * Gets the value of the <tt>kABRelatedNamesProperty</tt> constant. * * @return the value of the <tt>kABRelatedNamesProperty</tt> constant */ public static native long kABRelatedNamesProperty(); /** * Gets the value of the <tt>kABDepartmentProperty</tt> constant. * * @return the value of the <tt>kABDepartmentProperty</tt> constant */ public static native long kABDepartmentProperty(); /** * Gets the value of the <tt>kABInstantMessageProperty</tt> constant. * * @return the value of the <tt>kABInstantMessageProperty</tt> constant */ public static native long kABInstantMessageProperty(); /** * Gets the value of the <tt>kABNoteProperty</tt> constant. * * @return the value of the <tt>kABNoteProperty</tt> constant */ public static native long kABNoteProperty(); /** * Gets the value of the <tt>kABTitleProperty</tt> constant. * * @return the value of the <tt>kABTitleProperty</tt> constant */ public static native long kABTitleProperty(); /** * Gets the value of the <tt>kABSuffixProperty</tt> constant. * * @return the value of the <tt>kABSuffixProperty</tt> 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 <tt>ABPerson</tt> property with a specific * <tt>value</tt> matches the {@link #query} of this * <tt>AsyncContactQuery</tt>. * * @param property the <tt>ABPerson</tt> property to check * @param value the value of the <tt>property</tt> to check * @return <tt>true</tt> if the specified <tt>value</tt> of the specified * <tt>property</tt> matches the <tt>query</tt> of this * <tt>AsyncContactQuery</tt>; otherwise, <tt>false</tt> */ private boolean matches(int property, String value) { return query.matcher(value).find() || ((kABPhoneProperty == property) && phoneNumberMatches(value)); } /** * Determines whether an <tt>ABPerson</tt> represented by the values of its * {@link #ABPERSON_PROPERTIES} matches {@link #query}. * * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which * represent the <tt>ABPerson</tt> to be determined whether it matches * <tt>query</tt> * @return <tt>true</tt> if the <tt>ABPerson</tt> represented by the * specified <tt>values</tt> matches <tt>query</tt>; otherwise, * <tt>false</tt> */ 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 <tt>MacOSXAddrBookContactQuery</tt> about a specific * <tt>ABPerson</tt>. * * @param person a pointer to the <tt>ABPerson</tt> instance to notify about * @return <tt>true</tt> if this <tt>MacOSXAddrBookContactQuery</tt> is to * continue being called; otherwise, <tt>false</tt> */ 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<ContactDetail> 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 <tt>AsyncContactQuery</tt> in a background <tt>Thread</tt>. * * @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 <tt>ContactDetail</tt> (e.g. * <tt>supportedOpSets</tt>) depending on the <tt>ABPerson</tt> property * that it stands for. * * @param contactDetail the <tt>ContactDetail</tt> to set the capabilities * of * @param property the index in {@link #ABPERSON_PROPERTIES} of the * <tt>ABPerson</tt> property represented by <tt>ContactDetail</tt> * @return <tt>contactDetail</tt> */ private ContactDetail setCapabilities( ContactDetail contactDetail, int property) { List<Class<? extends OperationSet>> supportedOpSets = new LinkedList<Class<? extends OperationSet>>(); Map<Class<? extends OperationSet>, String> preferredProtocols = new HashMap<Class<? extends OperationSet>, 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<ContactDetail> 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<SubCategory> 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<ContactDetail>()); 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); } }