From da09f9d5bc05a3546a0cff79646368464f43a821 Mon Sep 17 00:00:00 2001 From: Yana Stamcheva Date: Wed, 23 Jan 2008 13:10:42 +0000 Subject: [PATCH] Contact details plugin --- build.xml | 21 +- lib/felix.client.run.properties | 1 + .../contactlist/ContactRightButtonMenu.java | 50 +- .../contactinfo/ContactInfoActivator.java | 71 +++ .../contactinfo/ContactInfoContactPanel.java | 185 +++++++ .../contactinfo/ContactInfoDetailsPanel.java | 490 ++++++++++++++++++ .../plugin/contactinfo/ContactInfoDialog.java | 83 +++ .../contactinfo/ContactInfoMenuItem.java | 67 +++ .../plugin/contactinfo/Resources.java | 87 ++++ .../contactinfo/contactinfo.manifest.mf | 27 + .../plugin/contactinfo/resources.properties | 22 + .../contactinfo/resources/personPhoto.png | Bin 0 -> 4045 bytes .../contactinfo/resources/userInfo16x16.png | Bin 0 -> 465 bytes 13 files changed, 1057 insertions(+), 47 deletions(-) create mode 100644 src/net/java/sip/communicator/plugin/contactinfo/ContactInfoActivator.java create mode 100644 src/net/java/sip/communicator/plugin/contactinfo/ContactInfoContactPanel.java create mode 100644 src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDetailsPanel.java create mode 100644 src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDialog.java create mode 100644 src/net/java/sip/communicator/plugin/contactinfo/ContactInfoMenuItem.java create mode 100644 src/net/java/sip/communicator/plugin/contactinfo/Resources.java create mode 100644 src/net/java/sip/communicator/plugin/contactinfo/contactinfo.manifest.mf create mode 100644 src/net/java/sip/communicator/plugin/contactinfo/resources.properties create mode 100644 src/net/java/sip/communicator/plugin/contactinfo/resources/personPhoto.png create mode 100644 src/net/java/sip/communicator/plugin/contactinfo/resources/userInfo16x16.png diff --git a/build.xml b/build.xml index 92599b7f2..c8bd8eb16 100644 --- a/build.xml +++ b/build.xml @@ -877,7 +877,7 @@ bundle-irc,bundle-plugin-ircaccregwizz, bundle-pluginmanager,bundle-notification, bundle-ssh,bundle-plugin-sshaccregwizz, - bundle-plugin-exampleplugin,bundle-contacteventhandler"/> + bundle-contacteventhandler,bundle-plugin-contactinfo"/> @@ -1785,4 +1785,23 @@ javax.swing.event, javax.swing.border"/> prefix="net/java/sip/communicator/service/contacteventhandler"/> + + + + + + + + + + + diff --git a/lib/felix.client.run.properties b/lib/felix.client.run.properties index ad5e592db..215f17d8d 100644 --- a/lib/felix.client.run.properties +++ b/lib/felix.client.run.properties @@ -105,6 +105,7 @@ felix.auto.start.60= \ reference:file:sc-bundles/zeroconfaccregwizz.jar \ reference:file:sc-bundles/ircaccregwizz.jar \ reference:file:sc-bundles/contacteventhandler.jar \ + reference:file:sc-bundles/contactinfo.jar \ reference:file:sc-bundles/shutdown.jar # Uncomment the following lines if you want to run the architect viewer diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactRightButtonMenu.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactRightButtonMenu.java index adfd2e5e0..796b840d9 100644 --- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactRightButtonMenu.java +++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactRightButtonMenu.java @@ -49,9 +49,6 @@ public class ContactRightButtonMenu private I18NString moveSubcontactString = Messages.getI18NString("moveSubcontact"); - private I18NString userInfoString - = Messages.getI18NString("userInfo"); - private I18NString addSubcontactString = Messages.getI18NString("addSubcontact"); @@ -79,9 +76,6 @@ public class ContactRightButtonMenu private SIPCommMenu moveSubcontactMenu = new SIPCommMenu(moveSubcontactString.getText()); - private SIPCommMenu userInfoMenu - = new SIPCommMenu(userInfoString.getText()); - private SIPCommMenu addSubcontactMenu = new SIPCommMenu(addSubcontactString.getText()); @@ -120,8 +114,6 @@ public class ContactRightButtonMenu private String moveSubcontactPrefix = "moveSubcontact:"; - private String infoSubcontactPrefix = "infoSubcontact:"; - private Contact contactToMove; private boolean moveAllContacts = false; @@ -166,9 +158,6 @@ private void init() { this.moveSubcontactMenu.setIcon(new ImageIcon(ImageLoader .getImage(ImageLoader.MOVE_CONTACT_ICON))); - - this.userInfoMenu.setIcon(new ImageIcon(ImageLoader - .getImage(ImageLoader.INFO_16x16_ICON))); //Initialize the addSubcontact menu. Iterator providers = this.mainFrame.getProtocolProviders(); @@ -255,7 +244,6 @@ private void init() { JMenuItem contactItem = new JMenuItem(contactDisplayName); JMenuItem contactItem1 = new JMenuItem(contactDisplayName); - JMenuItem contactItem2 = new JMenuItem(contactDisplayName); Icon protocolIcon = new ImageIcon( createContactStatusImage(contact)); @@ -269,25 +257,14 @@ private void init() { contactItem1.setName(moveSubcontactPrefix + contact.getAddress() + protocolProvider.getProtocolName()); - contactItem2.setName(infoSubcontactPrefix + contact.getAddress() - + protocolProvider.getProtocolName()); - contactItem.addActionListener(this); contactItem1.addActionListener(this); - contactItem2.addActionListener(this); - + this.removeContactMenu.add(contactItem); this.moveSubcontactMenu.add(contactItem1); OperationSetWebContactInfo wContactInfo = mainFrame.getWebContactInfoOpSet(protocolProvider); - - if(wContactInfo == null) { - contactItem2.setEnabled(false); - contactItem2.setToolTipText( - Messages.getI18NString("dontSupportWebInfo").getText()); - } - this.userInfoMenu.add(contactItem2); } this.add(sendMessageItem); @@ -311,7 +288,6 @@ private void init() { this.addSeparator(); this.add(viewHistoryItem); - this.add(userInfoMenu); this.initPluginComponents(); @@ -322,14 +298,12 @@ private void init() { this.addSubcontactMenu.setName("addSubcontact"); this.renameContactItem.setName("renameContact"); this.viewHistoryItem.setName("viewHistory"); - this.userInfoMenu.setName("userInfo"); this.sendMessageItem.addActionListener(this); this.callItem.addActionListener(this); this.sendFileItem.addActionListener(this); this.renameContactItem.addActionListener(this); this.viewHistoryItem.addActionListener(this); - this.userInfoMenu.addActionListener(this); // Disable all menu items that do nothing. if (contactItem.getDefaultContact(OperationSetFileTransfer.class) @@ -340,7 +314,7 @@ private void init() { this.callItem.setEnabled(false); if (contactItem.getDefaultContact(OperationSetBasicInstantMessaging.class) == null) - this.sendMessageItem.setEnabled(false); + this.sendMessageItem.setEnabled(false); } private void initPluginComponents() @@ -375,7 +349,6 @@ private void initMnemonics() this.removeContactMenu.setMnemonic(removeContactString.getMnemonic()); this.renameContactItem.setMnemonic(renameContactString.getMnemonic()); this.viewHistoryItem.setMnemonic(viewHistoryString.getMnemonic()); - this.userInfoMenu.setMnemonic(userInfoString.getMnemonic()); this.moveSubcontactMenu.setMnemonic(moveSubcontactString.getMnemonic()); } @@ -477,23 +450,8 @@ else if (itemName.equalsIgnoreCase("viewHistory")) { history.setVisible(true); } } - else if (itemName.startsWith(infoSubcontactPrefix)) { - - Contact contact = getContactFromMetaContact( - itemName.substring(infoSubcontactPrefix.length())); - - ProtocolProviderService contactProvider - = contact.getProtocolProvider(); - - OperationSetWebContactInfo wContactInfo - = mainFrame.getWebContactInfoOpSet(contactProvider); - - GuiActivator.getBrowserLauncher().openURL( - wContactInfo.getWebContactInfo(contact) - .toString()); - } - else if (itemName.startsWith(moveToPrefix)) { - + else if (itemName.startsWith(moveToPrefix)) + { MetaContactGroup group = mainFrame.getGroupByID( itemName.substring(moveToPrefix.length())); diff --git a/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoActivator.java b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoActivator.java new file mode 100644 index 000000000..0b1c88d29 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoActivator.java @@ -0,0 +1,71 @@ +package net.java.sip.communicator.plugin.contactinfo; + +import net.java.sip.communicator.service.browserlauncher.*; +import net.java.sip.communicator.service.gui.*; + +import org.osgi.framework.*; + +/** + * The Activator of the Contact Info bundle. + * + * @author Adam Goldstein + * @author Yana Stamcheva + */ +public class ContactInfoActivator implements BundleActivator +{ + private static BrowserLauncherService browserLauncherService; + + private static BundleContext bundleContext; + + /** + * Starts this bundle. + */ + public void start(BundleContext bc) throws Exception + { + bundleContext = bc; + + ServiceReference uiServiceRef + = bc.getServiceReference(UIService.class.getName()); + + UIService uiService + = (UIService) bc.getService(uiServiceRef); + + // Check if the desired place, where we would like to add + // our menu item is supported from the current UIService implementation. + if (uiService.isContainerSupported( + UIService.CONTAINER_CONTACT_RIGHT_BUTTON_MENU)) + { + ContactInfoMenuItem cinfoMenuItem = new ContactInfoMenuItem(); + + // We add the example plugin menu item in the right button menu + // for a contact. + uiService.addComponent( + UIService.CONTAINER_CONTACT_RIGHT_BUTTON_MENU, + cinfoMenuItem); + } + } + + public void stop(BundleContext bc) throws Exception + { + } + + /** + * Returns the BrowserLauncherService obtained from the bundle + * context. + * @return the BrowserLauncherService obtained from the bundle + * context + */ + public static BrowserLauncherService getBrowserLauncher() + { + if (browserLauncherService == null) + { + ServiceReference serviceReference = bundleContext + .getServiceReference(BrowserLauncherService.class.getName()); + + browserLauncherService = (BrowserLauncherService) bundleContext + .getService(serviceReference); + } + + return browserLauncherService; + } +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoContactPanel.java b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoContactPanel.java new file mode 100644 index 000000000..3e709c496 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoContactPanel.java @@ -0,0 +1,185 @@ +package net.java.sip.communicator.plugin.contactinfo; + +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. See terms of license at gnu.org. + */ + +import java.awt.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.event.*; + +import net.java.sip.communicator.service.protocol.*; + +/** + * The left side panel of ContactInfoDialog. Display all associated subcontacts + * and their respective protocols in a JList. If a user is selected, the + * ContactInfoDetailsPanel will be updated to the current contact. + * + * @author Adam Goldstein + * @author Yana Stamcheva + */ +public class ContactInfoContactPanel + extends JPanel +{ + /** + * The list of all subcontacts related to the selected contact. + */ + private JList contactList = new JList(); + + /** + * The scroll pane containing the list of all sub contacts of a selected + * contact. + */ + private JScrollPane contactScrollPane = new JScrollPane(); + + private DefaultListModel contactListModel = new DefaultListModel(); + + /** + * The associated ProtocolPanel on our parent ContactInfoDialog. + */ + private ContactInfoDetailsPanel protocolPanel; + + /** + * The parent dialog that makes the connection between the contacts and + * the details panel. + */ + private ContactInfoDialog contactInfoDialog; + + /** + * Create a panel with a list of all sub-contacts associated with the + * contact that was originally selected. Whenever a sub-contact is picked, + * notifies the protocolPanel of the change and it will update the displayed + * details. + * + * @param contacts the list of contacts + * @param dialog the contact info dialog + */ + public ContactInfoContactPanel( Iterator contacts, + ContactInfoDialog dialog) + { + super(new BorderLayout()); + + this.contactInfoDialog = dialog; + + this.setBorder(BorderFactory.createCompoundBorder(BorderFactory + .createTitledBorder(Resources.getString("contacts")), + BorderFactory.createEmptyBorder(5, 5, 5, 5))); + + this.contactList.setOpaque(false); + this.contactList.setModel(contactListModel); + this.contactList.setCellRenderer(new ContactPanelCellRenderer()); + this.contactList.addListSelectionListener(new ListSelectionListener() + { + public void valueChanged(ListSelectionEvent e) + { + // When the user release the mouse button and completes the + // selection, getValueIsAdjusting() becomes false + if (!e.getValueIsAdjusting()) + { + JList list = (JList) e.getSource(); + + Contact selectedContact + = (Contact) list.getSelectedValue(); + + contactInfoDialog.loadContactDetails(selectedContact); + } + } + }); + + boolean isFirstIter = true; + while (contacts.hasNext()) + { + Contact contact = contacts.next(); + + this.contactListModel.addElement(contact); + + if (isFirstIter) + { + isFirstIter = false; + contactInfoDialog.loadContactDetails(contact); + contactList.setSelectedIndex(0); + } + } + + this.contactScrollPane.setPreferredSize(new Dimension(100, 200)); + this.contactScrollPane.getViewport().add(contactList); + this.add(contactScrollPane); + } + + /** + * A cell renderer that allows both text and icons in our contactList. + */ + private class ContactPanelCellRenderer + extends DefaultListCellRenderer + { + private boolean isSelected; + + private Color blueGreyBorderColor = new Color(131, 149, 178); + + private Color selectedColor = new Color(209, 212, 225); + + public ContactPanelCellRenderer() + { + this.setOpaque(false); + } + + /** + * Renders a Contact object in a JList, by visualizing + * the contact name and the protocol icon. + * + * @param list the rendered JList + * @param value the object to be rendered + * @param index the index of the object in the list + * @param isSelected indicates if the rendered object is selected + * @param cellHasFocus indicates if the rendered object is in a focused + * cell + */ + public Component getListCellRendererComponent( JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) + { + super.getListCellRendererComponent(list, value, index, isSelected, + cellHasFocus); + + this.isSelected = isSelected; + + Contact contact = (Contact) value; + + this.setIcon(new ImageIcon(contact.getProtocolProvider() + .getProtocolIcon().getIcon(ProtocolIcon.ICON_SIZE_16x16))); + this.setText(((Contact) value).getDisplayName()); + + return this; + } + + /** + * Paint a round blue border and background when a cell is selected. + */ + public void paintComponent(Graphics g) + { + Graphics2D g2 = (Graphics2D) g; + + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + if (this.isSelected) + { + g2.setColor(selectedColor); + g2.fillRoundRect(1, 0, this.getWidth(), this.getHeight(), 7, 7); + + g2.setColor(blueGreyBorderColor); + g2.setStroke(new BasicStroke(1.5f)); + g2.drawRoundRect(1, 0, this.getWidth() - 2, this.getHeight() - 1, + 7, 7); + } + + super.paintComponent(g); + } + } +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDetailsPanel.java b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDetailsPanel.java new file mode 100644 index 000000000..d3a9be0d5 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDetailsPanel.java @@ -0,0 +1,490 @@ +package net.java.sip.communicator.plugin.contactinfo; + +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. See terms of license at gnu.org. + */ + +import java.awt.*; +import java.awt.event.*; + +import java.util.*; +import java.text.DateFormat; + +import javax.swing.*; +import javax.swing.event.*; + +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.ServerStoredDetails.*; + +/** + * The right side panel of ContactInfoDialog. Shows one tab of a summary of + * contact information for the selected subcontact, and has an extended tab + * listing all of the details. + * + * @author Adam Goldstein + * @author Yana Stamcheva + */ +public class ContactInfoDetailsPanel + extends JPanel +{ + /** + * The tabbed pane containing the two different tabs for details. + */ + private JTabbedPane tabbedPane = new JTabbedPane(); + + /** + * The operation set giving access to the server stored contact details. + */ + private OperationSetServerStoredContactInfo contactInfoOpSet; + + /** + * The currently selected sub-contact we are displaying information about. + */ + private Contact contact; + + /** + * Construct a tabbed pane that will have one tab with a summary of info for + * the selected subcontact and one tab for all of the extended details. + */ + public ContactInfoDetailsPanel() + { + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + this.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + this.setPreferredSize(new Dimension(400, 300)); + + this.tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + } + + /** + * Retrieve and display the information for the newly selected contact, c. + * + * @param c the sub-contact we are now focusing on. + */ + public void loadContactDetails(Contact c) + { + this.contact = c; + + ProtocolProviderService pps = contact.getProtocolProvider(); + contactInfoOpSet = + (OperationSetServerStoredContactInfo) pps + .getOperationSet(OperationSetServerStoredContactInfo.class); + + this.removeAll(); + + if (contactInfoOpSet == null || !pps.isRegistered()) + { + JPanel unsupportedPanel = createUnsupportedPanel(); + + this.add(unsupportedPanel); + + this.revalidate(); + this.repaint(); + + return; + } + + this.tabbedPane.removeAll(); + + ImageIcon icon = + new ImageIcon(contact.getProtocolProvider().getProtocolIcon() + .getIcon(ProtocolIcon.ICON_SIZE_16x16)); + + JPanel summaryPanel = createSummaryInfoPanel(); + + JPanel extendedPanel = createExtendedInfoPanel(); + + JScrollPane extendedScrollPane = new JScrollPane(extendedPanel); + + this.tabbedPane.addTab(Resources.getString("summary"), icon, + summaryPanel, Resources.getString("summaryDesc") + + contact.getDisplayName()); + + this.tabbedPane.setMnemonicAt(0, KeyEvent.VK_1); + + this.tabbedPane.addTab(Resources.getString("extended"), icon, + extendedScrollPane, Resources.getString("extendedDesc") + + contact.getDisplayName()); + + this.tabbedPane.setMnemonicAt(1, KeyEvent.VK_2); + + this.add(tabbedPane); + + this.revalidate(); + this.repaint(); + } + + /** + * Creates the panel that indicates to the user that the currently selected + * contact does not support server stored contact info. + * + * @return the panel that is added and shows a message that the selected + * sub-contact does not have the operation set for server stored + * contact info supported. + */ + private JPanel createUnsupportedPanel() + { + JTextArea unsupportedTextArea = + new JTextArea(Resources.getString("notSupported")); + + unsupportedTextArea.setEditable(false); + unsupportedTextArea.setLineWrap(true); + + JPanel unsupportedPanel = new JPanel(new BorderLayout()); + + unsupportedPanel.add(unsupportedTextArea); + + return unsupportedPanel; + } + + /** + * Creates a panel that can be added as the summary tab that displays the + * following details: - + *

