Works on preventing sound notifications from playing forever.

cusax-fix
Lyubomir Marinov 13 years ago
parent 93aeb79acf
commit 98fef59119

@ -6,31 +6,23 @@
*/
package net.java.sip.communicator.impl.neomedia;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.util.*;
import javax.swing.*;
import net.java.sip.communicator.impl.neomedia.codec.video.h264.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.notification.*;
import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.service.systray.*;
import net.java.sip.communicator.service.systray.event.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
import org.jitsi.impl.neomedia.*;
import org.jitsi.impl.neomedia.codec.*;
import org.jitsi.impl.neomedia.device.*;
import org.jitsi.service.audionotifier.*;
import org.jitsi.service.configuration.*;
import org.jitsi.service.fileaccess.*;
import org.jitsi.service.libjitsi.*;
import org.jitsi.service.neomedia.*;
import org.jitsi.service.neomedia.codec.*;
import org.jitsi.service.packetlogging.*;
import org.jitsi.service.resources.*;
import org.osgi.framework.*;
@ -146,11 +138,6 @@ public class NeomediaActivator
private AudioDeviceConfigurationListener
deviceConfigurationPropertyChangeListener;
/**
* Audio configuration dialog.
*/
private static SIPCommDialog audioConfigDialog = null;
/**
* A {@link MediaConfigurationService} instance.
*/
@ -553,7 +540,7 @@ public void managePopupMessageListenerRegistration(boolean enable)
}
/**
* Fonction called when an audio device is plugged or unplugged.
* Function called when an audio device is plugged or unplugged.
*
* @param The property change event which may concern the audio device.
*/
@ -564,6 +551,7 @@ public void propertyChange(PropertyChangeEvent event)
{
NotificationService notificationService
= getNotificationService();
if(notificationService != null)
{
// Registers only once to the popup message notification
@ -573,9 +561,15 @@ public void propertyChange(PropertyChangeEvent event)
isRegisteredToPopupMessageListener = true;
managePopupMessageListenerRegistration(true);
}
// Fires the popup notification.
ResourceManagementService resources
= NeomediaActivator.getResources();
Map<String,Object> extras = new HashMap<String,Object>();
extras.put(
NotificationData.POPUP_MESSAGE_HANDLER_TAG_EXTRA,
this);
notificationService.fireNotification(
DEVICE_CONFIGURATION_HAS_CHANGED,
resources.getI18NString(
@ -585,7 +579,7 @@ public void propertyChange(PropertyChangeEvent event)
"impl.media.configform"
+ ".AUDIO_DEVICE_CONFIG_MANAGMENT_CLICK"),
null,
this);
extras);
}
}
}

