aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl/googlecontacts
diff options
context:
space:
mode:
authorSebastien Vincent <seb@jitsi.org>2011-03-22 16:23:01 +0000
committerSebastien Vincent <seb@jitsi.org>2011-03-22 16:23:01 +0000
commit76767db08a34b0bdff9217111d680acce62d40dd (patch)
treedd5e7acd9e437df2e506a5b85c957289712289cb /src/net/java/sip/communicator/impl/googlecontacts
parent260fd209ae9a458309d4825740085e04ed75cf2c (diff)
downloadjitsi-76767db08a34b0bdff9217111d680acce62d40dd.zip
jitsi-76767db08a34b0bdff9217111d680acce62d40dd.tar.gz
jitsi-76767db08a34b0bdff9217111d680acce62d40dd.tar.bz2
Initial support for Google Contacts as contact source.
Diffstat (limited to 'src/net/java/sip/communicator/impl/googlecontacts')
-rw-r--r--src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsActivator.java393
-rw-r--r--src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsConnectionImpl.java108
-rw-r--r--src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsEntryImpl.java527
-rw-r--r--src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsQuery.java421
-rw-r--r--src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsServiceImpl.java427
-rw-r--r--src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsSourceService.java273
-rw-r--r--src/net/java/sip/communicator/impl/googlecontacts/configform/AccountSettingsForm.java282
-rw-r--r--src/net/java/sip/communicator/impl/googlecontacts/configform/GoogleContactsConfigForm.java394
-rw-r--r--src/net/java/sip/communicator/impl/googlecontacts/configform/GoogleContactsTableModel.java217
-rw-r--r--src/net/java/sip/communicator/impl/googlecontacts/configform/Resources.java68
-rw-r--r--src/net/java/sip/communicator/impl/googlecontacts/googlecontacts.manifest.mf26
11 files changed, 3136 insertions, 0 deletions
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsActivator.java b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsActivator.java
new file mode 100644
index 0000000..31475dd
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsActivator.java
@@ -0,0 +1,393 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.googlecontacts;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.credentialsstorage.*;
+import net.java.sip.communicator.service.googlecontacts.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.resources.*;
+import net.java.sip.communicator.util.*;
+
+import org.osgi.framework.*;
+
+/**
+ * Activates the Google Contacts Service
+ *
+ * @author Sebastien Vincent
+ */
+public class GoogleContactsActivator implements BundleActivator
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>GoogleContactsActivator</tt> class
+ * and its instances for logging output.
+ */
+ private final Logger logger = Logger.getLogger(
+ GoogleContactsActivator.class);
+
+ /**
+ * The OSGi <tt>ServiceRegistration</tt> of
+ * <tt>GoogleContactsServiceImpl</tt>.
+ */
+ private ServiceRegistration serviceRegistration = null;
+
+ /**
+ * BundleContext from the OSGI bus.
+ */
+ private static BundleContext bundleContext;
+
+ /**
+ * Reference to the configuration service
+ */
+ private static ConfigurationService configService;
+
+ /**
+ * Reference to the credentials service
+ */
+ private static CredentialsStorageService credentialsService;
+
+ /**
+ * Reference to the resource management service
+ */
+ private static ResourceManagementService resourceService;
+
+ /**
+ * The cached reference to the <tt>PhoneNumberI18nService</tt> instance used
+ * by the functionality of the Google Contacts plug-in and fetched from its
+ * <tt>BundleContext</tt>.
+ */
+ private static PhoneNumberI18nService phoneNumberI18nService;
+
+ /**
+ * Google contacts service.
+ */
+ private static GoogleContactsServiceImpl googleContactsService;
+
+ /**
+ * List of contact source service registrations.
+ */
+ private static Map<GoogleContactsSourceService, ServiceRegistration> cssList
+ = new HashMap<GoogleContactsSourceService, ServiceRegistration>();
+
+ /**
+ * Gets the <tt>PhoneNumberI18nService</tt> to be used by the functionality
+ * of the addrbook plug-in.
+ *
+ * @return the <tt>PhoneNumberI18nService</tt> to be used by the
+ * functionality of the addrbook plug-in
+ */
+ public static PhoneNumberI18nService getPhoneNumberI18nService()
+ {
+ if (phoneNumberI18nService == null)
+ {
+ phoneNumberI18nService
+ = ServiceUtils.getService(
+ bundleContext,
+ PhoneNumberI18nService.class);
+ }
+ return phoneNumberI18nService;
+ }
+
+ /**
+ * Returns a reference to a ConfigurationService implementation currently
+ * registered in the bundle context or null if no such implementation was
+ * found.
+ *
+ * @return a currently valid implementation of the ConfigurationService.
+ */
+ public static ConfigurationService getConfigService()
+ {
+ if(configService == null)
+ {
+ ServiceReference confReference
+ = bundleContext.getServiceReference(
+ ConfigurationService.class.getName());
+ configService
+ = (ConfigurationService) bundleContext.getService(
+ confReference);
+ }
+ return configService;
+ }
+
+ /**
+ * Returns a reference to a GoogleContactsService implementation currently
+ * registered in the bundle context or null if no such implementation was
+ * found.
+ *
+ * @return a currently valid implementation of the GoogleContactsService
+ */
+ public static GoogleContactsServiceImpl getGoogleContactsService()
+ {
+ return googleContactsService;
+ }
+
+ /**
+ * Returns a reference to a CredentialsStorageConfigurationService
+ * implementation currently registered in the bundle context or null if no
+ * such implementation was found.
+ *
+ * @return a currently valid implementation of the
+ * CredentialsStorageService.
+ */
+ public static CredentialsStorageService getCredentialsService()
+ {
+ if(credentialsService == null)
+ {
+ ServiceReference confReference
+ = bundleContext.getServiceReference(
+ CredentialsStorageService.class.getName());
+ credentialsService
+ = (CredentialsStorageService) bundleContext.getService(
+ confReference);
+ }
+ return credentialsService;
+ }
+
+ /**
+ * Returns a reference to a ResourceManagementService implementation
+ * currently registered in the bundle context or null if no such
+ * implementation was found.
+ *
+ * @return a currently valid implementation of the
+ * ResourceManagementService.
+ */
+ public static ResourceManagementService getResourceManagementService()
+ {
+ if(resourceService == null)
+ {
+ ServiceReference confReference
+ = bundleContext.getServiceReference(
+ ResourceManagementService.class.getName());
+ resourceService
+ = (ResourceManagementService) bundleContext.getService(
+ confReference);
+ }
+ return resourceService;
+ }
+
+ /**
+ * Starts the Google Contacts service
+ *
+ * @param bundleContext BundleContext
+ * @throws Exception if something goes wrong when starting service
+ */
+ public void start(BundleContext bundleContext)
+ throws Exception
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Started.");
+
+ GoogleContactsActivator.bundleContext = bundleContext;
+ googleContactsService =
+ new GoogleContactsServiceImpl();
+ serviceRegistration =
+ bundleContext.registerService(GoogleContactsService.class.getName(),
+ googleContactsService, null);
+
+ /* registers the configuration form */
+ Dictionary<String, String> properties =
+ new Hashtable<String, String>();
+ properties.put( ConfigurationForm.FORM_TYPE,
+ ConfigurationForm.ADVANCED_TYPE);
+
+ bundleContext.registerService(
+ ConfigurationForm.class.getName(),
+ new LazyConfigurationForm(
+ "net.java.sip.communicator.impl.googlecontacts.configform.GoogleContactsConfigForm",
+ getClass().getClassLoader(),
+ "impl.googlecontacts.PLUGIN_ICON",
+ "impl.googlecontacts.CONFIG_FORM_TITLE",
+ 2000, true),
+ properties);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Google Contacts Service ... [REGISTERED]");
+ }
+
+ /**
+ * Stops the Google Contacts service.
+ *
+ * @param bundleContext BundleContext
+ * @throws Exception if something goes wrong when stopping service
+ */
+ public void stop(BundleContext bundleContext)
+ throws Exception
+ {
+ if (serviceRegistration != null)
+ {
+ serviceRegistration.unregister();
+ serviceRegistration = null;
+ }
+
+ /* remove contact source services */
+ for(Map.Entry<GoogleContactsSourceService, ServiceRegistration> entry :
+ cssList.entrySet())
+ {
+ if (entry.getValue() != null)
+ {
+ try
+ {
+ entry.getValue().unregister();
+ }
+ finally
+ {
+ entry.getKey().stop();
+ }
+ }
+ }
+ cssList.clear();
+
+ GoogleContactsActivator.bundleContext = null;
+ }
+
+ /**
+ * Enable contact source service with specified
+ * <tt>GoogleContactsConnection</tt>.
+ *
+ * @param login login
+ * @param password password
+ */
+ public static void enableContactSource(String login, String password)
+ {
+ GoogleContactsSourceService css = new GoogleContactsSourceService(
+ login, password);
+ ServiceRegistration cssServiceRegistration = null;
+
+ try
+ {
+ cssServiceRegistration
+ = bundleContext.registerService(
+ ContactSourceService.class.getName(),
+ css,
+ null);
+ }
+ finally
+ {
+ if (cssServiceRegistration == null)
+ {
+ css.stop();
+ css = null;
+ }
+ else
+ {
+ cssList.put(css, cssServiceRegistration);
+ }
+ }
+ }
+
+ /**
+ * Enable contact source service with specified
+ * <tt>GoogleContactsConnection</tt>.
+ *
+ * @param cnx <tt>GoogleContactsConnection</tt>
+ */
+ public static void enableContactSource(GoogleContactsConnection cnx)
+ {
+ GoogleContactsSourceService css = new GoogleContactsSourceService(
+ cnx);
+ ServiceRegistration cssServiceRegistration = null;
+
+ try
+ {
+ cssServiceRegistration
+ = bundleContext.registerService(
+ ContactSourceService.class.getName(),
+ css,
+ null);
+ }
+ finally
+ {
+ if (cssServiceRegistration == null)
+ {
+ css.stop();
+ css = null;
+ }
+ else
+ {
+ cssList.put(css, cssServiceRegistration);
+ }
+ }
+ }
+
+ /**
+ * Disable contact source service with specified
+ * <tt>GoogleContactsConnection</tt>.
+ *
+ * @param login login
+ */
+ public static void disableContactSource(String login)
+ {
+ GoogleContactsSourceService found = null;
+
+ for(Map.Entry<GoogleContactsSourceService, ServiceRegistration> entry :
+ cssList.entrySet())
+ {
+ String cssName =
+ entry.getKey().getConnection().getLogin();
+
+ if(cssName.equals(login))
+ {
+ try
+ {
+ entry.getValue().unregister();
+ }
+ finally
+ {
+ entry.getKey().stop();
+ }
+ found = entry.getKey();
+ break;
+ }
+ }
+
+ if(found != null)
+ {
+ cssList.remove(found);
+ }
+ }
+
+ /**
+ * Disable contact source service with specified
+ * <tt>GoogleContactsConnection</tt>.
+ *
+ * @param cnx <tt>GoogleContactsConnection</tt>.
+ */
+ public static void disableContactSource(GoogleContactsConnection cnx)
+ {
+ GoogleContactsSourceService found = null;
+
+ for(Map.Entry<GoogleContactsSourceService, ServiceRegistration> entry :
+ cssList.entrySet())
+ {
+ String cssName =
+ entry.getKey().getConnection().getLogin();
+ String name = cnx.getLogin();
+ if(cssName.equals(name))
+ {
+ try
+ {
+ entry.getValue().unregister();
+ }
+ finally
+ {
+ entry.getKey().stop();
+ }
+ found = entry.getKey();
+ break;
+ }
+ }
+
+ if(found != null)
+ {
+ cssList.remove(found);
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsConnectionImpl.java b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsConnectionImpl.java
new file mode 100644
index 0000000..592c6ba
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsConnectionImpl.java
@@ -0,0 +1,108 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.googlecontacts;
+
+import com.google.gdata.client.contacts.*;
+
+import net.java.sip.communicator.service.googlecontacts.*;
+
+/**
+ * Google Contacts credentials to connect to the service.
+ *
+ * @author Sebastien Vincent
+ */
+public class GoogleContactsConnectionImpl
+ implements GoogleContactsConnection
+{
+ /**
+ * Login.
+ */
+ private final String login;
+
+ /**
+ * Password.
+ */
+ private final String password;
+
+ /**
+ * If the connection is enabled.
+ */
+ private boolean enabled = false;
+
+ /**
+ * Google Contacts service.
+ */
+ private final ContactsService googleService =
+ new ContactsService("GoogleContacts service for Jitsi");
+
+ /**
+ * Constructor.
+ *
+ * @param login the login to connect to the service
+ * @param password the password to connect to the service
+ * @throws Exception if something goes wrong during connection
+ */
+ public GoogleContactsConnectionImpl(String login, String password)
+ throws Exception
+ {
+ this.login = login;
+ this.password = password;
+
+ googleService.setUserCredentials(login, password);
+ googleService.useSsl();
+ }
+
+ /**
+ * Returns the Google service.
+ *
+ * @return the Google service
+ */
+ public ContactsService getGoogleService()
+ {
+ return googleService;
+ }
+
+ /**
+ * Get login.
+ *
+ * @return login to connect to the service
+ */
+ public String getLogin()
+ {
+ return login;
+ }
+
+ /**
+ * get password.
+ *
+ * @return password to connect to the service
+ */
+ public String getPassword()
+ {
+ return password;
+ }
+
+ /**
+ * Returns if the connection is enabled.
+ *
+ * @return true if connection is enabled, false otherwise
+ */
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ /**
+ * Set the connection to be enabled or not.
+ *
+ * @param enabled value to set
+ */
+ public void setEnabled(boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsEntryImpl.java b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsEntryImpl.java
new file mode 100644
index 0000000..1b4f739
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsEntryImpl.java
@@ -0,0 +1,527 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.googlecontacts;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.googlecontacts.*;
+
+import com.google.gdata.data.*;
+import com.google.gdata.data.extensions.*;
+import com.google.gdata.data.contacts.ContactEntry; //disambiguation
+
+/**
+ * Google Contacts entry implementation.
+ *
+ * @author Sebastien Vincent
+ */
+public class GoogleContactsEntryImpl
+ implements GoogleContactsEntry
+{
+ /**
+ * Google Talk protocol type.
+ */
+ private static final String GOOGLETALK_PROTOCOL =
+ "http://schemas.google.com/g/2005#GOOGLE_TALK";
+
+ /**
+ * Google Talk protocol type.
+ */
+ private static final String YAHOO_PROTOCOL =
+ "http://schemas.google.com/g/2005#YAHOO";
+
+ /**
+ * Google Talk protocol type.
+ */
+ private static final String AIM_PROTOCOL =
+ "http://schemas.google.com/g/2005#AIM";
+
+ /**
+ * Google Talk protocol type.
+ */
+ private static final String MSN_PROTOCOL =
+ "http://schemas.google.com/g/2005#MSN";
+
+ /**
+ * Google Talk protocol type.
+ */
+ private static final String ICQ_PROTOCOL =
+ "http://schemas.google.com/g/2005#ICQ";
+
+ /**
+ * Google Talk protocol type.
+ */
+ private static final String JABBER_PROTOCOL =
+ "http://schemas.google.com/g/2005#JABBER";
+
+ /**
+ * Full name.
+ */
+ private String fullName = null;
+
+ /**
+ * Family name.
+ */
+ private String familyName = null;
+
+ /**
+ * Given name.
+ */
+ private String givenName = null;
+
+ /**
+ * Home mails list.
+ */
+ private final List<String> homeMails = new ArrayList<String>();
+
+ /**
+ * Work mails list.
+ */
+ private final List<String> workMails = new ArrayList<String>();
+
+ /**
+ * Home phones list.
+ */
+ private final List<String> homePhones = new ArrayList<String>();
+
+ /**
+ * Work phones list.
+ */
+ private final List<String> workPhones = new ArrayList<String>();
+
+ /**
+ * Mobile phones list.
+ */
+ private final List<String> mobilePhones = new ArrayList<String>();
+
+ /**
+ * IM addresses map.
+ */
+ private final Map<String, IMProtocol> imAddresses =
+ new HashMap<String, IMProtocol>();
+
+ /**
+ * Photo link.
+ */
+ private String photoLink = null;
+
+ /**
+ * Google photo link.
+ */
+ private Link googlePhotoLink = null;
+
+ /**
+ * Get the full name.
+ *
+ * @return full name
+ */
+ public String getFullName()
+ {
+ return fullName;
+ }
+
+ /**
+ * Get the family name.
+ *
+ * @return family name
+ */
+ public String getFamilyName()
+ {
+ return familyName;
+ }
+
+ /**
+ * Get the given name.
+ *
+ * @return given name
+ */
+ public String getGivenName()
+ {
+ return givenName;
+ }
+
+ /**
+ * Returns mails.
+ *
+ * @return mails
+ */
+ public List<String> getAllMails()
+ {
+ List<String> mails = new ArrayList<String>();
+
+ for(String mail : homeMails)
+ {
+ mails.add(mail);
+ }
+
+ for(String mail : workMails)
+ {
+ mails.add(mail);
+ }
+
+ return mails;
+ }
+
+ /**
+ * Adds a home mail address.
+ *
+ * @param mail the mail address
+ */
+ public void addHomeMail(String mail)
+ {
+ homeMails.add(mail);
+ }
+
+ /**
+ * Returns home mail addresses.
+ *
+ * @return home mail addresses
+ */
+ public List<String> getHomeMails()
+ {
+ return homeMails;
+ }
+
+ /**
+ * Adds a work mail address.
+ *
+ * @param mail the mail address
+ */
+ public void addWorkMails(String mail)
+ {
+ workMails.add(mail);
+ }
+
+ /**
+ * Returns work mail addresses.
+ *
+ * @return work mail addresses
+ */
+ public List<String> getWorkMails()
+ {
+ return workMails;
+ }
+
+ /**
+ * Returns telephone numbers.
+ *
+ * @return telephone numbers
+ */
+ public List<String> getAllPhones()
+ {
+ List<String> phones = new ArrayList<String>();
+
+ for(String phone : mobilePhones)
+ {
+ phones.add(phone);
+ }
+
+ for(String phone : homePhones)
+ {
+ phones.add(phone);
+ }
+
+ for(String phone : workPhones)
+ {
+ phones.add(phone);
+ }
+
+ return phones;
+ }
+
+ /**
+ * Adds a work telephone number.
+ *
+ * @param telephoneNumber the work telephone number
+ */
+ public void addWorkPhone(String telephoneNumber)
+ {
+ workPhones.add(telephoneNumber);
+ }
+
+ /**
+ * Returns work telephone numbers.
+ *
+ * @return work telephone numbers
+ */
+ public List<String> getWorkPhones()
+ {
+ return workPhones;
+ }
+
+ /**
+ * Adds a mobile telephone numbers.
+ *
+ * @param telephoneNumber the mobile telephone number
+ */
+ public void addMobilePhone(String telephoneNumber)
+ {
+ mobilePhones.add(telephoneNumber);
+ }
+
+ /**
+ * Returns mobile telephone numbers.
+ *
+ * @return mobile telephone numbers
+ */
+ public List<String> getMobilePhones()
+ {
+ return mobilePhones;
+ }
+
+ /**
+ * Adds a home telephone numbers.
+ *
+ * @param telephoneNumber the home telephone number
+ */
+ public void addHomePhone(String telephoneNumber)
+ {
+ homePhones.add(telephoneNumber);
+ }
+
+ /**
+ * Returns home telephone numbers.
+ *
+ * @return home telephone numbers
+ */
+ public List<String> getHomePhones()
+ {
+ return homePhones;
+ }
+
+ /**
+ * Get the photo full URI.
+ *
+ * @return the photo URI or null if there isn't
+ */
+ public String getPhoto()
+ {
+ return photoLink;
+ }
+
+ /**
+ * Get the Google photo full URI.
+ *
+ * @return the Google photo URI or null if there isn't
+ */
+ public Link getPhotoLink()
+ {
+ return googlePhotoLink;
+ }
+
+ /**
+ * Returns IM addresses.
+ *
+ * @return IM addresses
+ */
+ public Map<String, IMProtocol> getIMAddresses()
+ {
+ return imAddresses;
+ }
+
+ /**
+ * Adds an IM address.
+ *
+ * @param imAddress IM address
+ */
+ public void addIMAddress(String imAddress, IMProtocol protocol)
+ {
+ imAddresses.put(imAddress, protocol);
+ }
+
+ /**
+ * Set information.
+ *
+ * @param contact Google Contacts's <tt>ContactEntry</tt>
+ */
+ public void setField(ContactEntry contact)
+ {
+ Name name = contact.getName();
+
+ if(name != null)
+ {
+ if(name.hasFullName())
+ {
+ fullName = name.getFullName().getValue();
+ }
+
+ if(name.hasFamilyName())
+ {
+ familyName = name.getFamilyName().getValue();
+ }
+
+ if(name.hasGivenName())
+ {
+ givenName = name.getGivenName().getValue();
+ }
+
+ }
+
+ photoLink = contact.getContactPhotoLink().getHref();
+ googlePhotoLink = contact.getContactPhotoLink();
+
+ for(Email mail : contact.getEmailAddresses())
+ {
+ if(mail.getRel().contains("#home"))
+ {
+ homeMails.add(mail.getAddress());
+ }
+ else if(mail.getRel().contains("#work"))
+ {
+ workMails.add(mail.getAddress());
+ }
+ else
+ {
+ homeMails.add(mail.getAddress());
+ }
+ }
+
+ for(PhoneNumber phone : contact.getPhoneNumbers())
+ {
+ if(phone.getRel().contains("#work"))
+ {
+ workPhones.add(phone.getPhoneNumber());
+ }
+ else if(phone.getRel().contains("#mobile"))
+ {
+ mobilePhones.add(phone.getPhoneNumber());
+ }
+ else if(phone.getRel().contains("#home"))
+ {
+ homePhones.add(phone.getPhoneNumber());
+ }
+ else
+ {
+ homePhones.add(phone.getPhoneNumber());
+ }
+ }
+
+ for(Im imAddress : contact.getImAddresses())
+ {
+ String protocol = imAddress.getProtocol();
+ IMProtocol proto;
+
+ if(protocol.equals(GOOGLETALK_PROTOCOL))
+ {
+ proto = GoogleContactsEntry.IMProtocol.GOOGLETALK;
+ }
+ else if(protocol.equals(YAHOO_PROTOCOL))
+ {
+ proto = GoogleContactsEntry.IMProtocol.YAHOO;
+ }
+ else if(protocol.equals(AIM_PROTOCOL))
+ {
+ proto = GoogleContactsEntry.IMProtocol.AIM;
+ }
+ else if(protocol.equals(MSN_PROTOCOL))
+ {
+ proto = GoogleContactsEntry.IMProtocol.MSN;
+ }
+ else if(protocol.equals(ICQ_PROTOCOL))
+ {
+ proto = GoogleContactsEntry.IMProtocol.ICQ;
+ }
+ else if(protocol.equals(JABBER_PROTOCOL))
+ {
+ proto = GoogleContactsEntry.IMProtocol.JABBER;
+ }
+ else
+ {
+ proto = GoogleContactsEntry.IMProtocol.OTHER;
+ }
+
+ imAddresses.put(imAddress.getAddress(), proto);
+ }
+ }
+
+ /**
+ * String representation of the <tt>GoogleContactsEntry</tt>.
+ */
+ @Override
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ if(fullName != null)
+ {
+ buffer.append("Full name: ");
+ buffer.append(fullName);
+ buffer.append("\n");
+ }
+
+ if(givenName != null || familyName != null)
+ {
+ buffer.append("Display name: ");
+ buffer.append(givenName != null ? givenName : "");
+ buffer.append(" ");
+ buffer.append(familyName != null ? familyName : "");
+ buffer.append("\n");
+ }
+
+ if(getAllMails().size() > 0)
+ {
+ buffer.append("Mail:\n");
+
+ for(String mail : getAllMails())
+ {
+ buffer.append("\t");
+ buffer.append(mail);
+ buffer.append("\n");
+ }
+ }
+
+ if(workPhones.size() > 0)
+ {
+ buffer.append("Work phones:\n");
+ for(String phone : workPhones)
+ {
+ buffer.append("\t");
+ buffer.append(phone);
+ buffer.append("\n");
+ }
+ }
+
+ if(homePhones.size() > 0)
+ {
+ buffer.append("Home phones:\n");
+ for(String phone : homePhones)
+ {
+ buffer.append("\t");
+ buffer.append(phone);
+ buffer.append("\n");
+ }
+ }
+
+ if(mobilePhones.size() > 0)
+ {
+ buffer.append("Mobile phones:\n");
+ for(String phone : mobilePhones)
+ {
+ buffer.append("\t");
+ buffer.append(phone);
+ buffer.append("\n");
+ }
+ }
+
+ if(imAddresses.size() > 0)
+ {
+ buffer.append("IM addresses:\n");
+
+ for(Map.Entry<String, IMProtocol> entry : imAddresses.entrySet())
+ {
+ buffer.append("\t");
+ buffer.append(entry.getKey());
+ buffer.append(" (");
+ buffer.append(entry.getValue());
+ buffer.append(")\n");
+ }
+ }
+
+ buffer.append("\n");
+ return buffer.toString();
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsQuery.java b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsQuery.java
new file mode 100644
index 0000000..decfe5e
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsQuery.java
@@ -0,0 +1,421 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.googlecontacts;
+
+import java.util.*;
+import java.util.regex.*;
+
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.googlecontacts.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * Implements <tt>ContactQuery</tt> for Google Contacts.
+ *
+ * @author Sebastien Vincent
+ */
+public class GoogleContactsQuery
+ extends AsyncContactQuery<GoogleContactsSourceService>
+{
+ /**
+ * Maximum results for Google Contacts query.
+ */
+ public static final int GOOGLECONTACTS_MAX_RESULTS = 20;
+
+ /**
+ * Maximum number of results for this instance.
+ */
+ private final int count;
+
+ /**
+ * The Google query.
+ */
+ private GoogleQuery gQuery = null;
+
+ /**
+ * Initializes a new <tt>GoogleContactsQuery</tt> instance which is to
+ * perform a specific <tt>query</tt> on behalf of a specific
+ * <tt>contactSource</tt>.
+ *
+ * @param contactSource the <tt>ContactSourceService</tt> which is to
+ * perform the new <tt>ContactQuery</tt> instance
+ * @param query the <tt>Pattern</tt> for which <tt>contactSource</tt> is
+ * being queried
+ * @param count maximum number of results
+ */
+ protected GoogleContactsQuery(GoogleContactsSourceService
+ contactSource, Pattern query, int count)
+ {
+ super(contactSource, query);
+ this.count = count;
+ }
+
+ /**
+ * Normalizes a <tt>String</tt> phone number by converting alpha characters
+ * to their respective digits on a keypad and then stripping non-digit
+ * characters.
+ *
+ * @param phoneNumber a <tt>String</tt> which represents a phone number to
+ * normalize
+ * @return a <tt>String</tt> which is a normalized form of the specified
+ * <tt>phoneNumber</tt>
+ */
+ protected String normalizePhoneNumber(String phoneNumber)
+ {
+ PhoneNumberI18nService phoneNumberI18nService
+ = GoogleContactsActivator.getPhoneNumberI18nService();
+
+ return
+ (phoneNumberI18nService == null)
+ ? phoneNumber
+ : phoneNumberI18nService.normalize(phoneNumber);
+ }
+
+ /**
+ * Determines whether a specific <tt>String</tt> phone number matches the
+ * {@link #query} of this <tt>AsyncContactQuery</tt>.
+ *
+ * @param phoneNumber the <tt>String</tt> which represents the phone number
+ * to match to the <tt>query</tt> of this <tt>AsyncContactQuery</tt>
+ * @return <tt>true</tt> if the specified <tt>phoneNumber</tt> matches the
+ * <tt>query</tt> of this <tt>AsyncContactQuery</tt>; otherwise,
+ * <tt>false</tt>
+ */
+ protected boolean phoneNumberMatches(String phoneNumber)
+ {
+ /*
+ * PhoneNumberI18nService implements functionality to aid the parsing,
+ * formatting and validation of international phone numbers so attempt to
+ * use it to determine whether the specified phoneNumber matches the
+ * query. For example, check whether the normalized phoneNumber matches
+ * the query.
+ */
+
+ PhoneNumberI18nService phoneNumberI18nService
+ = GoogleContactsActivator.getPhoneNumberI18nService();
+ boolean phoneNumberMatches = false;
+
+ if (phoneNumberI18nService != null)
+ {
+ if (query
+ .matcher(phoneNumberI18nService.normalize(phoneNumber))
+ .find())
+ {
+ phoneNumberMatches = true;
+ }
+ else
+ {
+ /*
+ * The fact that the normalized form of the phoneNumber doesn't
+ * match the query doesn't mean that, for example, it doesn't
+ * match the normalized form of the query. The latter, though,
+ * requires the query to look like a phone number as well. In
+ * order to not accidentally start matching all queries to phone
+ * numbers, it seems justified to normalize the query only when
+ * it is a phone number, not whenever it looks like a piece of a
+ * phone number.
+ */
+
+ String phoneNumberQuery = getPhoneNumberQuery();
+
+ if ((phoneNumberQuery != null)
+ && (phoneNumberQuery.length() != 0))
+ {
+ try
+ {
+ phoneNumberMatches
+ = phoneNumberI18nService.phoneNumbersMatch(
+ phoneNumberQuery,
+ phoneNumber);
+ }
+ catch (IllegalArgumentException iaex)
+ {
+ /*
+ * Ignore it, phoneNumberMatches will remain equal to
+ * false.
+ */
+ }
+ }
+ }
+ }
+ return phoneNumberMatches;
+ }
+
+
+ /**
+ * Create a <tt>SourceContact</tt> from a <tt>GoogleContactsEntry</tt>.
+ *
+ * @param entry <tt>GoogleContactsEntry</tt>
+ */
+ private void onGoogleContactsEntry(GoogleContactsEntry entry)
+ {
+ String displayName = entry.getFullName();
+ if(displayName == null || displayName.length() == 0)
+ {
+ if((entry.getGivenName() == null ||
+ entry.getGivenName().length() == 0) &&
+ (entry.getFamilyName() == null ||
+ entry.getFamilyName().length() == 0))
+ {
+ return;
+ }
+
+ displayName = entry.getGivenName() + " " +
+ entry.getFamilyName();
+ }
+
+ List<ContactDetail> contactDetails = getContactDetails(entry);
+
+ if (!contactDetails.isEmpty())
+ {
+ GenericSourceContact sourceContact
+ = new GenericSourceContact(
+ getContactSource(),
+ displayName,
+ contactDetails);
+
+ try
+ {
+ byte img[] = GoogleContactsServiceImpl.downloadPhoto(
+ ((GoogleContactsEntryImpl)entry).getPhotoLink(),
+ getContactSource().getConnection().
+ getGoogleService());
+ sourceContact.setImage(img);
+ }
+ catch (OutOfMemoryError oome)
+ {
+ // Ignore it, the image is not vital.
+ }
+
+ addQueryResult(sourceContact);
+ }
+ }
+
+ /**
+ * Performs this <tt>AsyncContactQuery</tt> in a background <tt>Thread</tt>.
+ *
+ * @see AsyncContactQuery#run()
+ */
+ @Override
+ protected void run()
+ {
+ GoogleContactsServiceImpl service = (GoogleContactsServiceImpl)
+ GoogleContactsActivator.getGoogleContactsService();
+ gQuery = new GoogleQuery(query);
+
+ GoogleContactsConnection cnx = getContactSource().getConnection();
+
+ if(cnx == null)
+ {
+ return;
+ }
+
+ service.searchContact(
+ cnx,
+ gQuery,
+ count,
+ new GoogleEntryCallback()
+ {
+ public void callback(GoogleContactsEntry entry)
+ {
+ onGoogleContactsEntry(entry);
+ }
+ });
+ }
+
+ /**
+ * Gets the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt>.
+ *
+ * @param entry <tt>GoogleContactsEntry</tt>
+ * @return the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt>
+ */
+ private List<ContactDetail> getContactDetails(GoogleContactsEntry entry)
+ {
+ List<ContactDetail> ret = new LinkedList<ContactDetail>();
+ List<String> homeMails = entry.getHomeMails();
+ List<String> workMails = entry.getWorkMails();
+ List<String> mobilePhones = entry.getMobilePhones();
+ List<String> homePhones = entry.getHomePhones();
+ List<String> workPhones = entry.getWorkPhones();
+ Map<String, GoogleContactsEntry.IMProtocol> ims =
+ entry.getIMAddresses();
+ ContactDetail detail = null;
+
+ for(String mail : homeMails)
+ {
+ detail = new ContactDetail(mail, ContactDetail.CATEGORY_EMAIL,
+ new String[]{ContactDetail.LABEL_HOME});
+ ret.add(detail);
+ }
+ for(String mail : workMails)
+ {
+ detail = new ContactDetail(mail, ContactDetail.CATEGORY_EMAIL,
+ new String[]{ContactDetail.LABEL_WORK});
+ ret.add(detail);
+ }
+
+ for(String homePhone : homePhones)
+ {
+ List<Class<? extends OperationSet>> supportedOpSets
+ = new ArrayList<Class<? extends OperationSet>>(1);
+
+ supportedOpSets.add(OperationSetBasicTelephony.class);
+ homePhone = normalizePhoneNumber(homePhone);
+ detail = new ContactDetail(homePhone,
+ ContactDetail.CATEGORY_PHONE,
+ new String[]{ContactDetail.LABEL_HOME});
+ detail.setSupportedOpSets(supportedOpSets);
+ ret.add(detail);
+ }
+
+ for(String workPhone : workPhones)
+ {
+ List<Class<? extends OperationSet>> supportedOpSets
+ = new ArrayList<Class<? extends OperationSet>>(1);
+
+ supportedOpSets.add(OperationSetBasicTelephony.class);
+ workPhone = normalizePhoneNumber(workPhone);
+ detail = new ContactDetail(workPhone,
+ ContactDetail.CATEGORY_PHONE,
+ new String[]{ContactDetail.LABEL_WORK});
+ detail.setSupportedOpSets(supportedOpSets);
+ ret.add(detail);
+ }
+
+ for(String mobilePhone : mobilePhones)
+ {
+ List<Class<? extends OperationSet>> supportedOpSets
+ = new ArrayList<Class<? extends OperationSet>>(1);
+
+ supportedOpSets.add(OperationSetBasicTelephony.class);
+ mobilePhone = normalizePhoneNumber(mobilePhone);
+ detail = new ContactDetail(mobilePhone,
+ ContactDetail.CATEGORY_PHONE,
+ new String[]{ContactDetail.LABEL_MOBILE});
+ detail.setSupportedOpSets(supportedOpSets);
+ ret.add(detail);
+ }
+
+ for(Map.Entry<String, GoogleContactsEntry.IMProtocol> im :
+ ims.entrySet())
+ {
+ if(im.getValue() != GoogleContactsEntry.IMProtocol.OTHER)
+ {
+ detail = new ContactDetail(im.getKey(),
+ ContactDetail.CATEGORY_INSTANT_MESSAGING,
+ new String[]{im.getValue().toString()});
+
+ setIMCapabilities(detail, im.getValue());
+ ret.add(detail);
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Sets the IM capabilities of a specific <tt>ContactDetail</tt> (e.g.
+ * <tt>supportedOpSets</tt>).
+ *
+ * @param contactDetail the <tt>ContactDetail</tt> to set the capabilities
+ * of
+ * @param protocol protocol
+ * @return <tt>contactDetail</tt>
+ */
+ private ContactDetail setIMCapabilities(
+ ContactDetail contactDetail,
+ GoogleContactsEntry.IMProtocol protocol)
+ {
+ List<Class<? extends OperationSet>> supportedOpSets
+ = new LinkedList<Class<? extends OperationSet>>();
+ Map<Class<? extends OperationSet>, String> preferredProtocols
+ = new HashMap<Class<? extends OperationSet>, String>();
+
+ switch (protocol)
+ {
+ case GOOGLETALK:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.AIM);
+ break;
+ case ICQ:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.ICQ);
+ break;
+ case JABBER:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.JABBER);
+ supportedOpSets.add(OperationSetBasicTelephony.class);
+ preferredProtocols.put(
+ OperationSetBasicTelephony.class,
+ ProtocolNames.JABBER);
+ break;
+ case MSN:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.MSN);
+ break;
+ case YAHOO:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.YAHOO);
+ break;
+ default:
+ break;
+ }
+ contactDetail.setSupportedOpSets(supportedOpSets);
+
+ if (!preferredProtocols.isEmpty())
+ contactDetail.setPreferredProtocols(preferredProtocols);
+
+ return contactDetail;
+ }
+
+ /**
+ * Notifies this <tt>GoogleContactsQuery</tt> that it has stopped performing
+ * in the associated background <tt>Thread</tt>.
+ *
+ * @param completed <tt>true</tt> if this <tt>ContactQuery</tt> has
+ * successfully completed, <tt>false</tt> if an error has been encountered
+ * during its execution
+ * @see AsyncContactQuery#stopped(boolean)
+ */
+ @Override
+ protected void stopped(boolean completed)
+ {
+ try
+ {
+ super.stopped(completed);
+ }
+ finally
+ {
+ getContactSource().stopped(this);
+ }
+ }
+
+ /**
+ * Cancels this <tt>ContactQuery</tt>.
+ *
+ * @see ContactQuery#cancel()
+ */
+ @Override
+ public void cancel()
+ {
+ if(gQuery != null)
+ {
+ gQuery.cancel();
+ }
+ super.cancel();
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsServiceImpl.java b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsServiceImpl.java
new file mode 100644
index 0000000..77efe3c
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsServiceImpl.java
@@ -0,0 +1,427 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.googlecontacts;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+
+import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.credentialsstorage.*;
+import net.java.sip.communicator.service.googlecontacts.*;
+import net.java.sip.communicator.util.*;
+
+import com.google.gdata.client.Service.*;
+import com.google.gdata.client.contacts.*;
+import com.google.gdata.data.*;
+import com.google.gdata.data.contacts.ContactFeed;
+import com.google.gdata.data.contacts.ContactEntry;
+import com.google.gdata.data.extensions.*;
+
+/**
+ * Implementation of Google Contacts service.
+ * We first get {@link #MAX_RESULT} contacts from Google Contacts then
+ * we filter it to get {@link #MAX_RESULT} that matched our query.
+ * If {@link #MAX_RESULT} is not reach, we try with additional
+ * contacts (if there are more than {@link #MAX_RESULT} contacts).
+ *
+ * @author Sebastien Vincent
+ */
+public class GoogleContactsServiceImpl
+ implements GoogleContactsService
+{
+ /**
+ * Logger.
+ */
+ private static final Logger logger =
+ Logger.getLogger(GoogleContactsServiceImpl.class);
+
+ /**
+ * Google Contacts feed URL string.
+ */
+ private static final String feedURL =
+ "https://www.google.com/m8/feeds/contacts/default/full";
+
+ /**
+ * Maximum number of results for a query.
+ */
+ public static final int MAX_RESULT = 20;
+
+ /**
+ * Maximum number of contacts retrieved for a query.
+ */
+ public static final int MAX_NUMBER = 1000;
+
+ /**
+ * List of Google Contacts account.
+ */
+ private final List<GoogleContactsConnectionImpl> accounts =
+ new ArrayList<GoogleContactsConnectionImpl>();
+
+ /**
+ * Path where to store the account settings
+ */
+ private final static String CONFIGURATION_PATH =
+ "net.java.sip.communicator.impl.googlecontacts";
+
+ /**
+ * Constructor.
+ */
+ public GoogleContactsServiceImpl()
+ {
+ loadConfig();
+ }
+
+ /**
+ * Get list of stored connections.
+ *
+ * @return list of connections
+ */
+ public List<GoogleContactsConnectionImpl> getAccounts()
+ {
+ return accounts;
+ }
+
+ /**
+ * Loads configuration.
+ */
+ private void loadConfig()
+ {
+ ConfigurationService configService =
+ GoogleContactsActivator.getConfigService();
+ CredentialsStorageService credentialsService =
+ GoogleContactsActivator.getCredentialsService();
+
+ List<String> list = configService.getPropertyNamesByPrefix(
+ CONFIGURATION_PATH, true);
+
+ for(Object configEntry : list)
+ {
+ String path = configEntry.toString();
+ Object oen = configService.getProperty(path + ".enabled");
+ boolean enabled = Boolean.parseBoolean((String)oen);
+ String login =
+ (String)configService.getProperty(path + ".account");
+ String password = credentialsService.loadPassword(path);
+
+ GoogleContactsConnectionImpl cnx = (GoogleContactsConnectionImpl)
+ getConnection(login, password);
+ cnx.setEnabled(enabled);
+
+ if(cnx != null)
+ {
+ accounts.add(cnx);
+ /* register contact source */
+ if(cnx.isEnabled())
+ {
+ addContactSource(cnx);
+ }
+ }
+ }
+ }
+
+ /**
+ * Perform a search for a contact using regular expression.
+ *
+ * @param cnx <tt>GoogleContactsConnection</tt> to perform the query
+ * @param gQuery Google query
+ * @param count maximum number of matched contacts
+ * @param callback object that will be notified for each new
+ * <tt>GoogleContactsEntry</tt> found
+ * @return list of <tt>GoogleContactsEntry</tt>
+ */
+ public List<GoogleContactsEntry> searchContact(
+ GoogleContactsConnection cnx, GoogleQuery gQuery, int count,
+ GoogleEntryCallback callback)
+ {
+ URL url = null;
+ ContactFeed contactFeed = null;
+ ContactQuery query = null;
+ List<GoogleContactsEntry> ret = new ArrayList<GoogleContactsEntry>();
+ boolean endOfContacts = false;
+ int matchedContacts = 0;
+ int index = 1;
+ GoogleContactsConnectionImpl cnxImpl =
+ (GoogleContactsConnectionImpl)cnx;
+
+ if(count <= 0)
+ {
+ count = MAX_RESULT;
+ }
+
+ try
+ {
+ url = new URL(feedURL);
+ }
+ catch(MalformedURLException e)
+ {
+ logger.info("Malformed URL", e);
+ return ret;
+ }
+
+ if(gQuery.isCancelled())
+ {
+ return ret;
+ }
+
+ while(matchedContacts < count || endOfContacts)
+ {
+ query = new ContactQuery(url);
+ query.setStartIndex(index);
+ query.setMaxResults(MAX_NUMBER);
+ query.setSortOrder(ContactQuery.SortOrder.DESCENDING);
+
+ if(gQuery.isCancelled())
+ {
+ return ret;
+ }
+
+ try
+ {
+ contactFeed = cnxImpl.getGoogleService().query(
+ query, ContactFeed.class);
+ }
+ catch(Exception e)
+ {
+ logger.info(
+ "Problem occurred during Google Contacts retrievment",
+ e);
+ return ret;
+ }
+
+ if(contactFeed.getEntries().size() == 0)
+ {
+ endOfContacts = true;
+ break;
+ }
+
+ for (int i = 0; i < contactFeed.getEntries().size(); i++)
+ {
+ if(gQuery.isCancelled())
+ {
+ return ret;
+ }
+
+ ContactEntry entry = contactFeed.getEntries().get(i);
+
+ if(filter(entry, gQuery.getQueryPattern()))
+ {
+ GoogleContactsEntry gcEntry = null;
+
+ gcEntry = getGoogleContactsEntry(entry);
+ matchedContacts++;
+ ret.add(gcEntry);
+
+ if(callback != null)
+ {
+ callback.callback(gcEntry);
+ }
+
+ if(matchedContacts >= count)
+ {
+ break;
+ }
+ }
+ }
+
+ index += contactFeed.getEntries().size();
+ }
+ return ret;
+ }
+
+ /**
+ * Filter according to <tt>filter</tt>.
+ *
+ * @param entry <tt>ContactEntry</tt>
+ * @param filter regular expression
+ * @return true if entry match the filter, false otherwise
+ */
+ private boolean filter(ContactEntry entry, Pattern filter)
+ {
+ Name name = entry.getName();
+
+ /* try to see if name, mail or phone match */
+
+ if(name != null)
+ {
+ if(name.hasFamilyName())
+ {
+ Matcher m = filter.matcher(name.getFamilyName().getValue());
+ if(m.matches())
+ {
+ return true;
+ }
+ }
+
+ if(name.hasGivenName())
+ {
+ Matcher m = filter.matcher(name.getGivenName().getValue());
+ if(m.find())
+ {
+ return true;
+ }
+ }
+
+ if(name.hasFullName())
+ {
+ Matcher m = filter.matcher(name.getFullName().getValue());
+ if(m.find())
+ {
+ return true;
+ }
+ }
+ }
+
+ for(Email mail : entry.getEmailAddresses())
+ {
+ Matcher m = filter.matcher(mail.getAddress());
+
+ if(m.find())
+ {
+ return true;
+ }
+ }
+
+ for(PhoneNumber phone : entry.getPhoneNumbers())
+ {
+ Matcher m = filter.matcher(phone.getPhoneNumber());
+
+ if(m.find())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get a <tt>GoogleContactsEntry</tt> from a <tt>ContactEntry</tt>
+ *
+ * @param entry <tt>ContactEntry</tt>
+ * @return <tt>GoogleContactsEntry</tt>
+ */
+ private GoogleContactsEntry getGoogleContactsEntry(ContactEntry entry)
+ {
+ GoogleContactsEntryImpl ret = new GoogleContactsEntryImpl();
+
+ ret.setField(entry);
+ return ret;
+ }
+
+ /**
+ * Get the full contacts list.
+ *
+ * @return list of <tt>GoogleContactsEntry</tt>
+ */
+ public List<GoogleContactsEntry> getContacts()
+ {
+ return null;
+ }
+
+ /**
+ * Get a <tt>GoogleContactsConnection</tt>.
+ *
+ * @param login login to connect to the service
+ * @param password password to connect to the service
+ * @return <tt>GoogleContactsConnection</tt>.
+ */
+ public GoogleContactsConnection getConnection(String login,
+ String password)
+ {
+ try
+ {
+ return new GoogleContactsConnectionImpl(login, password);
+ }
+ catch(Exception e)
+ {
+ logger.info("Failed to authenticate Google Contacts", e);
+ return null;
+ }
+ }
+
+ /**
+ * Add a contact source service with the specified.
+ *
+ * <tt>GoogleContactsConnection</tt>.
+ * @param cnx <tt>GoogleContactsConnection</tt>.
+ */
+ public void addContactSource(GoogleContactsConnection cnx)
+ {
+ GoogleContactsActivator.enableContactSource(cnx);
+ }
+
+ /**
+ * Add a contact source service with the specified
+ * <tt>GoogleContactsConnection</tt>.
+ *
+ * @param login login
+ * @param password password
+ */
+ public void addContactSource(String login, String password)
+ {
+ GoogleContactsActivator.enableContactSource(login, password);
+ }
+
+ /**
+ * Add a contact source service with the specified.
+ *
+ * <tt>GoogleContactsConnection</tt>.
+ * @param cnx <tt>GoogleContactsConnection</tt>.
+ */
+ public void removeContactSource(GoogleContactsConnection cnx)
+ {
+ GoogleContactsActivator.disableContactSource(cnx);
+ }
+
+ /**
+ * Remove a contact source service with the specified
+ * <tt>GoogleContactsConnection</tt>.
+ *
+ * @param login login
+ */
+ public void removeContactSource(String login)
+ {
+ GoogleContactsActivator.disableContactSource(login);
+ }
+
+ /**
+ * Retrieve photo of a contact. Adapted from Google sample.
+ *
+ * @param photoLink photo link
+ * @param service
+ * @return byte array containing image photo or null if problem happened
+ */
+ public static byte[] downloadPhoto(ILink photoLink,
+ ContactsService service)
+ {
+ try
+ {
+ if (photoLink != null)
+ {
+ GDataRequest request =
+ service.createLinkQueryRequest(photoLink);
+ request.execute();
+ InputStream in = request.getResponseStream();
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buffer = new byte[4096];
+
+ for (int read = 0 ; (read = in.read(buffer)) != -1;
+ out.write(buffer, 0, read));
+
+ return out.toByteArray();
+ }
+ }
+ catch(Exception e)
+ {
+ logger.info("Failed to retrieve photo of the contact", e);
+ }
+ return null;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsSourceService.java b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsSourceService.java
new file mode 100644
index 0000000..d36c78f
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsSourceService.java
@@ -0,0 +1,273 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license. See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.googlecontacts;
+
+import java.util.*;
+import java.util.regex.*;
+
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.googlecontacts.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implements <tt>ContactSourceService</tt> for Google Contacts.
+ *
+ * @author Sebastien Vincent
+ */
+public class GoogleContactsSourceService
+ implements ExtendedContactSourceService
+{
+ /**
+ * Logger.
+ */
+ private static final Logger logger =
+ Logger.getLogger(GoogleContactsSourceService.class);
+
+ /**
+ * The <tt>List</tt> of <tt>GoogleContactsQuery</tt> instances
+ * which have been started and haven't stopped yet.
+ */
+ private final List<GoogleContactsQuery> queries
+ = new LinkedList<GoogleContactsQuery>();
+
+ /**
+ * Login.
+ */
+ private final String login;
+
+ /**
+ * Password.
+ */
+ private final String password;
+
+ /**
+ * Google Contacts connection.
+ */
+ private GoogleContactsConnection cnx = null;
+
+ /**
+ * Constructor.
+ *
+ * @param login login
+ * @param password password
+ */
+ public GoogleContactsSourceService(String login, String password)
+ {
+ super();
+ this.login = login;
+ this.password = password;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param cnx connection
+ */
+ public GoogleContactsSourceService(GoogleContactsConnection cnx)
+ {
+ super();
+ this.cnx = cnx;
+ this.login = cnx.getLogin();
+ this.password = cnx.getPassword();
+ }
+
+ /**
+ * Queries this search source for the given <tt>searchPattern</tt>.
+ *
+ * @param queryPattern the pattern to search for
+ * @return the created query
+ */
+ public ContactQuery queryContactSource(Pattern queryPattern)
+ {
+ return queryContactSource(queryPattern,
+ GoogleContactsQuery.GOOGLECONTACTS_MAX_RESULTS);
+ }
+
+ /**
+ * Queries this search source for the given <tt>searchPattern</tt>.
+ *
+ * @param queryPattern the pattern to search for
+ * @param count maximum number of contact returned
+ * @return the created query
+ */
+ public ContactQuery queryContactSource(Pattern queryPattern, int count)
+ {
+ GoogleContactsQuery query = new GoogleContactsQuery(this, queryPattern,
+ count);
+
+ synchronized (queries)
+ {
+ queries.add(query);
+ }
+
+ boolean hasStarted = false;
+
+ try
+ {
+ query.start();
+ hasStarted = true;
+ }
+ finally
+ {
+ if (!hasStarted)
+ {
+ synchronized (queries)
+ {
+ if (queries.remove(query))
+ queries.notify();
+ }
+ }
+ }
+
+ return query;
+ }
+
+ /**
+ * Returns the Google Contacts connection.
+ *
+ * @return Google Contacts connection
+ */
+ public GoogleContactsConnectionImpl getConnection()
+ {
+ int s = login.indexOf('@');
+ boolean isGoogleAppsOrGmail = false;
+
+ if(s == -1)
+ {
+ return null;
+ }
+
+ String domain = login.substring((s + 1));
+
+ try
+ {
+ SRVRecord srvRecords[] =
+ NetworkUtils.getSRVRecords("xmpp-client", "tcp", domain);
+
+ if(srvRecords == null)
+ {
+ return null;
+ }
+
+ for(SRVRecord srv : srvRecords)
+ {
+ if(srv.getTarget().endsWith("google.com") ||
+ srv.getTarget().endsWith("google.com."))
+ {
+ isGoogleAppsOrGmail = true;
+ break;
+ }
+ }
+
+ if(isGoogleAppsOrGmail)
+ {
+ if(cnx == null)
+ {
+ cnx = new GoogleContactsConnectionImpl(login, password);
+ }
+ }
+ else
+ {
+ cnx = null;
+ }
+ }
+ catch(Exception e)
+ {
+ logger.info("GoogleContacts connection error", e);
+ return null;
+ }
+
+ return (GoogleContactsConnectionImpl)cnx;
+ }
+
+ /**
+ * Returns a user-friendly string that identifies this contact source.
+ * @return the display name of this contact source
+ */
+ public String getDisplayName()
+ {
+ return login;
+ }
+
+ /**
+ * Returns the identifier of this contact source. Some of the common
+ * identifiers are defined here (For example the CALL_HISTORY identifier
+ * should be returned by all call history implementations of this interface)
+ * @return the identifier of this contact source
+ */
+ public String getIdentifier()
+ {
+ return "GoogleContacts";
+ }
+
+ /**
+ * Queries this search source for the given <tt>queryString</tt>.
+ * @param query the string to search for
+ * @return the created query
+ */
+ public ContactQuery queryContactSource(String query)
+ {
+ return queryContactSource(
+ Pattern.compile(query),
+ GoogleContactsQuery.GOOGLECONTACTS_MAX_RESULTS);
+ }
+
+ /**
+ * Queries this search source for the given <tt>queryString</tt>.
+ *
+ * @param query the string to search for
+ * @param contactCount the maximum count of result contacts
+ * @return the created query
+ */
+ public ContactQuery queryContactSource(String query, int contactCount)
+ {
+ return queryContactSource(Pattern.compile(query), contactCount);
+ }
+
+ /**
+ * Stops this <tt>ContactSourceService</tt> implementation and prepares it
+ * for garbage collection.
+ *
+ * @see AsyncContactSourceService#stop()
+ */
+ public void stop()
+ {
+ boolean interrupted = false;
+
+ synchronized (queries)
+ {
+ while (!queries.isEmpty())
+ {
+ queries.get(0).cancel();
+ try
+ {
+ queries.wait();
+ }
+ catch (InterruptedException iex)
+ {
+ interrupted = true;
+ }
+ }
+ }
+ if (interrupted)
+ Thread.currentThread().interrupt();
+ }
+
+ /**
+ * Notifies this <tt>GoogleContactsSourceService</tt> that a specific
+ * <tt>GoogleContactsQuery</tt> has stopped.
+ *
+ * @param query the <tt>GoogleContactsQuery</tt> which has stopped
+ */
+ void stopped(GoogleContactsQuery query)
+ {
+ synchronized (queries)
+ {
+ if (queries.remove(query))
+ queries.notify();
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/configform/AccountSettingsForm.java b/src/net/java/sip/communicator/impl/googlecontacts/configform/AccountSettingsForm.java
new file mode 100644
index 0000000..8cc961a
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/googlecontacts/configform/AccountSettingsForm.java
@@ -0,0 +1,282 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.googlecontacts.configform;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+import net.java.sip.communicator.util.swing.*;
+import net.java.sip.communicator.impl.googlecontacts.*;
+import net.java.sip.communicator.service.googlecontacts.*;
+
+/**
+ * The page with hostname/port/encryption fields
+ *
+ * @author Sebastien Mazy
+ * @author Sebastien Vincent
+ */
+public class AccountSettingsForm
+ extends SIPCommDialog
+ implements ActionListener
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * component holding the name
+ */
+ private JTextField nameField;
+
+ /**
+ * the component holding the password
+ */
+ private JPasswordField passwordField;
+
+ /**
+ * Save button.
+ */
+ private JButton saveBtn = new JButton(
+ Resources.getString("impl.googlecontacts.SAVE"));
+
+ /**
+ * Cancel button.
+ */
+ private JButton cancelBtn = new JButton(
+ Resources.getString("impl.googlecontacts.CANCEL"));
+
+ /**
+ * Return code.
+ */
+ private int retCode = 0;
+
+ /**
+ * The Google Contacts connection.
+ */
+ private GoogleContactsConnection cnx = null;
+
+ /**
+ * Constructor.
+ */
+ public AccountSettingsForm()
+ {
+ this.setTitle(Resources.getString(
+ "impl.googlecontacts.CONFIG_FORM_TITLE"));
+ getContentPane().add(getContentPanel());
+ setMinimumSize(new Dimension(400, 200));
+ setSize(new Dimension(400, 400));
+ setPreferredSize(new Dimension(400, 200));
+ pack();
+ }
+
+ /**
+ * the panel to display in the card layout of the wizard
+ *
+ * @return this page's panel
+ */
+ public JPanel getContentPanel()
+ {
+ JPanel contentPanel = new TransparentPanel(new BorderLayout());
+ JPanel mainPanel = new TransparentPanel();
+ JPanel basePanel = new TransparentPanel(new GridBagLayout());
+ JPanel btnPanel = new TransparentPanel(new FlowLayout(
+ FlowLayout.RIGHT));
+ BoxLayout boxLayout = new BoxLayout(mainPanel, BoxLayout.Y_AXIS);
+
+ GridBagConstraints c = new GridBagConstraints();
+
+ /* name text field */
+ JLabel nameLabel = new JLabel(
+ Resources.getString("impl.googlecontacts.ACCOUNT_NAME"));
+ this.nameField = new JTextField();
+ nameLabel.setLabelFor(nameField);
+ c.gridx = 0;
+ c.gridy = 0;
+ c.weightx = 0;
+ c.weighty = 0;
+ c.gridwidth = 1;
+ c.insets = new Insets(2, 50, 0, 5);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LINE_START;
+ basePanel.add(nameLabel, c);
+ c.gridx = 1;
+ c.gridy = 0;
+ c.weightx = 1;
+ c.weighty = 0;
+ c.gridwidth = GridBagConstraints.REMAINDER;
+ c.insets = new Insets(2, 5, 0, 50);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LINE_END;
+ basePanel.add(nameField, c);
+ JLabel nameExampleLabel = new JLabel("myaccount@gmail.com");
+ nameExampleLabel.setForeground(Color.GRAY);
+ nameExampleLabel.setFont(nameExampleLabel.getFont().deriveFont(8));
+ c.gridx = 1;
+ c.gridy = 1;
+ c.weightx = 1;
+ c.weighty = 0;
+ c.gridwidth = GridBagConstraints.REMAINDER;
+ c.insets = new Insets(0, 13, 2, 0);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LINE_START;
+ basePanel.add(nameExampleLabel, c);
+
+ JLabel passwordLabel = new JLabel(
+ Resources.getString("impl.googlecontacts.PASSWORD"));
+ this.passwordField = new JPasswordField();
+ nameLabel.setLabelFor(passwordField);
+ c.gridx = 0;
+ c.gridy = 2;
+ c.weightx = 0;
+ c.weighty = 0;
+ c.gridwidth = 1;
+ c.insets = new Insets(2, 50, 0, 5);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LINE_START;
+ basePanel.add(passwordLabel, c);
+ c.gridx = 1;
+ c.gridy = 2;
+ c.weightx = 1;
+ c.weighty = 0;
+ c.gridwidth = GridBagConstraints.REMAINDER;
+ c.insets = new Insets(2, 5, 0, 50);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LINE_END;
+ basePanel.add(passwordField, c);
+
+ mainPanel.setLayout(boxLayout);
+ mainPanel.add(basePanel);
+
+ /* listeners */
+ this.nameField.addActionListener(this);
+ this.passwordField.addActionListener(this);
+ this.saveBtn.addActionListener(this);
+ this.cancelBtn.addActionListener(this);
+
+ btnPanel.add(saveBtn);
+ btnPanel.add(cancelBtn);
+
+ contentPanel.add(mainPanel, BorderLayout.CENTER);
+ contentPanel.add(btnPanel, BorderLayout.SOUTH);
+
+ return contentPanel;
+ }
+
+ /**
+ * Loads the information.
+ *
+ * @param cnx connection
+ */
+ public void loadData(GoogleContactsConnection cnx)
+ {
+ if(cnx != null)
+ {
+ this.nameField.setText(cnx.getLogin());
+ this.passwordField.setText(cnx.getPassword());
+ }
+ else
+ {
+ this.nameField.setText("");
+ this.passwordField.setText("");
+ }
+ }
+
+ /**
+ * Implementation of actionPerformed.
+ *
+ * @param e the ActionEvent triggered
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ Object src = e.getSource();
+
+ if(src == saveBtn)
+ {
+ String login = nameField.getText();
+ String password = new String(passwordField.getPassword());
+
+ cnx = GoogleContactsActivator.getGoogleContactsService().
+ getConnection(login, password);
+
+ if(cnx == null)
+ {
+ JOptionPane.showMessageDialog(
+ this,
+ Resources.getString(
+ "impl.googlecontacts.WRONG_CREDENTIALS",
+ new String[]{login}),
+ Resources.getString(
+ "impl.googlecontacts.WRONG_CREDENTIALS",
+ new String[]{login}),
+ JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ retCode = 1;
+ dispose();
+ }
+ else if(src == cancelBtn)
+ {
+ retCode = 0;
+ dispose();
+ }
+ }
+
+ /**
+ * Get the connection.
+ *
+ * @return GoogleContactsConnection
+ */
+ public GoogleContactsConnection getConnection()
+ {
+ return cnx;
+ }
+
+ /**
+ * All functions implemented in this method will be invoked when user
+ * presses the Escape key.
+ *
+ * @param escaped <tt>true</tt> if this dialog has been closed by pressing
+ * the Esc key; otherwise, <tt>false</tt>
+ */
+ protected void close(boolean escaped)
+ {
+ cancelBtn.doClick();
+ }
+
+ /**
+ * Show the dialog and returns if the user has modified something (create
+ * or modify entry).
+ *
+ * @return true if the user has modified something (create
+ * or modify entry), false otherwise.
+ */
+ public int showDialog()
+ {
+ retCode = 0;
+
+ cnx = null;
+ setVisible(true);
+
+ // this will block until user click on save/cancel/press escape/close
+ // the window
+ setVisible(false);
+ return retCode;
+ }
+
+ /**
+ * Set the name field enable or not
+ *
+ * @param enable parameter to set
+ */
+ public void setNameFieldEnabled(boolean enable)
+ {
+ this.nameField.setEnabled(enable);
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/configform/GoogleContactsConfigForm.java b/src/net/java/sip/communicator/impl/googlecontacts/configform/GoogleContactsConfigForm.java
new file mode 100644
index 0000000..60ad270
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/googlecontacts/configform/GoogleContactsConfigForm.java
@@ -0,0 +1,394 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.googlecontacts.configform;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+import javax.swing.event.*;
+
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.swing.*;
+import net.java.sip.communicator.impl.googlecontacts.*;
+import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.credentialsstorage.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.googlecontacts.*;
+
+/**
+ * This ConfigurationForm shows the list of Google Contacts account and allow
+ * users to manage them.
+ *
+ * @author Sebastien Mazy
+ * @author Sebastien Vincent
+ */
+public class GoogleContactsConfigForm
+ extends TransparentPanel
+ implements ConfigurationForm,
+ ActionListener,
+ ListSelectionListener
+
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * The logger for this class.
+ */
+ private static Logger logger = Logger.getLogger(
+ GoogleContactsConfigForm.class);
+
+ /**
+ * Opens the new directory registration wizard
+ */
+ private JButton newButton = new JButton("+");
+
+ /**
+ * Opens a directory modification dialog
+ */
+ private JButton modifyButton = new JButton(
+ Resources.getString("impl.googlecontacts.EDIT"));
+
+ /**
+ * Pops a directory deletion confirmation dialog
+ */
+ private JButton removeButton = new JButton("-");
+
+ /**
+ * Displays the registered Google Contacts account.
+ */
+ private JTable accountTable = new JTable();
+
+ /**
+ * Contains the new/modify/remove buttons
+ */
+ private TransparentPanel buttonsPanel
+ = new TransparentPanel(new FlowLayout(FlowLayout.LEFT));
+
+ /**
+ * Contains the directoryTable
+ */
+ private JScrollPane scrollPane = new JScrollPane();
+
+ /**
+ * Contains the buttonsPanel,
+ */
+ private JPanel rightPanel = new TransparentPanel(new BorderLayout());
+
+ /**
+ * Contains listPanel and rightPanel
+ */
+ private JPanel mainPanel = this;
+
+ /**
+ * Model for the directoryTable
+ */
+ private GoogleContactsTableModel tableModel =
+ new GoogleContactsTableModel();
+
+ /**
+ * Settings form.
+ */
+ private final AccountSettingsForm settingsForm =
+ new AccountSettingsForm();
+
+ /**
+ * Path where to store the account settings
+ */
+ private final static String CONFIGURATION_PATH =
+ "net.java.sip.communicator.impl.googlecontacts";
+
+ /**
+ * Constructor
+ */
+ public GoogleContactsConfigForm()
+ {
+ super(new BorderLayout());
+ logger.trace("GoogleContacts configuration form.");
+ initComponents();
+ }
+
+ /**
+ * Remove a connection.
+ *
+ * @param cnx connection to save
+ */
+ private void removeConfig(GoogleContactsConnection cnx)
+ {
+ ConfigurationService configService =
+ GoogleContactsActivator.getConfigService();
+ configService.removeProperty(CONFIGURATION_PATH + ".acc" +
+ Math.abs(cnx.getLogin().hashCode()));
+ }
+
+ /**
+ * Save configuration.
+ *
+ * @param cnx connection to save
+ */
+ private void saveConfig(GoogleContactsConnection cnx)
+ {
+ ConfigurationService configService =
+ GoogleContactsActivator.getConfigService();
+ CredentialsStorageService credentialsService =
+ GoogleContactsActivator.getCredentialsService();
+ String login = cnx.getLogin();
+ String path = CONFIGURATION_PATH + ".acc" + Math.abs(login.hashCode());
+
+ configService.setProperty(
+ path,
+ login);
+ configService.setProperty(
+ path + ".account",
+ login);
+ configService.setProperty(
+ path + ".enabled",
+ ((GoogleContactsConnectionImpl)cnx).isEnabled());
+ credentialsService.storePassword(path, cnx.getPassword());
+ }
+
+ /**
+ * Inits the swing components
+ */
+ private void initComponents()
+ {
+ modifyButton.setEnabled(false);
+ removeButton.setEnabled(false);
+
+ newButton.setSize(newButton.getMinimumSize());
+ modifyButton.setSize(modifyButton.getMinimumSize());
+ removeButton.setSize(removeButton.getMinimumSize());
+
+ accountTable.setRowHeight(22);
+ accountTable.setSelectionMode(
+ ListSelectionModel.SINGLE_SELECTION);
+
+ accountTable.setShowHorizontalLines(false);
+ accountTable.setShowVerticalLines(false);
+ accountTable.setModel(tableModel);
+ accountTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
+ accountTable.addMouseListener(new MouseAdapter()
+ {
+ @Override
+ public void mouseClicked(MouseEvent e)
+ {
+ if(e.getClickCount() > 1)
+ {
+ }
+ }
+ });
+
+ settingsForm.setModal(true);
+
+ /* consistency with the accounts config form */
+ rightPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
+ rightPanel.add(buttonsPanel, BorderLayout.NORTH);
+
+ scrollPane.getViewport().add(accountTable);
+ mainPanel.add(scrollPane, BorderLayout.CENTER);
+ mainPanel.add(rightPanel, BorderLayout.SOUTH);
+
+ mainPanel.setPreferredSize(new Dimension(500, 400));
+
+ buttonsPanel.add(newButton);
+ buttonsPanel.add(removeButton);
+ buttonsPanel.add(modifyButton);
+
+ accountTable.getSelectionModel().addListSelectionListener(this);
+
+ newButton.setActionCommand("new");
+ newButton.addActionListener(this);
+ modifyButton.addActionListener(this);
+ modifyButton.setActionCommand("modify");
+ removeButton.addActionListener(this);
+ removeButton.setActionCommand("remove");
+ }
+
+ /**
+ * @see net.java.sip.communicator.service.gui.ConfigurationForm#getTitle
+ */
+ public String getTitle()
+ {
+ return Resources.getString("impl.googlecontacts.CONFIG_FORM_TITLE");
+ }
+
+ /**
+ * @see net.java.sip.communicator.service.gui.ConfigurationForm#getIcon
+ */
+ public byte[] getIcon()
+ {
+ return Resources.getImageInBytes(
+ "GOOGLECONTACTS_CONFIG_FORM_ICON");
+ }
+
+ /**
+ * @see net.java.sip.communicator.service.gui.ConfigurationForm#getForm
+ */
+ public Object getForm()
+ {
+ return this;
+ }
+
+ /**
+ * Required by ConfirgurationForm interface
+ *
+ * Returns the index of this configuration form in the configuration window.
+ * This index is used to put configuration forms in the desired order.
+ * <p>
+ * 0 is the first position
+ * -1 means that the form will be put at the end
+ * </p>
+ * @return the index of this configuration form in the configuration window.
+ *
+ * @see net.java.sip.communicator.service.gui.ConfigurationForm#getIndex
+ */
+ public int getIndex()
+ {
+ return -1;
+ }
+
+ /**
+ * Processes buttons events (new, modify, remove)
+ *
+ * @see java.awt.event.ActionListener#actionPerformed
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ int row = accountTable.getSelectedRow();
+
+ if (e.getActionCommand().equals("new"))
+ {
+ settingsForm.setNameFieldEnabled(true);
+ settingsForm.loadData(null);
+ int ret = settingsForm.showDialog();
+
+ if(ret == 1)
+ {
+ GoogleContactsConnection cnx = settingsForm.getConnection();
+ tableModel.addAccount(cnx, true);
+ new RefreshContactSourceThread(null, cnx).start();
+ saveConfig(cnx);
+ refresh();
+ }
+ }
+
+ if (e.getActionCommand().equals("modify") && row != -1)
+ {
+ settingsForm.setNameFieldEnabled(false);
+ GoogleContactsConnection cnx = tableModel.getAccountAt(row);
+ settingsForm.loadData(cnx);
+
+ int ret = settingsForm.showDialog();
+
+ if(ret == 1)
+ {
+ refresh();
+ }
+ }
+
+ if (e.getActionCommand().equals("remove") && row != -1)
+ {
+ GoogleContactsConnection cnx = tableModel.getAccountAt(row);
+ tableModel.removeAccount(cnx.getLogin());
+ removeConfig(cnx);
+ new RefreshContactSourceThread(cnx, null).start();
+ refresh();
+ }
+ }
+
+ /**
+ * Required by ListSelectionListener. Enables the "modify"
+ * button when a server is selected in the table
+ *
+ * @param e event triggered
+ */
+ public void valueChanged(ListSelectionEvent e)
+ {
+ if(accountTable.getSelectedRow() == -1)
+ {
+ modifyButton.setEnabled(false);
+ removeButton.setEnabled(false);
+ return;
+ }
+ else if(!e.getValueIsAdjusting())
+ {
+ modifyButton.setEnabled(true);
+ removeButton.setEnabled(true);
+ saveConfig(tableModel.getAccountAt(accountTable.getSelectedRow()));
+ }
+ }
+
+ /**
+ * refreshes the table display
+ */
+ private void refresh()
+ {
+ tableModel.fireTableStructureChanged();
+ }
+
+ /**
+ * Indicates if this is an advanced configuration form.
+ * @return <tt>true</tt> if this is an advanced configuration form,
+ * otherwise it returns <tt>false</tt>
+ */
+ public boolean isAdvanced()
+ {
+ return true;
+ }
+
+ /**
+ * Thread that will perform refresh of contact sources.
+ */
+ public static class RefreshContactSourceThread
+ extends Thread
+ {
+ /**
+ * Old connection.
+ */
+ private GoogleContactsConnection oldCnx = null;
+
+ /**
+ * New connection.
+ */
+ private GoogleContactsConnection newCnx = null;
+
+ /**
+ * Constructor.
+ *
+ * @param oldCnx old connection.
+ * @param newCnx new connection.
+ */
+ RefreshContactSourceThread(GoogleContactsConnection oldCnx,
+ GoogleContactsConnection newCnx)
+ {
+ this.oldCnx = oldCnx;
+ this.newCnx = newCnx;
+ }
+
+ /**
+ * Thread entry point.
+ */
+ public void run()
+ {
+ if(oldCnx != null)
+ {
+ GoogleContactsActivator.getGoogleContactsService().
+ removeContactSource(oldCnx);
+ }
+
+ if(newCnx != null)
+ {
+ GoogleContactsActivator.getGoogleContactsService().
+ addContactSource(newCnx);
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/configform/GoogleContactsTableModel.java b/src/net/java/sip/communicator/impl/googlecontacts/configform/GoogleContactsTableModel.java
new file mode 100644
index 0000000..bc51bbc
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/googlecontacts/configform/GoogleContactsTableModel.java
@@ -0,0 +1,217 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.googlecontacts.configform;
+
+import java.util.*;
+
+import javax.swing.table.*;
+
+import net.java.sip.communicator.service.googlecontacts.*;
+import net.java.sip.communicator.impl.googlecontacts.*;
+
+
+/**
+ * A table model suitable for the directories list in
+ * the configuration form. Takes its data in an LdapDirectorySet.
+ *
+ * @author Sebastien Mazy
+ * @author Sebastien Vincent
+ */
+public class GoogleContactsTableModel
+ extends AbstractTableModel
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * Google Contacts service reference.
+ */
+ private final GoogleContactsServiceImpl googleService =
+ GoogleContactsActivator.getGoogleContactsService();
+
+ /**
+ * Add account from table.
+ *
+ * @param cnx account
+ * @param enabled if the account should be enabled
+ */
+ public void addAccount(GoogleContactsConnection cnx, boolean enabled)
+ {
+ if(cnx != null)
+ {
+ ((GoogleContactsConnectionImpl)cnx).setEnabled(enabled);
+ googleService.getAccounts().add((GoogleContactsConnectionImpl)cnx);
+ }
+ }
+
+ /**
+ * Remove account from table.
+ *
+ * @param login account login to remove
+ */
+ public void removeAccount(String login)
+ {
+ Iterator<GoogleContactsConnectionImpl> it =
+ googleService.getAccounts().iterator();
+
+ while(it.hasNext())
+ {
+ GoogleContactsConnection cnx = it.next();
+ if(cnx.getLogin().equals(login))
+ {
+ it.remove();
+ return;
+ }
+ }
+ }
+
+ /**
+ * Returns the title for this column
+ *
+ * @param column the column
+ *
+ * @return the title for this column
+ *
+ * @see javax.swing.table.AbstractTableModel#getColumnName
+ */
+ public String getColumnName(int column)
+ {
+ switch(column)
+ {
+ case 0:
+ return Resources.getString("impl.googlecontacts.ENABLED");
+ case 1:
+ return Resources.getString("impl.googlecontacts.ACCOUNT_NAME");
+ default:
+ throw new IllegalArgumentException("column not found");
+ }
+ }
+
+ /**
+ * Returns the number of rows in the table
+ *
+ * @return the number of rows in the table
+ * @see javax.swing.table.AbstractTableModel#getRowCount
+ */
+ public int getRowCount()
+ {
+ return googleService.getAccounts().size();
+ }
+
+ /**
+ * Returns the number of column in the table
+ *
+ * @return the number of columns in the table
+ *
+ * @see javax.swing.table.AbstractTableModel#getColumnCount
+ */
+ public int getColumnCount()
+ {
+ // 2 columns: "enable" and "account name"
+ return 2;
+ }
+
+ /**
+ * Returns the text for the given cell of the table
+ *
+ * @param row cell row
+ * @param column cell column
+ *
+ * @see javax.swing.table.AbstractTableModel#getValueAt
+ */
+ public Object getValueAt(int row, int column)
+ {
+ switch(column)
+ {
+ case 0:
+ return new Boolean(getAccountAt(row).isEnabled());
+ case 1:
+ return getAccountAt(row).getLogin();
+ default:
+ throw new IllegalArgumentException("column not found");
+ }
+ }
+
+ /**
+ * Returns the account credentials at the row 'row'
+ *
+ * @param row the row
+ *
+ * @return the login/password for the account
+ */
+ public GoogleContactsConnectionImpl getAccountAt(int row)
+ {
+ if(row < 0 || row >= googleService.getAccounts().size())
+ {
+ throw new IllegalArgumentException("row not found");
+ }
+ else
+ {
+ return googleService.getAccounts().get(row);
+ }
+ }
+
+ /**
+ * Returns whether a cell is editable. Only "enable" column (checkboxes)
+ * is editable
+ *
+ * @param row row of the cell
+ * @param col column of the cell
+ *
+ * @return whether the cell is editable
+ */
+ public boolean isCellEditable(int row, int col)
+ {
+ if(col == 0)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Overrides a method that always returned Object.class
+ * Now it will return Boolean.class for the first method,
+ * letting the DefaultTableCellRenderer create checkboxes.
+ *
+ * @param columnIndex index of the column
+ * @return Column class
+ */
+ public Class<?> getColumnClass(int columnIndex)
+ {
+ return getValueAt(0, columnIndex).getClass();
+ }
+
+ /**
+ * Sets a value in an editable cell, that is to say
+ * an enable/disable chekbox in colum 0
+ */
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex)
+ {
+ if(columnIndex != 0)
+ throw new IllegalArgumentException("non editable column!");
+
+ GoogleContactsConfigForm.RefreshContactSourceThread th = null;
+ GoogleContactsConnectionImpl cnx = getAccountAt(rowIndex);
+
+ if(cnx.isEnabled())
+ {
+ th = new GoogleContactsConfigForm.RefreshContactSourceThread(cnx,
+ null);
+ }
+ else
+ {
+ th = new GoogleContactsConfigForm.RefreshContactSourceThread(null,
+ cnx);
+ }
+
+ cnx.setEnabled(!cnx.isEnabled());
+
+ th.start();
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/configform/Resources.java b/src/net/java/sip/communicator/impl/googlecontacts/configform/Resources.java
new file mode 100644
index 0000000..2e01fc4
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/googlecontacts/configform/Resources.java
@@ -0,0 +1,68 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.googlecontacts.configform;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.googlecontacts.*;
+
+/**
+ * The <tt>Resources</tt> class manages the access to the internationalization
+ * properties files and the image resources used in this plugin.
+ *
+ * @author Yana Stamcheva
+ */
+public class Resources
+{
+ /**
+ * Returns an internationalized string corresponding to the given key.
+ *
+ * @param key The key of the string.
+ * @return An internationalized string corresponding to the given key.
+ */
+ public static String getString(String key)
+ {
+ return GoogleContactsActivator.getResourceManagementService()
+ .getI18NString(key);
+ }
+
+ /**
+ * Returns an internationalized string corresponding to the given key.
+ *
+ * @param key The key of the string.
+ * @param params additionnal parameters
+ * @return An internationalized string corresponding to the given key.
+ */
+ public static String getString(String key, String[] params)
+ {
+ return GoogleContactsActivator.getResourceManagementService()
+ .getI18NString(key, params);
+ }
+
+ /**
+ * Loads an image from a given image identifier.
+ *
+ * @param imageID The identifier of the image.
+ * @return The image for the given identifier.
+ */
+ public static ImageIcon getImage(String imageID)
+ {
+ return GoogleContactsActivator.getResourceManagementService().getImage(imageID);
+ }
+
+ /**
+ * Loads an image from a given image identifier.
+ *
+ * @param imageID The identifier of the image.
+ * @return The image for the given identifier.
+ */
+ public static byte[] getImageInBytes(String imageID)
+ {
+ return GoogleContactsActivator.getResourceManagementService().
+ getImageInBytes(imageID);
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/googlecontacts.manifest.mf b/src/net/java/sip/communicator/impl/googlecontacts/googlecontacts.manifest.mf
new file mode 100644
index 0000000..04c9fba
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/googlecontacts/googlecontacts.manifest.mf
@@ -0,0 +1,26 @@
+Bundle-Activator: net.java.sip.communicator.impl.googlecontacts.GoogleContactsActivator
+Bundle-Name: Google Contacts Service Implementation
+Bundle-Description: A bundle that offers access to Google Contacts
+Bundle-Vendor: sip-communicator.org
+Bundle-Version: 0.0.1
+System-Bundle: yes
+Import-Package: org.osgi.framework,
+ net.java.sip.communicator.service.configuration,
+ net.java.sip.communicator.service.credentialsstorage,
+ net.java.sip.communicator.service.contactsource,
+ net.java.sip.communicator.service.gui,
+ net.java.sip.communicator.service.resources,
+ net.java.sip.communicator.service.protocol,
+ net.java.sip.communicator.util,
+ net.java.sip.communicator.util.swing,
+ org.xml.sax,
+ org.xml.sax.helpers,
+ javax.xml.parsers,
+ javax.xml,
+ javax.swing,
+ javax.swing.border,
+ javax.swing.event,
+ javax.swing.table,
+ javax.swing.tree,
+ javax.swing.text,
+Export-Package: net.java.sip.communicator.service.googlecontacts \ No newline at end of file