/* * 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.impl.msghistory; import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.contactsource.*; import net.java.sip.communicator.service.msghistory.*; import net.java.sip.communicator.service.muc.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.protocol.globalstatus.*; import net.java.sip.communicator.util.*; import java.text.*; import java.util.*; /** * Represents a contact source displaying a recent message for contact. * @author Damian Minkov */ public class MessageSourceContact extends DataObject implements SourceContact, Comparable<MessageSourceContact> { /** * The parent service. */ private final MessageSourceService service; /** * The address. */ private String address = null; /** * The display name. */ private String displayName = null; /** * The protocol provider. */ private ProtocolProviderService ppService = null; /** * The status. Will reuse global status offline. */ private PresenceStatus status = GlobalStatusEnum.OFFLINE; /** * The image. */ private byte[] image = null; /** * The message content. */ private String messageContent = null; /** * A list of all contact details. */ private final List<ContactDetail> contactDetails = new LinkedList<ContactDetail>(); /** * The contact instance. */ private Contact contact = null; /** * The room instance. */ private ChatRoom room = null; /** * The timestamp. */ private Date timestamp = null; /** * Date format used to mark today messages. */ public static final String TODAY_DATE_FORMAT = "HH:mm', '"; /** * Date format used to mark past messages. */ public static final String PAST_DATE_FORMAT = "MMM d', '"; /** * The protocol provider. * @return the protocol provider. */ public ProtocolProviderService getProtocolProviderService() { return ppService; } /** * Constructs <tt>MessageSourceContact</tt>. * @param source the source event. * @param service the message source service. */ MessageSourceContact(EventObject source, MessageSourceService service) { this.service = service; update(source); } /** * Make sure the content of the message is not too long, * as it will fill up tooltips and ui components. */ private void updateMessageContent() { if(isToday(timestamp)) { // just hour this.messageContent = new SimpleDateFormat(TODAY_DATE_FORMAT).format(timestamp) + this.messageContent; } else { // just date this.messageContent = new SimpleDateFormat(PAST_DATE_FORMAT).format(timestamp) + this.messageContent; } if(this.messageContent != null && this.messageContent.length() > 60) { // do not display too long texts this.messageContent = this.messageContent.substring(0, 60); this.messageContent += "..."; } } /** * Checks whether <tt>timestamp</tt> is today. * @param timestamp the date to check * @return whether <tt>timestamp</tt> is today. */ private boolean isToday(Date timestamp) { Calendar today = Calendar.getInstance(); Calendar tsCalendar = Calendar.getInstance(); tsCalendar.setTime(timestamp); return today.get(Calendar.YEAR) == tsCalendar.get(Calendar.YEAR) && today.get(Calendar.DAY_OF_YEAR) == tsCalendar.get(Calendar.DAY_OF_YEAR); } /** * Updates fields. * @param source the event object */ void update(EventObject source) { if(source instanceof MessageDeliveredEvent) { MessageDeliveredEvent e = (MessageDeliveredEvent)source; this.contact = e.getDestinationContact(); this.address = contact.getAddress(); this.updateDisplayName(); this.ppService = contact.getProtocolProvider(); this.image = contact.getImage(); this.status = contact.getPresenceStatus(); this.messageContent = e.getSourceMessage().getContent(); this.timestamp = e.getTimestamp(); } else if(source instanceof MessageReceivedEvent) { MessageReceivedEvent e = (MessageReceivedEvent)source; this.contact = e.getSourceContact(); this.address = contact.getAddress(); this.updateDisplayName(); this.ppService = contact.getProtocolProvider(); this.image = contact.getImage(); this.status = contact.getPresenceStatus(); this.messageContent = e.getSourceMessage().getContent(); this.timestamp = e.getTimestamp(); } else if(source instanceof ChatRoomMessageDeliveredEvent) { ChatRoomMessageDeliveredEvent e = (ChatRoomMessageDeliveredEvent)source; this.room = e.getSourceChatRoom(); this.address = room.getIdentifier(); this.displayName = room.getName(); this.ppService = room.getParentProvider(); this.image = null; this.status = room.isJoined() ? ChatRoomPresenceStatus.CHAT_ROOM_ONLINE : ChatRoomPresenceStatus.CHAT_ROOM_OFFLINE; this.messageContent = e.getMessage().getContent(); this.timestamp = e.getTimestamp(); } else if(source instanceof ChatRoomMessageReceivedEvent) { ChatRoomMessageReceivedEvent e = (ChatRoomMessageReceivedEvent)source; this.room = e.getSourceChatRoom(); this.address = room.getIdentifier(); this.displayName = room.getName(); this.ppService = room.getParentProvider(); this.image = null; this.status = room.isJoined() ? ChatRoomPresenceStatus.CHAT_ROOM_ONLINE : ChatRoomPresenceStatus.CHAT_ROOM_OFFLINE; this.messageContent = e.getMessage().getContent(); this.timestamp = e.getTimestamp(); } if(service.isSMSEnabled()) { this.status = MessageSourceContactPresenceStatus.MSG_SRC_CONTACT_ONLINE; } updateMessageContent(); } @Override public String toString() { return "MessageSourceContact{" + "address='" + address + '\'' + ", ppService=" + ppService + '}'; } /** * Init details. Check contact capabilities. * @param source the source event. */ void initDetails(EventObject source) { if(source instanceof MessageDeliveredEvent) { initDetails(false, ((MessageDeliveredEvent)source).getDestinationContact()); } else if(source instanceof MessageReceivedEvent) { initDetails(false, ((MessageReceivedEvent)source).getSourceContact()); } else if(source instanceof ChatRoomMessageDeliveredEvent || source instanceof ChatRoomMessageReceivedEvent) { initDetails(true, null); } } /** * We will the details for this source contact. * Will skip OperationSetBasicInstantMessaging for chat rooms. * @param isChatRoom is current source contact a chat room. */ void initDetails(boolean isChatRoom, Contact contact) { if(!isChatRoom && contact != null) this.updateDisplayName(); ContactDetail contactDetail = new ContactDetail( this.address, this.displayName); Map<Class<? extends OperationSet>, ProtocolProviderService> preferredProviders; ProtocolProviderService preferredProvider = this.ppService; OperationSetContactCapabilities capOpSet = preferredProvider .getOperationSet(OperationSetContactCapabilities.class); Map<String, OperationSet> opsetCapabilities = null; if(capOpSet != null && contact != null) opsetCapabilities = capOpSet.getSupportedOperationSets(contact); if (preferredProvider != null) { preferredProviders = new Hashtable<Class<? extends OperationSet>, ProtocolProviderService>(); LinkedList<Class<? extends OperationSet>> supportedOpSets = new LinkedList<Class<? extends OperationSet>>(); for(Class<? extends OperationSet> opset : preferredProvider.getSupportedOperationSetClasses()) { // skip opset IM as we want explicitly muc support if(opset.equals(OperationSetPresence.class) || opset.equals(OperationSetPersistentPresence.class) || ((isChatRoom || service.isSMSEnabled()) && opset.equals( OperationSetBasicInstantMessaging.class))) { continue; } if(!isChatRoom && opsetCapabilities != null && !opsetCapabilities.containsKey(opset.getName())) continue; preferredProviders.put(opset, preferredProvider); supportedOpSets.add(opset); } contactDetail.setPreferredProviders(preferredProviders); contactDetail.setSupportedOpSets(supportedOpSets); } contactDetails.clear(); contactDetails.add(contactDetail); } @Override public String getDisplayName() { if(this.displayName != null) return this.displayName; else return MessageHistoryActivator.getResources() .getI18NString("service.gui.UNKNOWN"); } @Override public String getContactAddress() { if(this.address != null) return this.address; return null; } @Override public ContactSourceService getContactSource() { return service; } @Override public String getDisplayDetails() { return messageContent; } /** * Returns a list of available contact details. * @return a list of available contact details */ @Override public List<ContactDetail> getContactDetails() { return new LinkedList<ContactDetail>(contactDetails); } /** * Returns a list of all <tt>ContactDetail</tt>s supporting the given * <tt>OperationSet</tt> class. * @param operationSet the <tt>OperationSet</tt> class we're looking for * @return a list of all <tt>ContactDetail</tt>s supporting the given * <tt>OperationSet</tt> class */ @Override public List<ContactDetail> getContactDetails( Class<? extends OperationSet> operationSet) { List<ContactDetail> res = new LinkedList<ContactDetail>(); for(ContactDetail det : contactDetails) { if(det.getPreferredProtocolProvider(operationSet) != null) res.add(det); } return res; } /** * Returns a list of all <tt>ContactDetail</tt>s corresponding to the given * category. * @param category the <tt>OperationSet</tt> class we're looking for * @return a list of all <tt>ContactDetail</tt>s corresponding to the given * category */ @Override public List<ContactDetail> getContactDetails( ContactDetail.Category category) throws OperationNotSupportedException { // We don't support category for message source history details, // so we return null. throw new OperationNotSupportedException( "Categories are not supported for message source contact history."); } /** * Returns the preferred <tt>ContactDetail</tt> for a given * <tt>OperationSet</tt> class. * @param operationSet the <tt>OperationSet</tt> class, for which we would * like to obtain a <tt>ContactDetail</tt> * @return the preferred <tt>ContactDetail</tt> for a given * <tt>OperationSet</tt> class */ @Override public ContactDetail getPreferredContactDetail( Class<? extends OperationSet> operationSet) { return contactDetails.get(0); } @Override public byte[] getImage() { return image; } @Override public boolean isDefaultImage() { return image == null; } @Override public void setContactAddress(String contactAddress) {} @Override public PresenceStatus getPresenceStatus() { return status; } /** * Sets current status. * @param status */ public void setStatus(PresenceStatus status) { this.status = status; } @Override public int getIndex() { return service.getIndex(this); } /** * The contact. * @return the contact. */ public Contact getContact() { return contact; } /** * The room. * @return the room. */ public ChatRoom getRoom() { return room; } /** * The timestamp of the message. * @return the timestamp of the message. */ public Date getTimestamp() { return timestamp; } /** * Changes display name. * @param displayName */ public void setDisplayName(String displayName) { this.displayName = displayName; } /** * Updates display name if contact is not null. */ private void updateDisplayName() { if(this.contact == null) return; MetaContact metaContact = MessageHistoryActivator.getContactListService() .findMetaContactByContact(contact); if(metaContact == null) return; this.displayName = metaContact.getDisplayName(); } /** * Compares two MessageSourceContacts. * @param o the object to compare with * @return 0, less than zero, greater than zero, if equals, less or greater. */ @Override public int compareTo(MessageSourceContact o) { if(o == null || o.getTimestamp() == null) return 1; return o.getTimestamp() .compareTo(getTimestamp()); } @Override public boolean equals(Object o) { if(this == o) return true; if(o == null || getClass() != o.getClass()) return false; MessageSourceContact that = (MessageSourceContact) o; if(!address.equals(that.address)) return false; if(!ppService.equals(that.ppService)) return false; return true; } @Override public int hashCode() { int result = address.hashCode(); result = 31 * result + ppService.hashCode(); return result; } }