A new custom contact action service allowing to represent more actions for a contact entry in the contact list.

cusax-fix
Yana Stamcheva 13 years ago
parent 3301f2a10d
commit 8992d147b7

@ -950,7 +950,8 @@
bundle-provdisc,bundle-provdisc-dhcp,bundle-provdisc-mdns,
bundle-provisioning,bundle-addrbook,bundle-plugin-ldap,
bundle-plugin-contactsourceconfig,bundle-plugin-certconfig,
bundle-globalshortcut,bundle-plugin-msofficecomm,bundle-libjitsi"/>
bundle-globalshortcut,bundle-plugin-msofficecomm,bundle-libjitsi,
bundle-customcontactactions"/>
<!--BUNDLE-SC-LAUNCHER-->
<target name="bundle-sc-launcher">
@ -2750,4 +2751,15 @@ javax.swing.event, javax.swing.border"/>
prefix="net/java/sip/communicator/plugin/msofficecomm" />
</jar>
</target>
<!-- BUNDLE-CUSTOM-CONTACT-ACTIONS -->
<target name="bundle-customcontactactions">
<!-- Creates a bundle containing the contact source interfaces.-->
<jar compress="false" destfile="${bundles.dest}/customcontactactions.jar"
manifest="${src}/net/java/sip/communicator/service/customcontactactions/customcontactactions.manifest.mf">
<zipfileset dir="${dest}/net/java/sip/communicator/service/customcontactactions"
prefix="net/java/sip/communicator/service/customcontactactions"/>
</jar>
</target>
</project>

@ -118,7 +118,8 @@ felix.auto.start.60= \
reference:file:sc-bundles/filehistory.jar \
reference:file:sc-bundles/metahistory.jar \
reference:file:sc-bundles/keybindings.jar \
reference:file:sc-bundles/contactsource.jar
reference:file:sc-bundles/contactsource.jar \
reference:file:sc-bundles/customcontactactions.jar
felix.auto.start.66= \
reference:file:sc-bundles/swing-ui.jar \

@ -16,6 +16,7 @@
import net.java.sip.communicator.service.callhistory.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.customcontactactions.*;
import net.java.sip.communicator.service.desktop.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.keybindings.*;
@ -89,6 +90,8 @@ public class GuiActivator implements BundleActivator
private static List<ContactSourceService> contactSources;
private static List<CustomContactActionsService> contactActionsServices;
private static SecurityAuthority securityAuthority;
private static final Map<Object, ProtocolProviderFactory>

@ -613,7 +613,8 @@ else if(d instanceof MobilePhoneDetail)
new ArrayList<String>(),
null,
null,
null)
null,
pnd)
{
public PresenceStatus getPresenceStatus()
{

@ -0,0 +1,233 @@
/*
* Jitsi, 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.gui.main.contactlist;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.skin.*;
import net.java.sip.communicator.util.swing.*;
/**
* The <tt>ChooseCallAccountDialog</tt> is the dialog shown when calling a
* contact in order to let the user choose the account he'd prefer to use in
* order to call this contact.
*
* @author Yana Stamcheva
* @author Adam Netocny
*/
public class ChooseUIContactDetailPopupMenu
extends SIPCommPopupMenu
implements Skinnable
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
/**
* The invoker component.
*/
private final JComponent invoker;
/**
* Creates this dialog by specifying a list of telephony contacts to choose
* from.
*
* @param invoker the invoker of this pop up
* @param telephonyObjects the list of telephony contacts to select through
* @param opSetClass the operation class, which indicates what action would
* be performed if an item is selected from the list
*/
public ChooseUIContactDetailPopupMenu(JComponent invoker,
List<UIContactDetail> contactDetails,
UIContactDetailAction action)
{
this.invoker = invoker;
this.init(GuiActivator.getResources()
.getI18NString("service.gui.CHOOSE_CONTACT"));
for (UIContactDetail detail : contactDetails)
{
this.addContactDetailItem(detail, action);
}
}
/**
* Initializes and add some common components.
*
* @param infoString the string we'd like to show on the top of this
* popup menu
*/
private void init(String infoString)
{
setInvoker(invoker);
this.add(createInfoLabel(infoString));
this.addSeparator();
this.setFocusable(true);
}
/**
* Adds the given <tt>telephonyContact</tt> to the list of available
* telephony contact.
*
* @param telephonyContact the telephony contact to add
* @param opSetClass the operation set class, that indicates the action that
* would be performed when an item is selected
*/
private void addContactDetailItem(
final UIContactDetail contactDetail,
final UIContactDetailAction contactDetailAction)
{
final ContactMenuItem contactItem
= new ContactMenuItem(contactDetail);
contactItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
contactDetailAction.actionPerformed(contactDetail);
ChooseUIContactDetailPopupMenu.this.setVisible(false);
}
});
add(contactItem);
}
/**
* Shows the dialog at the given location.
*
* @param x the x coordinate
* @param y the y coordinate
*/
public void showPopupMenu(int x, int y)
{
setLocation(x, y);
setVisible(true);
}
/**
* Shows this popup menu regarding to its invoker location.
*/
public void showPopupMenu()
{
Point location = new Point(invoker.getX(),
invoker.getY() + invoker.getHeight());
SwingUtilities
.convertPointToScreen(location, invoker.getParent());
setLocation(location);
setVisible(true);
}
/**
* Creates the info label.
*
* @param infoString the string we'd like to show on the top of this
* popup menu
* @return the created info label
*/
private Component createInfoLabel(String infoString)
{
JMenuItem infoLabel = new JMenuItem();
infoLabel.setEnabled(false);
infoLabel.setFocusable(false);
infoLabel.setText("<html><b>" + infoString + "</b></html>");
return infoLabel;
}
/**
* A custom menu item corresponding to a specific protocol <tt>Contact</tt>.
*/
private class ContactMenuItem
extends JMenuItem
implements Skinnable
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
private final UIContactDetail contact;
public ContactMenuItem(UIContactDetail contact)
{
this.contact = contact;
String itemName = "<html>";
Iterator<String> labels = contact.getLabels();
if (labels != null && labels.hasNext())
while (labels.hasNext())
itemName += "<b style=\"color: gray\">"
+ labels.next().toLowerCase() + "</b> ";
itemName += contact.getAddress() + "</html>";
this.setText(itemName);
loadSkin();
}
/**
* Reloads contact icon.
*/
public void loadSkin()
{
ImageIcon contactIcon = contact.getStatusIcon();
if (contactIcon == null)
{
PresenceStatus status = contact.getPresenceStatus();
BufferedImage statusIcon = null;
if (status != null)
statusIcon = Constants.getStatusIcon(status);
if (statusIcon != null)
contactIcon = ImageLoader.getIndexedProtocolIcon(
statusIcon,
contact.getPreferredProtocolProvider(null));
}
if (contactIcon != null)
this.setIcon(ImageLoader.getIndexedProtocolIcon(
contactIcon.getImage(),
contact.getPreferredProtocolProvider(null)));
}
}
/**
* Reloads all menu items.
*/
public void loadSkin()
{
Component[] components = getComponents();
for(Component component : components)
{
if(component instanceof Skinnable)
{
Skinnable skinnableComponent = (Skinnable) component;
skinnableComponent.loadSkin();
}
}
}
}