@ -57,11 +57,14 @@ public void popupMessage(PopupMessageNotificationAction action,
return;
if(!StringUtils.isNullOrEmpty(message))
{
systray.showPopupMessage(
new PopupMessage(title, message, icon, tag));
new PopupMessage(title, message, icon, tag));
}
else
logger.error("Message is null or empty!",
new Throwable("Null or empty message"));
{
logger.error("Message is null or empty!");
}
}
/**

@ -8,6 +8,7 @@
import java.awt.*;
import java.util.*;
import java.util.concurrent.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.notification.*;
@ -23,13 +24,15 @@
public class SoundNotificationHandlerImpl
implements SoundNotificationHandler
{
WeakHashMap<SCAudioClip, NotificationData> playedClips =
new WeakHashMap<SCAudioClip, NotificationData>();
/**
* If the sound is currently disabled.
* The indicator which determines whether this
* <tt>SoundNotificationHandler</tt> is currently muted i.e. the sounds are
* off.
*/
private boolean isMute;
private boolean mute;
private Map<SCAudioClip, NotificationData> playedClips
= new WeakHashMap<SCAudioClip, NotificationData>();
/**
* {@inheritDoc}
@ -40,55 +43,13 @@ public String getActionType()
}
/**
* Plays the sound given by the containing <tt>soundFileDescriptor</tt>. The
* sound is played in loop if the loopInterval is defined.
* @param action The action to act upon.
* @param data Additional data for the event.
* Specifies if currently the sound is off.
*
* @return TRUE if currently the sound is off, FALSE otherwise
*/
public void start(SoundNotificationAction action, NotificationData data)
public boolean isMute()
{
if(isMute())
return;
boolean playOnlyOnPlayback = true;
AudioNotifierService audioNotifService
= NotificationActivator.getAudioNotifier();
if(audioNotifService != null)
playOnlyOnPlayback =
audioNotifService.audioOutAndNotificationsShareSameDevice();
if(playOnlyOnPlayback)
{
if(action.isSoundNotificationEnabled()
|| action.isSoundPlaybackEnabled())
{
play(action, data, true);
}
}
else
{
if(action.isSoundNotificationEnabled())
{
play(action, data, false);
}
if(action.isSoundPlaybackEnabled())
{
play(action, data, true);
}
}
if(action.isSoundPCSpeakerEnabled())
{
PCSpeakerClip audio = new PCSpeakerClip();
playedClips.put(audio, data);
if(action.getLoopInterval() > -1)
audio.playInLoop(action.getLoopInterval());
else
audio.play();
}
return mute;
}
/**
@ -96,16 +57,18 @@ public void start(SoundNotificationAction action, NotificationData data)
* sound is played in loop if the loopInterval is defined.
* @param action The action to act upon.
* @param data Additional data for the event.
* @param playback to use or not the playback or notification device.
* @param device
*/
private void play(SoundNotificationAction action, NotificationData data,
boolean playback)
private void play(
SoundNotificationAction action,
NotificationData data,
SCAudioClipDevice device)
{
AudioNotifierService audioNotifService
= NotificationActivator.getAudioNotifier();
if(audioNotifService == null
|| StringUtils.isNullOrEmpty(action.getDescriptor(), true))
if((audioNotifService == null)
|| StringUtils.isNullOrEmpty(action.getDescriptor(), true))
return;
// this is hack, seen on some os (particularly seen on macosx with
@ -113,51 +76,42 @@ private void play(SoundNotificationAction action, NotificationData data,
// when playing notification in the call, can break the call and
// no further communicating can be done after the notification.
// So we skip playing notification if we have a call running
if(playback)
if(SCAudioClipDevice.PLAYBACK.equals(device))
{
UIService uiService = NotificationActivator.getUIService();
if(uiService.getInProgressCalls().size() > 0)
{
return;
}
}
SCAudioClip audio = audioNotifService
.createAudio(action.getDescriptor(), playback);
SCAudioClip audio = null;
switch (device)
{
case NOTIFICATION:
case PLAYBACK:
audio = audioNotifService.createAudio(action.getDescriptor(), SCAudioClipDevice.PLAYBACK.equals(device));
break;
case PC_SPEAKER:
audio = new PCSpeakerClip();
break;
}
// it is possible that audio cannot be created
if(audio == null)
return;
playedClips.put(audio, data);
if(action.getLoopInterval() > -1)
audio.playInLoop(action.getLoopInterval());
else
audio.play();
}
/**
* Stops the sound.
* @param data Additional data for the event.
*/
public void stop(NotificationData data)
{
AudioNotifierService audioNotifService
= NotificationActivator.getAudioNotifier();
if(audioNotifService == null)
return;
@SuppressWarnings("unchecked")
Callable<Boolean> loopCondition
= (Callable<Boolean>)
data.getExtra(
NotificationData
.SOUND_NOTIFICATION_HANDLER_LOOP_CONDITION_EXTRA);
for (Map.Entry<SCAudioClip, NotificationData> entry : playedClips
.entrySet())
{
if(entry.getValue() == data)
{
SCAudioClip audio = entry.getKey();
audio.stop();
audioNotifService.destroyAudio(audio);
}
}
audio.play(action.getLoopInterval(), loopCondition);
}
/**
@ -167,7 +121,7 @@ public void stop(NotificationData data)
*/
public void setMute(boolean isMute)
{
this.isMute = isMute;
this.mute = isMute;
if(isMute)
{
@ -189,182 +143,117 @@ public void setMute(boolean isMute)
}
/**
* Specifies if currently the sound is off.
*
* @return TRUE if currently the sound is off, FALSE otherwise
*/
public boolean isMute()
{
return isMute;
}
/**
* Plays beep on the pc speaker.
* Plays the sound given by the containing <tt>soundFileDescriptor</tt>. The
* sound is played in loop if the loopInterval is defined.
* @param action The action to act upon.
* @param data Additional data for the event.
*/
private class PCSpeakerClip
implements SCAudioClip
public void start(SoundNotificationAction action, NotificationData data)
{
/**
* Synching start/stop.
*/
private final Object syncObject = new Object();
/**
* Is beep started.
*/
private boolean started = false;
if(isMute())
return;
/**
* Is looping.
*/
private boolean isLooping;
boolean playOnlyOnPlayback = true;
/**
* The interval to loop.
*/
private int loopInterval;
AudioNotifierService audioNotifService
= NotificationActivator.getAudioNotifier();
/**
* Plays this audio.
*/
public void play()
if(audioNotifService != null)
{
started = true;
new Thread("Playing beep:" + this.getClass())
{
@Override
public void run()
{
runInPlayThread();
}
}.start();
playOnlyOnPlayback
= audioNotifService.audioOutAndNotificationsShareSameDevice();
}
/**
* Plays this audio in loop.
*
* @param silenceInterval interval between loops
*/
public void playInLoop(int silenceInterval)
if(playOnlyOnPlayback)
{
setLoopInterval(silenceInterval);
setIsLooping(true);
play();
if(action.isSoundNotificationEnabled()
|| action.isSoundPlaybackEnabled())
{
play(action, data, SCAudioClipDevice.PLAYBACK);
}
}
/**
* Stops this audio.
*/
public void stop()
else
{
internalStop();
setIsLooping(false);
if(action.isSoundNotificationEnabled())
play(action, data, SCAudioClipDevice.NOTIFICATION);
if(action.isSoundPlaybackEnabled())
play(action, data, SCAudioClipDevice.PLAYBACK);
}
/**
* Stops this audio without setting the isLooping property in the case of
* a looping audio.
*/
public void internalStop()
if(action.isSoundPCSpeakerEnabled())
play(action, data, SCAudioClipDevice.PC_SPEAKER);
}
/**
* Stops the sound.
* @param data Additional data for the event.
*/
public void stop(NotificationData data)
{
AudioNotifierService audioNotifService
= NotificationActivator.getAudioNotifier();
if(audioNotifService == null)
return;
for (Map.Entry<SCAudioClip, NotificationData> entry
: playedClips.entrySet())
{
synchronized (syncObject)
if(entry.getValue() == data)
{
if (started)
{
started = false;
syncObject.notifyAll();
}
SCAudioClip audio = entry.getKey();
audio.stop();
audioNotifService.destroyAudio(audio);
}
}
}
/**
* Beeps the PC speaker.
*/
private static class PCSpeakerClip
extends AbstractSCAudioClip
{
/**
* Runs in a separate thread to perform the actual playback.
* Initializes a new <tt>PCSpeakerClip</tt> instance.
*/
private void runInPlayThread()
public PCSpeakerClip()
{
while (started)
{
if (!runOnceInPlayThread())
break;
if(isLooping())
{
synchronized(syncObject)
{
if (started)
{
try
{
if(getLoopInterval() > 0)
syncObject.wait(getLoopInterval());
}
catch (InterruptedException e)
{
}
}
}
}
else
break;
}
super(null, NotificationActivator.getAudioNotifier());
}
/**
* Beeps.
* Beeps the PC speaker.
*
* @return <tt>true</tt> if the playback was successful;
* otherwise, <tt>false</tt>
* @return <tt>true</tt> if the playback was successful; otherwise,
* <tt>false</tt>
*/
private boolean runOnceInPlayThread()
protected boolean runOnceInPlayThread()
{
try
{
Toolkit.getDefaultToolkit().beep();
return true;
}
catch (Throwable t)
{
//logger.error("Failed to get audio stream " + url, ioex);
return false;
if (t instanceof ThreadDeath)
throw (ThreadDeath) t;
else
return false;
}
return true;
}
/**
* Returns TRUE if this audio is currently playing in loop,
* FALSE otherwise.
* @return TRUE if this audio is currently playing in loop,
* FALSE otherwise.
*/
public boolean isLooping()
{
return isLooping;
}
/**
* Returns the loop interval if this audio is looping.
* @return the loop interval if this audio is looping
*/
public int getLoopInterval()
{
return loopInterval;
}
/**
* @param isLooping the isLooping to set
*/
public void setIsLooping(boolean isLooping)
{
this.isLooping = isLooping;
}
}
/**
* @param loopInterval the loopInterval to set
*/
public void setLoopInterval(int loopInterval)
{
this.loopInterval = loopInterval;
}
/**
* Enumerates the types of devices on which <tt>SCAudioClip</tt>s may be
* played back.
*/
private static enum SCAudioClipDevice
{
NOTIFICATION,
PC_SPEAKER,
PLAYBACK
}
}

@ -474,22 +474,19 @@ private void collectLogs()
if(notificationService != null)
{
String bodyMsgKey = null;
if(dest != null)
bodyMsgKey = "plugin.loggingutils.ARCHIVE_MESSAGE_OK";
else
bodyMsgKey = "plugin.loggingutils.ARCHIVE_MESSAGE_NOTOK";
String bodyMsgKey
= (dest == null)
? "plugin.loggingutils.ARCHIVE_MESSAGE_NOTOK"
: "plugin.loggingutils.ARCHIVE_MESSAGE_OK";
notificationService.fireNotification(
LOGFILES_ARCHIVED,
resources.getI18NString(
"plugin.loggingutils.ARCHIVE_BUTTON"),
resources.getI18NString(
bodyMsgKey,
new String[]{dest.getAbsolutePath()}),
null,
null);
LOGFILES_ARCHIVED,
resources.getI18NString(
"plugin.loggingutils.ARCHIVE_BUTTON"),
resources.getI18NString(
bodyMsgKey,
new String[]{dest.getAbsolutePath()}),
null);
}
}
@ -721,20 +718,18 @@ static void uploadLogs(
if(notificationService != null)
{
ResourceManagementService resources
= LoggingUtilsActivator.getResourceService();
String bodyMsgKey = "plugin.loggingutils.ARCHIVE_MESSAGE_OK";
ResourceManagementService resources =
LoggingUtilsActivator.getResourceService();
notificationService.fireNotification(
LOGFILES_ARCHIVED,
resources.getI18NString(
"plugin.loggingutils.ARCHIVE_BUTTON"),
resources.getI18NString(
bodyMsgKey,
new String[]{uploadLocation}),
null,
null);
LOGFILES_ARCHIVED,
resources.getI18NString(
"plugin.loggingutils.ARCHIVE_BUTTON"),
resources.getI18NString(
bodyMsgKey,
new String[]{uploadLocation}),
null);
}
}
}