+ * Avatar(Contact image) - FirstNameDetail - MiddleNameDetail - + * LastNameDetail - BirthdateDetail (and calculate age) - GenderDetail - + * EmailAddressDetail - PhoneNumberDetail. All other details will be* added + * to our list of extended details. + * + * @return the panel that will be added as the summary tab. + */ + private JPanel createSummaryInfoPanel() + { + JPanel summaryPanel = new JPanel(); + + summaryPanel.setLayout(new BorderLayout(10, 5)); + summaryPanel.setSize(this.getWidth(), this.getHeight()); + + // Create the avatar panel. + JPanel avatarPanel = new JPanel(); + + avatarPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + byte[] bytes = this.contact.getImage(); + + ImageIcon avatarImage = null; + // If the user has a contact image, let's use it. If not, add the + // default + if (bytes != null) + avatarImage = new ImageIcon(bytes); + else + avatarImage = Resources.getImage("defaultPersonIcon"); + + ImageIcon scaledImage = + new ImageIcon(avatarImage.getImage().getScaledInstance(105, 130, + Image.SCALE_SMOOTH)); + + JLabel label = new JLabel(scaledImage); + avatarPanel.add(label); + summaryPanel.add(avatarPanel, BorderLayout.WEST); + + // Create the summary details panel. + JPanel detailsPanel = new JPanel(); + detailsPanel.setLayout(new BorderLayout()); + detailsPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + summaryPanel.add(detailsPanel); + + // Labels panel. + JPanel labelsPanel = new JPanel(new GridLayout(0, 1, 5, 5)); + labelsPanel.add(new JLabel(Resources.getString("firstNameNS"))); + labelsPanel.add(new JLabel(Resources.getString("middleNameNS"))); + labelsPanel.add(new JLabel(Resources.getString("lastNameNS"))); + labelsPanel.add(new JLabel(Resources.getString("genderNS"))); + labelsPanel.add(new JLabel(Resources.getString("ageNS"))); + labelsPanel.add(new JLabel(Resources.getString("bdayNS"))); + labelsPanel.add(new JLabel(Resources.getString("emailNS"))); + labelsPanel.add(new JLabel(Resources.getString("phoneNS"))); + + detailsPanel.add(labelsPanel, BorderLayout.WEST); + + // Values panel. + JPanel valuesPanel = new JPanel(new GridLayout(0, 1, 5, 5)); + + detailsPanel.add(valuesPanel, BorderLayout.CENTER); + + Iterator contactDetails; + GenericDetail genericDetail; + + // First name details. + contactDetails = + contactInfoOpSet.getDetails(contact, FirstNameDetail.class); + + String firstNameDetail = ""; + while (contactDetails.hasNext()) + { + genericDetail = (FirstNameDetail) contactDetails.next(); + + firstNameDetail = + firstNameDetail + " " + genericDetail.getDetailValue(); + } + + if (firstNameDetail.equals("")) + firstNameDetail = Resources.getString("notSpecified"); + + valuesPanel.add(new JLabel(firstNameDetail)); + + // Middle name details. + contactDetails = + contactInfoOpSet.getDetails(contact, MiddleNameDetail.class); + + String middleNameDetail = ""; + while (contactDetails.hasNext()) + { + genericDetail = (MiddleNameDetail) contactDetails.next(); + middleNameDetail = + middleNameDetail + " " + genericDetail.getDetailValue(); + } + + if (middleNameDetail.trim().equals("")) + middleNameDetail = Resources.getString("notSpecified"); + + valuesPanel.add(new JLabel(middleNameDetail)); + + // Last name details. + contactDetails = + contactInfoOpSet.getDetails(contact, LastNameDetail.class); + + String lastNameDetail = ""; + while (contactDetails.hasNext()) + { + genericDetail = (LastNameDetail) contactDetails.next(); + + lastNameDetail = + lastNameDetail + " " + genericDetail.getDetailValue(); + } + + if (lastNameDetail.trim().equals("")) + lastNameDetail = Resources.getString("notSpecified"); + + valuesPanel.add(new JLabel(lastNameDetail)); + + // Gender details. + contactDetails = + contactInfoOpSet.getDetails(contact, GenderDetail.class); + + String genderDetail = ""; + while (contactDetails.hasNext()) + { + genericDetail = (GenderDetail) contactDetails.next(); + genderDetail = genderDetail + " " + genericDetail.getDetailValue(); + } + + if (genderDetail.trim().equals("")) + genderDetail = Resources.getString("notSpecified"); + + valuesPanel.add(new JLabel(genderDetail)); + + // Birthday details. + contactDetails = + contactInfoOpSet.getDetails(contact, BirthDateDetail.class); + + String birthDateDetail = ""; + String ageDetail = ""; + if (contactDetails.hasNext()) + { + genericDetail = (BirthDateDetail) contactDetails.next(); + + Calendar calendarDetail = + (Calendar) genericDetail.getDetailValue(); + + Date birthDate = calendarDetail.getTime(); + DateFormat dateFormat = DateFormat.getDateInstance(); + + birthDateDetail = dateFormat.format(birthDate).trim(); + + Calendar c = Calendar.getInstance(); + int age = c.get(Calendar.YEAR) - calendarDetail.get(Calendar.YEAR); + + if (c.get(Calendar.MONTH) < calendarDetail.get(Calendar.MONTH)) + age--; + + ageDetail = new Integer(age).toString().trim(); + } + + if (birthDateDetail.equals("")) + birthDateDetail = Resources.getString("notSpecified"); + + if (ageDetail.equals("")) + ageDetail = Resources.getString("notSpecified"); + + valuesPanel.add(new JLabel(birthDateDetail)); + valuesPanel.add(new JLabel(ageDetail)); + + // Email details. + contactDetails = + contactInfoOpSet.getDetails(contact, EmailAddressDetail.class); + + String emailDetail = ""; + while (contactDetails.hasNext()) + { + genericDetail = (EmailAddressDetail) contactDetails.next(); + emailDetail = emailDetail + " " + genericDetail.getDetailValue(); + } + + if (emailDetail.trim().equals("")) + emailDetail = Resources.getString("notSpecified"); + + valuesPanel.add(new JLabel(emailDetail)); + + // Phone number details. + contactDetails = + contactInfoOpSet.getDetails(contact, PhoneNumberDetail.class); + + String phoneNumberDetail = ""; + while (contactDetails.hasNext()) + { + genericDetail = (PhoneNumberDetail) contactDetails.next(); + phoneNumberDetail = + phoneNumberDetail + " " + genericDetail.getDetailValue(); + } + + if (phoneNumberDetail.trim().equals("")) + phoneNumberDetail = Resources.getString("notSpecified"); + + valuesPanel.add(new JLabel(phoneNumberDetail)); + + return summaryPanel; + } + + /** + * A panel that displays all of the details retrieved from the opSet. + * + * @return a panel that will be added as the extended tab. + */ + private JPanel createExtendedInfoPanel() + { + JPanel mainExtendedPanel = new JPanel(new BorderLayout()); + + JPanel extendedPanel = new JPanel(); + extendedPanel.setLayout(new BoxLayout(extendedPanel, BoxLayout.Y_AXIS)); + + JPanel imagePanel = new JPanel(); + + // The imagePanel will be used for any BinaryDetails and will be added at + // the bottom so we don't disrupt the standard look of the other details + imagePanel.setLayout(new BoxLayout(imagePanel, BoxLayout.LINE_AXIS)); + imagePanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory + .createTitledBorder(Resources.getString("userPictures")), + BorderFactory.createEmptyBorder(0, 5, 5, 5))); + + // Obtain all the details for a contact. + Iterator iter = contactInfoOpSet.getAllDetailsForContact(contact); + + GenericDetail detail; + JLabel detailLabel; + JTextArea detailValueArea; + JPanel detailPanel; + + while (iter.hasNext()) + { + detail = (GenericDetail) iter.next(); + + if (detail.getDetailValue().toString().equals("")) + continue; + + detailLabel = new JLabel(); + detailValueArea = new JTextArea(); + detailPanel = new JPanel(new BorderLayout(10, 10)); + + detailValueArea.setAlignmentX(JTextArea.CENTER_ALIGNMENT); + detailValueArea.setEditable(false); + detailValueArea.setLineWrap(true); + + detailPanel.add(detailLabel, BorderLayout.WEST); + detailPanel.add(detailValueArea, BorderLayout.CENTER); + detailPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + extendedPanel.add(detailPanel); + + if (detail instanceof BinaryDetail) + { + JLabel imageLabel = + new JLabel(new ImageIcon((byte[]) detail + .getDetailValue())); + + imagePanel.add(imageLabel); + } + else if (detail instanceof CalendarDetail) + { + detailLabel.setText(detail.getDetailDisplayName() + ": "); + + Date detailDate = + ((Calendar) detail.getDetailValue()).getTime(); + DateFormat df = DateFormat.getDateInstance(); + + detailValueArea.setText(df.format(detailDate).trim()); + } + else if (detail instanceof LocaleDetail) + { + detailLabel.setText(detail.getDetailDisplayName() + ": "); + + detailValueArea.setText(((Locale) detail.getDetailValue()) + .getDisplayName().trim()); + } + else if (detail instanceof TimeZoneDetail) + { + detailLabel.setText(detail.getDetailDisplayName() + ": "); + + detailValueArea.setText(((TimeZone) detail.getDetailValue()) + .getDisplayName().trim()); + } + else + { + detailLabel.setText(detail.getDetailDisplayName() + ": "); + + detailValueArea.setText( + detail.getDetailValue().toString().trim()); + } + } + + // If the contact's protocol supports web info, give them a button to + // get it + if (contact.getProtocolProvider().getOperationSet( + OperationSetWebContactInfo.class) != null) + { + final String urlString = ((OperationSetWebContactInfo) contact + .getProtocolProvider().getOperationSet( + OperationSetWebContactInfo.class)) + .getWebContactInfo(contact).toString(); + + JLabel webInfoLabel = new JLabel("Click to see web info: "); + JEditorPane webInfoValue = new JEditorPane(); + JPanel webInfoPanel = new JPanel(new BorderLayout()); + + webInfoPanel.add(webInfoLabel, BorderLayout.WEST); + webInfoPanel.add(webInfoValue, BorderLayout.CENTER); + + extendedPanel.add(webInfoPanel); + + webInfoValue.setOpaque(false); + webInfoValue.setContentType("text/html"); + webInfoValue.setEditable(false); + webInfoValue.setText( "" + + contact.getDisplayName() + + " web info"); + + webInfoValue.addHyperlinkListener(new HyperlinkListener() + { + public void hyperlinkUpdate(HyperlinkEvent e) + { + if (e.getEventType() + .equals(HyperlinkEvent.EventType.ACTIVATED)) + { + ContactInfoActivator + .getBrowserLauncher().openURL(urlString); + } + } + }); + } + + if (imagePanel.getComponentCount() > 0) + mainExtendedPanel.add(imagePanel, BorderLayout.CENTER); + + mainExtendedPanel.add(extendedPanel, BorderLayout.NORTH); + + return mainExtendedPanel; + } +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDialog.java b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDialog.java new file mode 100644 index 000000000..c4ea25a62 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoDialog.java @@ -0,0 +1,83 @@ +package net.java.sip.communicator.plugin.contactinfo; +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ + +import java.awt.*; +import java.util.*; + +import javax.swing.*; + +import net.java.sip.communicator.service.contactlist.*; +import net.java.sip.communicator.service.protocol.*; + +/** + * A GUI plug-in for SIP Communicator that will allow cross protocol contact + * information viewing and editing. + * + * @author Adam Goldstein + * @author Yana Stamcheva + */ +public class ContactInfoDialog + extends JFrame +{ + /** + * The right side of this frame that contains protocol specific contact + * details. + */ + protected ContactInfoDetailsPanel detailsPanel + = new ContactInfoDetailsPanel(); + + /** + * The left side of this frame that contains a list of all sub-contacts + * associated with the selected contact. + */ + protected ContactInfoContactPanel contactPanel; + + /** + * The contact that was right clicked on. The sub-contacts of contactItem + * will be the ones selectable in contactPanel. + */ + protected MetaContact metaContact; + + /** + * Accepts a MetaContact and constructs a frame with ContactInfoSearchPanel + * on the left and an information interface, ContactInfoDetailsPanel, + * on the right. + * @param metaContact the sub-contacts of this MetaContact that was right + * clicked on will be the ones selectable in contactPanel. + */ + public ContactInfoDialog(MetaContact metaContact) + { + super(Resources.getString("contactInfo")); + + this.metaContact = metaContact; + + this.setTitle(Resources.getString("contactInfo") + + ": " + + metaContact.getDisplayName()); + + Iterator subContacts = metaContact.getContacts(); + + this.contactPanel + = new ContactInfoContactPanel(subContacts, this); + + this.getContentPane().add(contactPanel, BorderLayout.WEST); + this.getContentPane().add(detailsPanel, BorderLayout.CENTER); + + this.pack(); + } + + /** + * Loads the details of the given contact. + * + * @param contact the Contact, which details we load + */ + public void loadContactDetails(Contact contact) + { + this.detailsPanel.loadContactDetails(contact); + } +} diff --git a/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoMenuItem.java b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoMenuItem.java new file mode 100644 index 000000000..dbddc900d --- /dev/null +++ b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoMenuItem.java @@ -0,0 +1,67 @@ +package net.java.sip.communicator.plugin.contactinfo; +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ + +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import net.java.sip.communicator.service.contactlist.*; +import net.java.sip.communicator.service.gui.*; + +/** + * + * @author Adam Goldstein + */ +public class ContactInfoMenuItem + extends JMenuItem + implements ContactAwareComponent, + ActionListener +{ + private MetaContact metaContact; + + /** + * Creates a ContactInfoMenuItem. + */ + public ContactInfoMenuItem() + { + super( Resources.getString("contactInfo"), + Resources.getImage("infoIcon")); + + this.addActionListener(this); + } + + /** + * Sets the currently selected MetaContact. + * @param metaContact the currently selected meta contact + */ + public void setCurrentContact(MetaContact metaContact) + { + this.metaContact = metaContact; + } + + public void setCurrentContactGroup(MetaContactGroup metaGroup) + {} + + /** + * Initializes and shows the contact details dialog. + */ + public void actionPerformed(ActionEvent e) + { + ContactInfoDialog cinfoDialog = new ContactInfoDialog(metaContact); + + cinfoDialog.setLocation( + Toolkit.getDefaultToolkit().getScreenSize().width/2 + - cinfoDialog.getWidth()/2, + Toolkit.getDefaultToolkit().getScreenSize().height/2 + - cinfoDialog.getHeight()/2 + ); + + cinfoDialog.setVisible(true); + } +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/contactinfo/Resources.java b/src/net/java/sip/communicator/plugin/contactinfo/Resources.java new file mode 100644 index 000000000..18910c8ce --- /dev/null +++ b/src/net/java/sip/communicator/plugin/contactinfo/Resources.java @@ -0,0 +1,87 @@ +/* + * 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.plugin.contactinfo; + +import java.awt.image.*; +import java.io.*; +import java.util.*; + +import javax.imageio.*; +import javax.swing.*; + +import net.java.sip.communicator.util.*; +/** + * The Messages class manages the access to the internationalization + * properties files. + * @author Yana Stamcheva + */ +public class Resources { + + private static Logger log = Logger.getLogger(Resources.class); + + private static final String BUNDLE_NAME + = "net.java.sip.communicator.plugin.contactinfo.resources"; + + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle + .getBundle(BUNDLE_NAME); + + /** + * 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) { + try { + return RESOURCE_BUNDLE.getString(key); + + } catch (MissingResourceException e) { + + return '!' + key + '!'; + } + } + + /** + * 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) { + BufferedImage image = null; + + String path = Resources.getString(imageID); + try { + image = ImageIO.read(Resources.class.getClassLoader() + .getResourceAsStream(path)); + + } catch (IOException e) { + log.error("Failed to load image:" + path, e); + } + + return new ImageIcon(image); + } + + /** + * 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) { + byte[] image = new byte[100000]; + + String path = Resources.getString(imageID); + try { + Resources.class.getClassLoader() + .getResourceAsStream(path).read(image); + + } catch (IOException e) { + log.error("Failed to load image:" + path, e); + } + + return image; + } +} diff --git a/src/net/java/sip/communicator/plugin/contactinfo/contactinfo.manifest.mf b/src/net/java/sip/communicator/plugin/contactinfo/contactinfo.manifest.mf new file mode 100644 index 000000000..530dcec5f --- /dev/null +++ b/src/net/java/sip/communicator/plugin/contactinfo/contactinfo.manifest.mf @@ -0,0 +1,27 @@ +Bundle-Activator: net.java.sip.communicator.plugin.contactinfo.ContactInfoActivator +Bundle-Name: Contact Info +Bundle-Description: A plug-in that can show cross protocol user info. +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: org.osgi.framework, + net.java.sip.communicator.util, + net.java.sip.communicator.service.contactlist, + net.java.sip.communicator.service.contactlist.event, + net.java.sip.communicator.service.gui, + net.java.sip.communicator.service.gui.event, + net.java.sip.communicator.service.protocol, + net.java.sip.communicator.service.browserlauncher, + javax.swing, + javax.swing.event, + javax.swing.table, + javax.swing.text, + javax.swing.text.html, + javax.accessibility, + javax.swing.plaf, + javax.swing.plaf.metal, + javax.swing.plaf.basic, + javax.imageio, + javax.swing.filechooser, + javax.swing.tree, + javax.swing.undo, + javax.swing.border \ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/contactinfo/resources.properties b/src/net/java/sip/communicator/plugin/contactinfo/resources.properties new file mode 100644 index 000000000..f0100025d --- /dev/null +++ b/src/net/java/sip/communicator/plugin/contactinfo/resources.properties @@ -0,0 +1,22 @@ +contactInfo=Contact details +contacts=Contacts +selectUser=Select a user +summary=Summary +summaryDesc=Summary of contact info for +extended=Extended +extendedDesc=Extended contact info for +notSupported=This protocol doesn't support server stored details for now. Try one of the other protocols. +clickWeb=Click for Web Info +webInfo=Web Info +firstNameNS=First Name: +middleNameNS=Middle Name: +lastNameNS=Last Name: +ageNS=Age: +bdayNS=Birth Date: +genderNS=Gender: +emailNS=E-mail: +phoneNS=Phone: +userPictures=User Pictures +notSpecified=[Not specified] +infoIcon=net/java/sip/communicator/plugin/contactinfo/resources/userInfo16x16.png +defaultPersonIcon=net/java/sip/communicator/plugin/contactinfo/resources/personPhoto.png \ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/contactinfo/resources/personPhoto.png b/src/net/java/sip/communicator/plugin/contactinfo/resources/personPhoto.png new file mode 100644 index 0000000000000000000000000000000000000000..47a8837b7c9f8ddeb52c01df1daa0dcdded9650f GIT binary patch literal 4045 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 literal 0 HcmV?d00001