@ -8,6 +8,7 @@
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
@ -193,6 +194,11 @@ public class ContactListTreeCellRenderer
*/
private TreeContactList tree;
/**
* A list of the custom action buttons.
*/
private List<JButton> customActionButtons;
/**
* Initializes the panel containing the node.
*/
@ -224,26 +230,7 @@ public ContactListTreeCellRenderer()
constraints.weighty = 1f;
this.add(statusLabel, constraints);
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.NONE;
constraints.gridx = 1;
constraints.gridy = 0;
constraints.weightx = 1f;
constraints.weighty = 0f;
constraints.gridheight = 1;
constraints.gridwidth = 5;
this.add(nameLabel, constraints);
rightLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2));
constraints.anchor = GridBagConstraints.NORTHEAST;
constraints.fill = GridBagConstraints.VERTICAL;
constraints.gridx = 6;
constraints.gridy = 0;
constraints.gridheight = 3;
constraints.weightx = 0f;
constraints.weighty = 1f;
this.add(rightLabel, constraints);
addLabels(5);
callButton.addActionListener(new ActionListener()
{
@ -454,6 +441,16 @@ else if (value instanceof GroupNode)
this.remove(chatButton);
this.remove(addContactButton);
if (customActionButtons != null && customActionButtons.size() > 0)
{
Iterator<JButton> buttonsIter = customActionButtons.iterator();
while (buttonsIter.hasNext())
{
remove(buttonsIter.next());
}
customActionButtons.clear();
}
this.statusLabel.setIcon(
expanded
? openedGroupIcon
@ -584,6 +581,39 @@ else if (isSelected)
return preferredSize;
}
/**
* Adds contact entry labels.
*
* @param nameLabelGridWidth the grid width of the contact entry name
* label
*/
private void addLabels(int nameLabelGridWidth)
{
remove(nameLabel);
remove(rightLabel);
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.NONE;
constraints.gridx = 1;
constraints.gridy = 0;
constraints.weightx = 1f;
constraints.weighty = 0f;
constraints.gridheight = 1;
constraints.gridwidth = nameLabelGridWidth;
this.add(nameLabel, constraints);
rightLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2));
constraints.anchor = GridBagConstraints.NORTHEAST;
constraints.fill = GridBagConstraints.VERTICAL;
constraints.gridx = nameLabelGridWidth + 1;
constraints.gridy = 0;
constraints.gridheight = 3;
constraints.weightx = 0f;
constraints.weighty = 1f;
this.add(rightLabel, constraints);
}
/**
* Initializes the display details component for the given
* <tt>UIContact</tt>.
@ -631,6 +661,16 @@ private void initButtonsPanel(UIContact uiContact)
this.remove(desktopSharingButton);
this.remove(addContactButton);
if (customActionButtons != null && customActionButtons.size() > 0)
{
Iterator<JButton> buttonsIter = customActionButtons.iterator();
while (buttonsIter.hasNext())
{
remove(buttonsIter.next());
}
customActionButtons.clear();
}
if (!isSelected)
return;
@ -652,6 +692,9 @@ private void initButtonsPanel(UIContact uiContact)
+ LEFT_BORDER
+ STATUS_RIGHT_BORDER;
// Re-initialize the x grid.
constraints.gridx = 0;
if (imContact != null)
{
constraints.anchor = GridBagConstraints.WEST;
@ -739,8 +782,8 @@ private void initButtonsPanel(UIContact uiContact)
null,
null);
if ((telephonyContact != null && telephonyContact.getAddress() != null) ||
uiContact.getDescriptor() instanceof SourceContact ||
if ((telephonyContact != null && telephonyContact.getAddress() != null)
|| uiContact.getDescriptor() instanceof SourceContact ||
(hasPhone && providers.size() > 0))
{
constraints.anchor = GridBagConstraints.WEST;
@ -841,9 +884,68 @@ private void initButtonsPanel(UIContact uiContact)
x += addContactButton.getWidth();
}
// The list of the contact actions
// we will create a button for every action
Collection<SIPCommButton> contactActions
= uiContact.getContactCustomActionButtons();
if (contactActions != null)
{
initContactActionButtons(contactActions, constraints.gridx, x);
}
this.setBounds(0, 0, tree.getWidth(), getPreferredSize().height);
}
/**
* Initializes custom contact action buttons.
*
* @param contactActionButtons the list of buttons to initialize
* @param gridX the X grid of the first button
* @param xBounds the x bounds of the first button
*/
private void initContactActionButtons(
Collection<SIPCommButton> contactActionButtons,
int gridX,
int xBounds)
{
// Reinit the labels to take the whole horizontal space.
addLabels(5 + contactActionButtons.size());
Iterator<SIPCommButton> actionsIter = contactActionButtons.iterator();
while (actionsIter.hasNext())
{
final SIPCommButton actionButton = actionsIter.next();
if (customActionButtons == null)
customActionButtons = new LinkedList<JButton>();
customActionButtons.add(actionButton);
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.NONE;
constraints.gridx = ++gridX;
constraints.gridy = 2;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 0f;
constraints.weighty = 0f;
actionButton.setBorder(null);
this.add(actionButton, constraints);
int statusMessageLabelHeight = 0;
if (displayDetailsLabel.getText().length() > 0)
statusMessageLabelHeight = 20;
else
statusMessageLabelHeight = 15;
actionButton.setBounds(xBounds,
nameLabel.getHeight() + statusMessageLabelHeight, 28, 28);
xBounds += actionButton.getWidth();
}
}
/**
* Draw the icon at the specified location. Paints this component as an
* icon.
@ -1289,7 +1391,8 @@ else if(d instanceof MobilePhoneDetail)
new ArrayList<String>(),
null,
null,
null)
null,
pnd)
{
public PresenceStatus getPresenceStatus()
{

@ -12,6 +12,7 @@
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.swing.*;
/**
* The <tt>UIContact</tt> represents the user interface contact contained in the
@ -146,4 +147,20 @@ public UIContactDetail getDefaultContactDetail(
*/
public List<UIContactDetail> getContactDetailsForOperationSet(
Class<? extends OperationSet> opSetClass);
/**
* Returns a list of all <tt>UIContactDetail</tt>s within this
* <tt>UIContact</tt>.
*
* @return a list of all <tt>UIContactDetail</tt>s within this
* <tt>UIContact</tt>
*/
public List<UIContactDetail> getContactDetails();
/**
* Returns all custom action buttons for this notification contact.
*
* @return a list of all custom action buttons for this notification contact
*/
public Collection<SIPCommButton> getContactCustomActionButtons();
}