@ -606,7 +606,6 @@ private void notify(String title, String i18nKey, String[] params)
NETWORK_NOTIFICATIONS,
title,
getResources().getI18NString(i18nKey, params),
null,
null);
}

@ -19,10 +19,12 @@ public interface CommandNotificationHandler
{
/**
* Executes the program pointed by the descriptor.
*
* @param action the action to act upon
* @param cmdargs arguments that are passed to the command line specified
* in the action
*/
public void execute(CommandNotificationAction action,
Map<String,String> cmdargs);
public void execute(
CommandNotificationAction action,
Map<String,String> cmdargs);
}

@ -16,34 +16,71 @@
*/
public class NotificationData
{
/**
* The name/key of the <tt>NotificationData</tt> extra which is provided to
* {@link CommandNotificationHandler#execute(CommandNotificationAction,
* Map)} i.e. a <tt>Map&lt;String,String&gt;</tt> which is known by the
* (argument) name <tt>cmdargs</tt>.
*/
public static final String COMMAND_NOTIFICATION_HANDLER_CMDARGS_EXTRA
= "CommandNotificationHandler.cmdargs";
/**
* The name/key of the <tt>NotificationData</tt> extra which is provided to
* {@link PopupMessageNotificationHandler#popupMessage(
* PopupMessageNotificationAction, String, String, byte[], Object)} i.e. an
* <tt>Object</tt> which is known by the (argument) name <tt>tag</tt>.
*/
public static final String POPUP_MESSAGE_HANDLER_TAG_EXTRA
= "PopupMessageNotificationHandler.tag";
/**
* The name/key of the <tt>NotificationData</tt> extra which is provided to
* {@link SoundNotificationHandler} i.e. a <tt>Callable&lt;Boolean&gt;</tt>
* which is known as the condition which determines whether looping sounds
* are to continue playing.
*/
public static final String SOUND_NOTIFICATION_HANDLER_LOOP_CONDITION_EXTRA
= "SoundNotificationHandler.loopCondition";
private final String eventType;
private final String title;
private final String message;
private final Map<String,String> extra;
/**
* The {@link NotificationHandler}-specific extras provided to this
* instance. The keys are among the <tt>XXX_EXTRA</tt> constants defined by
* the <tt>NotificationData</tt> class.
*/
private final Map<String, Object> extras;
private final byte[] icon;
private final Object tag;
private final String message;
private final String title;
/**
* Creates a new instance of this class.
*
* @param eventType the type of the event that we'd like to fire a
* notification for.
* notification for.
* @param title the title of the given message
* @param message the message to use if and where appropriate (e.g. with
* systray or log notification.)
* @param extra additional data (such as caller information)
* systray or log notification.)
* @param icon the icon to show in the notification if and where appropriate
* @param tag additional info to be used by the notification handler
* @param extras additional/extra {@link NotificationHandler}-specific data
* to be provided by the new instance to the various
* <tt>NotificationHandler</tt>s
*/
NotificationData(String eventType, String title, String message,
Map<String,String> extra, byte[] icon, Object tag)
NotificationData(
String eventType,
String title,
String message,
byte[] icon,
Map<String, Object> extras)
{
this.eventType = eventType;
this.title = title;
this.message = message;
this.extra = extra;
this.icon = icon;
this.tag = tag;
this.extras = extras;
}
/**
@ -57,53 +94,61 @@ public String getEventType()
}
/**
* Gets the title of the given message.
*
* @return the title
* Gets the {@link NotificationHandler}-specific extras provided to this
* instance.
*
* @return the <tt>NotificationHandler</tt>-specific extras provided to this
* instance. The keys are among the <tt>XXX_EXTRA</tt> constants defined by
* the <tt>NotificationData</tt> class
*/
String getTitle()
Map<String, Object> getExtras()
{
return title;
return Collections.unmodifiableMap(extras);
}
/**
* Gets the message to use if and where appropriate (e.g. with systray or
* log notification).
*
* @return the message
* Gets the {@link NotificationHandler}-specific extra provided to this
* instance associated with a specific key.
*
* @param key the key whose associated <tt>NotificationHandler</tt>-specific
* extra is to be returned. Well known keys are defined by the
* <tt>NotificationData</tt> class as the <tt>XXX_EXTRA</tt> constants.
* @return the <tt>NotificationHandler</tt>-specific extra provided to this
* instance associated with the specified <tt>key</tt>
*/
String getMessage()
public Object getExtra(String key)
{
return message;
return (extras == null) ? null : extras.get(key);
}
/**
* Gets additional data (such as caller information).
* Gets the icon to show in the notification if and where appropriate.
*
* @return the extra data
* @return the icon
*/
public Map<String,String> getExtra()
byte[] getIcon()
{
return extra;
return icon;
}
/**
* Gets the icon to show in the notification if and where appropriate.
* Gets the message to use if and where appropriate (e.g. with systray or
* log notification).
*
* @return the icon
* @return the message
*/
byte[] getIcon()
String getMessage()
{
return icon;
return message;
}
/**
* Gets additional info to be used by the notification handler.
* Gets the title of the given message.
*
* @return the tag
* @return the title
*/
Object getTag()
String getTitle()
{
return tag;
return title;
}
}

