Adds recent messages contact source.

fix-message-formatting
Damian Minkov 12 years ago
parent 7efe461ee2
commit 44d672d134

@ -290,6 +290,7 @@ service.gui.INSERT_SMILEY=Insert smiley
service.gui.INCOMING_CALL=Incoming call received from: {0}
service.gui.INCOMING_CALL_STATUS=Incoming call
service.gui.INSTANT_MESSAGINGS=IMs
service.gui.IM=IM
service.gui.INITIATING_CALL_STATUS=Initiating call
service.gui.INVITATION=Invitation text
service.gui.INVITATION_RECEIVED=Invitation received
@ -441,6 +442,7 @@ service.gui.PUT_OFF_HOLD=Put off hold
service.gui.PUT_ON_HOLD=Put on hold
service.gui.QUIT=&Quit
service.gui.READY=Ready
service.gui.RECENT_MESSAGES=Recent messages
service.gui.REASON=Reason
service.gui.RECEIVED={0} received
service.gui.RECONNECTION_LIMIT_EXCEEDED=You have have been disconnecting and reconnecting to the server too fast. The following account: User name: {0}, Server name: {1} is temporarily banned and would have to wait a bit before trying to login again.
@ -1010,6 +1012,7 @@ plugin.generalconfig.ERROR_PORT_NUMBER=Wrong port number
plugin.generalconfig.CHECK_FOR_UPDATES=Check for updates on startup
plugin.generalconfig.STARTUP_CONFIG=Startup
plugin.generalconfig.LEAVE_CHATROOM_ON_WINDOW_CLOSE=Leave chat rooms when closing window
plugin.generalconfig.SHOW_RECENT_MESSAGES=Show recent messages (depends on chat history)
plugin.generalconfig.REMOVE_SPECIAL_PHONE_SYMBOLS=Remove special symbols before calling phone numbers
plugin.generalconfig.ACCEPT_PHONE_NUMBER_WITH_ALPHA_CHARS=Convert letters in phone numbers
plugin.generalconfig.ACCEPT_PHONE_NUMBER_WITH_ALPHA_CHARS_EXAMPLE=e.g. +1-800-MYPHONE -> +1-800-694663

@ -3668,6 +3668,6 @@ public void supportedOperationSetsChanged(ContactCapabilitiesEvent event)
*/
public int getSourceIndex()
{
return 1;
return 2;
}
}

@ -11,6 +11,8 @@
import net.java.sip.communicator.service.msghistory.*;
import net.java.sip.communicator.util.*;
import org.jitsi.service.configuration.*;
import org.jitsi.service.resources.*;
import org.osgi.framework.*;
/**
@ -32,13 +34,23 @@ public class MessageHistoryActivator
/**
* The <tt>MessageHistoryService</tt> reference.
*/
private MessageHistoryServiceImpl msgHistoryService = null;
private static MessageHistoryServiceImpl msgHistoryService = null;
/**
* The <tt>ResourceManagementService</tt> reference.
*/
private static ResourceManagementService resourcesService;
/**
* The <tt>MetaContactListService</tt> reference.
*/
private static MetaContactListService metaCListService;
/**
* The <tt>ConfigurationService</tt> reference.
*/
private static ConfigurationService configService;
/**
* The <tt>BundleContext</tt> of the service.
*/
@ -114,4 +126,52 @@ public static MetaContactListService getContactListService()
}
return metaCListService;
}
/**
* Returns the <tt>MessageHistoryService</tt> registered to the bundle
* context.
* @return the <tt>MessageHistoryService</tt> registered to the bundle
* context
*/
public static MessageHistoryServiceImpl getMessageHistoryService()
{
return msgHistoryService;
}
/**
* Returns the <tt>ResourceManagementService</tt>, through which we will
* access all resources.
*
* @return the <tt>ResourceManagementService</tt>, through which we will
* access all resources.
*/
public static ResourceManagementService getResources()
{
if (resourcesService == null)
{
resourcesService
= ServiceUtils.getService(
bundleContext,
ResourceManagementService.class);
}
return resourcesService;
}
/**
* Returns the <tt>ConfigurationService</tt> obtained from the bundle
* context.
* @return the <tt>ConfigurationService</tt> obtained from the bundle
* context
*/
public static ConfigurationService getConfigurationService()
{
if(configService == null)
{
configService
= ServiceUtils.getService(
bundleContext,
ConfigurationService.class);
}
return configService;
}
}