@ -62,6 +62,11 @@ public abstract class UIContactDetail
*/
private final String category;
/**
* The underlying object that this class is wrapping
*/
private final Object descriptor;
/**
* Creates a <tt>UIContactDetail</tt> by specifying the contact
* <tt>address</tt>, the <tt>displayName</tt> and <tt>preferredProvider</tt>.
@ -73,6 +78,7 @@ public abstract class UIContactDetail
* @param preferredProvider the preferred protocol provider
* @param preferredProtocol the preferred protocol if no protocol provider
* is set
* @param descriptor the underlying object that this class is wrapping
*/
public UIContactDetail(
String address,
@ -81,7 +87,8 @@ public UIContactDetail(
Collection<String> labels,
ImageIcon statusIcon,
ProtocolProviderService preferredProvider,
String preferredProtocol)
String preferredProtocol,
Object descriptor)
{
this.address = address;
this.displayName = displayName;
@ -90,6 +97,7 @@ public UIContactDetail(
this.statusIcon = statusIcon;
this.protocolProvider = preferredProvider;
this.preferredProtocol = preferredProtocol;
this.descriptor = descriptor;
}
/**
@ -194,6 +202,16 @@ public void setPrefix(String prefix)
this.prefix = prefix;
}
/**
* Returns the underlying object that this class is wrapping
*
* @return the underlying object that this class is wrapping
*/
public Object getDescriptor()
{
return descriptor;
}
/**
* Returns the <tt>PresenceStatus</tt> of this <tt>ContactDetail</tt> or
* null if the detail doesn't support presence.

@ -0,0 +1,23 @@
/*
* Jitsi, 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.gui.main.contactlist;
/**
* Defines an action for an <tt>UIContactDetail</tt>.
*
* @author Yana Stamcheva
*/
public interface UIContactDetailAction
{
/**
* Indicates this action is executed for the given <tt>UIContactDetail</tt>.
*
* @param contactDetail the <tt>UIContactDetail</tt> for which this action
* is performed
*/
public void actionPerformed (UIContactDetail contactDetail);
}

@ -6,18 +6,23 @@
*/
package net.java.sip.communicator.impl.gui.main.contactlist.contactsource;
import java.awt.event.*;
import java.util.*;
import java.util.regex.*;
import javax.swing.*;
import org.osgi.framework.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.contactlist.event.*;
import net.java.sip.communicator.service.customcontactactions.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
/**
* The <tt>MetaContactListSource</tt> is an abstraction of the
@ -59,6 +64,17 @@ public class MetaContactListSource
*/
private final int INITIAL_CONTACT_COUNT = 30;
/**
* The list of action buttons for this meta contact.
*/
private static Map<ContactAction<Contact>, SIPCommButton>
customActionButtons;
/**
* Currently selected custom action contact.
*/
private static MetaUIContact customActionContact;
/**
* The logger.
*/
@ -944,4 +960,155 @@ public void protoContactRemoved(ProtoContactEvent evt)
GuiActivator.getContactList().nodeChanged(contactNode);
}
}
/**
* Returns all custom action buttons for this meta contact.
*
* @return a list of all custom action buttons for this meta contact
*/
public static Collection<SIPCommButton> getContactCustomActionButtons(
final MetaUIContact metaContact)
{
customActionContact = metaContact;
if (customActionButtons == null)
initCustomActionButtons();
return customActionButtons.values();
}
/**
* Initializes custom action buttons for this contact source.
*/
private static void initCustomActionButtons()
{
customActionButtons
= new Hashtable<ContactAction<Contact>, SIPCommButton>();
for (CustomContactActionsService<Contact> ccas
: getContactActionsServices())
{
Iterator<ContactAction<Contact>> actionIterator
= ccas.getCustomContactActions();
while (actionIterator!= null && actionIterator.hasNext())
{
final ContactAction<Contact> ca = actionIterator.next();
SIPCommButton actionButton = customActionButtons.get(ca);
if (actionButton == null)
{
actionButton = new SIPCommButton(
new ImageIcon(ca.getIcon()).getImage(),
new ImageIcon(ca.getPressedIcon()).getImage(),
null);
actionButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
ChooseUIContactDetailPopupMenu
detailsPopupMenu
= new ChooseUIContactDetailPopupMenu(
GuiActivator.getContactList(),
customActionContact.getContactDetails(),
new MetaContactListSource
.UIContactDetailCustomAction(ca));
detailsPopupMenu.showPopupMenu();
}
});
customActionButtons.put(ca, actionButton);
}
}
}
}
/**
* An implementation of <tt>UIContactDetail</tt> for a custom action.
*/
private static class UIContactDetailCustomAction
implements UIContactDetailAction
{
/**
* The contact action.
*/
private final ContactAction<Contact> contactAction;
/**
* Creates an instance of <tt>UIContactDetailCustomAction</tt>.
*
* @param contactAction the contact action this detail is about
*/
public UIContactDetailCustomAction(ContactAction<Contact> contactAction)
{
this.contactAction = contactAction;
}
/**
* Performs the contact action on button click.
*/
public void actionPerformed(UIContactDetail contactDetail)
{
try
{
contactAction.actionPerformed(
(Contact) contactDetail.getDescriptor());
}
catch (OperationFailedException e)
{
new ErrorDialog(null,
GuiActivator.getResources()
.getI18NString("service.gui.ERROR"),
e.getMessage());
}
}
}
/**
* Returns a list of all custom contact action services.
*
* @return a list of all custom contact action services.
*/
@SuppressWarnings ("unchecked")
private static List<CustomContactActionsService<Contact>>
getContactActionsServices()
{
List<CustomContactActionsService<Contact>> contactActionsServices
= new ArrayList<CustomContactActionsService<Contact>>();
ServiceReference[] serRefs = null;
try
{
// get all registered provider factories
serRefs
= GuiActivator.bundleContext.getServiceReferences(
CustomContactActionsService.class.getName(), null);
}
catch (InvalidSyntaxException e)
{
logger.error("GuiActivator : " + e);
}
if (serRefs != null)
{
for (ServiceReference serRef : serRefs)
{
CustomContactActionsService<?> customActionService
= (CustomContactActionsService<?>)
GuiActivator.bundleContext.getService(serRef);
if (customActionService.getContactSourceClass()
.equals(Contact.class))
{
contactActionsServices.add(
(CustomContactActionsService<Contact>)
customActionService);
}
}
}
return contactActionsServices;
}
}

