mirror of https://github.com/sipwise/jitsi.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1759 lines
57 KiB
1759 lines
57 KiB
/*
|
|
* 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.plugin.notificationwiring;
|
|
|
|
import java.awt.image.*;
|
|
import java.net.*;
|
|
import java.util.*;
|
|
import java.util.concurrent.*;
|
|
|
|
import javax.imageio.*;
|
|
|
|
import net.java.sip.communicator.service.contactlist.*;
|
|
import net.java.sip.communicator.service.gui.*;
|
|
import net.java.sip.communicator.service.notification.*;
|
|
import net.java.sip.communicator.service.protocol.*;
|
|
import net.java.sip.communicator.service.protocol.event.*;
|
|
import net.java.sip.communicator.service.resources.*;
|
|
import net.java.sip.communicator.util.*;
|
|
|
|
import org.jitsi.service.neomedia.*;
|
|
import org.jitsi.service.protocol.event.*;
|
|
import org.jitsi.service.resources.*;
|
|
import org.osgi.framework.*;
|
|
|
|
/**
|
|
* Listens to various events which are related to the display and/or playback of
|
|
* notifications and shows/starts or hides/stops the notifications in question.
|
|
*
|
|
* @author Damian Minkov
|
|
* @author Lyubomir Marinov
|
|
*/
|
|
public class NotificationManager
|
|
implements AdHocChatRoomMessageListener,
|
|
CallChangeListener,
|
|
CallListener,
|
|
CallPeerConferenceListener,
|
|
CallPeerListener,
|
|
CallPeerSecurityListener,
|
|
ChatRoomMessageListener,
|
|
FileTransferListener,
|
|
LocalUserAdHocChatRoomPresenceListener,
|
|
LocalUserChatRoomPresenceListener,
|
|
MessageListener,
|
|
Recorder.Listener,
|
|
ServiceListener,
|
|
TypingNotificationsListener
|
|
{
|
|
/**
|
|
* Default event type for a busy call.
|
|
*/
|
|
public static final String BUSY_CALL = "BusyCall";
|
|
|
|
/**
|
|
* Default event type for call been saved using a recorder.
|
|
*/
|
|
public static final String CALL_SAVED = "CallSaved";
|
|
|
|
/**
|
|
* Default event type for security error on a call.
|
|
*/
|
|
public static final String CALL_SECURITY_ERROR = "CallSecurityError";
|
|
|
|
/**
|
|
* Default event type for activated security on a call.
|
|
*/
|
|
public static final String CALL_SECURITY_ON = "CallSecurityOn";
|
|
|
|
/**
|
|
* The image used, when a contact has no photo specified.
|
|
*/
|
|
public static final ImageID DEFAULT_USER_PHOTO
|
|
= new ImageID("service.gui.DEFAULT_USER_PHOTO");
|
|
|
|
/**
|
|
* Default event type for dialing.
|
|
*/
|
|
public static final String DIALING = "Dialing";
|
|
|
|
/**
|
|
* Default event type for hanging up calls.
|
|
*/
|
|
public static final String HANG_UP = "HangUp";
|
|
|
|
/**
|
|
* The cache of <tt>BufferedImage</tt> instances which we have already
|
|
* loaded by <tt>ImageID</tt> and which we store so that we do not have to
|
|
* load them again.
|
|
*/
|
|
private static final Map<ImageID, BufferedImage> images
|
|
= new Hashtable<ImageID, BufferedImage>();
|
|
|
|
/**
|
|
* Default event type for receiving calls (incoming calls).
|
|
*/
|
|
public static final String INCOMING_CALL = "IncomingCall";
|
|
|
|
/**
|
|
* Default event type for incoming file transfers.
|
|
*/
|
|
public static final String INCOMING_FILE = "IncomingFile";
|
|
|
|
/**
|
|
* Default event type for receiving messages.
|
|
*/
|
|
public static final String INCOMING_MESSAGE = "IncomingMessage";
|
|
|
|
/**
|
|
* The <tt>Logger</tt> used by the <tt>NotificationManager</tt> class and
|
|
* its instances for logging output.
|
|
*/
|
|
private static final Logger logger
|
|
= Logger.getLogger(NotificationManager.class);
|
|
|
|
/**
|
|
* Default event type for outgoing calls.
|
|
*/
|
|
public static final String OUTGOING_CALL = "OutgoingCall";
|
|
|
|
/**
|
|
* Default event type for
|
|
* proactive notifications (typing notifications when chatting).
|
|
*/
|
|
public static final String PROACTIVE_NOTIFICATION = "ProactiveNotification";
|
|
|
|
/**
|
|
* Default event type when a secure message received.
|
|
*/
|
|
public static final String SECURITY_MESSAGE = "SecurityMessage";
|
|
|
|
/**
|
|
* Fires a chat message notification for the given event type through the
|
|
* <tt>NotificationService</tt>.
|
|
*
|
|
* @param chatContact the chat contact to which the chat message corresponds;
|
|
* the chat contact could be a Contact or a ChatRoom.
|
|
* @param eventType the event type for which we fire a notification
|
|
* @param messageTitle the title of the message
|
|
* @param message the content of the message
|
|
*/
|
|
public static void fireChatNotification(Object chatContact,
|
|
String eventType,
|
|
String messageTitle,
|
|
String message)
|
|
{
|
|
NotificationService notificationService
|
|
= NotificationWiringActivator.getNotificationService();
|
|
|
|
if(notificationService == null)
|
|
return;
|
|
|
|
NotificationAction popupActionHandler = null;
|
|
UIService uiService = NotificationWiringActivator.getUIService();
|
|
|
|
Chat chatPanel = null;
|
|
byte[] contactIcon = null;
|
|
if (chatContact instanceof Contact)
|
|
{
|
|
Contact contact = (Contact) chatContact;
|
|
|
|
if(uiService != null)
|
|
chatPanel = uiService.getChat(contact);
|
|
|
|
contactIcon = contact.getImage();
|
|
if(contactIcon == null)
|
|
{
|
|
contactIcon =
|
|
ImageUtils.toByteArray(getImage(DEFAULT_USER_PHOTO));
|
|
}
|
|
}
|
|
else if (chatContact instanceof ChatRoom)
|
|
{
|
|
ChatRoom chatRoom = (ChatRoom) chatContact;
|
|
|
|
// For system rooms we don't want to send notification events.
|
|
if (chatRoom.isSystem())
|
|
return;
|
|
|
|
if(uiService != null)
|
|
chatPanel = uiService.getChat(chatRoom);
|
|
}
|
|
|
|
if (chatPanel != null)
|
|
{
|
|
if (eventType.equals(INCOMING_MESSAGE)
|
|
&& chatPanel.isChatFocused())
|
|
{
|
|
popupActionHandler = notificationService
|
|
.getEventNotificationAction(eventType,
|
|
NotificationAction.ACTION_POPUP_MESSAGE);
|
|
|
|
popupActionHandler.setEnabled(false);
|
|
}
|
|
}
|
|
|
|
Map<String,Object> extras = new HashMap<String,Object>();
|
|
|
|
extras.put(
|
|
NotificationData.POPUP_MESSAGE_HANDLER_TAG_EXTRA,
|
|
chatContact);
|
|
notificationService.fireNotification(
|
|
eventType,
|
|
messageTitle,
|
|
message,
|
|
contactIcon,
|
|
extras);
|
|
|
|
if(popupActionHandler != null)
|
|
popupActionHandler.setEnabled(true);
|
|
}
|
|
|
|
/**
|
|
* Fires a notification for the given event type through the
|
|
* <tt>NotificationService</tt>. The event type is one of the static
|
|
* constants defined in the <tt>NotificationManager</tt> class.
|
|
* <p>
|
|
* <b>Note</b>: The uses of the method at the time of this writing do not
|
|
* take measures to stop looping sounds if the respective notifications use
|
|
* them i.e. there is implicit agreement that the notifications fired
|
|
* through the method do not loop sounds. Consequently, the method passes
|
|
* arguments to <tt>NotificationService</tt> so that sounds are played once
|
|
* only.
|
|
* </p>
|
|
*
|
|
* @param eventType the event type for which we want to fire a notification
|
|
*/
|
|
private static void fireNotification(String eventType)
|
|
{
|
|
NotificationService notificationService
|
|
= NotificationWiringActivator.getNotificationService();
|
|
|
|
if (notificationService != null)
|
|
notificationService.fireNotification(eventType);
|
|
}
|
|
|
|
/**
|
|
* Fires a notification for the given event type through the
|
|
* <tt>NotificationService</tt>. The event type is one of the static
|
|
* constants defined in the <tt>NotificationManager</tt> class.
|
|
*
|
|
* @param eventType the event type for which we want to fire a notification
|
|
* @param loopCondition the method which will determine whether any sounds
|
|
* played as part of the specified notification will continue looping
|
|
* @return a reference to the fired notification to stop it.
|
|
*/
|
|
private static NotificationData fireNotification(
|
|
String eventType,
|
|
Callable<Boolean> loopCondition)
|
|
{
|
|
return fireNotification(eventType, null, null, null, loopCondition);
|
|
}
|
|
|
|
/**
|
|
* Fires a notification through the <tt>NotificationService</tt> with a
|
|
* specific event type, a specific message title and a specific message.
|
|
* <p>
|
|
* <b>Note</b>: The uses of the method at the time of this writing do not
|
|
* take measures to stop looping sounds if the respective notifications use
|
|
* them i.e. there is implicit agreement that the notifications fired
|
|
* through the method do not loop sounds. Consequently, the method passes
|
|
* arguments to <tt>NotificationService</tt> so that sounds are played once
|
|
* only.
|
|
* </p>
|
|
*
|
|
* @param eventType the event type of the notification to be fired
|
|
* @param messageTitle the title of the message to be displayed by the
|
|
* notification to be fired if such a display is supported
|
|
* @param message the message to be displayed by the notification to be
|
|
* fired if such a display is supported
|
|
*/
|
|
private static void fireNotification(
|
|
String eventType,
|
|
String messageTitle,
|
|
String message)
|
|
{
|
|
NotificationService notificationService
|
|
= NotificationWiringActivator.getNotificationService();
|
|
|
|
if (notificationService != null)
|
|
{
|
|
notificationService.fireNotification(
|
|
eventType,
|
|
messageTitle,
|
|
message,
|
|
null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fires a message notification for the given event type through the
|
|
* <tt>NotificationService</tt>.
|
|
*
|
|
* @param eventType the event type for which we fire a notification
|
|
* @param messageTitle the title of the message
|
|
* @param message the content of the message
|
|
* @param cmdargs the value to be provided to
|
|
* {@link CommandNotificationHandler#execute(CommandNotificationAction,
|
|
* Map)} as the <tt>cmdargs</tt> argument
|
|
* @param loopCondition the method which will determine whether any sounds
|
|
* played as part of the specified notification will continue looping
|
|
* @return a reference to the fired notification to stop it.
|
|
*/
|
|
private static NotificationData fireNotification(
|
|
String eventType,
|
|
String messageTitle,
|
|
String message,
|
|
Map<String,String> cmdargs,
|
|
Callable<Boolean> loopCondition)
|
|
{
|
|
NotificationService notificationService
|
|
= NotificationWiringActivator.getNotificationService();
|
|
|
|
if (notificationService == null)
|
|
return null;
|
|
else
|
|
{
|
|
Map<String,Object> extras = new HashMap<String,Object>();
|
|
|
|
if (cmdargs != null)
|
|
{
|
|
extras.put(
|
|
NotificationData
|
|
.COMMAND_NOTIFICATION_HANDLER_CMDARGS_EXTRA,
|
|
cmdargs);
|
|
}
|
|
if (loopCondition != null)
|
|
{
|
|
extras.put(
|
|
NotificationData
|
|
.SOUND_NOTIFICATION_HANDLER_LOOP_CONDITION_EXTRA,
|
|
loopCondition);
|
|
}
|
|
return
|
|
notificationService.fireNotification(
|
|
eventType,
|
|
messageTitle,
|
|
message,
|
|
null,
|
|
extras);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads an image from a given image identifier.
|
|
*
|
|
* @param imageID The identifier of the image.
|
|
* @return The image for the given identifier.
|
|
*/
|
|
public static BufferedImage getImage(ImageID imageID)
|
|
{
|
|
/*
|
|
* If we were mapping ImageID to null, we would be using the method
|
|
* Map.containsKey. However, that does not seem to be the case.
|
|
*/
|
|
BufferedImage image = images.get(imageID);
|
|
|
|
if (image == null)
|
|
{
|
|
URL path
|
|
= NotificationWiringActivator.getResources().getImageURL(
|
|
imageID.getId());
|
|
|
|
if (path != null)
|
|
{
|
|
try
|
|
{
|
|
image = ImageIO.read(path);
|
|
images.put(imageID, image);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.error("Failed to load image: " + path, ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
/**
|
|
* Returns all <tt>ProtocolProviderFactory</tt>s obtained from the bundle
|
|
* context.
|
|
*
|
|
* @return all <tt>ProtocolProviderFactory</tt>s obtained from the bundle
|
|
* context
|
|
*/
|
|
public static Map<Object, ProtocolProviderFactory>
|
|
getProtocolProviderFactories()
|
|
{
|
|
ServiceReference[] serRefs = null;
|
|
try
|
|
{
|
|
// get all registered provider factories
|
|
serRefs
|
|
= NotificationWiringActivator.bundleContext.getServiceReferences(
|
|
ProtocolProviderFactory.class.getName(),
|
|
null);
|
|
}
|
|
catch (InvalidSyntaxException e)
|
|
{
|
|
logger.error("NotificationManager : " + e);
|
|
}
|
|
|
|
Map<Object, ProtocolProviderFactory>
|
|
providerFactoriesMap = new Hashtable<Object, ProtocolProviderFactory>();
|
|
|
|
if (serRefs != null)
|
|
{
|
|
for (ServiceReference serRef : serRefs)
|
|
{
|
|
ProtocolProviderFactory providerFactory
|
|
= (ProtocolProviderFactory)
|
|
NotificationWiringActivator.bundleContext.getService(serRef);
|
|
|
|
providerFactoriesMap.put(
|
|
serRef.getProperty(ProtocolProviderFactory.PROTOCOL),
|
|
providerFactory);
|
|
}
|
|
}
|
|
return providerFactoriesMap;
|
|
}
|
|
|
|
/**
|
|
* Returns all protocol providers currently registered.
|
|
* @return all protocol providers currently registered.
|
|
*/
|
|
public static List<ProtocolProviderService>
|
|
getProtocolProviders()
|
|
{
|
|
ServiceReference[] serRefs = null;
|
|
try
|
|
{
|
|
// get all registered provider factories
|
|
serRefs
|
|
= NotificationWiringActivator.bundleContext.getServiceReferences(
|
|
ProtocolProviderService.class.getName(),
|
|
null);
|
|
}
|
|
catch (InvalidSyntaxException e)
|
|
{
|
|
logger.error("NotificationManager : " + e);
|
|
}
|
|
|
|
List<ProtocolProviderService>
|
|
providersList = new ArrayList<ProtocolProviderService>();
|
|
|
|
if (serRefs != null)
|
|
{
|
|
for (ServiceReference serRef : serRefs)
|
|
{
|
|
ProtocolProviderService pp
|
|
= (ProtocolProviderService)
|
|
NotificationWiringActivator.bundleContext.getService(serRef);
|
|
|
|
providersList.add(pp);
|
|
}
|
|
}
|
|
return providersList;
|
|
}
|
|
|
|
/**
|
|
* Determines whether a specific <code>ChatRoom</code> is private i.e.
|
|
* represents a one-to-one conversation which is not a channel. Since the
|
|
* interface {@link ChatRoom} does not expose the private property, an
|
|
* heuristic is used as a workaround: (1) a system <code>ChatRoom</code> is
|
|
* obviously not private and (2) a <code>ChatRoom</code> is private if it
|
|
* has only one <code>ChatRoomMember</code> who is not the local user.
|
|
*
|
|
* @param chatRoom
|
|
* the <code>ChatRoom</code> to be determined as private or not
|
|
* @return <tt>true</tt> if the specified <code>ChatRoom</code> is private;
|
|
* otherwise, <tt>false</tt>
|
|
*/
|
|
private static boolean isPrivate(ChatRoom chatRoom)
|
|
{
|
|
if (!chatRoom.isSystem()
|
|
&& chatRoom.isJoined()
|
|
&& (chatRoom.getMembersCount() == 1))
|
|
{
|
|
String nickname = chatRoom.getUserNickname();
|
|
|
|
if (nickname != null)
|
|
{
|
|
for (ChatRoomMember member : chatRoom.getMembers())
|
|
if (nickname.equals(member.getName()))
|
|
return false;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Stops all sounds for the given event type.
|
|
*
|
|
* @param data the event type for which we should stop sounds. One of
|
|
* the static event types defined in this class.
|
|
*/
|
|
public static void stopSound(NotificationData data)
|
|
{
|
|
NotificationService notificationService
|
|
= NotificationWiringActivator.getNotificationService();
|
|
|
|
if(notificationService != null)
|
|
notificationService.stopNotification(data);
|
|
}
|
|
|
|
/**
|
|
* Stores notification references to stop them if a notification has expired
|
|
* (e.g. to stop the dialing sound).
|
|
*/
|
|
private final Map<Call, NotificationData> callNotifications
|
|
= new WeakHashMap<Call, NotificationData>();
|
|
|
|
/**
|
|
* The pseudo timer which is used to delay multiple typing notifications
|
|
* before receiving the message.
|
|
*/
|
|
private final Map<Contact, Long> proactiveTimer
|
|
= new HashMap<Contact, Long>();
|
|
|
|
/**
|
|
* Implements CallListener.callEnded. Stops sounds that are playing at
|
|
* the moment if there're any.
|
|
* @param event the <tt>CallEvent</tt>
|
|
*/
|
|
public void callEnded(CallEvent event)
|
|
{
|
|
try
|
|
{
|
|
// Stop all telephony related sounds.
|
|
// stopAllTelephonySounds();
|
|
stopSound(callNotifications.get(event.getSourceCall()));
|
|
|
|
// Play the hangup sound.
|
|
fireNotification(HANG_UP);
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
logger.error("Error notifying for call ended", t);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements the <tt>CallChangeListener.callPeerAdded</tt> method.
|
|
* @param evt the <tt>CallPeerEvent</tt> that notifies us for the change
|
|
*/
|
|
public void callPeerAdded(CallPeerEvent evt)
|
|
{
|
|
CallPeer peer = evt.getSourceCallPeer();
|
|
|
|
if(peer == null)
|
|
return;
|
|
|
|
peer.addCallPeerListener(this);
|
|
peer.addCallPeerSecurityListener(this);
|
|
peer.addCallPeerConferenceListener(this);
|
|
}
|
|
|
|
/**
|
|
* Implements the <tt>CallChangeListener.callPeerRemoved</tt> method.
|
|
* @param evt the <tt>CallPeerEvent</tt> that has been triggered
|
|
*/
|
|
public void callPeerRemoved(CallPeerEvent evt)
|
|
{
|
|
CallPeer peer = evt.getSourceCallPeer();
|
|
|
|
if(peer == null)
|
|
return;
|
|
|
|
peer.removeCallPeerListener(this);
|
|
peer.removeCallPeerSecurityListener(this);
|
|
peer.addCallPeerConferenceListener(this);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void callStateChanged(CallChangeEvent ev) {}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void conferenceFocusChanged(CallPeerConferenceEvent ev) {}
|
|
|
|
/**
|
|
* Indicates that the given conference member has been added to the given
|
|
* peer.
|
|
*
|
|
* @param conferenceEvent the event
|
|
*/
|
|
public void conferenceMemberAdded(CallPeerConferenceEvent conferenceEvent)
|
|
{
|
|
try
|
|
{
|
|
CallPeer peer
|
|
= conferenceEvent
|
|
.getConferenceMember()
|
|
.getConferenceFocusCallPeer();
|
|
|
|
if(peer.getConferenceMemberCount() > 0)
|
|
{
|
|
CallPeerSecurityStatusEvent securityEvent
|
|
= peer.getCurrentSecuritySettings();
|
|
|
|
if (securityEvent instanceof CallPeerSecurityOnEvent)
|
|
fireNotification(CALL_SECURITY_ON);
|
|
}
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
if (t instanceof ThreadDeath)
|
|
throw (ThreadDeath) t;
|
|
else
|
|
logger.error("Error notifying for secured call member", t);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void conferenceMemberRemoved(CallPeerConferenceEvent ev) {}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void fileTransferCreated(FileTransferCreatedEvent ev) {}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void fileTransferRequestCanceled(FileTransferRequestEvent ev) {}
|
|
|
|
/**
|
|
* When a request has been received we show a notification.
|
|
*
|
|
* @param event <tt>FileTransferRequestEvent</tt>
|
|
* @see FileTransferListener#fileTransferRequestReceived(FileTransferRequestEvent)
|
|
*/
|
|
public void fileTransferRequestReceived(FileTransferRequestEvent event)
|
|
{
|
|
try
|
|
{
|
|
IncomingFileTransferRequest request = event.getRequest();
|
|
Contact sourceContact = request.getSender();
|
|
|
|
//Fire notification
|
|
String title = NotificationWiringActivator.getResources().getI18NString(
|
|
"service.gui.FILE_RECEIVING_FROM",
|
|
new String[]{sourceContact.getDisplayName()});
|
|
|
|
fireChatNotification(
|
|
sourceContact,
|
|
INCOMING_FILE,
|
|
title,
|
|
request.getFileName());
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
logger.error("Error notifying for file transfer req received", t);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void fileTransferRequestRejected(FileTransferRequestEvent ev) {}
|
|
|
|
/**
|
|
* Adds all listeners related to the given protocol provider.
|
|
*
|
|
* @param protocolProvider the <tt>ProtocolProviderService</tt>
|
|
*/
|
|
private void handleProviderAdded(ProtocolProviderService protocolProvider)
|
|
{
|
|
if(!protocolProvider.getAccountID().isEnabled())
|
|
return;
|
|
|
|
Map<String, OperationSet> supportedOperationSets
|
|
= protocolProvider.getSupportedOperationSets();
|
|
|
|
// Obtain the basic instant messaging operation set.
|
|
String imOpSetClassName = OperationSetBasicInstantMessaging
|
|
.class.getName();
|
|
|
|
if (supportedOperationSets.containsKey(imOpSetClassName))
|
|
{
|
|
OperationSetBasicInstantMessaging im
|
|
= (OperationSetBasicInstantMessaging)
|
|
supportedOperationSets.get(imOpSetClassName);
|
|
|
|
//Add to all instant messaging operation sets the Message
|
|
//listener which handles all received messages.
|
|
im.addMessageListener(this);
|
|
}
|
|
|
|
// Obtain the typing notifications operation set.
|
|
String tnOpSetClassName = OperationSetTypingNotifications
|
|
.class.getName();
|
|
|
|
if (supportedOperationSets.containsKey(tnOpSetClassName))
|
|
{
|
|
OperationSetTypingNotifications tn
|
|
= (OperationSetTypingNotifications)
|
|
supportedOperationSets.get(tnOpSetClassName);
|
|
|
|
//Add to all typing notification operation sets the Message
|
|
//listener implemented in the ContactListPanel, which handles
|
|
//all received messages.
|
|
tn.addTypingNotificationsListener(this);
|
|
}
|
|
|
|
// Obtain file transfer operation set.
|
|
OperationSetFileTransfer fileTransferOpSet
|
|
= protocolProvider.getOperationSet(OperationSetFileTransfer.class);
|
|
|
|
if (fileTransferOpSet != null)
|
|
{
|
|
fileTransferOpSet.addFileTransferListener(this);
|
|
}
|
|
|
|
OperationSetMultiUserChat multiChatOpSet
|
|
= protocolProvider.getOperationSet(OperationSetMultiUserChat.class);
|
|
|
|
if (multiChatOpSet != null)
|
|
{
|
|
multiChatOpSet.addPresenceListener(this);
|
|
}
|
|
|
|
OperationSetAdHocMultiUserChat multiAdHocChatOpSet
|
|
= protocolProvider.getOperationSet(OperationSetAdHocMultiUserChat.class);
|
|
|
|
if (multiAdHocChatOpSet != null)
|
|
{
|
|
multiAdHocChatOpSet.addPresenceListener(this);
|
|
}
|
|
|
|
OperationSetBasicTelephony<?> basicTelephonyOpSet
|
|
= protocolProvider.getOperationSet(OperationSetBasicTelephony.class);
|
|
|
|
if (basicTelephonyOpSet != null)
|
|
{
|
|
basicTelephonyOpSet.addCallListener(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes all listeners related to the given protocol provider.
|
|
*
|
|
* @param protocolProvider the <tt>ProtocolProviderService</tt>
|
|
*/
|
|
private void handleProviderRemoved(ProtocolProviderService protocolProvider)
|
|
{
|
|
Map<String, OperationSet> supportedOperationSets
|
|
= protocolProvider.getSupportedOperationSets();
|
|
|
|
// Obtain the basic instant messaging operation set.
|
|
String imOpSetClassName = OperationSetBasicInstantMessaging
|
|
.class.getName();
|
|
|
|
if (supportedOperationSets.containsKey(imOpSetClassName))
|
|
{
|
|
OperationSetBasicInstantMessaging im
|
|
= (OperationSetBasicInstantMessaging)
|
|
supportedOperationSets.get(imOpSetClassName);
|
|
|
|
//Add to all instant messaging operation sets the Message
|
|
//listener which handles all received messages.
|
|
im.removeMessageListener(this);
|
|
}
|
|
|
|
// Obtain the typing notifications operation set.
|
|
String tnOpSetClassName = OperationSetTypingNotifications
|
|
.class.getName();
|
|
|
|
if (supportedOperationSets.containsKey(tnOpSetClassName))
|
|
{
|
|
OperationSetTypingNotifications tn
|
|
= (OperationSetTypingNotifications)
|
|
supportedOperationSets.get(tnOpSetClassName);
|
|
|
|
//Add to all typing notification operation sets the Message
|
|
//listener implemented in the ContactListPanel, which handles
|
|
//all received messages.
|
|
tn.removeTypingNotificationsListener(this);
|
|
}
|
|
|
|
// Obtain file transfer operation set.
|
|
OperationSetFileTransfer fileTransferOpSet
|
|
= protocolProvider.getOperationSet(OperationSetFileTransfer.class);
|
|
|
|
if (fileTransferOpSet != null)
|
|
{
|
|
fileTransferOpSet.removeFileTransferListener(this);
|
|
}
|
|
|
|
OperationSetMultiUserChat multiChatOpSet
|
|
= protocolProvider.getOperationSet(OperationSetMultiUserChat.class);
|
|
|
|
if (multiChatOpSet != null)
|
|
{
|
|
multiChatOpSet.removePresenceListener(this);
|
|
}
|
|
|
|
OperationSetAdHocMultiUserChat multiAdHocChatOpSet
|
|
= protocolProvider.getOperationSet(OperationSetAdHocMultiUserChat.class);
|
|
|
|
if (multiAdHocChatOpSet != null)
|
|
{
|
|
multiAdHocChatOpSet.removePresenceListener(this);
|
|
}
|
|
|
|
OperationSetBasicTelephony<?> basicTelephonyOpSet
|
|
= protocolProvider.getOperationSet(OperationSetBasicTelephony.class);
|
|
|
|
if (basicTelephonyOpSet != null)
|
|
{
|
|
basicTelephonyOpSet.removeCallListener(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements CallListener.incomingCallReceived. When a call is received
|
|
* plays the ring phone sound to the user and gathers caller information
|
|
* that may be used by a user-specified command (incomingCall event
|
|
* trigger).
|
|
*
|
|
* @param ev the <tt>CallEvent</tt>
|
|
*/
|
|
public void incomingCallReceived(CallEvent ev)
|
|
{
|
|
try
|
|
{
|
|
final Call call = ev.getSourceCall();
|
|
CallPeer peer = call.getCallPeers().next();
|
|
Map<String,String> peerInfo = new HashMap<String, String>();
|
|
String peerName = peer.getDisplayName();
|
|
|
|
peerInfo.put("caller.uri", peer.getURI());
|
|
peerInfo.put("caller.address", peer.getAddress());
|
|
peerInfo.put("caller.name", peerName);
|
|
peerInfo.put("caller.id", peer.getPeerID());
|
|
|
|
NotificationData notification
|
|
= fireNotification(
|
|
INCOMING_CALL,
|
|
"",
|
|
NotificationWiringActivator.getResources()
|
|
.getI18NString(
|
|
"service.gui.INCOMING_CALL",
|
|
new String[] { peerName }),
|
|
peerInfo,
|
|
new Callable<Boolean>()
|
|
{
|
|
public Boolean call()
|
|
{
|
|
/*
|
|
* INCOMING_CALL should be played for a Call
|
|
* only while there is a CallPeer in the
|
|
* INCOMING_CALL state.
|
|
*/
|
|
Iterator<? extends CallPeer> peerIter
|
|
= call.getCallPeers();
|
|
boolean loop = false;
|
|
|
|
while (peerIter.hasNext())
|
|
{
|
|
CallPeer peer = peerIter.next();
|
|
|
|
if (CallPeerState.INCOMING_CALL.equals(
|
|
peer.getState()))
|
|
{
|
|
loop = true;
|
|
break;
|
|
}
|
|
}
|
|
return loop;
|
|
}
|
|
});
|
|
|
|
if (notification != null)
|
|
callNotifications.put(call, notification);
|
|
|
|
call.addCallChangeListener(this);
|
|
|
|
peer.addCallPeerListener(this);
|
|
peer.addCallPeerSecurityListener(this);
|
|
peer.addCallPeerConferenceListener(this);
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
if (t instanceof ThreadDeath)
|
|
throw (ThreadDeath) t;
|
|
else
|
|
{
|
|
logger.error(
|
|
"An error occurred while trying to notify"
|
|
+ " about an incoming call",
|
|
t);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize, register default notifications and start listening for
|
|
* new protocols or removed one and find any that are already registered.
|
|
*/
|
|
void init()
|
|
{
|
|
registerDefaultNotifications();
|
|
|
|
// listens for new protocols
|
|
NotificationWiringActivator.bundleContext.addServiceListener(this);
|
|
|
|
// enumerate currently registered protocols
|
|
for(ProtocolProviderService pp : getProtocolProviders())
|
|
{
|
|
handleProviderAdded(pp);
|
|
}
|
|
|
|
NotificationWiringActivator.getMediaService().addRecorderListener(this);
|
|
}
|
|
|
|
/**
|
|
* Checks if the contained call is a conference call.
|
|
*
|
|
* @param call the call to check
|
|
* @return <code>true</code> if the contained <tt>Call</tt> is a conference
|
|
* call, otherwise - returns <code>false</code>.
|
|
*/
|
|
public boolean isConference(Call call)
|
|
{
|
|
// If we're the focus of the conference.
|
|
if (call.isConferenceFocus())
|
|
return true;
|
|
|
|
// If one of our peers is a conference focus, we're in a
|
|
// conference call.
|
|
Iterator<? extends CallPeer> callPeers = call.getCallPeers();
|
|
|
|
while (callPeers.hasNext())
|
|
{
|
|
CallPeer callPeer = callPeers.next();
|
|
|
|
if (callPeer.isConferenceFocus())
|
|
return true;
|
|
}
|
|
|
|
// the call can have two peers at the same time and there is no one
|
|
// is conference focus. This is situation when some one has made an
|
|
// attended transfer and has transfered us. We have one call with two
|
|
// peers the one we are talking to and the one we have been transfered
|
|
// to. And the first one is been hanguped and so the call passes through
|
|
// conference call fo a moment and than go again to one to one call.
|
|
return call.getCallPeerCount() > 1;
|
|
}
|
|
|
|
/**
|
|
* Implements the
|
|
* <tt>LocalUserAdHocChatRoomPresenceListener.localUserPresenceChanged</tt>
|
|
* method
|
|
*
|
|
* @param evt the <tt>LocalUserAdHocChatRoomPresenceChangeEvent</tt> that
|
|
* notified us of a presence change
|
|
*/
|
|
public void localUserAdHocPresenceChanged(
|
|
LocalUserAdHocChatRoomPresenceChangeEvent evt)
|
|
{
|
|
String eventType = evt.getEventType();
|
|
|
|
if (LocalUserAdHocChatRoomPresenceChangeEvent
|
|
.LOCAL_USER_JOINED.equals(eventType))
|
|
{
|
|
evt.getAdHocChatRoom().addMessageListener(this);
|
|
}
|
|
else if (LocalUserAdHocChatRoomPresenceChangeEvent
|
|
.LOCAL_USER_LEFT.equals(eventType)
|
|
|| LocalUserAdHocChatRoomPresenceChangeEvent
|
|
.LOCAL_USER_DROPPED.equals(eventType))
|
|
{
|
|
evt.getAdHocChatRoom().removeMessageListener(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements the
|
|
* <tt>LocalUserChatRoomPresenceListener.localUserPresenceChanged</tt>
|
|
* method.
|
|
* @param evt the <tt>LocalUserChatRoomPresenceChangeEvent</tt> that
|
|
* notified us
|
|
*/
|
|
public void localUserPresenceChanged(
|
|
LocalUserChatRoomPresenceChangeEvent evt)
|
|
{
|
|
ChatRoom sourceChatRoom = evt.getChatRoom();
|
|
String eventType = evt.getEventType();
|
|
|
|
if (LocalUserChatRoomPresenceChangeEvent
|
|
.LOCAL_USER_JOINED.equals(eventType))
|
|
{
|
|
sourceChatRoom.addMessageListener(this);
|
|
}
|
|
else if (LocalUserChatRoomPresenceChangeEvent
|
|
.LOCAL_USER_LEFT.equals(eventType)
|
|
|| LocalUserChatRoomPresenceChangeEvent
|
|
.LOCAL_USER_KICKED.equals(eventType)
|
|
|| LocalUserChatRoomPresenceChangeEvent
|
|
.LOCAL_USER_DROPPED.equals(eventType))
|
|
{
|
|
sourceChatRoom.removeMessageListener(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void messageDelivered(AdHocChatRoomMessageDeliveredEvent ev) {}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void messageDelivered(ChatRoomMessageDeliveredEvent ev) {}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used
|
|
*/
|
|
public void messageDelivered(MessageDeliveredEvent ev) {}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void messageDeliveryFailed(
|
|
AdHocChatRoomMessageDeliveryFailedEvent ev) {}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void messageDeliveryFailed(ChatRoomMessageDeliveryFailedEvent ev) {}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void messageDeliveryFailed(MessageDeliveryFailedEvent ev) {}
|
|
|
|
/**
|
|
* Implements the <tt>AdHocChatRoomMessageListener.messageReceived</tt>
|
|
* method.
|
|
* <br>
|
|
* @param evt the <tt>AdHocChatRoomMessageReceivedEvent</tt> that notified
|
|
* us
|
|
*/
|
|
public void messageReceived(AdHocChatRoomMessageReceivedEvent evt)
|
|
{
|
|
try
|
|
{
|
|
AdHocChatRoom sourceChatRoom = evt.getSourceChatRoom();
|
|
Contact sourceParticipant = evt.getSourceChatRoomParticipant();
|
|
|
|
// Fire notification
|
|
boolean fireChatNotification;
|
|
|
|
String nickname = sourceChatRoom.getName();
|
|
String messageContent = evt.getMessage().getContent();
|
|
|
|
fireChatNotification =
|
|
(nickname == null)
|
|
|| messageContent.toLowerCase().contains(
|
|
nickname.toLowerCase());
|
|
|
|
if (fireChatNotification)
|
|
{
|
|
String title
|
|
= NotificationWiringActivator.getResources().getI18NString(
|
|
"service.gui.MSG_RECEIVED",
|
|
new String[] { sourceParticipant.getDisplayName() });
|
|
|
|
fireChatNotification(
|
|
sourceChatRoom,
|
|
INCOMING_MESSAGE,
|
|
title,
|
|
messageContent);
|
|
}
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
logger.error("Error notifying for adhoc message received", t);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements the <tt>ChatRoomMessageListener.messageReceived</tt> method.
|
|
* <br>
|
|
* Obtains the corresponding <tt>ChatPanel</tt> and process the message
|
|
* there.
|
|
* @param evt the <tt>ChatRoomMessageReceivedEvent</tt> that notified us
|
|
* that a message has been received
|
|
*/
|
|
public void messageReceived(ChatRoomMessageReceivedEvent evt)
|
|
{
|
|
try
|
|
{
|
|
ChatRoom sourceChatRoom = evt.getSourceChatRoom();
|
|
ChatRoomMember sourceMember = evt.getSourceChatRoomMember();
|
|
|
|
// Fire notification
|
|
boolean fireChatNotification;
|
|
|
|
String messageContent = evt.getMessage().getContent();
|
|
|
|
/*
|
|
* It is uncommon for IRC clients to display popup notifications for
|
|
* messages which are sent to public channels and which do not mention
|
|
* the nickname of the local user.
|
|
*/
|
|
if (sourceChatRoom.isSystem()
|
|
|| isPrivate(sourceChatRoom)
|
|
|| (messageContent == null))
|
|
fireChatNotification = true;
|
|
else
|
|
{
|
|
String nickname = sourceChatRoom.getUserNickname();
|
|
|
|
int atIx = -1;
|
|
|
|
if(nickname != null)
|
|
atIx = nickname.indexOf("@");
|
|
|
|
fireChatNotification =
|
|
(nickname == null)
|
|
|| messageContent.toLowerCase().contains(
|
|
nickname.toLowerCase())
|
|
|| ((atIx == -1)? false : messageContent.toLowerCase()
|
|
.contains(nickname.substring(0, atIx).toLowerCase()));
|
|
}
|
|
|
|
if (fireChatNotification)
|
|
{
|
|
String title
|
|
= NotificationWiringActivator.getResources().getI18NString(
|
|
"service.gui.MSG_RECEIVED",
|
|
new String[] { sourceMember.getName() });
|
|
|
|
fireChatNotification(
|
|
sourceChatRoom,
|
|
INCOMING_MESSAGE,
|
|
title,
|
|
messageContent);
|
|
}
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
logger.error("Error notifying for chat room message received", t);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fired on new messages.
|
|
* @param evt the <tt>MessageReceivedEvent</tt> containing
|
|
* details on the received message
|
|
*/
|
|
public void messageReceived(MessageReceivedEvent evt)
|
|
{
|
|
try
|
|
{
|
|
// Fire notification
|
|
String title = NotificationWiringActivator.getResources().getI18NString(
|
|
"service.gui.MSG_RECEIVED",
|
|
new String[]{evt.getSourceContact().getDisplayName()});
|
|
|
|
fireChatNotification(
|
|
evt.getSourceContact(),
|
|
INCOMING_MESSAGE,
|
|
title,
|
|
evt.getSourceMessage().getContent());
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
logger.error("Error notifying for message received", t);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Do nothing. Implements CallListener.outGoingCallCreated.
|
|
* @param event the <tt>CallEvent</tt>
|
|
*/
|
|
public void outgoingCallCreated(CallEvent event)
|
|
{
|
|
Call call = event.getSourceCall();
|
|
call.addCallChangeListener(this);
|
|
|
|
if(call.getCallPeers().hasNext())
|
|
{
|
|
CallPeer peer = call.getCallPeers().next();
|
|
peer.addCallPeerListener(this);
|
|
peer.addCallPeerSecurityListener(this);
|
|
peer.addCallPeerConferenceListener(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void peerAddressChanged(CallPeerChangeEvent ev) {}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void peerDisplayNameChanged(CallPeerChangeEvent ev) {}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void peerImageChanged(CallPeerChangeEvent ev) {}
|
|
|
|
/**
|
|
* Fired when peer's state is changed
|
|
*
|
|
* @param ev fired CallPeerEvent
|
|
*/
|
|
public void peerStateChanged(CallPeerChangeEvent ev)
|
|
{
|
|
try
|
|
{
|
|
final CallPeer peer = ev.getSourceCallPeer();
|
|
Call call = peer.getCall();
|
|
CallPeerState newState = (CallPeerState) ev.getNewValue();
|
|
CallPeerState oldState = (CallPeerState) ev.getOldValue();
|
|
|
|
// Play the dialing audio when in connecting and initiating call state.
|
|
// Stop the dialing audio when we enter any other state.
|
|
if ((newState == CallPeerState.INITIATING_CALL)
|
|
|| (newState == CallPeerState.CONNECTING))
|
|
{
|
|
NotificationData notification
|
|
= fireNotification(
|
|
DIALING,
|
|
new Callable<Boolean>()
|
|
{
|
|
public Boolean call()
|
|
{
|
|
CallPeerState state = peer.getState();
|
|
|
|
return
|
|
CallPeerState.INITIATING_CALL.equals(
|
|
state)
|
|
|| CallPeerState.CONNECTING.equals(
|
|
state);
|
|
}
|
|
});
|
|
|
|
if (notification != null)
|
|
callNotifications.put(call, notification);
|
|
}
|
|
else
|
|
{
|
|
stopSound(callNotifications.get(call));
|
|
}
|
|
|
|
if (newState == CallPeerState.ALERTING_REMOTE_SIDE
|
|
//if we were already in state CONNECTING_WITH_EARLY_MEDIA the server
|
|
//is already taking care of playing the notifications so we don't
|
|
//need to fire a notification here.
|
|
&& oldState != CallPeerState.CONNECTING_WITH_EARLY_MEDIA)
|
|
{
|
|
NotificationData notification
|
|
= fireNotification(
|
|
OUTGOING_CALL,
|
|
new Callable<Boolean>()
|
|
{
|
|
public Boolean call()
|
|
{
|
|
return
|
|
CallPeerState.ALERTING_REMOTE_SIDE
|
|
.equals(peer.getState());
|
|
}
|
|
});
|
|
|
|
if (notification != null)
|
|
callNotifications.put(call, notification);
|
|
}
|
|
else if (newState == CallPeerState.BUSY)
|
|
{
|
|
// We start the busy sound only if we're in a simple call.
|
|
if (!isConference(call))
|
|
{
|
|
NotificationData notification
|
|
= fireNotification(
|
|
BUSY_CALL,
|
|
new Callable<Boolean>()
|
|
{
|
|
public Boolean call()
|
|
{
|
|
return
|
|
CallPeerState.BUSY.equals(
|
|
peer.getState());
|
|
}
|
|
});
|
|
|
|
if (notification != null)
|
|
callNotifications.put(call, notification);
|
|
}
|
|
}
|
|
else if ((newState == CallPeerState.DISCONNECTED)
|
|
|| (newState == CallPeerState.FAILED))
|
|
{
|
|
fireNotification(HANG_UP);
|
|
}
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
logger.error("Error notifying after peer state changed", t);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void peerTransportAddressChanged(CallPeerChangeEvent ev) {}
|
|
|
|
/**
|
|
* Notifies that a specific <tt>Recorder</tt> has
|
|
* stopped recording the media associated with it.
|
|
*
|
|
* @param recorder the <tt>Recorder</tt> which has stopped recording its
|
|
* associated media
|
|
*/
|
|
public void recorderStopped(Recorder recorder)
|
|
{
|
|
try
|
|
{
|
|
ResourceManagementService resources
|
|
= NotificationWiringActivator.getResources();
|
|
|
|
fireNotification(
|
|
CALL_SAVED,
|
|
resources.getI18NString(
|
|
"plugin.callrecordingconfig.CALL_SAVED"),
|
|
resources.getI18NString(
|
|
"plugin.callrecordingconfig.CALL_SAVED_TO",
|
|
new String[] { recorder.getFilename() }));
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
if (t instanceof ThreadDeath)
|
|
throw (ThreadDeath) t;
|
|
else
|
|
{
|
|
logger.error(
|
|
"An error occurred while trying to notify that"
|
|
+ " the recording of a call has stopped.",
|
|
t);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register all default notifications.
|
|
*/
|
|
private void registerDefaultNotifications()
|
|
{
|
|
NotificationService notificationService
|
|
= NotificationWiringActivator.getNotificationService();
|
|
|
|
if(notificationService == null)
|
|
return;
|
|
|
|
// Register incoming message notifications.
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
INCOMING_MESSAGE,
|
|
NotificationAction.ACTION_POPUP_MESSAGE,
|
|
null,
|
|
null);
|
|
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
INCOMING_MESSAGE,
|
|
new SoundNotificationAction(
|
|
SoundProperties.INCOMING_MESSAGE, -1, true, false, false));
|
|
|
|
// Register incoming call notifications.
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
INCOMING_CALL,
|
|
NotificationAction.ACTION_POPUP_MESSAGE,
|
|
null,
|
|
null);
|
|
|
|
SoundNotificationAction inCallSoundHandler
|
|
= new SoundNotificationAction(
|
|
SoundProperties.INCOMING_CALL, 2000, true, true, true);
|
|
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
INCOMING_CALL,
|
|
inCallSoundHandler);
|
|
|
|
// Register outgoing call notifications.
|
|
SoundNotificationAction outCallSoundHandler
|
|
= new SoundNotificationAction(
|
|
SoundProperties.OUTGOING_CALL, 3000, false, true, false);
|
|
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
OUTGOING_CALL,
|
|
outCallSoundHandler);
|
|
|
|
// Register busy call notifications.
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
BUSY_CALL,
|
|
new SoundNotificationAction(
|
|
SoundProperties.BUSY,
|
|
1,
|
|
false, true, false));
|
|
|
|
// Register dial notifications.
|
|
SoundNotificationAction dialSoundHandler
|
|
= new SoundNotificationAction(
|
|
SoundProperties.DIALING, -1, false, true, false);
|
|
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
DIALING,
|
|
dialSoundHandler);
|
|
|
|
// Register the hangup sound notification.
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
HANG_UP,
|
|
new SoundNotificationAction(
|
|
SoundProperties.HANG_UP,
|
|
-1,
|
|
false, true, false));
|
|
|
|
// Register proactive notifications.
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
PROACTIVE_NOTIFICATION,
|
|
NotificationAction.ACTION_POPUP_MESSAGE,
|
|
null,
|
|
null);
|
|
|
|
// Register warning message notifications.
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
SECURITY_MESSAGE,
|
|
NotificationAction.ACTION_POPUP_MESSAGE,
|
|
null,
|
|
null);
|
|
|
|
// Register sound notification for security state on during a call.
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
CALL_SECURITY_ON,
|
|
new SoundNotificationAction(
|
|
SoundProperties.CALL_SECURITY_ON, -1,
|
|
false, true, false));
|
|
|
|
// Register sound notification for security state off during a call.
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
CALL_SECURITY_ERROR,
|
|
new SoundNotificationAction(
|
|
SoundProperties.CALL_SECURITY_ERROR, -1,
|
|
false, true, false));
|
|
|
|
// Register sound notification for incoming files.
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
INCOMING_FILE,
|
|
NotificationAction.ACTION_POPUP_MESSAGE,
|
|
null,
|
|
null);
|
|
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
INCOMING_FILE,
|
|
new SoundNotificationAction(
|
|
SoundProperties.INCOMING_FILE, -1,
|
|
true, false, false));
|
|
|
|
// Register notification for saved calls.
|
|
notificationService.registerDefaultNotificationForEvent(
|
|
CALL_SAVED,
|
|
NotificationAction.ACTION_POPUP_MESSAGE,
|
|
null,
|
|
null);
|
|
}
|
|
|
|
/**
|
|
* Processes the received security message.
|
|
* @param ev the event we received
|
|
*/
|
|
public void securityMessageRecieved(CallPeerSecurityMessageEvent ev)
|
|
{
|
|
try
|
|
{
|
|
String messageTitleKey;
|
|
|
|
switch (ev.getEventSeverity())
|
|
{
|
|
// Don't play alert sound for Info or warning.
|
|
case CallPeerSecurityMessageEvent.INFORMATION:
|
|
messageTitleKey = "service.gui.SECURITY_INFO";
|
|
break;
|
|
|
|
case CallPeerSecurityMessageEvent.WARNING:
|
|
messageTitleKey = "service.gui.SECURITY_WARNING";
|
|
break;
|
|
|
|
// Security cannot be established! Play an alert sound.
|
|
case CallPeerSecurityMessageEvent.SEVERE:
|
|
case CallPeerSecurityMessageEvent.ERROR:
|
|
messageTitleKey = "service.gui.SECURITY_ERROR";
|
|
fireNotification(CALL_SECURITY_ERROR);
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* Whatever other severity there is or will be, we do not how to
|
|
* react to it yet.
|
|
*/
|
|
messageTitleKey = null;
|
|
}
|
|
|
|
if (messageTitleKey != null)
|
|
{
|
|
fireNotification(
|
|
SECURITY_MESSAGE,
|
|
NotificationWiringActivator.getResources()
|
|
.getI18NString(messageTitleKey),
|
|
ev.getI18nMessage());
|
|
}
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
if (t instanceof ThreadDeath)
|
|
throw (ThreadDeath) t;
|
|
else
|
|
{
|
|
logger.error(
|
|
"An error occurred while trying to notify"
|
|
+ " about a security message",
|
|
t);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void securityNegotiationStarted(
|
|
CallPeerSecurityNegotiationStartedEvent ev) {}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void securityOff(CallPeerSecurityOffEvent ev) {}
|
|
|
|
/**
|
|
* When a <tt>securityOnEvent</tt> is received.
|
|
* @param ev the event we received
|
|
*/
|
|
public void securityOn(CallPeerSecurityOnEvent ev)
|
|
{
|
|
try
|
|
{
|
|
SrtpControl securityController = ev.getSecurityController();
|
|
CallPeer peer = (CallPeer) ev.getSource();
|
|
|
|
if(!securityController.requiresSecureSignalingTransport()
|
|
|| peer.getProtocolProvider().isSignalingTransportSecure())
|
|
{
|
|
fireNotification(CALL_SECURITY_ON);
|
|
}
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
if (t instanceof ThreadDeath)
|
|
throw (ThreadDeath) t;
|
|
else
|
|
{
|
|
logger.error(
|
|
"An error occurred while trying to notify"
|
|
+ " about a security-related event",
|
|
t);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void securityTimeout(CallPeerSecurityTimeoutEvent ev) {}
|
|
|
|
/**
|
|
* Implements the <tt>ServiceListener</tt> method. Verifies whether the
|
|
* passed event concerns a <tt>ProtocolProviderService</tt> and adds the
|
|
* corresponding listeners.
|
|
*
|
|
* @param event The <tt>ServiceEvent</tt> object.
|
|
*/
|
|
public void serviceChanged(ServiceEvent event)
|
|
{
|
|
ServiceReference serviceRef = event.getServiceReference();
|
|
|
|
// if the event is caused by a bundle being stopped, we don't want to
|
|
// know
|
|
if (serviceRef.getBundle().getState() == Bundle.STOPPING)
|
|
return;
|
|
|
|
Object service
|
|
= NotificationWiringActivator.bundleContext.getService(serviceRef);
|
|
|
|
// we don't care if the source service is not a protocol provider
|
|
if (service instanceof ProtocolProviderService)
|
|
{
|
|
switch (event.getType())
|
|
{
|
|
case ServiceEvent.REGISTERED:
|
|
handleProviderAdded((ProtocolProviderService) service);
|
|
break;
|
|
case ServiceEvent.UNREGISTERING:
|
|
handleProviderRemoved((ProtocolProviderService) service);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Not used.
|
|
*/
|
|
public void typingNotificationDeliveryFailed(TypingNotificationEvent ev) {}
|
|
|
|
/**
|
|
* Informs the user what is the typing state of his chat contacts.
|
|
*
|
|
* @param ev the event containing details on the typing notification
|
|
*/
|
|
public void typingNotificationReceived(TypingNotificationEvent ev)
|
|
{
|
|
try
|
|
{
|
|
Contact contact = ev.getSourceContact();
|
|
|
|
// we don't care for proactive notifications, different than typing
|
|
// sometimes after closing chat we can see someone is typing us
|
|
// its just server sanding that the chat is inactive (STATE_STOPPED)
|
|
if(ev.getTypingState()
|
|
!= OperationSetTypingNotifications.STATE_TYPING)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// check whether the current chat window shows the
|
|
// chat we received a typing info for and in such case don't show
|
|
// notifications
|
|
UIService uiService = NotificationWiringActivator.getUIService();
|
|
|
|
if(uiService != null)
|
|
{
|
|
Chat chat = uiService.getCurrentChat();
|
|
|
|
if(chat != null)
|
|
{
|
|
MetaContact metaContact = uiService.getChatContact(chat);
|
|
|
|
if((metaContact != null)
|
|
&& metaContact.containsContact(contact)
|
|
&& chat.isChatFocused())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
long currentTime = System.currentTimeMillis();
|
|
|
|
if (proactiveTimer.size() > 0)
|
|
{
|
|
// first remove contacts that have been here longer than the
|
|
// timeout to avoid memory leaks
|
|
Iterator<Map.Entry<Contact, Long>> entries
|
|
= proactiveTimer.entrySet().iterator();
|
|
|
|
while (entries.hasNext())
|
|
{
|
|
Map.Entry<Contact, Long> entry = entries.next();
|
|
Long lastNotificationDate = entry.getValue();
|
|
|
|
if (lastNotificationDate.longValue() + 30000 < currentTime)
|
|
{
|
|
// The entry is outdated
|
|
entries.remove();
|
|
}
|
|
}
|
|
|
|
// Now, check if the contact is still in the map
|
|
if (proactiveTimer.containsKey(contact))
|
|
{
|
|
// We already notified the others about this
|
|
return;
|
|
}
|
|
}
|
|
|
|
proactiveTimer.put(contact, currentTime);
|
|
|
|
fireChatNotification(
|
|
contact,
|
|
PROACTIVE_NOTIFICATION,
|
|
contact.getDisplayName(),
|
|
NotificationWiringActivator.getResources().getI18NString(
|
|
"service.gui.PROACTIVE_NOTIFICATION"));
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
if (t instanceof ThreadDeath)
|
|
throw (ThreadDeath) t;
|
|
else
|
|
{
|
|
logger.error(
|
|
"An error occurred while handling"
|
|
+ " a typing notification.",
|
|
t);
|
|
}
|
|
}
|
|
}
|
|
}
|