@ -15,6 +15,7 @@
import java.util.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.history.*;
import net.java.sip.communicator.service.history.event.*;
import net.java.sip.communicator.service.history.event.ProgressEvent;
@ -27,7 +28,6 @@
import net.java.sip.communicator.util.account.*;
import org.jitsi.service.configuration.*;
import org.jitsi.service.resources.*;
import org.osgi.framework.*;
/**
@ -84,13 +84,20 @@ public class MessageHistoryServiceImpl
private MessageHistoryPropertyChangeListener msgHistoryPropListener;
private static ResourceManagementService resourcesService;
/**
* Indicates if history logging is enabled.
*/
private static boolean isHistoryLoggingEnabled;
/**
* The message source service, can be null if not enabled.
*/
private MessageSourceService messageSourceService;
/**
* The message source service registration.
*/
private ServiceRegistration messageSourceServiceReg = null;
/**
* Returns the history service.
@ -342,15 +349,20 @@ public Collection<EventObject> findLast(MetaContact contact, int count)
* Returns the messages for the recently contacted <tt>count</tt> contacts.
*
* @param count contacts count
* @param providerToFilter can be filtered by provider, or <tt>null</tt> to
* search for all providers
* @param contactToFilter can be filtered by contac, or <tt>null</tt> to
* search for all contacts
* @return Collection of MessageReceivedEvents or MessageDeliveredEvents
* @throws RuntimeException
*/
Collection<EventObject> findRecentMessagesPerContact(int count)
Collection<EventObject> findRecentMessagesPerContact(
int count, String providerToFilter, String contactToFilter)
throws RuntimeException
{
TreeSet<EventObject> result
= new TreeSet<EventObject>(
new MessageEventComparator<EventObject>());
new MessageEventComparator<EventObject>(true));
List<HistoryID> historyIDs=
this.historyService.getExistingHistories(
@ -363,11 +375,31 @@ Collection<EventObject> findRecentMessagesPerContact(int count)
try
{
// find contact for historyID
Contact contact = getContactForHistory(id);
// this history id is: "messages", localId, account, remoteId
if(id.getID().length != 4)
continue;
// filter by protocol provider
String accountID = id.getID()[2].replace('_', ':');
if(providerToFilter != null
&& !accountID.startsWith(providerToFilter))
{
continue;
}
if(contactToFilter != null
&& !contactToFilter.equals(id.getID()[3]))
{
continue;
}
// find contact or chatroom for historyID
Object descriptor = getContactOrRoomByID(
accountID,
id.getID()[3]);
// skip not found contacts, disabled accounts and hidden one
if(contact == null)
if(descriptor == null)
continue;
History history;
@ -386,8 +418,20 @@ Collection<EventObject> findRecentMessagesPerContact(int count)
Iterator<HistoryRecord> recs = reader.findLast(1);
while (recs.hasNext())
{
result.add(convertHistoryRecordToMessageEvent(
recs.next(), contact));
if(descriptor instanceof Contact)
{
EventObject o = convertHistoryRecordToMessageEvent(
recs.next(), (Contact) descriptor);
result.add(o);
}
if(descriptor instanceof ChatRoom)
{
EventObject o = convertHistoryRecordToMessageEvent(
recs.next(), (ChatRoom) descriptor);
result.add(o);
}
break;
}
}
@ -401,21 +445,16 @@ Collection<EventObject> findRecentMessagesPerContact(int count)
}
/**
* Founds the contact corresponding this HistoryID. Checks the account and
* then searches for the contact.
* Founds the contact or chat room corresponding this HistoryID. Checks the
* account and then searches for the contact or chat room.
* Will skip hidden and disabled accounts.
*
* @param id the history id.
* @return
* @param accountID the account id.
* @param id the contact or room id.
* @return contact or chat room.
*/
private Contact getContactForHistory(HistoryID id)
private Object getContactOrRoomByID(String accountID, String id)
{
// this history id is: "messages", localId, account, remoteId
if(id.getID().length != 4)
return null;
String accountID = id.getID()[2].replace('_', ':');
AccountID account = null;
for(AccountID acc : AccountUtils.getStoredAccounts())
{
@ -443,7 +482,29 @@ private Contact getContactForHistory(HistoryID id)
if(opSetPresence == null)
return null;
return opSetPresence.findContactByID(id.getID()[3]);
Contact contact = opSetPresence.findContactByID(id);
if(contact != null)
return contact;
OperationSetMultiUserChat opSetMuc =
pps.getOperationSet(OperationSetMultiUserChat.class);
if(opSetMuc == null)
return null;
try
{
// will remove the server part
id = id.substring(0, id.lastIndexOf('@'));
return opSetMuc.findRoom(id);
}
catch(Exception e)
{
//logger.error("Cannot find room", e);
return null;
}
}
/**
@ -915,7 +976,8 @@ public void start(BundleContext bc)
// service, and if not do not register the service.
boolean isMessageHistoryEnabled = configService.getBoolean(
MessageHistoryService.PNAME_IS_MESSAGE_HISTORY_ENABLED,
Boolean.parseBoolean(getResources().getSettingsString(
Boolean.parseBoolean(
MessageHistoryActivator.getResources().getSettingsString(
MessageHistoryService.PNAME_IS_MESSAGE_HISTORY_ENABLED))
);
@ -944,6 +1006,31 @@ public void start(BundleContext bc)
}
}
/**
* Loads and registers the contact source service.
*/
private void loadRecentMessages()
{
this.messageSourceService = new MessageSourceService();
messageSourceServiceReg = bundleContext.registerService(
ContactSourceService.class.getName(),
messageSourceService, null);
}
/**
* Unloads the contact source service.
*/
private void stopRecentMessages()
{
if(messageSourceServiceReg != null)
{
messageSourceServiceReg.unregister();
messageSourceServiceReg = null;
this.messageSourceService = null;
}
}
/**
* Stops the service.
*
@ -1342,6 +1429,9 @@ private void handleProviderAdded(ProtocolProviderService provider)
if (opSetIm != null)
{
opSetIm.addMessageListener(this);
if(this.messageSourceService != null)
opSetIm.addMessageListener(messageSourceService);
}
else
{
@ -1355,6 +1445,9 @@ private void handleProviderAdded(ProtocolProviderService provider)
if (opSetSMS != null)
{
opSetSMS.addMessageListener(this);
if(this.messageSourceService != null)
opSetIm.addMessageListener(messageSourceService);
}
else
{
@ -1377,12 +1470,26 @@ private void handleProviderAdded(ProtocolProviderService provider)
}
opSetMultiUChat.addPresenceListener(this);
if(messageSourceService != null)
opSetMultiUChat.addPresenceListener(messageSourceService);
}
else
{
if (logger.isTraceEnabled())
logger.trace("Service did not have a multi im op. set.");
}
OperationSetPresence opSetPresence =
provider.getOperationSet(OperationSetPresence.class);
if (opSetPresence != null && messageSourceService != null)
{
opSetPresence
.addContactPresenceStatusListener(messageSourceService);
opSetPresence
.addProviderPresenceStatusListener(messageSourceService);
}
}
/**
@ -1399,6 +1506,9 @@ private void handleProviderRemoved(ProtocolProviderService provider)
if (opSetIm != null)
{
opSetIm.removeMessageListener(this);
if(this.messageSourceService != null)
opSetIm.removeMessageListener(messageSourceService);
}
OperationSetSmsMessaging opSetSMS =
@ -1407,6 +1517,9 @@ private void handleProviderRemoved(ProtocolProviderService provider)
if (opSetSMS != null)
{
opSetSMS.removeMessageListener(this);
if(this.messageSourceService != null)
opSetIm.removeMessageListener(messageSourceService);
}
OperationSetMultiUserChat opSetMultiUChat =
@ -1422,6 +1535,22 @@ private void handleProviderRemoved(ProtocolProviderService provider)
ChatRoom room = iter.next();
room.removeMessageListener(this);
}
opSetMultiUChat.removePresenceListener(this);
if(messageSourceService != null)
opSetMultiUChat.removePresenceListener(messageSourceService);
}
OperationSetPresence opSetPresence =
provider.getOperationSet(OperationSetPresence.class);
if (opSetPresence != null && messageSourceService != null)
{
opSetPresence
.removeContactPresenceStatusListener(messageSourceService);
opSetPresence
.removeProviderPresenceStatusListener(messageSourceService);
}
}
@ -1438,11 +1567,19 @@ public void localUserPresenceChanged(LocalUserChatRoomPresenceChangeEvent evt)
LocalUserChatRoomPresenceChangeEvent.LOCAL_USER_JOINED)
{
if (!evt.getChatRoom().isSystem())
{
evt.getChatRoom().addMessageListener(this);
if(this.messageSourceService != null)
evt.getChatRoom().addMessageListener(messageSourceService);
}
}
else
{
evt.getChatRoom().removeMessageListener(this);
if(this.messageSourceService != null)
evt.getChatRoom().removeMessageListener(messageSourceService);
}
}
@ -2116,30 +2253,6 @@ public Collection<EventObject> findLastMessagesBefore( ChatRoom room,
return resultAsList.subList(startIndex, resultAsList.size());
}
/**
* Get <tt>ResourceManagementService<tt> registered.
*
* @return <tt>ResourceManagementService</tt>
*/
public static ResourceManagementService getResources()
{
if (resourcesService == null)
{
ServiceReference serviceReference
= MessageHistoryActivator.bundleContext
.getServiceReference(ResourceManagementService.class.getName());
if(serviceReference == null)
return null;
resourcesService = (ResourceManagementService)
MessageHistoryActivator.bundleContext
.getService(serviceReference);
}
return resourcesService;
}
/**
* A wrapper around HistorySearchProgressListener
* that fires events for MessageHistorySearchProgressListener
@ -2254,6 +2367,18 @@ public Date getMessageReceivedDate()
private static class MessageEventComparator<T>
implements Comparator<T>
{
private final boolean reverseOrder;
MessageEventComparator(boolean reverseOrder)
{
this.reverseOrder = reverseOrder;
}
MessageEventComparator()
{
this(false);
}
public int compare(T o1, T o2)
{
Date date1;
@ -2263,6 +2388,10 @@ public int compare(T o1, T o2)
date1 = ((MessageDeliveredEvent)o1).getTimestamp();
else if(o1 instanceof MessageReceivedEvent)
date1 = ((MessageReceivedEvent)o1).getTimestamp();
else if(o1 instanceof ChatRoomMessageDeliveredEvent)
date1 = ((ChatRoomMessageDeliveredEvent)o1).getTimestamp();
else if(o1 instanceof ChatRoomMessageReceivedEvent)
date1 = ((ChatRoomMessageReceivedEvent)o1).getTimestamp();
else
return 0;
@ -2270,10 +2399,17 @@ else if(o1 instanceof MessageReceivedEvent)
date2 = ((MessageDeliveredEvent)o2).getTimestamp();
else if(o2 instanceof MessageReceivedEvent)
date2 = ((MessageReceivedEvent)o2).getTimestamp();
else if(o2 instanceof ChatRoomMessageDeliveredEvent)
date2 = ((ChatRoomMessageDeliveredEvent)o2).getTimestamp();
else if(o2 instanceof ChatRoomMessageReceivedEvent)
date2 = ((ChatRoomMessageReceivedEvent)o2).getTimestamp();
else
return 0;
return date1.compareTo(date2);
if(reverseOrder)
return date2.compareTo(date1);
else
return date1.compareTo(date2);
}
}
@ -2410,15 +2546,35 @@ private class MessageHistoryPropertyChangeListener
{
public void propertyChange(PropertyChangeEvent evt)
{
String newPropertyValue = (String) evt.getNewValue();
isHistoryLoggingEnabled
= new Boolean(newPropertyValue).booleanValue();
if(evt.getPropertyName()
.equals(MessageHistoryService.PNAME_IS_MESSAGE_HISTORY_ENABLED))
{
String newPropertyValue = (String) evt.getNewValue();
isHistoryLoggingEnabled
= new Boolean(newPropertyValue).booleanValue();
// If the message history is not enabled we stop here.
if (isHistoryLoggingEnabled)
loadMessageHistoryService();
else
stop(bundleContext);
// If the message history is not enabled we stop here.
if (isHistoryLoggingEnabled)
loadMessageHistoryService();
else
stop(bundleContext);
}
else if(evt.getPropertyName().equals(
MessageHistoryService.PNAME_IS_RECENT_MESSAGES_DISABLED))
{
String newPropertyValue = (String) evt.getNewValue();
boolean isDisabled
= new Boolean(newPropertyValue).booleanValue();
if(isDisabled)
{
stopRecentMessages();
}
else if(isHistoryLoggingEnabled)
{
loadRecentMessages();
}
}
}
}
@ -2429,6 +2585,17 @@ public void propertyChange(PropertyChangeEvent evt)
*/
private void loadMessageHistoryService()
{
configService.addPropertyChangeListener(
MessageHistoryService.PNAME_IS_RECENT_MESSAGES_DISABLED,
msgHistoryPropListener);
boolean isRecentMessagesDisabled = configService.getBoolean(
MessageHistoryService.PNAME_IS_RECENT_MESSAGES_DISABLED,
false);
if(!isRecentMessagesDisabled)
loadRecentMessages();
// start listening for newly register or removed protocol providers
bundleContext.addServiceListener(this);

@ -0,0 +1,416 @@
/*
* 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.msghistory;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
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.
*/
private PresenceStatus status = null;
/**
* 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;
/**
* 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)
{
update(source);
if(source instanceof MessageDeliveredEvent
|| source instanceof MessageReceivedEvent)
{
initDetails(false);
}
else if(source instanceof ChatRoomMessageDeliveredEvent
|| source instanceof ChatRoomMessageReceivedEvent)
{
initDetails(true);
}
this.service = service;
if(this.messageContent != null
&& this.messageContent.length() > 60)
{
// do not display too long texts
this.messageContent = this.messageContent.substring(0, 60);
this.messageContent += "...";
}
}
/**
* 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.displayName = contact.getDisplayName();
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.displayName = contact.getDisplayName();
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 = null;
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 = null;
this.messageContent = e.getMessage().getContent();
this.timestamp = e.getTimestamp();
}
}
/**
* We will the details for this source contact.
* Will skip OperationSetBasicInstantMessaging for chat rooms.
* @param isChatRoom is current source contact a chat room.
*/
private void initDetails(boolean isChatRoom)
{
ContactDetail contactDetail =
new ContactDetail(
this.address,
this.displayName);
Map<Class<? extends OperationSet>, ProtocolProviderService>
preferredProviders;
ProtocolProviderService preferredProvider
= this.ppService;
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
&& opset.equals(
OperationSetBasicInstantMessaging.class)))
{
continue;
}
preferredProviders.put(opset, preferredProvider);
supportedOpSets.add(opset);
}
contactDetail.setPreferredProviders(preferredProviders);
contactDetail.setSupportedOpSets(supportedOpSets);
}
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;
}
/**
* 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());
}
}

@ -0,0 +1,684 @@
/*
* 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.msghistory;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.history.*;
import net.java.sip.communicator.service.history.records.*;
import net.java.sip.communicator.service.muc.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import org.jitsi.util.*;
import java.beans.*;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.regex.*;
/**
* The source contact service. The will show most recent messages.
*
* @author Damian Minkov
*/
public class MessageSourceService
implements ContactSourceService,
ContactPresenceStatusListener,
ProviderPresenceStatusListener,
LocalUserChatRoomPresenceListener,
MessageListener,
ChatRoomMessageListener,
AdHocChatRoomMessageListener
{
/**
* The logger for this class.
*/
private static Logger logger = Logger
.getLogger(MessageSourceService.class);
/**
* The display name of this contact source.
*/
private final String MESSAGE_HISTORY_NAME;
/**
* The type of the source service, the place to be shown in the ui.
*/
private int sourceServiceType = RECENT_MESSAGES_TYPE;
/**
* Whether to show recent messages in history or in contactlist.
* By default we show it in contactlist.
*/
private static final String IN_HISTORY_PROPERTY
= "net.java.sip.communicator.impl.msghistory.contactsrc.IN_HISTORY";
/**
* Property to control number of recent messages.
*/
private static final String NUMBER_OF_RECENT_MSGS_PROP
= "net.java.sip.communicator.impl.msghistory.contactsrc.MSG_NUMBER";
/**
* Number of messages to show.
*/
private int numberOfMessages = 10;
/**
* The structure to save recent messages list.
*/
private static final String[] STRUCTURE_NAMES
= new String[] { "provider", "contact"};
/**
* The structure.
*/
private static final HistoryRecordStructure recordStructure =
new HistoryRecordStructure(STRUCTURE_NAMES);
/**
* Recent messages history ID.
*/
private static final HistoryID historyID = HistoryID.createFromRawID(
new String[] { "recent_messages"});
/**
* List of recent messages.
*/
private List<MessageSourceContact> recentMessages = null;
/**
* The last query created.
*/
private MessageHistoryContactQuery recentQuery = null;
/**
* Constructs MessageSourceService.
*/
MessageSourceService()
{
if(MessageHistoryActivator.getConfigurationService()
.getBoolean(IN_HISTORY_PROPERTY , false))
{
sourceServiceType = HISTORY_TYPE;
}
MESSAGE_HISTORY_NAME = MessageHistoryActivator.getResources()
.getI18NString("service.gui.RECENT_MESSAGES");
numberOfMessages = MessageHistoryActivator.getConfigurationService()
.getInt(NUMBER_OF_RECENT_MSGS_PROP, numberOfMessages);
}
/**
* Returns the display name of this contact source.
* @return the display name of this contact source
*/
@Override
public String getDisplayName()
{
return MESSAGE_HISTORY_NAME;
}
/**
* Returns default type to indicate that this contact source can be queried
* by default filters.
*
* @return the type of this contact source
*/
@Override
public int getType()
{
return sourceServiceType;
}
/**
* Returns the index of the contact source in the result list.
*
* @return the index of the contact source in the result list
*/
@Override
public int getIndex()
{
return 0;
}
/**
* Creates query for the given <tt>searchString</tt>.
* @param queryString the string to search for
* @return the created query
*/
@Override
public ContactQuery createContactQuery(String queryString)
{
recentQuery =
(MessageHistoryContactQuery)createContactQuery(
queryString, numberOfMessages);
return recentQuery;
}
private List<MessageSourceContact> getRecentMessages()
{
if(recentMessages == null)
{
// find locally stored list of recent messages
// time, provider, contact
List<MessageSourceContact> cachedRecent
= getRecentMessagesFromHistory();
if(cachedRecent != null)
{
recentMessages = cachedRecent;
Collections.sort(recentMessages);
return recentMessages;
}
recentMessages = new LinkedList<MessageSourceContact>();
// If missing search and construct it and save it
MessageHistoryServiceImpl msgHistoryService =
MessageHistoryActivator.getMessageHistoryService();
Collection<EventObject> res = msgHistoryService
.findRecentMessagesPerContact(numberOfMessages, null, null);
for(EventObject obj : res)
{
recentMessages.add(
new MessageSourceContact(obj, MessageSourceService.this));
}
Collections.sort(recentMessages);
// save it
saveRecentMessagesToHistory();
}
return recentMessages;
}
/**
* Loads recent messages if saved in history.
* @return
*/
private List<MessageSourceContact> getRecentMessagesFromHistory()
{
MessageHistoryServiceImpl msgService
= MessageHistoryActivator.getMessageHistoryService();
HistoryService historyService = msgService.getHistoryService();
List<MessageSourceContact> res
= new LinkedList<MessageSourceContact>();
// and load it
try
{
SimpleDateFormat sdf
= new SimpleDateFormat(HistoryService.DATE_FORMAT);
History history;
if (historyService.isHistoryExisting(historyID))
history = historyService.getHistory(historyID);
else
history
= historyService.createHistory(historyID, recordStructure);
Iterator<HistoryRecord> recs
= history.getReader().findLast(numberOfMessages);
while(recs.hasNext())
{
HistoryRecord hr = recs.next();
String provider = null;
String contact = null;
for (int i = 0; i < hr.getPropertyNames().length; i++)
{
String propName = hr.getPropertyNames()[i];
if (propName.equals(STRUCTURE_NAMES[0]))
provider = hr.getPropertyValues()[i];
else if (propName.equals(STRUCTURE_NAMES[1]))
contact = hr.getPropertyValues()[i];
}
if(provider == null || contact == null)
return res;
for(EventObject ev : msgService.findRecentMessagesPerContact(
numberOfMessages, provider, contact))
{
res.add(new MessageSourceContact(ev, this));
}
}
}
catch(IOException ex)
{
logger.error("cannot create recent_messages history", ex);
return null;
}
return res;
}
/**
* Saves cached list of recent messages in history.
*/
private void saveRecentMessagesToHistory()
{
HistoryService historyService = MessageHistoryActivator
.getMessageHistoryService().getHistoryService();
if (historyService.isHistoryExisting(historyID))
{
// delete it
try
{
historyService.purgeLocallyStoredHistory(historyID);
}
catch(IOException ex)
{
logger.error("Cannot delete recent_messages history", ex);
return;
}
}
// and create it
try
{
History history = historyService.createHistory(
historyID, recordStructure);
HistoryWriter writer = history.getWriter();
SimpleDateFormat sdf
= new SimpleDateFormat(HistoryService.DATE_FORMAT);
for(MessageSourceContact msc : recentMessages)
{
writer.addRecord(
new String[]
{
msc.getProtocolProviderService()
.getAccountID().getAccountUniqueID(),
msc.getContactAddress()
});
}
}
catch(IOException ex)
{
logger.error("cannot create recent_messages history", ex);
return;
}
}
/**
* Returns the index of the source contact, in the list of recent messages.
* @param messageSourceContact
* @return
*/
int getIndex(MessageSourceContact messageSourceContact)
{
return recentMessages.indexOf(messageSourceContact);
}
/**
* Creates query for the given <tt>searchString</tt>.
* @param queryString the string to search for
* @param contactCount the maximum count of result contacts
* @return the created query
*/
@Override
public ContactQuery createContactQuery(String queryString, int contactCount)
{
if(!StringUtils.isNullOrEmpty(queryString))
return null;
recentQuery = new MessageHistoryContactQuery(numberOfMessages);
return recentQuery;
}
/**
* Updates contact source contacts with status.
* @param evt the ContactPresenceStatusChangeEvent describing the status
*/
@Override
public void contactPresenceStatusChanged(ContactPresenceStatusChangeEvent evt)
{
if(recentQuery == null)
return;
for(MessageSourceContact msgSC : getRecentMessages())
{
if(msgSC.getContact() != null
&& msgSC.getContact().equals(evt.getSourceContact()))
{
msgSC.setStatus(evt.getNewStatus());
recentQuery.fireContactChanged(msgSC);
}
}
}
@Override
public void providerStatusChanged(ProviderPresenceStatusChangeEvent evt)
{
if(!evt.getNewStatus().isOnline())
return;
// now check for chat rooms as we are connected
MessageHistoryServiceImpl msgHistoryService =
MessageHistoryActivator.getMessageHistoryService();
Collection<EventObject> res = msgHistoryService
.findRecentMessagesPerContact(
numberOfMessages,
evt.getProvider().getAccountID().getAccountUniqueID(),
null);
List<String> recentMessagesForProvider = new LinkedList<String>();
for(MessageSourceContact msc : recentMessages)
{
if(msc.getProtocolProviderService().equals(evt.getProvider()))
recentMessagesForProvider.add(msc.getContactAddress());
}
List<MessageSourceContact> newContactSources
= new LinkedList<MessageSourceContact>();
for(EventObject obj : res)
{
if(obj instanceof ChatRoomMessageDeliveredEvent
|| obj instanceof ChatRoomMessageReceivedEvent)
{
MessageSourceContact msc
= new MessageSourceContact(obj, MessageSourceService.this);
if(recentMessagesForProvider.contains(msc.getContactAddress()))
continue;
recentMessages.add(msc);
newContactSources.add(msc);
}
}
// sort
Collections.sort(recentMessages);
// and now fire events to update ui
if(recentQuery != null)
{
for(MessageSourceContact msc : newContactSources)
{
recentQuery.addQueryResult(msc);
}
}
}
@Override
public void providerStatusMessageChanged(PropertyChangeEvent evt)
{}
@Override
public void localUserPresenceChanged(
LocalUserChatRoomPresenceChangeEvent evt)
{
if(recentQuery == null)
return;
MessageSourceContact srcContact = null;
for(MessageSourceContact msg : getRecentMessages())
{
if(msg.getRoom() != null
&& msg.getRoom().equals(evt.getChatRoom()))
{
srcContact = msg;
break;
}
}
if(srcContact == null)
return;
String eventType = evt.getEventType();
if (LocalUserChatRoomPresenceChangeEvent
.LOCAL_USER_JOINED.equals(eventType))
{
srcContact.setStatus(ChatRoomPresenceStatus.CHAT_ROOM_ONLINE);
recentQuery.fireContactChanged(srcContact);
}
else if ((LocalUserChatRoomPresenceChangeEvent
.LOCAL_USER_LEFT.equals(eventType)
|| LocalUserChatRoomPresenceChangeEvent
.LOCAL_USER_KICKED.equals(eventType)
|| LocalUserChatRoomPresenceChangeEvent
.LOCAL_USER_DROPPED.equals(eventType))
)
{
srcContact.setStatus(ChatRoomPresenceStatus.CHAT_ROOM_OFFLINE);
recentQuery.fireContactChanged(srcContact);
}
}
/**
* Handles new events.
*
* @param obj the event object
* @param provider the provider
* @param id the id of the source of the event
*/
private void handle(EventObject obj,
ProtocolProviderService provider,
String id)
{
// check if provider - contact exist update message content
for(MessageSourceContact msc : recentMessages)
{
if(msc.getProtocolProviderService().equals(provider)
&& msc.getContactAddress().equals(id))
{
// update
msc.update(obj);
if(recentQuery != null)
recentQuery.fireContactChanged(msc);
return;
}
}
// if missing create source contact
// and update recent messages, trim and sort
MessageSourceContact newSourceContact =
new MessageSourceContact(obj, MessageSourceService.this);
recentMessages.add(newSourceContact);
Collections.sort(recentMessages);
// trim
List<MessageSourceContact> removedItems = null;
if(recentMessages.size() > numberOfMessages)
{
removedItems = recentMessages.subList(
numberOfMessages, recentMessages.size());
recentMessages = recentMessages.subList(0, numberOfMessages);
}
// save
saveRecentMessagesToHistory();
// no query nothing to fire
if(recentQuery == null)
return;
// now fire
if(removedItems != null)
{
for(MessageSourceContact msc : removedItems)
{
recentQuery.fireContactRemoved(msc);
}
}
recentQuery.fireContactReceived(newSourceContact);
}
@Override
public void messageReceived(MessageReceivedEvent evt)
{
handle(
evt,
evt.getSourceContact().getProtocolProvider(),
evt.getSourceContact().getAddress());
}
@Override
public void messageDelivered(MessageDeliveredEvent evt)
{
handle(
evt,
evt.getDestinationContact().getProtocolProvider(),
evt.getDestinationContact().getAddress());
}
/**
* Not used.
* @param evt the <tt>MessageFailedEvent</tt> containing the ID of the
*/
@Override
public void messageDeliveryFailed(MessageDeliveryFailedEvent evt)
{}
@Override
public void messageReceived(ChatRoomMessageReceivedEvent evt)
{
handle(
evt,
evt.getSourceChatRoom().getParentProvider(),
evt.getSourceChatRoom().getIdentifier());
}
@Override
public void messageDelivered(ChatRoomMessageDeliveredEvent evt)
{
handle(
evt,
evt.getSourceChatRoom().getParentProvider(),
evt.getSourceChatRoom().getIdentifier());
}
/**
* Not used.
* @param evt the <tt>ChatroomMessageDeliveryFailedEvent</tt> containing
*/
@Override
public void messageDeliveryFailed(ChatRoomMessageDeliveryFailedEvent evt)
{}
@Override
public void messageReceived(AdHocChatRoomMessageReceivedEvent evt)
{
// TODO
}
@Override
public void messageDelivered(AdHocChatRoomMessageDeliveredEvent evt)
{
// TODO
}
/**
* Not used.
* @param evt the <tt>AdHocChatroomMessageDeliveryFailedEvent</tt>
*/
@Override
public void messageDeliveryFailed(AdHocChatRoomMessageDeliveryFailedEvent evt)
{}
/**
* The contact query implementation.
*/
private class MessageHistoryContactQuery
extends AsyncContactQuery<MessageSourceService>
{
MessageHistoryContactQuery(int contactCount)
{
super(MessageSourceService.this,
Pattern.compile("",
Pattern.CASE_INSENSITIVE | Pattern.LITERAL),
false);
}
@Override
public void run()
{
for(MessageSourceContact rm : getRecentMessages())
{
addQueryResult(rm);
}
}
/**
* Notifies the <tt>ContactQueryListener</tt>s registered with this
* <tt>ContactQuery</tt> that a new <tt>SourceContact</tt> has been
* received.
*
* @param contact the <tt>SourceContact</tt> which has been received and
* which the registered <tt>ContactQueryListener</tt>s are to be notified
* about
*/
public void fireContactReceived(SourceContact contact)
{
fireContactReceived(contact, false);
}
/**
* Notifies the <tt>ContactQueryListener</tt>s registered with this
* <tt>ContactQuery</tt> that a <tt>SourceContact</tt> has been
* changed.
*
* @param contact the <tt>SourceContact</tt> which has been changed and
* which the registered <tt>ContactQueryListener</tt>s are to be notified
* about
*/
public void fireContactChanged(SourceContact contact)
{
super.fireContactChanged(contact);
}
/**
* Notifies the <tt>ContactQueryListener</tt>s registered with this
* <tt>ContactQuery</tt> that a <tt>SourceContact</tt> has been
* removed.
*
* @param contact the <tt>SourceContact</tt> which has been removed and
* which the registered <tt>ContactQueryListener</tt>s are to be notified
* about
*/
public void fireContactRemoved(SourceContact contact)
{
super.fireContactRemoved(contact);
}
/**
* Adds a specific <tt>SourceContact</tt> to the list of
* <tt>SourceContact</tt>s to be returned by this <tt>ContactQuery</tt> in
* response to {@link #getQueryResults()}.
*
* @param sourceContact the <tt>SourceContact</tt> to be added to the
* <tt>queryResults</tt> of this <tt>ContactQuery</tt>
* @return <tt>true</tt> if the <tt>queryResults</tt> of this
* <tt>ContactQuery</tt> has changed in response to the call
*/
public boolean addQueryResult(SourceContact sourceContact)
{
return super.addQueryResult(sourceContact);
}
}
}