@ -14,6 +14,7 @@
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.OperationSetExtendedAuthorizations.SubscriptionStatus;
import net.java.sip.communicator.service.protocol.ServerStoredDetails.FaxDetail;
@ -23,6 +24,7 @@
import net.java.sip.communicator.service.protocol.ServerStoredDetails.PhoneNumberDetail;
import net.java.sip.communicator.service.protocol.ServerStoredDetails.WorkPhoneDetail;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
/**
* The <tt>MetaUIContact</tt> is the implementation of the UIContact interface
@ -256,6 +258,27 @@ public List<UIContactDetail> getContactDetailsForOperationSet(
return resultList;
}
/**
* Returns a list of all <tt>UIContactDetail</tt>s within this
* <tt>UIContact</tt>.
*
* @return a list of all <tt>UIContactDetail</tt>s within this
* <tt>UIContact</tt>
*/
public List<UIContactDetail> getContactDetails()
{
List<UIContactDetail> resultList
= new LinkedList<UIContactDetail>();
Iterator<Contact> contacts = metaContact.getContacts();
while (contacts.hasNext())
{
resultList.add(new MetaContactDetail(contacts.next()));
}
return resultList;
}
/**
* Gets the avatar of a specific <tt>MetaContact</tt> in the form of an
* <tt>ImageIcon</tt> value.
@ -592,7 +615,8 @@ public MetaContactDetail(Contact contact)
new ImageIcon(
contact.getPresenceStatus().getStatusIcon()),
contact.getProtocolProvider(),
contact.getProtocolProvider().getProtocolName());
contact.getProtocolProvider().getProtocolName(),
contact);
this.contact = contact;
}
@ -617,4 +641,14 @@ public JPopupMenu getRightButtonMenu()
{
return new MetaContactRightButtonMenu(metaContact);
}
/**
* Returns all custom action buttons for this meta contact.
*
* @return a list of all custom action buttons for this meta contact
*/
public Collection<SIPCommButton> getContactCustomActionButtons()
{
return MetaContactListSource.getContactCustomActionButtons(this);
}
}

