From 22cb35dd3d39404ab31a24744b35237963307602 Mon Sep 17 00:00:00 2001 From: Damian Minkov Date: Wed, 18 Jan 2012 12:14:04 +0000 Subject: Fixes ui blocking displaying contact in contactlist or in the tooltip, when checking for contact server stored details like phones etc. --- resources/languages/resources.properties | 1 + .../impl/gui/main/chat/toolBars/MainToolBar.java | 94 ++++++-- .../contactlist/ContactListTreeCellRenderer.java | 122 ++++++++-- .../contactlist/contactsource/MetaUIContact.java | 110 +++++---- .../impl/gui/utils/ExtendedTooltip.java | 8 + .../impl/protocol/icq/InfoRetreiver.java | 65 ++++-- ...OperationSetServerStoredContactInfoIcqImpl.java | 103 ++++++++ .../impl/protocol/jabber/InfoRetreiver.java | 260 +++++++++++---------- ...rationSetServerStoredContactInfoJabberImpl.java | 90 +++++++ .../OperationSetServerStoredContactInfo.java | 24 ++ 10 files changed, 666 insertions(+), 211 deletions(-) diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index 2a3b186..cbeaeda 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -274,6 +274,7 @@ service.gui.LAST=Last service.gui.LEAVE=&Leave service.gui.LIMIT_REACHED_FOR_IP=You have too many existing registrations from the local IP address and the {0} server doesn''t allow to open any more of them. service.gui.LOADING_ROOMS=Loading rooms... +service.gui.LOADING=Loading... service.gui.LOCALLY_ON_HOLD_STATUS=Locally on hold service.gui.LOGIN_NETWORK_ERROR=Unable to log in with account: User name: {0}, Server name: {1}, due to a network failure. Please check your network connection. service.gui.LOGIN_GENERAL_ERROR=An error occurred while logging in with account: User name: {0}, Server name: {1}:{2}. diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java b/src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java index 6947f11..d909b1b 100644 --- a/src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java +++ b/src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java @@ -314,8 +314,13 @@ public class MainToolBar chatPanel.findFileTransferChatTransport() != null); boolean hasPhone = false; + boolean hasTelephony = + !getOperationSetForCapabilities( + chatPanel.chatSession.getTransportsForOperationSet( + OperationSetBasicTelephony.class), + OperationSetBasicTelephony.class).isEmpty(); - if(contact != null) + if(!hasTelephony && contact != null) { Iterator contacts = contact.getContacts(); while(contacts.hasNext()) @@ -324,25 +329,30 @@ public class MainToolBar OperationSetServerStoredContactInfo infoOpSet = c.getProtocolProvider().getOperationSet( OperationSetServerStoredContactInfo.class); - Iterator details = null; + Iterator details; if(infoOpSet != null) { - details = infoOpSet.getAllDetailsForContact(c); + details = infoOpSet.requestAllDetailsForContact(c, + new DetailsListener( + chatPanel.chatSession, callButton)); - while(details.hasNext()) + if(details != null) { - GenericDetail d = details.next(); - if(d instanceof PhoneNumberDetail && - !(d instanceof PagerDetail) && - !(d instanceof FaxDetail)) + while(details.hasNext()) { - PhoneNumberDetail pnd = (PhoneNumberDetail)d; - if(pnd.getNumber() != null && - pnd.getNumber().length() > 0) + GenericDetail d = details.next(); + if(d instanceof PhoneNumberDetail && + !(d instanceof PagerDetail) && + !(d instanceof FaxDetail)) { - hasPhone = true; - break; + PhoneNumberDetail pnd = (PhoneNumberDetail)d; + if(pnd.getNumber() != null && + pnd.getNumber().length() > 0) + { + hasPhone = true; + break; + } } } } @@ -350,10 +360,7 @@ public class MainToolBar } } - callButton.setEnabled(hasPhone || !getOperationSetForCapabilities( - chatPanel.chatSession.getTransportsForOperationSet( - OperationSetBasicTelephony.class), - OperationSetBasicTelephony.class).isEmpty()); + callButton.setEnabled(hasTelephony || hasPhone); desktopSharingButton.setEnabled(!getOperationSetForCapabilities( chatPanel.chatSession.getTransportsForOperationSet( OperationSetDesktopSharingServer.class), @@ -854,4 +861,57 @@ public class MainToolBar optionsButton.setIconImage(ImageLoader.getImage( ImageLoader.CHAT_CONFIGURE_ICON)); } + + /** + * Listens for responses if later received deliver them. + * If meanwhile chat session has been changed ignore any events. + */ + private class DetailsListener + implements OperationSetServerStoredContactInfo.DetailsResponseListener + { + private ChatSession source; + private JButton callButton; + + /** + * Creates listener. + * @param chatSession the source chat session. + * @param callButton the call button. + */ + DetailsListener(ChatSession chatSession, JButton callButton) + { + this.source = chatSession; + this.callButton = callButton; + } + + /** + * Details has been retrieved. + * @param details the details retrieved if any. + */ + public void detailsRetrieved(Iterator details) + { + if(!source.equals(chatSession)) + return; + + if(callButton.isEnabled()) + return; + + while(details.hasNext()) + { + GenericDetail d = details.next(); + if(d instanceof PhoneNumberDetail && + !(d instanceof PagerDetail) && + !(d instanceof FaxDetail)) + { + PhoneNumberDetail pnd = (PhoneNumberDetail)d; + if(pnd.getNumber() != null && + pnd.getNumber().length() > 0) + { + callButton.setEnabled(true); + return; + } + } + } + } + } + } diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java index 1b9745b..e6ab7fd 100644 --- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java @@ -193,7 +193,7 @@ public class ContactListTreeCellRenderer /** * The parent tree. */ - private JTree tree; + private TreeContactList tree; /** * Initializes the panel containing the node. @@ -339,7 +339,7 @@ public class ContactListTreeCellRenderer boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { - this.tree = tree; + this.tree = (TreeContactList)tree; this.row = row; this.isSelected = selected; this.treeNode = (TreeNode) value; @@ -740,8 +740,10 @@ public class ContactListTreeCellRenderer // call button boolean hasPhone = false; + // check for phone stored in contact info only + // if telephony contact is missing if(uiContact.getContactNode().getContactDescriptor().getDescriptor() - instanceof MetaContact) + instanceof MetaContact && telephonyContact == null) { MetaContact metaContact = (MetaContact)uiContact.getContactNode().getContactDescriptor(). @@ -754,27 +756,32 @@ public class ContactListTreeCellRenderer OperationSetServerStoredContactInfo infoOpSet = contact.getProtocolProvider().getOperationSet( OperationSetServerStoredContactInfo.class); - Iterator details = null; + Iterator details; if(infoOpSet != null) { - details = infoOpSet.getAllDetailsForContact(contact); + details = infoOpSet.requestAllDetailsForContact( + contact, + new DetailsListener(treeNode, callButton, uiContact)); - while(details.hasNext()) + if(details != null) { - GenericDetail d = details.next(); - if(d instanceof PhoneNumberDetail && - !(d instanceof PagerDetail) && - !(d instanceof FaxDetail)) + while(details.hasNext()) { - PhoneNumberDetail pnd = (PhoneNumberDetail)d; - if(pnd.getNumber() != null && - pnd.getNumber().length() > 0) + GenericDetail d = details.next(); + if(d instanceof PhoneNumberDetail && + !(d instanceof PagerDetail) && + !(d instanceof FaxDetail)) { - hasPhone = true; - break; - } - } + PhoneNumberDetail pnd = (PhoneNumberDetail)d; + if(pnd.getNumber() != null && + pnd.getNumber().length() > 0) + { + hasPhone = true; + break; + } + } + } } } } @@ -1488,4 +1495,85 @@ public class ContactListTreeCellRenderer desktopSharingButton.setPressedImage(ImageLoader.getImage( ImageLoader.DESKTOP_BUTTON_SMALL_PRESSED)); } + + /** + * Listens for contact details if not cached, we will receive when they + * are retrieved to update current call button state, if meanwhile + * user hasn't changed the current contact. + */ + private class DetailsListener + implements OperationSetServerStoredContactInfo.DetailsResponseListener + { + /** + * The source this listener is created for, if current tree node + * changes ignore any event. + */ + private Object source; + + /** + * The button to change. + */ + private JButton callButton; + + /** + * The ui contact to update after changes. + */ + private UIContact uiContact; + + /** + * Create listener. + * @param source the contact this listener is for, if different + * than current ignore. + * @param callButton + * @param uiContact the contact to refresh + */ + DetailsListener(Object source, JButton callButton, UIContact uiContact) + { + this.source = source; + this.callButton = callButton; + this.uiContact = uiContact; + } + + /** + * Details have been retrieved. + * @param details the details retrieved if any. + */ + public void detailsRetrieved(Iterator details) + { + // if treenode has changed ignore + if(!source.equals(treeNode)) + return; + + // if call button is enabled nothing to check + if(callButton.isEnabled()) + return; + + while(details.hasNext()) + { + GenericDetail d = details.next(); + + if(d instanceof PhoneNumberDetail && + !(d instanceof PagerDetail) && + !(d instanceof FaxDetail)) + { + PhoneNumberDetail pnd = (PhoneNumberDetail)d; + if(pnd.getNumber() != null && + pnd.getNumber().length() > 0) + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + callButton.setEnabled(false); + + tree.refreshContact(uiContact); + } + }); + + return; + } + } + } + } + } } \ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaUIContact.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaUIContact.java index 3aa1062..600f2d7 100644 --- a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaUIContact.java +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaUIContact.java @@ -379,7 +379,7 @@ public class MetaUIContact */ public ExtendedTooltip getToolTip() { - ExtendedTooltip tip = new ExtendedTooltip( + final ExtendedTooltip tip = new ExtendedTooltip( GuiActivator.getUIService().getMainFrame(), true); byte[] avatarImage = metaContact.getAvatar(); @@ -414,47 +414,24 @@ public class MetaUIContact if(infoOpSet != null) { Iterator details = - infoOpSet.getAllDetailsForContact(protocolContact); - - while(details.hasNext()) - { - GenericDetail d = details.next(); - if(d instanceof PhoneNumberDetail && - !(d instanceof FaxDetail) && - !(d instanceof PagerDetail)) - { - PhoneNumberDetail pnd = (PhoneNumberDetail)d; - if(pnd.getNumber() != null && - pnd.getNumber().length() > 0) + infoOpSet.requestAllDetailsForContact(protocolContact, + new OperationSetServerStoredContactInfo + .DetailsResponseListener() { - String localizedType = null; - - if(d instanceof WorkPhoneDetail) - { - localizedType = - GuiActivator.getResources(). - getI18NString( - "service.gui.WORK_PHONE"); - } - else if(d instanceof MobilePhoneDetail) - { - localizedType = - GuiActivator.getResources(). - getI18NString( - "service.gui.MOBILE_PHONE"); - } - else + public void detailsRetrieved( + Iterator details) { - localizedType = - GuiActivator.getResources(). - getI18NString( - "service.gui.PHONE"); + fillTooltipLines(tip, details); } - - tip.addLine(null, pnd.getNumber() + - " (" + localizedType + ")"); - } - } + }); + + if(details != null) + fillTooltipLines(tip, details); + else + { + tip.addLine(null, + GuiActivator.getResources(). + getI18NString("service.gui.LOADING")); } } @@ -472,6 +449,61 @@ public class MetaUIContact } /** + * Fills the tooltip with details. + * @param tip the tooltip to fill + * @param details the available details. + */ + private void fillTooltipLines(ExtendedTooltip tip, + Iterator details) + { + tip.removeAllLines(); + + while(details.hasNext()) + { + GenericDetail d = details.next(); + if(d instanceof PhoneNumberDetail && + !(d instanceof FaxDetail) && + !(d instanceof PagerDetail)) + { + PhoneNumberDetail pnd = (PhoneNumberDetail)d; + if(pnd.getNumber() != null && + pnd.getNumber().length() > 0) + { + String localizedType = null; + + if(d instanceof WorkPhoneDetail) + { + localizedType = + GuiActivator.getResources(). + getI18NString( + "service.gui.WORK_PHONE"); + } + else if(d instanceof MobilePhoneDetail) + { + localizedType = + GuiActivator.getResources(). + getI18NString( + "service.gui.MOBILE_PHONE"); + } + else + { + localizedType = + GuiActivator.getResources(). + getI18NString( + "service.gui.PHONE"); + } + + tip.addLine(null, (pnd.getNumber() + + " (" + localizedType + ")")); + } + } + } + + tip.validate(); + tip.repaint(); + } + + /** * Returns the corresponding ContactNode in the contact list * component data model. * @return the corresponding ContactNode diff --git a/src/net/java/sip/communicator/impl/gui/utils/ExtendedTooltip.java b/src/net/java/sip/communicator/impl/gui/utils/ExtendedTooltip.java index 5bb62b7..7beac81 100644 --- a/src/net/java/sip/communicator/impl/gui/utils/ExtendedTooltip.java +++ b/src/net/java/sip/communicator/impl/gui/utils/ExtendedTooltip.java @@ -197,6 +197,14 @@ public class ExtendedTooltip } /** + * Clear all lines. + */ + public void removeAllLines() + { + linesPanel.removeAll(); + } + + /** * Sets the text that would appear on the bottom of the tooltip. * @param text the text to set */ diff --git a/src/net/java/sip/communicator/impl/protocol/icq/InfoRetreiver.java b/src/net/java/sip/communicator/impl/protocol/icq/InfoRetreiver.java index c9c1d7a..16b9986 100644 --- a/src/net/java/sip/communicator/impl/protocol/icq/InfoRetreiver.java +++ b/src/net/java/sip/communicator/impl/protocol/icq/InfoRetreiver.java @@ -113,37 +113,62 @@ public class InfoRetreiver */ protected List getContactDetails(String uin) { - List result = retreivedDetails.get(uin); + List result = getCachedContactDetails(uin); if(result == null) { - int reqID = requestID++; + return retrieveDetails(uin); + } + + return result; + } - //retrieve the details - long toICQUin = Long.parseLong(uin); - MetaFullInfoRequest infoRequest = - new MetaFullInfoRequest( - Long.parseLong(ownerUin), - reqID, - toICQUin); + /** + * Retrieve details and return them or if missing return an empty list. + * @param uin the uin to search for. + * @return the details or empty list. + */ + protected List retrieveDetails(String uin) + { + int reqID = requestID++; - UserInfoResponseRetriever responseRetriever = - new UserInfoResponseRetriever(reqID); + //retrieve the details + long toICQUin = Long.parseLong(uin); + MetaFullInfoRequest infoRequest = + new MetaFullInfoRequest( + Long.parseLong(ownerUin), + reqID, + toICQUin); - icqProvider.getAimConnection().getInfoService().getOscarConnection() - .sendSnacRequest(infoRequest, responseRetriever); + UserInfoResponseRetriever responseRetriever = + new UserInfoResponseRetriever(reqID); - responseRetriever.waitForLastInfo(60000); + icqProvider.getAimConnection().getInfoService().getOscarConnection() + .sendSnacRequest(infoRequest, responseRetriever); - result = responseRetriever.result; + responseRetriever.waitForLastInfo(60000); - if (result == null) - result = new LinkedList(); + List result = responseRetriever.result; - retreivedDetails.put(uin, result); - } + if (result == null) + result = new LinkedList(); - return new LinkedList(result); + // put even empty result to bypass further retrieve + retreivedDetails.put(uin, responseRetriever.result); + + return responseRetriever.result; + } + + /** + * Request the full info for the given uin if available in cache, + * if missing return null. + * + * @param uin to search for in cache. + * @return list of details. + */ + protected List getCachedContactDetails(String uin) + { + return retreivedDetails.get(uin); } /** diff --git a/src/net/java/sip/communicator/impl/protocol/icq/OperationSetServerStoredContactInfoIcqImpl.java b/src/net/java/sip/communicator/impl/protocol/icq/OperationSetServerStoredContactInfoIcqImpl.java index 9811d85..664061e 100644 --- a/src/net/java/sip/communicator/impl/protocol/icq/OperationSetServerStoredContactInfoIcqImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/icq/OperationSetServerStoredContactInfoIcqImpl.java @@ -10,6 +10,7 @@ import java.util.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.ServerStoredDetails.*; +import net.java.sip.communicator.util.*; /** * @author Damian Minkov @@ -17,7 +18,21 @@ import net.java.sip.communicator.service.protocol.ServerStoredDetails.*; public class OperationSetServerStoredContactInfoIcqImpl implements OperationSetServerStoredContactInfo { + /** + * The logger. + */ + private static final Logger logger = + Logger.getLogger(OperationSetServerStoredContactInfoIcqImpl.class); + private InfoRetreiver infoRetreiver; + + /** + * If we got several listeners for the same contact lets retrieve once + * but deliver result to all. + */ + private Hashtable> + listenersForDetails = + new Hashtable>(); /** * The icq provider that created us. @@ -126,4 +141,92 @@ public class OperationSetServerStoredContactInfoIcqImpl "The icq provider must be signed on the ICQ service before " +"being able to communicate."); } + + /** + * Requests all details existing for the specified contact. + * @param contact the specified contact + * @return a java.util.Iterator over all details existing for the specified + * contact. + */ + public Iterator requestAllDetailsForContact( + final Contact contact, final DetailsResponseListener listener) + { + assertConnected(); + + List res = + infoRetreiver.getCachedContactDetails(contact.getAddress()); + + if(res != null) + { + if(contact.getImage() != null) + { + res.add(new ServerStoredDetails.ImageDetail( + "Image", contact.getImage())); + } + return res.iterator(); + } + + synchronized(listenersForDetails) + { + List ls = + listenersForDetails.get(contact.getAddress()); + + boolean isFirst = false; + if(ls == null) + { + ls = new ArrayList(); + isFirst = true; + listenersForDetails.put(contact.getAddress(), ls); + } + + if(!ls.contains(listener)) + ls.add(listener); + + // there is already scheduled retrieve, will deliver at listener. + if(!isFirst) + return null; + } + + new Thread(new Runnable() + { + public void run() + { + List result = + infoRetreiver.retrieveDetails(contact.getAddress()); + + if(contact.getImage() != null) + { + result.add(new ServerStoredDetails.ImageDetail( + "Image", contact.getImage())); + } + + List listeners; + + synchronized(listenersForDetails) + { + listeners = + listenersForDetails.remove(contact.getAddress()); + } + + if(listeners == null) + return; + + for(DetailsResponseListener l : listeners) + { + try + { + l.detailsRetrieved(result.iterator()); + } + catch(Throwable t) + { + logger.error( + "Error delivering for retrieved details", t); + } + } + } + }, getClass().getName() + ".RetrieveDetails").start(); + + // return null as there is no cache and we will try to retrieve + return null; + } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java b/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java index 695b488..808a124 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java @@ -102,162 +102,186 @@ public class InfoRetreiver */ List getContactDetails(String contactAddress) { - List result = retreivedDetails.get(contactAddress); + List result = getCachedContactDetails(contactAddress); if(result == null) { - result = new LinkedList(); - try - { - XMPPConnection connection = jabberProvider.getConnection(); + return retrieveDetails(contactAddress); + } - if(connection == null || !connection.isAuthenticated()) - return null; + return result; + } - VCard card = new VCard(); - card.load(connection, contactAddress); + /** + * Retrieve details and return them or if missing return an empty list. + * @param contactAddress the address to search for. + * @return the details or empty list. + */ + protected List retrieveDetails(String contactAddress) + { + List result = new LinkedList(); + try + { + XMPPConnection connection = jabberProvider.getConnection(); - String tmp = null; + if(connection == null || !connection.isAuthenticated()) + return null; - tmp = checkForFullName(card); - if(tmp != null) - result.add(new ServerStoredDetails.DisplayNameDetail(tmp)); + VCard card = new VCard(); + card.load(connection, contactAddress); - tmp = card.getFirstName(); - if(tmp != null) - result.add(new ServerStoredDetails.FirstNameDetail(tmp)); + String tmp; - tmp = card.getMiddleName(); - if(tmp != null) - result.add(new ServerStoredDetails.MiddleNameDetail(tmp)); + tmp = checkForFullName(card); + if(tmp != null) + result.add(new ServerStoredDetails.DisplayNameDetail(tmp)); - tmp = card.getLastName(); - if(tmp != null) - result.add(new ServerStoredDetails.LastNameDetail(tmp)); + tmp = card.getFirstName(); + if(tmp != null) + result.add(new ServerStoredDetails.FirstNameDetail(tmp)); - tmp = card.getNickName(); - if(tmp != null) - result.add(new ServerStoredDetails.NicknameDetail(tmp)); + tmp = card.getMiddleName(); + if(tmp != null) + result.add(new ServerStoredDetails.MiddleNameDetail(tmp)); - // Home Details - // addrField one of - // POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET, - // LOCALITY, REGION, PCODE, CTRY - tmp = card.getAddressFieldHome("STREET"); - if(tmp != null) - result.add(new ServerStoredDetails.AddressDetail(tmp)); + tmp = card.getLastName(); + if(tmp != null) + result.add(new ServerStoredDetails.LastNameDetail(tmp)); - tmp = card.getAddressFieldHome("LOCALITY"); - if(tmp != null) - result.add(new ServerStoredDetails.CityDetail(tmp)); + tmp = card.getNickName(); + if(tmp != null) + result.add(new ServerStoredDetails.NicknameDetail(tmp)); - tmp = card.getAddressFieldHome("REGION"); - if(tmp != null) - result.add(new ServerStoredDetails.ProvinceDetail(tmp)); + // Home Details + // addrField one of + // POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET, + // LOCALITY, REGION, PCODE, CTRY + tmp = card.getAddressFieldHome("STREET"); + if(tmp != null) + result.add(new ServerStoredDetails.AddressDetail(tmp)); - tmp = card.getAddressFieldHome("PCODE"); - if(tmp != null) - result.add(new ServerStoredDetails.PostalCodeDetail(tmp)); + tmp = card.getAddressFieldHome("LOCALITY"); + if(tmp != null) + result.add(new ServerStoredDetails.CityDetail(tmp)); + + tmp = card.getAddressFieldHome("REGION"); + if(tmp != null) + result.add(new ServerStoredDetails.ProvinceDetail(tmp)); + + tmp = card.getAddressFieldHome("PCODE"); + if(tmp != null) + result.add(new ServerStoredDetails.PostalCodeDetail(tmp)); // tmp = card.getAddressFieldHome("CTRY"); // if(tmp != null) // result.add(new ServerStoredDetails.CountryDetail(tmp); - // phoneType one of - //VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF + // phoneType one of + //VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF - tmp = card.getPhoneHome("VOICE"); - if(tmp != null) - result.add(new ServerStoredDetails.PhoneNumberDetail(tmp)); + tmp = card.getPhoneHome("VOICE"); + if(tmp != null) + result.add(new ServerStoredDetails.PhoneNumberDetail(tmp)); - tmp = card.getPhoneHome("FAX"); - if(tmp != null) - result.add(new ServerStoredDetails.FaxDetail(tmp)); + tmp = card.getPhoneHome("FAX"); + if(tmp != null) + result.add(new ServerStoredDetails.FaxDetail(tmp)); - tmp = card.getPhoneHome("PAGER"); - if(tmp != null) - result.add(new ServerStoredDetails.PagerDetail(tmp)); + tmp = card.getPhoneHome("PAGER"); + if(tmp != null) + result.add(new ServerStoredDetails.PagerDetail(tmp)); - tmp = card.getPhoneHome("CELL"); - if(tmp != null) - result.add(new ServerStoredDetails.MobilePhoneDetail(tmp)); + tmp = card.getPhoneHome("CELL"); + if(tmp != null) + result.add(new ServerStoredDetails.MobilePhoneDetail(tmp)); - tmp = card.getEmailHome(); - if(tmp != null) - result.add(new ServerStoredDetails.EmailAddressDetail(tmp)); + tmp = card.getEmailHome(); + if(tmp != null) + result.add(new ServerStoredDetails.EmailAddressDetail(tmp)); - // Work Details - // addrField one of - // POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET, - // LOCALITY, REGION, PCODE, CTRY - tmp = card.getAddressFieldWork("STREET"); - if(tmp != null) - result.add(new ServerStoredDetails.WorkAddressDetail(tmp)); + // Work Details + // addrField one of + // POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET, + // LOCALITY, REGION, PCODE, CTRY + tmp = card.getAddressFieldWork("STREET"); + if(tmp != null) + result.add(new ServerStoredDetails.WorkAddressDetail(tmp)); - tmp = card.getAddressFieldWork("LOCALITY"); - if(tmp != null) - result.add(new ServerStoredDetails.WorkCityDetail(tmp)); + tmp = card.getAddressFieldWork("LOCALITY"); + if(tmp != null) + result.add(new ServerStoredDetails.WorkCityDetail(tmp)); - tmp = card.getAddressFieldWork("REGION"); - if(tmp != null) - result.add(new ServerStoredDetails.WorkProvinceDetail(tmp)); + tmp = card.getAddressFieldWork("REGION"); + if(tmp != null) + result.add(new ServerStoredDetails.WorkProvinceDetail(tmp)); - tmp = card.getAddressFieldWork("PCODE"); - if(tmp != null) - result.add(new ServerStoredDetails.WorkPostalCodeDetail(tmp)); + tmp = card.getAddressFieldWork("PCODE"); + if(tmp != null) + result.add(new ServerStoredDetails.WorkPostalCodeDetail(tmp)); // tmp = card.getAddressFieldWork("CTRY"); // if(tmp != null) // result.add(new ServerStoredDetails.WorkCountryDetail(tmp); - // phoneType one of - //VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF - - tmp = card.getPhoneWork("VOICE"); - if(tmp != null) - result.add(new ServerStoredDetails.WorkPhoneDetail(tmp)); - - tmp = card.getPhoneWork("FAX"); - if(tmp != null) - result.add(new WorkFaxDetail(tmp)); - - tmp = card.getPhoneWork("PAGER"); - if(tmp != null) - result.add(new WorkPagerDetail(tmp)); - - tmp = card.getPhoneWork("CELL"); - if(tmp != null) - result.add(new ServerStoredDetails.WorkMobilePhoneDetail(tmp)); - - - tmp = card.getEmailWork(); - if(tmp != null) - result.add(new ServerStoredDetails.EmailAddressDetail(tmp)); - - tmp = card.getOrganization(); - if(tmp != null) - result.add(new ServerStoredDetails.WorkOrganizationNameDetail(tmp)); - - tmp = card.getOrganizationUnit(); - if(tmp != null) - result.add(new WorkDepartmentNameDetail(tmp)); - - byte[] imageBytes = card.getAvatar(); - if(imageBytes != null && imageBytes.length > 0) - result.add(new ServerStoredDetails.ImageDetail( - "Image", imageBytes)); - } - catch (Exception exc) - { - logger.error("Cannot load details for contact " - + this + " : " + exc.getMessage() - , exc); - } + // phoneType one of + //VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF + + tmp = card.getPhoneWork("VOICE"); + if(tmp != null) + result.add(new ServerStoredDetails.WorkPhoneDetail(tmp)); + + tmp = card.getPhoneWork("FAX"); + if(tmp != null) + result.add(new WorkFaxDetail(tmp)); + + tmp = card.getPhoneWork("PAGER"); + if(tmp != null) + result.add(new WorkPagerDetail(tmp)); + + tmp = card.getPhoneWork("CELL"); + if(tmp != null) + result.add(new ServerStoredDetails.WorkMobilePhoneDetail(tmp)); + + + tmp = card.getEmailWork(); + if(tmp != null) + result.add(new ServerStoredDetails.EmailAddressDetail(tmp)); + + tmp = card.getOrganization(); + if(tmp != null) + result.add(new ServerStoredDetails.WorkOrganizationNameDetail(tmp)); + + tmp = card.getOrganizationUnit(); + if(tmp != null) + result.add(new WorkDepartmentNameDetail(tmp)); + + byte[] imageBytes = card.getAvatar(); + if(imageBytes != null && imageBytes.length > 0) + result.add(new ServerStoredDetails.ImageDetail( + "Image", imageBytes)); + } + catch (Throwable exc) + { + logger.error("Cannot load details for contact " + + this + " : " + exc.getMessage() + , exc); } retreivedDetails.put(contactAddress, result); - return new LinkedList(result); + return result; + } + + /** + * request the full info for the given contactAddress if available + * in cache. + * + * @param contactAddress to search for + * @return list of the details if any. + */ + List getCachedContactDetails(String contactAddress) + { + return retreivedDetails.get(contactAddress); } private String checkForFullName(VCard card) diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetServerStoredContactInfoJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetServerStoredContactInfoJabberImpl.java index 11d770c..e900d6e 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetServerStoredContactInfoJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetServerStoredContactInfoJabberImpl.java @@ -10,6 +10,7 @@ import java.util.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.ServerStoredDetails.*; +import net.java.sip.communicator.util.*; /** * @author Damian Minkov @@ -17,8 +18,22 @@ import net.java.sip.communicator.service.protocol.ServerStoredDetails.*; public class OperationSetServerStoredContactInfoJabberImpl implements OperationSetServerStoredContactInfo { + /** + * The logger. + */ + private static final Logger logger = + Logger.getLogger(OperationSetServerStoredContactInfoJabberImpl.class); + private InfoRetreiver infoRetreiver = null; + /** + * If we got several listeners for the same contact lets retrieve once + * but deliver result to all. + */ + private Hashtable> + listenersForDetails = + new Hashtable>(); + protected OperationSetServerStoredContactInfoJabberImpl( InfoRetreiver infoRetreiver) { @@ -96,4 +111,79 @@ public class OperationSetServerStoredContactInfoJabberImpl else return new LinkedList(details).iterator(); } + + /** + * Requests all details existing for the specified contact. + * @param contact the specified contact + * @return a java.util.Iterator over all details existing for the specified + * contact. + */ + public Iterator requestAllDetailsForContact( + final Contact contact, DetailsResponseListener listener) + { + List res = + infoRetreiver.getCachedContactDetails(contact.getAddress()); + + if(res != null) + { + return res.iterator(); + } + + synchronized(listenersForDetails) + { + List ls = + listenersForDetails.get(contact.getAddress()); + + boolean isFirst = false; + if(ls == null) + { + ls = new ArrayList(); + isFirst = true; + listenersForDetails.put(contact.getAddress(), ls); + } + + if(!ls.contains(listener)) + ls.add(listener); + + // there is already scheduled retrieve, will deliver at listener. + if(!isFirst) + return null; + } + + new Thread(new Runnable() + { + public void run() + { + List result = + infoRetreiver.retrieveDetails(contact.getAddress()); + + List listeners; + + synchronized(listenersForDetails) + { + listeners = + listenersForDetails.remove(contact.getAddress()); + } + + if(listeners == null) + return; + + for(DetailsResponseListener l : listeners) + { + try + { + l.detailsRetrieved(result.iterator()); + } + catch(Throwable t) + { + logger.error( + "Error delivering for retrieved details", t); + } + } + } + }, getClass().getName() + ".RetrieveDetails").start(); + + // return null as there is no cache and we will try to retrieve + return null; + } } diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetServerStoredContactInfo.java b/src/net/java/sip/communicator/service/protocol/OperationSetServerStoredContactInfo.java index 48056cc..d81d33c 100644 --- a/src/net/java/sip/communicator/service/protocol/OperationSetServerStoredContactInfo.java +++ b/src/net/java/sip/communicator/service/protocol/OperationSetServerStoredContactInfo.java @@ -80,4 +80,28 @@ public interface OperationSetServerStoredContactInfo * contact. */ public Iterator getAllDetailsForContact(Contact contact); + + /** + * Requests all details existing for the specified contact. + * @param contact the specified contact + * @return a java.util.Iterator over all details existing for the specified + * contact. If there are missing in the local cache null value will + * be returned and they will be scheduled for retrieve. + * The listener will be used to inform that retrieve has finished. + */ + public Iterator requestAllDetailsForContact( + Contact contact, DetailsResponseListener listener); + + /** + * Retrieving details can take some time, this listener will inform + * when retrieving has ended and will return the details if any. + */ + public interface DetailsResponseListener + { + /** + * Informs for details retrieved. + * @param detailIterator the details retrieved if any. + */ + public void detailsRetrieved(Iterator detailIterator); + } } -- cgit v1.1