@ -235,6 +235,7 @@ public void removeNotificationChangeListener(
* <p>
* This method does nothing if the given <tt>eventType</tt> is not contained
* in the list of registered event types.
* </p>
*
* @param eventType the type of the event that we'd like to fire a
* notification for.
@ -243,7 +244,6 @@ public void removeNotificationChangeListener(
* @param message the message to use if and where appropriate (e.g. with
* systray or log notification.)
* @param icon the icon to show in the notification if and where appropriate
* @param tag additional info to be used by the notification handler
* @return An object referencing the notification. It may be used to stop a
* still running notification. Can be null if the eventType is
* unknown or the notification is not active.
@ -251,8 +251,7 @@ public void removeNotificationChangeListener(
public NotificationData fireNotification( String eventType,
String messageTitle,
String message,
byte[] icon,
Object tag);
byte[] icon);
/**
* Fires all notifications registered for the specified <tt>eventType</tt>
@ -268,19 +267,21 @@ public NotificationData fireNotification( String eventType,
* (e.g. with systray)
* @param message the message to use if and where appropriate (e.g. with
* systray or log notification.)
* @param extra the extra data to pass (especially for Command execution)
* @param icon the icon to show in the notification if and where appropriate
* @param tag additional info to be used by the notification handler
* @param extras additional/extra {@link NotificationHandler}-specific data
* to be provided to the firing of the specified notification(s). The
* well-known keys are defined by the <tt>NotificationData</tt>
* <tt>XXX_EXTRA</tt> constants.
* @return An object referencing the notification. It may be used to stop a
* still running notification. Can be null if the eventType is
* unknown or the notification is not active.
*/
public NotificationData fireNotification( String eventType,
String messageTitle,
String message,
Map<String,String> extra,
byte[] icon,
Object tag);
public NotificationData fireNotification(
String eventType,
String messageTitle,
String message,
byte[] icon,
Map<String,Object> extras);
/**
* Fires all notifications registered for the specified <tt>eventType</tt>

@ -29,11 +29,12 @@ public interface PopupMessageNotificationHandler
* appropriate
* @param tag additional info to be used by the notification handler
*/
public void popupMessage(PopupMessageNotificationAction action,
String title,
String message,
byte[] icon,
Object tag);
public void popupMessage(
PopupMessageNotificationAction action,
String title,
String message,
byte[] icon,
Object tag);
/**
* Adds a listener for <tt>SystrayPopupMessageEvent</tt>s posted when user

@ -142,9 +142,10 @@ protected void validateMessage(SecureMessage msg)
|| last.before(new Date(new Date().getTime() - 1000*60*5)))
{
DnsUtilActivator.getNotificationService().fireNotification(
EVENT_TYPE,
R.getI18NString("util.dns.INSECURE_ANSWER_TITLE"),
text, null, null);
EVENT_TYPE,
R.getI18NString("util.dns.INSECURE_ANSWER_TITLE"),
text,
null);
lastNotifications.put(text, new Date());
}
throw new DnssecRuntimeException(text);

@ -10,11 +10,9 @@
import net.java.sip.communicator.service.notification.*;
import net.java.sip.communicator.service.systray.*;
import net.java.sip.communicator.service.systray.event.*;
import net.java.sip.communicator.util.*;
import org.osgi.framework.*;
/**
* Test suite for the popup message handler interface.
* @author Symphorien Wanko
@ -22,10 +20,6 @@
public class TestPopupMessageHandler
extends TestCase
{
/** Logger for this class */
private static final Logger logger
= Logger.getLogger(TestPopupMessageHandler.class);
/**
* the <tt>SystrayService</tt> reference we will get from bundle
* context to register ours handlers
@ -121,7 +115,6 @@ public void testNotificationHandling()
{
serviceReference = bc.getServiceReference(
NotificationService.class.getName());
notificationService
= (NotificationService) bc.getService(serviceReference);
@ -129,7 +122,6 @@ public void testNotificationHandling()
NotificationAction.ACTION_POPUP_MESSAGE,
messageStart,
messageStart,
null,
null);
}

Loading…
Cancel
Save