@ -15,6 +15,7 @@
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.swing.*;
/**
*
@ -224,6 +225,16 @@ public UIContactDetail getDefaultContactDetail(
return null;
}
/**
* Returns null to indicate that this contact has no contact details.
*
* @return null
*/
public List<UIContactDetail> getContactDetails()
{
return null;
}
/**
* Returns null to indicate that this contact has no contact details.
*
@ -280,4 +291,14 @@ public void contactClicked(ContactListEvent evt)
}
public void groupClicked(ContactListEvent evt) {}
/**
* Returns all custom action buttons for this meta contact.
*
* @return a list of all custom action buttons for this meta contact
*/
public Collection<SIPCommButton> getContactCustomActionButtons()
{
return null;
}
}

@ -19,6 +19,7 @@
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.globalstatus.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
/**
* The <tt>SourceUIContact</tt> is the implementation of the UIContact for the
@ -186,6 +187,28 @@ public String getDisplayDetails()
return sourceContact.getDisplayDetails();
}
/**
* Returns a list of all contained <tt>UIContactDetail</tt>s.
*
* @return a list of all contained <tt>UIContactDetail</tt>s
*/
public List<UIContactDetail> getContactDetails()
{
List<UIContactDetail> resultList
= new LinkedList<UIContactDetail>();
Iterator<ContactDetail> details
= sourceContact.getContactDetails().iterator();
while (details.hasNext())
{
ContactDetail detail = details.next();
resultList.add(new SourceContactDetail(detail, null));
}
return resultList;
}
/**
* Returns a list of <tt>UIContactDetail</tt>s supporting the given
* <tt>OperationSet</tt> class.
@ -270,7 +293,8 @@ public SourceContactDetail( ContactDetail detail,
detail.getLabels(),
null,
detail.getPreferredProtocolProvider(opSetClass),
detail.getPreferredProtocol(opSetClass));
detail.getPreferredProtocol(opSetClass),
detail);
ContactSourceService contactSource
= sourceContact.getContactSource();
@ -407,4 +431,14 @@ private void addDetailsToToolTip( List<ContactDetail> details,
toolTip.addLine(jLabels);
}
}
/**
* Returns all custom action buttons for this meta contact.
*
* @return a list of all custom action buttons for this meta contact
*/
public Collection<SIPCommButton> getContactCustomActionButtons()
{
return null;
}
}

