/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.java.sip.communicator.plugin.generalconfig.autoaway;
import java.beans.*;
import java.util.*;
import net.java.sip.communicator.plugin.generalconfig.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.sysactivity.*;
import net.java.sip.communicator.service.sysactivity.event.*;
import net.java.sip.communicator.util.*;
import org.osgi.framework.*;
/**
* Listens for idle events from SystemActivityNotifications Service.
*
* @author Damian Minkov
*/
public class AutoAwayWatcher
implements ServiceListener,
RegistrationStateChangeListener
{
/**
* The logger.
*/
private static final Logger logger
= Logger.getLogger(AutoAwayWatcher.class);
/**
* The states of providers before going to away.
*/
private final Map<ProtocolProviderService, PresenceStatus> lastStates
= new HashMap<ProtocolProviderService, PresenceStatus>();
/**
* Listens for idle events.
*/
private IdleListener idleListener = null;
/**
* Creates AutoAway handler.
*/
public AutoAwayWatcher()
{
if (Preferences.isEnabled())
{
start();
}
Preferences.addEnableChangeListener(
new PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent evt)
{
if(Boolean.parseBoolean((String) evt.getNewValue()))
start();
else
stopInner();
}
}
);
// listens for changes in configured value.
Preferences.addTimerChangeListener(
new PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent evt)
{
stopInner();
start();
}
}
);
}
/**
* Starts and add needed listeners.
*/
private void start()
{
if(idleListener == null)
{
idleListener = new IdleListener();
SystemActivityNotificationsService
systemActivityNotificationsService
= getSystemActivityNotificationsService();
systemActivityNotificationsService.addIdleSystemChangeListener(
Preferences.getTimer() * 60 * 1000,
idleListener);
systemActivityNotificationsService
.addSystemActivityChangeListener(idleListener);
startListeningForNewProviders();
}
}
/**
* Start listening for new providers and their registration states.
*/
private void startListeningForNewProviders()
{
// listen for new providers
GeneralConfigPluginActivator.bundleContext.addServiceListener(this);
// lets check current providers
ServiceReference[] protocolProviderRefs = null;
try
{
protocolProviderRefs = GeneralConfigPluginActivator.bundleContext
.getServiceReferences(ProtocolProviderService.class.getName(),
null);
}
catch (InvalidSyntaxException ex)
{
// this shouldn't happen since we're providing no parameter string
// but let's log just in case.
logger.error(
"Error while retrieving service refs", ex);
return;
}
// in case we found any
if (protocolProviderRefs != null)
{
for (int i = 0; i < protocolProviderRefs.length; i++)
{
ProtocolProviderService provider = (ProtocolProviderService)
GeneralConfigPluginActivator.bundleContext
.getService(protocolProviderRefs[i]);
this.handleProviderAdded(provider);
}
}
}
/**
* Stop listening for new providers and their registration states.
*/
private void stopListeningForNewProviders()
{
// stop listen for new providers
GeneralConfigPluginActivator.bundleContext.removeServiceListener(this);
// lets check current providers and remove registration state listener
ServiceReference[] protocolProviderRefs = null;
try
{
protocolProviderRefs = GeneralConfigPluginActivator.bundleContext
.getServiceReferences(ProtocolProviderService.class.getName(),
null);
}
catch (InvalidSyntaxException ex)
{
// this shouldn't happen since we're providing no parameter string
// but let's log just in case.
logger.error(
"Error while retrieving service refs", ex);
return;
}
// in case we found any
if (protocolProviderRefs != null)
{
for (int i = 0; i < protocolProviderRefs.length; i++)
{
ProtocolProviderService provider = (ProtocolProviderService)
GeneralConfigPluginActivator.bundleContext
.getService(protocolProviderRefs[i]);
this.handleProviderRemoved(provider);
}
}
}
/**
* Stops and removes the listeners.
*/
public void stop()
{
GeneralConfigPluginActivator.bundleContext.removeServiceListener(this);
stopInner();
}
/**
* Stops and removes the listeners.
*/
private void stopInner()
{
if(idleListener != null)
{
SystemActivityNotificationsService
systemActivityNotificationsService
= getSystemActivityNotificationsService();
systemActivityNotificationsService.removeIdleSystemChangeListener(
idleListener);
systemActivityNotificationsService
.removeSystemActivityChangeListener(idleListener);
stopListeningForNewProviders();
idleListener = null;
}
}
/**
* Change protocol to away saving status so it can be set again when
* out of idle state.
*/
private void changeProtocolsToAway()
{
for (ProtocolProviderService protocolProvider
: GeneralConfigPluginActivator.getProtocolProviders())
{
OperationSetPresence presence
= protocolProvider.getOperationSet(
OperationSetPresence.class);
if(presence == null)
continue;
PresenceStatus status = presence.getPresenceStatus();
if (status.getStatus() < PresenceStatus.AVAILABLE_THRESHOLD)
{
// already (manually) set to away or lower
continue;
}
PresenceStatus newStatus
= StatusUpdateThread.findAwayStatus(presence);
try
{
if (newStatus != null && !status.equals(newStatus))
{
addProviderToLastStates(protocolProvider, status);
presence.publishPresenceStatus(
newStatus,
newStatus.getStatusName());
}
}
catch (IllegalArgumentException e)
{
}
catch (IllegalStateException e)
{
}
catch (OperationFailedException e)
{
}
}
}
/**
* Back to status which was already saved before going to idle.
*/
private void changeProtocolsToPreviousState()
{
for (ProtocolProviderService protocolProvider
: GeneralConfigPluginActivator.getProtocolProviders())
{
PresenceStatus lastState = lastStates.get(protocolProvider);
if (lastState != null)
{
OperationSetPresence presence
= protocolProvider.getOperationSet(
OperationSetPresence.class);
try
{
presence.publishPresenceStatus(lastState, "");
}
catch (IllegalArgumentException e)
{
}
catch (IllegalStateException e)
{
}
catch (OperationFailedException e)
{
}
removeProviderFromLastStates(protocolProvider);
}
}
}
/**
* The SystemActivityNotifications Service.
* @return the SystemActivityNotifications Service.
*/
private SystemActivityNotificationsService
getSystemActivityNotificationsService()
{
return
ServiceUtils.getService(
GeneralConfigPluginActivator.bundleContext,
SystemActivityNotificationsService.class);
}
/**
* When new protocol provider is registered we add our
* registration change listener.
* If unregistered remove reference to the provider and the
* registration change listener.
*
* @param serviceEvent ServiceEvent
*/
public void serviceChanged(ServiceEvent serviceEvent)
{
Object service
= GeneralConfigPluginActivator.bundleContext.getService(
serviceEvent.getServiceReference());
// we don't care if the source service is not a protocol provider
if (service instanceof ProtocolProviderService)
{
int serviceEventType = serviceEvent.getType();
if (serviceEventType == ServiceEvent.REGISTERED)
handleProviderAdded((ProtocolProviderService) service);
else if (serviceEventType == ServiceEvent.UNREGISTERING)
handleProviderRemoved((ProtocolProviderService) service);
}
}
/**
* Used to set registration state change listener.
*
* @param provider ProtocolProviderService
*/
private synchronized void handleProviderAdded(
ProtocolProviderService provider)
{
provider.addRegistrationStateChangeListener(this);
}
/**
* Removes the registration change listener.
*
* @param provider the ProtocolProviderService that has been unregistered.
*/
private void handleProviderRemoved(ProtocolProviderService provider)
{
provider.removeRegistrationStateChangeListener(this);
}
/**
* Remove provider from list with last statuses.
* If this is the last provider stop listening for idle events.
* @param provider
*/
private synchronized void removeProviderFromLastStates(
ProtocolProviderService provider)
{
lastStates.remove(provider);
}
/**
* Remember provider's last status, normally before setting it to away.
* If needed start listening for idle events.
* @param provider the provider.
* @param status the status to save.
*/
private synchronized void addProviderToLastStates(
ProtocolProviderService provider,
PresenceStatus status)
{
if(lastStates.size() == 0)
start();
lastStates.put(provider, status);
}
/**
* Listens for provider states.
* @param evt
*/
public void registrationStateChanged(RegistrationStateChangeEvent evt)
{
if(evt.getSource() instanceof ProtocolProviderService)
{
if(evt.getNewState().equals(RegistrationState.UNREGISTERED)
|| evt.getNewState().equals(RegistrationState.CONNECTION_FAILED))
{
removeProviderFromLastStates(evt.getProvider());
}
else if(evt.getNewState().equals(
RegistrationState.REGISTERED))
{
// we have at least one provider, so lets start listening
if(lastStates.size() == 0)
{
start();
}
else
{
// or check are we away
if(getSystemActivityNotificationsService()
.getTimeSinceLastInput()
> Preferences.getTimer()*60*1000)
{
// we are away, so update the newly registered provider
// do it in new thread to give the provider
// time dispatch his status
new Thread(new Runnable()
{
public void run()
{
try{
Thread.sleep(1000);
}
catch(Throwable t){}
changeProtocolsToAway();
}
}).start();
}
}
}
}
}
/**
* Listener waiting for idle state change.
*/
private class IdleListener
implements SystemActivityChangeListener
{
/**
* Listens for activities and set corresponding statuses.
*
* @param event the <tt>NotificationActionTypeEvent</tt>, which is
*/
public void activityChanged(SystemActivityEvent event)
{
switch(event.getEventID())
{
case SystemActivityEvent.EVENT_DISPLAY_SLEEP:
case SystemActivityEvent.EVENT_SCREEN_LOCKED:
case SystemActivityEvent.EVENT_SCREENSAVER_START:
case SystemActivityEvent.EVENT_SYSTEM_IDLE:
changeProtocolsToAway();
break;
case SystemActivityEvent.EVENT_DISPLAY_WAKE:
case SystemActivityEvent.EVENT_SCREEN_UNLOCKED:
case SystemActivityEvent.EVENT_SCREENSAVER_STOP:
case SystemActivityEvent.EVENT_SYSTEM_IDLE_END:
changeProtocolsToPreviousState();
break;
}
}
}
}