@ -14,7 +14,8 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.contactlist,
net.java.sip.communicator.service.contactsource,
net.java.sip.communicator.service.history.records,
net.java.sip.communicator.util,
net.java.sip.communicator.service.muc,
net.java.sip.communicator.util, org.jitsi.util,
net.java.sip.communicator.util.account,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.icqconstants,

@ -35,6 +35,11 @@ public interface ContactSourceService
*/
public static final int CHAT_ROOM_TYPE = 3;
/**
* Type of a recent messages source.
*/
public static final int RECENT_MESSAGES_TYPE = 4;
/**
* Returns the type of this contact source.
*

@ -29,6 +29,14 @@ public interface MessageHistoryService
= "net.java.sip.communicator.service.msghistory."
+ "IS_MESSAGE_HISTORY_ENABLED";
/**
* Name of the property that indicates whether the recent messages is
* enabled.
*/
public static final String PNAME_IS_RECENT_MESSAGES_DISABLED
= "net.java.sip.communicator.service.msghistory."
+ "IS_RECENT_MESSAGES_DISABLED";
/**
* Name of the property that indicates whether the logging of messages is
* enabled.

@ -278,6 +278,17 @@ public static OperationSetMultiUserChat getMultiUserChatOpSet(
: null;
}
/**
* Finds the <tt>ChatRoomWrapper</tt> instance associated with the
* chat room.
* @param chatRoomID the id of the chat room.
* @param pps the provider of the chat room.
* @return the <tt>ChatRoomWrapper</tt> instance.
*/
public abstract ChatRoomWrapper findChatRoomWrapperFromChatRoomID(
String chatRoomID,
ProtocolProviderService pps);
/**
* Goes through the locally stored chat rooms list and for each
* {@link ChatRoomWrapper} tries to find the corresponding server stored

Loading…
Cancel
Save