@ -15,10 +15,12 @@
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.OperationSetMessageWaiting.MessageType;
import net.java.sip.communicator.service.protocol.OperationSetMessageWaiting.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.globalstatus.*;
import net.java.sip.communicator.util.swing.*;
import org.jitsi.service.resources.*;
/**
@ -57,6 +59,11 @@ public class NotificationContact
*/
private final ProtocolProviderService protocolProvider;
/**
* The notification message corresponding to the source message.
*/
private final NotificationMessage notificationMessage;
/**
* The corresponding <tt>ContactNode</tt> in the contact list component
* data model.
@ -78,18 +85,29 @@ public class NotificationContact
*/
private int readMessageCount = 0;
/**
* The type of the message.
*/
private MessageType messageType;
/**
* Creates an instance of <tt>NotificationContact</tt> by specifying the
* parent group and the corresponding <tt>ProtocolProviderService</tt>.
*
* @param group the parent group
* @param protocolProvider the corresponding protocol provider
* @param messageType the type of the message
* @param notificationMessage the actual notification message
*/
public NotificationContact( NotificationGroup group,
ProtocolProviderService protocolProvider)
ProtocolProviderService protocolProvider,
MessageType messageType,
NotificationMessage notificationMessage)
{
this.parentGroup = group;
this.protocolProvider = protocolProvider;
this.messageType = messageType;
this.notificationMessage = notificationMessage;
protocolProvider.addRegistrationStateChangeListener(this);
@ -117,6 +135,9 @@ public Object getDescriptor()
*/
public String getDisplayName()
{
if (notificationMessage != null)
return notificationMessage.getFromContact();
return GuiActivator.getUIService().getMainFrame()
.getAccountDisplayName(protocolProvider);
}
@ -132,6 +153,11 @@ public String getDisplayDetails()
{
String displayDetails;
if (notificationMessage != null)
{
return notificationMessage.getMessageDetails();
}
ResourceManagementService resources = GuiActivator.getResources();
if (unreadMessageCount > 0 && readMessageCount > 0)
@ -169,7 +195,7 @@ else if (readMessageCount > 0)
*/
public int getSourceIndex()
{
return 0;
return -1;
}
/**
@ -183,7 +209,7 @@ public int getSourceIndex()
public ImageIcon getAvatar(boolean isSelected, int width, int height)
{
ImageIcon avatarIcon = null;
if (parentGroup.getMessageType().equals(MessageType.VOICE))
if (messageType.equals(MessageType.VOICE))
{
avatarIcon = GuiActivator.getResources().getImage(
"service.gui.icons.VOICEMAIL");
@ -335,6 +361,20 @@ public UIContactDetail getDefaultContactDetail(
return null;
}
/**
* Returns a list of all contained <tt>UIContactDetail</tt>s.
*
* @return a list of all contained <tt>UIContactDetail</tt>s
*/
public List<UIContactDetail> getContactDetails()
{
List<UIContactDetail> resultList = new LinkedList<UIContactDetail>();
resultList.add(notificationDetail);
return resultList;
}
/**
* Returns a list of <tt>UIContactDetail</tt>s supporting the given
* <tt>OperationSet</tt> class.
@ -422,7 +462,8 @@ public MessageWaitingDetail(ProtocolProviderService protocolProvider,
null,
ImageLoader.getAccountStatusImage(protocolProvider),
protocolProvider,
protocolProvider.getProtocolName());
protocolProvider.getProtocolName(),
notificationMessage);
}
/**
@ -468,5 +509,19 @@ public void providerStatusChanged(ProviderPresenceStatusChangeEvent evt)
contactList.refreshContact(this);
}
/**
* Returns all custom action buttons for this notification contact.
*
* @return a list of all custom action buttons for this notification contact
*/
public Collection<SIPCommButton> getContactCustomActionButtons()
{
if (notificationMessage != null)
return NotificationContactSource
.getContactCustomActionButtons(this);
return null;
}
public void providerStatusMessageChanged(PropertyChangeEvent evt) {}
}

@ -6,11 +6,21 @@
*/
package net.java.sip.communicator.impl.gui.main.contactlist.notifsource;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import org.osgi.framework.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.service.customcontactactions.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.OperationSetMessageWaiting.MessageType;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
/**
* The <tt>NotificationContactSource</tt> represents a contact source that would
@ -27,8 +37,22 @@ public class NotificationContactSource
* corresponding <tt>MessageType</tt>, for which notifications
* are received.
*/
private final Hashtable<MessageType, NotificationGroup> groups
= new Hashtable<MessageType, NotificationGroup>();
private final Hashtable<String, NotificationGroup> groups
= new Hashtable<String, NotificationGroup>();
/**
* The list of action buttons for this meta contact.
*/
private static Map<ContactAction<NotificationMessage>, SIPCommButton>
customActionButtons;
private static NotificationContact customActionContact;
/**
* The logger.
*/
private static final Logger logger
= Logger.getLogger(NotificationContactSource.class);
/**
* Adds the received waiting message to the corresponding group and contact.
@ -38,19 +62,52 @@ public class NotificationContactSource
* @param evt the notification event.
*/
public void messageWaitingNotify(MessageWaitingEvent evt)
{
Iterator<NotificationMessage> messages = evt.getMessages();
if (messages != null)
{
while (messages.hasNext())
{
NotificationMessage message = messages.next();
String messageGroupName = message.getMessageGroup();
NotificationGroup messageGroup = groups.get(messageGroupName);
if (messageGroup == null)
{
messageGroup
= new NotificationGroup(messageGroupName);
groups.put(messageGroupName, messageGroup);
}
messageGroup.messageWaitingNotify(evt);
}
}
else
{
MessageType type = evt.getMessageType();
NotificationGroup group = groups.get(type);
NotificationGroup group = groups.get(type.toString());
if (group == null)
{
group = new NotificationGroup(type);
groups.put(type, group);
String displayName;
if (type.equals(MessageType.VOICE))
displayName = GuiActivator.getResources()
.getI18NString("service.gui.VOICEMAIL_TITLE");
else
displayName = type.toString();
group = new NotificationGroup(displayName);
groups.put(type.toString(), group);
}
group.messageWaitingNotify(evt);
}
}
/**
* Returns an <tt>Iterator</tt> over a list of all notification groups
@ -81,4 +138,157 @@ public Iterator<? extends UIContact> getNotifications(UIGroup group)
return notifGroup.getNotifications();
}
/**
* Returns all custom action buttons for this meta contact.
*
* @return a list of all custom action buttons for this meta contact
*/
public static Collection<SIPCommButton> getContactCustomActionButtons(
final NotificationContact notificationContact)
{
customActionContact = notificationContact;
if (customActionButtons == null)
initCustomActionButtons();
return customActionButtons.values();
}
/**
* Initializes custom action buttons.
*/
private static void initCustomActionButtons()
{
customActionButtons = new Hashtable<ContactAction<NotificationMessage>,
SIPCommButton>();
for (CustomContactActionsService<NotificationMessage> ccas
: getNotificationActionsServices())
{
Iterator<ContactAction<NotificationMessage>> actionIterator
= ccas.getCustomContactActions();
while (actionIterator!= null && actionIterator.hasNext())
{
final ContactAction<NotificationMessage>
ca = actionIterator.next();
SIPCommButton actionButton = customActionButtons.get(ca);
if (actionButton == null)
{
actionButton = new SIPCommButton(
new ImageIcon(ca.getIcon()).getImage(),
new ImageIcon(ca.getPressedIcon()).getImage(),
null);
actionButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
ChooseUIContactDetailPopupMenu
detailsPopupMenu
= new ChooseUIContactDetailPopupMenu(
GuiActivator.getContactList(),
customActionContact.getContactDetails(),
new NotificationContactSource
.UIContactDetailCustomAction(ca));
detailsPopupMenu.showPopupMenu();
}
});
customActionButtons.put(ca, actionButton);
}
}
}
}
/**
* An implementation of <tt>UIContactDetail</tt> for a custom action.
*/
private static class UIContactDetailCustomAction
implements UIContactDetailAction
{
/**
* The contact action.
*/
private final ContactAction<NotificationMessage> contactAction;
/**
* Creates an instance of <tt>UIContactDetailCustomAction</tt>.
*/
public UIContactDetailCustomAction(
ContactAction<NotificationMessage> contactAction)
{
this.contactAction = contactAction;
}
/**
* Performs the action on button click.
*/
public void actionPerformed(UIContactDetail contactDetail)
{
try
{
contactAction.actionPerformed(
(NotificationMessage) contactDetail.getDescriptor());
}
catch (OperationFailedException e)
{
new ErrorDialog(null,
GuiActivator.getResources()
.getI18NString("service.gui.ERROR"),
e.getMessage());
}
}
}
/**
* Returns a list of all custom contact action services.
*
* @return a list of all custom contact action services.
*/
@SuppressWarnings ("unchecked")
private static List<CustomContactActionsService<NotificationMessage>>
getNotificationActionsServices()
{
List<CustomContactActionsService<NotificationMessage>>
contactActionsServices
= new ArrayList<CustomContactActionsService
<NotificationMessage>>();
ServiceReference[] serRefs = null;
try
{
// get all registered provider factories
serRefs
= GuiActivator.bundleContext.getServiceReferences(
CustomContactActionsService.class.getName(), null);
}
catch (InvalidSyntaxException e)
{
logger.error("NotificationContactSource : " + e);
}
if (serRefs != null)
{
for (ServiceReference serRef : serRefs)
{
CustomContactActionsService<?> customActionService
= (CustomContactActionsService<?>)
GuiActivator.bundleContext.getService(serRef);
if (customActionService.getContactSourceClass()
.equals(NotificationMessage.class))
{
contactActionsServices.add(
(CustomContactActionsService<NotificationMessage>)
customActionService);
}
}
}
return contactActionsServices;
}
}

