/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.plugin.contactinfo; import java.awt.*; import java.awt.event.*; import java.text.*; import java.util.*; import java.util.regex.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.text.html.*; import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.ServerStoredDetails.BinaryDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.BirthDateDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.CalendarDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.EmailAddressDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.FirstNameDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.GenderDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.GenericDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.LastNameDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.LocaleDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.MiddleNameDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.PhoneNumberDetail; import net.java.sip.communicator.service.protocol.ServerStoredDetails.TimeZoneDetail; /** * 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 TransparentPanel { /** * Serial version UID. */ private static final long serialVersionUID = 0L; /** * The tabbed pane containing the two different tabs for details. */ private final JTabbedPane tabbedPane = new SIPCommTabbedPane(); /** * 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; /** * The default width of hte avater area. */ private static final int AVATAR_AREA_WIDTH = 105; /** * The default height of hte avater area. */ private static final int AVATAR_AREA_HEIGHT = 130; /** * 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 = 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("service.gui.SUMMARY"), icon, summaryPanel, Resources.getString( "plugin.contactinfo.CONTACT_SUMMARY_DESCRIPTION") + contact.getDisplayName()); this.tabbedPane.setMnemonicAt(0, KeyEvent.VK_1); this.tabbedPane.addTab( Resources.getString("plugin.accountinfo.EXTENDED"), icon, extendedScrollPane, Resources.getString( "plugin.contactinfo.CONTACT_EXTENDED_DESCRIPTION") + 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("service.gui.CONTACT_INFO_NOT_SUPPORTED")); unsupportedTextArea.setEditable(false); unsupportedTextArea.setLineWrap(true); JPanel unsupportedPanel = new TransparentPanel(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 TransparentPanel(); summaryPanel.setLayout(new BorderLayout(10, 5)); summaryPanel.setSize(this.getWidth(), this.getHeight()); // Create the avatar panel. JPanel avatarPanel = new TransparentPanel(); avatarPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); byte[] bytes = this.contact.getImage(); ImageIcon scaledImage = null; // If the user has a contact image, let's use it. If not, add the // default if (bytes != null) { scaledImage = ImageUtils.getScaledRoundedIcon( bytes, AVATAR_AREA_WIDTH, AVATAR_AREA_HEIGHT ); } else scaledImage = ImageUtils.getScaledRoundedIcon(Resources .getImage("service.gui.DEFAULT_USER_PHOTO"), AVATAR_AREA_WIDTH, AVATAR_AREA_HEIGHT); JLabel label = new JLabel(scaledImage); label.setVerticalAlignment(JLabel.CENTER); label.setHorizontalAlignment(JLabel.CENTER); label.setPreferredSize(new Dimension( AVATAR_AREA_WIDTH, AVATAR_AREA_HEIGHT) ); avatarPanel.add(label); summaryPanel.add(avatarPanel, BorderLayout.WEST); // Create the summary details panel. JPanel detailsPanel = new TransparentPanel(); detailsPanel.setLayout(new BorderLayout()); detailsPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); summaryPanel.add(detailsPanel); // Labels panel. JPanel labelsPanel = new TransparentPanel(new GridLayout(0, 1, 5, 5)); labelsPanel.add(new JLabel( Resources.getString("plugin.accountinfo.FIRST_NAME"))); labelsPanel.add(new JLabel( Resources.getString("plugin.accountinfo.MIDDLE_NAME"))); labelsPanel.add(new JLabel( Resources.getString("plugin.accountinfo.LAST_NAME"))); labelsPanel.add(new JLabel( Resources.getString("plugin.accountinfo.GENDER"))); labelsPanel.add(new JLabel( Resources.getString("plugin.accountinfo.BDAY"))); labelsPanel.add(new JLabel( Resources.getString("plugin.accountinfo.AGE"))); labelsPanel.add(new JLabel( Resources.getString("plugin.accountinfo.EMAIL"))); labelsPanel.add(new JLabel( Resources.getString("plugin.accountinfo.PHONE"))); detailsPanel.add(labelsPanel, BorderLayout.WEST); // Values panel. JPanel valuesPanel = new TransparentPanel(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 = contactDetails.next(); firstNameDetail = firstNameDetail + " " + genericDetail.getDetailValue(); } if (firstNameDetail.equals("")) firstNameDetail = Resources.getString("plugin.contactinfo.NOT_SPECIFIED"); valuesPanel.add(new JLabel(firstNameDetail)); // Middle name details. contactDetails = contactInfoOpSet.getDetails(contact, MiddleNameDetail.class); String middleNameDetail = ""; while (contactDetails.hasNext()) { genericDetail = contactDetails.next(); middleNameDetail = middleNameDetail + " " + genericDetail.getDetailValue(); } if (middleNameDetail.trim().equals("")) middleNameDetail = Resources.getString("plugin.contactinfo.NOT_SPECIFIED"); valuesPanel.add(new JLabel(middleNameDetail)); // Last name details. contactDetails = contactInfoOpSet.getDetails(contact, LastNameDetail.class); String lastNameDetail = ""; while (contactDetails.hasNext()) { genericDetail = contactDetails.next(); lastNameDetail = lastNameDetail + " " + genericDetail.getDetailValue(); } if (lastNameDetail.trim().equals("")) lastNameDetail = Resources.getString("plugin.contactinfo.NOT_SPECIFIED"); valuesPanel.add(new JLabel(lastNameDetail)); // Gender details. contactDetails = contactInfoOpSet.getDetails(contact, GenderDetail.class); String genderDetail = ""; while (contactDetails.hasNext()) { genericDetail = contactDetails.next(); genderDetail = genderDetail + " " + genericDetail.getDetailValue(); } if (genderDetail.trim().equals("")) genderDetail = Resources.getString("plugin.contactinfo.NOT_SPECIFIED"); valuesPanel.add(new JLabel(genderDetail)); // Birthday details. contactDetails = contactInfoOpSet.getDetails(contact, BirthDateDetail.class); String birthDateDetail = ""; String ageDetail = ""; if (contactDetails.hasNext()) { genericDetail = 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 = Integer.toString(age).trim(); } if (birthDateDetail.equals("")) birthDateDetail = Resources.getString("plugin.contactinfo.NOT_SPECIFIED"); if (ageDetail.equals("")) ageDetail = Resources.getString("plugin.contactinfo.NOT_SPECIFIED"); valuesPanel.add(new JLabel(birthDateDetail)); valuesPanel.add(new JLabel(ageDetail)); // Email details. contactDetails = contactInfoOpSet.getDetails(contact, EmailAddressDetail.class); String emailDetail = ""; while (contactDetails.hasNext()) { genericDetail = contactDetails.next(); emailDetail = emailDetail + " " + genericDetail.getDetailValue(); } if (emailDetail.trim().equals("")) emailDetail = Resources.getString("plugin.contactinfo.NOT_SPECIFIED"); valuesPanel.add(new JLabel(emailDetail)); // Phone number details. contactDetails = contactInfoOpSet.getDetails(contact, PhoneNumberDetail.class); String phoneNumberDetail = ""; while (contactDetails.hasNext()) { genericDetail = contactDetails.next(); phoneNumberDetail = phoneNumberDetail + " " + genericDetail.getDetailValue(); } if (phoneNumberDetail.trim().equals("")) phoneNumberDetail = Resources.getString("plugin.contactinfo.NOT_SPECIFIED"); 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 TransparentPanel(new BorderLayout()); JPanel extendedPanel = new TransparentPanel(); extendedPanel.setLayout(new BoxLayout(extendedPanel, BoxLayout.Y_AXIS)); JPanel imagePanel = new TransparentPanel(); // 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("plugin.contactinfo.USER_PICTURES")), 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 = iter.next(); if (detail.getDetailValue().toString().equals("")) continue; detailLabel = new JLabel(); detailValueArea = new JTextArea(); detailPanel = new TransparentPanel(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() + ": "); Object value = detail.getDetailValue(); String valueStr = ""; if(value instanceof Locale) valueStr = ((Locale) value).getDisplayName().trim(); else if(value instanceof String) valueStr = (String)value; detailValueArea.setText(valueStr); } 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()); } } // Add users status message to extended details if it exists String statusMessage = contact.getStatusMessage(); if(statusMessage != null && statusMessage.length() > 0) { detailLabel = new JLabel(); HTMLTextPane detailValuePane = new HTMLTextPane(); detailPanel = new TransparentPanel(new BorderLayout(10, 10)); detailValuePane.setEditable(false); detailValuePane.setOpaque(false); detailPanel.add(detailLabel, BorderLayout.WEST); detailPanel.add(detailValuePane, BorderLayout.CENTER); detailPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); extendedPanel.add(detailPanel); detailLabel.setText(Resources.getString( "plugin.contactinfo.USER_STATUS_MESSAGE") + ": "); detailValuePane.setText(statusMessage); } // If the contact's protocol supports web info, give them a button to // get it OperationSetWebContactInfo webContactInfo = contact .getProtocolProvider() .getOperationSet(OperationSetWebContactInfo.class); if (webContactInfo != null) { final String urlString = webContactInfo.getWebContactInfo(contact).toString(); JLabel webInfoLabel = new JLabel("Click to see web info: "); JEditorPane webInfoValue = new JEditorPane(); JPanel webInfoPanel = new TransparentPanel(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; } /** * The HTMLTextPane is a pane that handles displaying HTML and * hyperlinking urls found in the text. */ private class HTMLTextPane extends JTextPane implements HyperlinkListener { /** * Serial version UID. */ private static final long serialVersionUID = 0L; /** * The regular expression (in the form of compiled Pattern) * which matches URLs for the purposed of turning them into links. */ private final Pattern URL_PATTERN = Pattern.compile("(" + "(\\bwww\\.[^\\s<>\"]+\\.[^\\s<>\"]+/*[?#]*(\\w+[&=;?]\\w+)*\\b)" // wwwURL + "|" + "(\\b\\w+://[^\\s<>\"]+/*[?#]*(\\w+[&=;?]\\w+)*\\b)" // protocolURL + ")"); private SIPCommHTMLEditorKit editorKit; private HTMLDocument document; /** * Creates and instance of HTMLTextPane */ public HTMLTextPane() { editorKit = new SIPCommHTMLEditorKit(this); this.document = (HTMLDocument) editorKit.createDefaultDocument(); this.addHyperlinkListener(this); this.setContentType("text/html"); this.setEditorKitForContentType("text/html", editorKit); this.setEditorKit(editorKit); this.setDocument(document); putClientProperty( JTextPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); } /** * Override of parent setText(String) to search for URLs and * set as hyperlinks. * @param string String to display. */ @Override public void setText(String string) { Matcher m = URL_PATTERN.matcher(string); StringBuffer msgBuffer = new StringBuffer(); int prevEnd = 0; while (m.find()) { String fromPrevEndToStart = string.substring(prevEnd, m.start()); msgBuffer.append(fromPrevEndToStart); prevEnd = m.end(); String url = m.group().trim(); msgBuffer.append(""); msgBuffer.append(url); msgBuffer.append(""); } String fromPrevEndToEnd = string.substring(prevEnd); msgBuffer.append(fromPrevEndToEnd); super.setText(msgBuffer.toString()); } /** * Handles activations of hyperlinks * @param e HyperlinkEvent to handle. */ public void hyperlinkUpdate(HyperlinkEvent e) { if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) ContactInfoActivator.getBrowserLauncher() .openURL(e.getURL().toString()); } } }