@ -13,7 +13,6 @@
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.service.protocol.OperationSetMessageWaiting.MessageType;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
@ -30,7 +29,7 @@ public class NotificationGroup
/**
* The type of the notification message, identifying this group.
*/
private final MessageType type;
private final String groupName;
/**
* The corresponding group node in the contact list component.
@ -42,9 +41,9 @@ public class NotificationGroup
* corresponding <tt>ProtocolProviderService</tt>, for which notifications
* are received.
*/
private final Hashtable<ProtocolProviderService, NotificationContact>
private final Hashtable<String, NotificationContact>
contacts
= new Hashtable<ProtocolProviderService, NotificationContact>();
= new Hashtable<String, NotificationContact>();
/**
* The group of UI notifications.
@ -57,9 +56,9 @@ public class NotificationGroup
*
* @param type the type of messages that this group would contain
*/
public NotificationGroup(MessageType type)
public NotificationGroup(String groupName)
{
this.type = type;
this.groupName = groupName;
}
/**
@ -70,7 +69,7 @@ public NotificationGroup(MessageType type)
*/
public Object getDescriptor()
{
return type;
return groupName;
}
/**
@ -81,14 +80,7 @@ public Object getDescriptor()
*/
public String getDisplayName()
{
String displayName;
if (type.equals(MessageType.VOICE))
displayName = GuiActivator.getResources()
.getI18NString("service.gui.VOICEMAIL_TITLE");
else
displayName = type.toString();
return displayName;
return groupName;
}
/**
@ -197,16 +189,6 @@ public Iterator<? extends UIContact> getNotifications()
return contacts.values().iterator();
}
/**
* Returns the message type indicating the identity of this group.
*
* @return the message type corresponding to this group
*/
public MessageType getMessageType()
{
return type;
}
/**
* Creates all necessary notification contacts coming from the given
* <tt>MessageWaitingEvent</tt>.
@ -215,17 +197,54 @@ public MessageType getMessageType()
*/
public void messageWaitingNotify(MessageWaitingEvent event)
{
ProtocolProviderService protocolProvider = event.getSourceProvider();
Iterator<NotificationMessage> messages = event.getMessages();
NotificationContact contact = contacts.get(protocolProvider);
if (messages != null)
{
while (messages.hasNext())
{
NotificationMessage message = messages.next();
TreeContactList contactList = GuiActivator.getContactList();
if (message.getMessageGroup().equals(groupName))
{
String messageIdentifier
= message.getFromContact() + message.getMessageDetails();
NotificationContact contact
= contacts.get(messageIdentifier);
boolean isNew = false;
if (contact == null)
{
contact = new NotificationContact(
this, event.getSourceProvider(),
event.getMessageType(), message);
contacts.put(messageIdentifier, contact);
isNew = true;
}
contact.setMessageAccount(event.getAccount());
contact.setUnreadMessageCount(event.getUnreadMessages());
contact.setReadMessageCount(event.getReadMessages());
addNotificationContact(contact, isNew);
}
}
}
else
{
ProtocolProviderService protocolProvider = event.getSourceProvider();
NotificationContact contact
= contacts.get(protocolProvider.toString());
boolean isNew = false;
if (contact == null)
{
contact = new NotificationContact(this, protocolProvider);
contacts.put(protocolProvider, contact);
contact = new NotificationContact(this, protocolProvider,
event.getMessageType(), null);
contacts.put(protocolProvider.toString(), contact);
isNew = true;
}
@ -234,6 +253,21 @@ public void messageWaitingNotify(MessageWaitingEvent event)
contact.setUnreadMessageCount(event.getUnreadMessages());
contact.setReadMessageCount(event.getReadMessages());
addNotificationContact(contact, isNew);
}
}
/**
* Adds a notification contact this notification group.
*
* @param contact the <tt>NotificationContact</tt> to add
* @param isNew indicates if this is a new contact
*/
private void addNotificationContact(
NotificationContact contact, boolean isNew)
{
TreeContactList contactList = GuiActivator.getContactList();
if (contactList.getCurrentFilter().isMatching(contact))
{
if (isNew)

@ -52,6 +52,7 @@ Import-Package: com.apple.eawt,
net.java.sip.communicator.util.swing.event,
net.java.sip.communicator.util.swing.plaf,
net.java.sip.communicator.util.swing.transparent,
net.java.sip.communicator.service.customcontactactions,
org.jdesktop.jdic.desktop,
org.jitsi.service.audionotifier,
org.jitsi.service.configuration,

@ -0,0 +1,49 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.customcontactactions;
import net.java.sip.communicator.service.protocol.*;
/**
* A custom contact action, used to define an action that can be represented in
* the contact list entry in the user interface.
*
* @author Damian Minkov
* @author Yana Stamcheva
*/
public interface ContactAction<T>
{
/**
* Invoked when an action occurs.
*
* @param actionSource the source of the action
*/
public void actionPerformed(T actionSource)
throws OperationFailedException;
/**
* The icon used by the UI to visualise this action.
* @return the button icon.
*/
public byte[] getIcon();
/**
* The icon used by the UI to visualise this action.
* @return the button icon.
*/
public byte[] getPressedIcon();
/**
* Indicates if this action is visible for the given <tt>actionSource</tt>.
*
* @param actionSource the action source for which we're verifying the
* action.
* @return <tt>true</tt> if the action should be visible for the given
* <tt>actionSource</tt>, <tt>false</tt> - otherwise
*/
public boolean isVisible(T actionSource);
}

@ -0,0 +1,32 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.customcontactactions;
import java.util.*;
/**
* The <tt>CustomContactActionsService</tt> can be used to define a set of
* custom actions for a contact list entry.
*
* @author Damian Minkov
*/
public interface CustomContactActionsService<T>
{
/**
* Returns the template class that this service has been initialized with
*
* @return the template class
*/
public Class<T> getContactSourceClass();
/**
* Returns all custom actions defined by this service.
*
* @return an iterator over a list of <tt>ContactAction</tt>s
*/
public Iterator<ContactAction<T>> getCustomContactActions();
}

@ -0,0 +1,7 @@
Bundle-Name: Custom Contact Actions
Bundle-Description: Custom Contact Actions
Bundle-Vendor: jitsi.org
Bundle-Version: 0.0.1
System-Bundle: yes
Import-Package: net.java.sip.communicator.service.protocol
Export-Package: net.java.sip.communicator.service.customcontactactions

@ -0,0 +1,103 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.protocol;
/**
* A notification message that is used to deliver notifications for an waiting
* server message.
*
* @see MessageWaitingListener, MessageWaitingEvent
*
* @author Yana Stamcheva
*/
public class NotificationMessage
{
/**
* The contact from which the message is coming.
*/
private final String fromContact;
/**
* The name of the group of messages to which this message belongs,
* if there's any.
*/
private final String messageGroup;
/**
* Additional details related to the message.
*/
private final String messageDetails;
/**
* The text of the message.
*/
private final String messageText;
/**
* Creates an instance of <tt>NotificationMessage</tt> by specifying the
* name of the contact from which the message is, the message group, any
* additional details and the message actual text.
*
* @param fromContact the contact from which the message is coming
* @param messageGroup the name of the group of messages to which this
* message belongs
* @param messageDetails additional details related to the message
* @param messageText the text of the message
*/
public NotificationMessage( String fromContact,
String messageGroup,
String messageDetails,
String messageText)
{
this.fromContact = fromContact;
this.messageGroup = messageGroup;
this.messageDetails = messageDetails;
this.messageText = messageText;
}
/**
* Returns the contact from which the message is coming
*
* @return the contact from which the message is coming
*/
public String getFromContact()
{
return fromContact;
}
/**
* Returns the name of the group of messages to which this
* message belongs.
*
* @return the name of the group of messages to which this
* message belongs
*/
public String getMessageGroup()
{
return messageGroup;
}
/**
* Returns the additional details related to the message
*
* @return the additional details related to the message
*/
public String getMessageDetails()
{
return messageDetails;
}
/**
* Returns the text of the message
*
* @return the text of the message
*/
public String getMessageText()
{
return messageText;
}
}

@ -55,6 +55,11 @@ public class MessageWaitingEvent
*/
private OperationSetMessageWaiting.MessageType messageType;
/**
* The list of notification messages concerned by this event.
*/
private List<NotificationMessage> messageList;
/**
* Constructs the Event with the given source, typically the provider and
* number of messages.
@ -75,6 +80,39 @@ public MessageWaitingEvent(
int readMessages,
int unreadUrgentMessages,
int readUrgentMessages)
{
this( source,
messageType,
account,
unreadMessages,
readMessages,
unreadUrgentMessages,
readUrgentMessages,
null);
}
/**
* Constructs the Event with the given source, typically the provider and
* number of messages.
*
* @param messageType the message type for this event.
* @param source the protocol provider from which this event is coming.
* @param account the account URI we can use to reach the messages.
* @param unreadMessages the unread messages.
* @param readMessages the read messages.
* @param unreadUrgentMessages the unread urgent messages.
* @param readUrgentMessages the read urgent messages.
* @param messages the list of messages that this event is about.
*/
public MessageWaitingEvent(
ProtocolProviderService source,
OperationSetMessageWaiting.MessageType messageType,
String account,
int unreadMessages,
int readMessages,
int unreadUrgentMessages,
int readUrgentMessages,
List<NotificationMessage> messages)
{
super(source);
@ -84,6 +122,7 @@ public MessageWaitingEvent(
this.readMessages = readMessages;
this.unreadUrgentMessages = unreadUrgentMessages;
this.readUrgentMessages = readUrgentMessages;
this.messageList = messages;
}
/**
@ -150,4 +189,12 @@ public OperationSetMessageWaiting.MessageType getMessageType()
{
return messageType;
}
public Iterator<NotificationMessage> getMessages()
{
if (messageList != null)
return messageList.iterator();
return null;
}
}

Loading…
Cancel
Save