/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.impl.globaldisplaydetails; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.protocol.globalstatus.*; import net.java.sip.communicator.util.*; import net.java.sip.communicator.util.account.*; import org.jitsi.service.configuration.*; import java.util.*; /** * Global statuses service impl. Gives access to the outside for some * methods to change the global status and individual protocol provider * status. Use to implement global status menu with list of all account * statuses. * @author Damian Minkov */ public class GlobalStatusServiceImpl implements GlobalStatusService, RegistrationStateChangeListener { /** * The object used for logging. */ private final Logger logger = Logger.getLogger(GlobalStatusServiceImpl.class); /** * Handles newly added providers. * @param pps */ void handleProviderAdded(ProtocolProviderService pps) { pps.addRegistrationStateChangeListener(this); if(pps.isRegistered()) { handleProviderRegistered(pps, false); } } /** * Handles removed providers. * @param pps the provider. */ void handleProviderRemoved(ProtocolProviderService pps) { pps.removeRegistrationStateChangeListener(this); } /** * Returns the global presence status. * * @return the current global presence status */ public PresenceStatus getGlobalPresenceStatus() { int status = 0; Collection<ProtocolProviderService> pProviders = AccountUtils.getRegisteredProviders(); // If we don't have registered providers we return offline status. if (pProviders == null || pProviders.size() <= 0) return getPresenceStatus(status); Iterator<ProtocolProviderService> providersIter = pProviders.iterator(); boolean hasAvailableProvider = false; while (providersIter.hasNext()) { ProtocolProviderService protocolProvider = providersIter.next(); // We do not show hidden protocols in our status bar, so we do not // care about their status here. if (protocolProvider.getAccountID().isHidden()) continue; if (!protocolProvider.isRegistered()) continue; OperationSetPresence presence = protocolProvider.getOperationSet(OperationSetPresence.class); if(presence == null) { hasAvailableProvider = true; continue; } int presenceStatus = (presence == null) ? PresenceStatus.AVAILABLE_THRESHOLD : presence.getPresenceStatus().getStatus(); if (status < presenceStatus) status = presenceStatus; } // if we have at least one online provider if(status == 0 && hasAvailableProvider) status = PresenceStatus.AVAILABLE_THRESHOLD; return getPresenceStatus(status); } /** * Returns the <tt>JCheckBoxMenuItem</tt> corresponding to the given status. * For status constants we use here the values defined in the * <tt>PresenceStatus</tt>, but this is only for convenience. * * @param status the status to which the item should correspond * @return the <tt>JCheckBoxMenuItem</tt> corresponding to the given status */ private PresenceStatus getPresenceStatus(int status) { if(status < PresenceStatus.ONLINE_THRESHOLD) { return GlobalStatusEnum.OFFLINE; } else if(status < PresenceStatus.EXTENDED_AWAY_THRESHOLD) { return GlobalStatusEnum.DO_NOT_DISTURB; } else if(status < PresenceStatus.AWAY_THRESHOLD) { return GlobalStatusEnum.EXTENDED_AWAY; } else if(status < PresenceStatus.AVAILABLE_THRESHOLD) { return GlobalStatusEnum.AWAY; } else if(status < PresenceStatus.EAGER_TO_COMMUNICATE_THRESHOLD) { return GlobalStatusEnum.ONLINE; } else if(status < PresenceStatus.MAX_STATUS_VALUE) { return GlobalStatusEnum.FREE_FOR_CHAT; } else { return GlobalStatusEnum.OFFLINE; } } /** * Returns the last status that was stored in the configuration for the * given protocol provider. * * @param protocolProvider the protocol provider * @return the last status that was stored in the configuration for the * given protocol provider */ public PresenceStatus getLastPresenceStatus( ProtocolProviderService protocolProvider) { String lastStatus = getLastStatusString(protocolProvider); PresenceStatus status = null; if (lastStatus != null) { OperationSetPresence presence = protocolProvider.getOperationSet(OperationSetPresence.class); if (presence == null) return null; Iterator<PresenceStatus> i = presence.getSupportedStatusSet(); // Check if there's such status in the supported presence status // set. while (i.hasNext()) { PresenceStatus nextStatus = i.next(); if (nextStatus.getStatusName().equals(lastStatus)) status = nextStatus; } // If we haven't found the last status in the protocol provider // supported status set, we'll have a look for a corresponding // global status and its protocol representation. if (status == null) { if (lastStatus.equals(GlobalStatusEnum.ONLINE_STATUS)) { status = getPresenceStatus( protocolProvider, PresenceStatus.AVAILABLE_THRESHOLD, PresenceStatus.EAGER_TO_COMMUNICATE_THRESHOLD); } else if (lastStatus.equals(GlobalStatusEnum.AWAY_STATUS)) { status = getPresenceStatus( protocolProvider, PresenceStatus.AWAY_THRESHOLD, PresenceStatus.AVAILABLE_THRESHOLD); } else if (lastStatus.equals( GlobalStatusEnum.EXTENDED_AWAY_STATUS)) { status = getPresenceStatus( protocolProvider, PresenceStatus.EXTENDED_AWAY_THRESHOLD, PresenceStatus.AWAY_THRESHOLD); } else if (lastStatus .equals(GlobalStatusEnum.DO_NOT_DISTURB_STATUS)) { status = getPresenceStatus( protocolProvider, PresenceStatus.ONLINE_THRESHOLD, PresenceStatus.EXTENDED_AWAY_THRESHOLD); } else if (lastStatus .equals(GlobalStatusEnum.FREE_FOR_CHAT_STATUS)) { status = getPresenceStatus( protocolProvider, PresenceStatus.AVAILABLE_THRESHOLD, PresenceStatus.MAX_STATUS_VALUE); } else if (lastStatus.equals(GlobalStatusEnum.OFFLINE_STATUS)) { status = getPresenceStatus( protocolProvider, 0, GlobalStatusEnum.ONLINE_THRESHOLD); } } } return status; } /** * Returns the last contact status saved in the configuration. * * @param protocolProvider the protocol provider to which the status * corresponds * @return the last contact status saved in the configuration. */ public String getLastStatusString(ProtocolProviderService protocolProvider) { // find the last contact status saved in the configuration. String lastStatus = null; ConfigurationService configService = GlobalDisplayDetailsActivator.getConfigurationService(); String prefix = "net.java.sip.communicator.impl.gui.accounts"; List<String> accounts = configService.getPropertyNamesByPrefix(prefix, true); String protocolProviderAccountUID = protocolProvider.getAccountID().getAccountUniqueID(); for (String accountRootPropName : accounts) { String accountUID = configService.getString(accountRootPropName); if (accountUID.equals(protocolProviderAccountUID)) { lastStatus = configService.getString(accountRootPropName + ".lastAccountStatus"); if (lastStatus != null) break; } } return lastStatus; } /** * Publish present status. We search for the highest value in the * given interval. * * @param protocolProvider the protocol provider to which we * change the status. * @param status the status to publish. */ public void publishStatus( ProtocolProviderService protocolProvider, PresenceStatus status) { publishStatusInternal(protocolProvider, status, false); } /** * Publish present status. We search for the highest value in the * given interval. * * @param protocolProvider the protocol provider to which we * change the status. * @param status the status to publish. * @param dueToRegistrationStateChanged whether the publish status is * invoked after registrationStateChanged for a provider, where the provider * is expected to be REGISTERED, if not we do nothing (means it has * connection failed soon after firing registered). */ private void publishStatusInternal( ProtocolProviderService protocolProvider, PresenceStatus status, boolean dueToRegistrationStateChanged) { OperationSetPresence presence = protocolProvider.getOperationSet(OperationSetPresence.class); LoginManager loginManager = null; UIService uiService = GlobalDisplayDetailsActivator.getUIService(); if(uiService != null) { loginManager = uiService.getLoginManager(); } RegistrationState registrationState = protocolProvider.getRegistrationState(); if (registrationState == RegistrationState.REGISTERED && presence != null && !presence.getPresenceStatus().equals(status)) { if (status.isOnline()) { new PublishPresenceStatusThread( protocolProvider, presence, status).start(); } else { if(loginManager != null) loginManager.setManuallyDisconnected(true); LoginManager.logoff(protocolProvider); } } else if (registrationState != RegistrationState.REGISTERED && registrationState != RegistrationState.REGISTERING && registrationState != RegistrationState.AUTHENTICATING && status.isOnline()) { if(dueToRegistrationStateChanged) { // If provider fires registered, and while dispatching // the registered event a fatal error rise in the connection // and the provider goes in connection_failed we can end up here // calling login and going over the same cycle over and over // again logger.warn("Called publish status for provider in wrong state " + " provider: " + protocolProvider + " registrationState: " + registrationState + " status: " + status); return; } else { GlobalDisplayDetailsActivator.getUIService().getLoginManager() .login(protocolProvider); } } else if (!status.isOnline() && !(registrationState == RegistrationState.UNREGISTERING)) { if(loginManager != null) loginManager.setManuallyDisconnected(true); LoginManager.logoff(protocolProvider); } saveStatusInformation( protocolProvider, status.getStatusName()); } /** * Publish present status. We search for the highest value in the * given interval. * * change the status. * @param globalStatus */ public void publishStatus(GlobalStatusEnum globalStatus) { String itemName = globalStatus.getStatusName(); Iterator<ProtocolProviderService> pProviders = AccountUtils.getRegisteredProviders().iterator(); while (pProviders.hasNext()) { ProtocolProviderService protocolProvider = pProviders.next(); if(itemName.equals(GlobalStatusEnum.ONLINE_STATUS)) { if(!protocolProvider.isRegistered()) { saveStatusInformation(protocolProvider, itemName); GlobalDisplayDetailsActivator.getUIService() .getLoginManager().login(protocolProvider); } else { OperationSetPresence presence = protocolProvider .getOperationSet(OperationSetPresence.class); if (presence == null) { saveStatusInformation(protocolProvider, itemName); continue; } Iterator<PresenceStatus> statusSet = presence.getSupportedStatusSet(); while (statusSet.hasNext()) { PresenceStatus status = statusSet.next(); if( status.getStatus() < PresenceStatus.EAGER_TO_COMMUNICATE_THRESHOLD && status.getStatus() >= PresenceStatus.AVAILABLE_THRESHOLD) { new PublishPresenceStatusThread(protocolProvider, presence, status) .start(); this.saveStatusInformation( protocolProvider, status.getStatusName()); break; } } } } else if (itemName.equals(GlobalStatusEnum.OFFLINE_STATUS)) { if( !protocolProvider.getRegistrationState() .equals(RegistrationState.UNREGISTERED) && !protocolProvider.getRegistrationState() .equals(RegistrationState.UNREGISTERING)) { OperationSetPresence presence = protocolProvider .getOperationSet(OperationSetPresence.class); if (presence == null) { saveStatusInformation( protocolProvider, itemName); LoginManager.logoff(protocolProvider); continue; } Iterator<PresenceStatus> statusSet = presence.getSupportedStatusSet(); while (statusSet.hasNext()) { PresenceStatus status = statusSet.next(); if(status.getStatus() < PresenceStatus.ONLINE_THRESHOLD) { this.saveStatusInformation( protocolProvider, status.getStatusName()); break; } } try { protocolProvider.unregister(true); } catch (OperationFailedException e1) { logger.error( "Unable to unregister the protocol provider: " + protocolProvider + " due to the following exception: " + e1); } } } else if (itemName.equals(GlobalStatusEnum.FREE_FOR_CHAT_STATUS)) { if(!protocolProvider.isRegistered()) { saveStatusInformation(protocolProvider, itemName); GlobalDisplayDetailsActivator.getUIService() .getLoginManager().login(protocolProvider); } else // we search for highest available status here publishStatus( protocolProvider, PresenceStatus.AVAILABLE_THRESHOLD, PresenceStatus.MAX_STATUS_VALUE); } else if (itemName.equals(GlobalStatusEnum.DO_NOT_DISTURB_STATUS)) { if(!protocolProvider.isRegistered()) { saveStatusInformation(protocolProvider, itemName); GlobalDisplayDetailsActivator.getUIService() .getLoginManager().login(protocolProvider); } else // status between online and away is DND publishStatus( protocolProvider, PresenceStatus.ONLINE_THRESHOLD, PresenceStatus.EXTENDED_AWAY_THRESHOLD); } else if (itemName.equals(GlobalStatusEnum.AWAY_STATUS)) { if(!protocolProvider.isRegistered()) { saveStatusInformation(protocolProvider, itemName); GlobalDisplayDetailsActivator.getUIService() .getLoginManager().login(protocolProvider); } else // a status in the away interval publishStatus( protocolProvider, PresenceStatus.AWAY_THRESHOLD, PresenceStatus.AVAILABLE_THRESHOLD); } else if (itemName.equals(GlobalStatusEnum.EXTENDED_AWAY_STATUS)) { if(!protocolProvider.isRegistered()) { saveStatusInformation(protocolProvider, itemName); GlobalDisplayDetailsActivator.getUIService() .getLoginManager().login(protocolProvider); } else // a status in the away interval publishStatus( protocolProvider, PresenceStatus.EXTENDED_AWAY_THRESHOLD, PresenceStatus.AWAY_THRESHOLD); } } } /** * Publish present status. We search for the highest value in the * given interval. * * @param protocolProvider the protocol provider to which we * change the status. * @param floorStatusValue the min status value. * @param ceilStatusValue the max status value. */ private void publishStatus( ProtocolProviderService protocolProvider, int floorStatusValue, int ceilStatusValue) { if (!protocolProvider.isRegistered()) return; PresenceStatus status = getPresenceStatus(protocolProvider, floorStatusValue, ceilStatusValue); if (status != null) { OperationSetPresence presence = protocolProvider .getOperationSet(OperationSetPresence.class); new PublishPresenceStatusThread(protocolProvider, presence, status) .start(); this.saveStatusInformation( protocolProvider, status.getStatusName()); } } private PresenceStatus getPresenceStatus( ProtocolProviderService protocolProvider, int floorStatusValue, int ceilStatusValue) { OperationSetPresence presence = protocolProvider .getOperationSet(OperationSetPresence.class); if (presence == null) return null; Iterator<PresenceStatus> statusSet = presence.getSupportedStatusSet(); PresenceStatus status = null; while (statusSet.hasNext()) { PresenceStatus currentStatus = statusSet.next(); if (status == null && currentStatus.getStatus() < ceilStatusValue && currentStatus.getStatus() >= floorStatusValue) { status = currentStatus; } if (status != null) { if (currentStatus.getStatus() < ceilStatusValue && currentStatus.getStatus() >= floorStatusValue && currentStatus.getStatus() > status.getStatus()) { status = currentStatus; } } } return status; } /** * Saves the last status for all accounts. This information is used * on loging. Each time user logs in he's logged with the same status * as he was the last time before closing the application. * @param protocolProvider the protocol provider to save status information * for * @param statusName the name of the status to save */ private void saveStatusInformation( ProtocolProviderService protocolProvider, String statusName) { ConfigurationService configService = GlobalDisplayDetailsActivator.getConfigurationService(); String prefix = "net.java.sip.communicator.impl.gui.accounts"; List<String> accounts = configService .getPropertyNamesByPrefix(prefix, true); boolean savedAccount = false; for (String accountRootPropName : accounts) { String accountUID = configService.getString(accountRootPropName); if(accountUID.equals(protocolProvider .getAccountID().getAccountUniqueID())) { configService.setProperty( accountRootPropName + ".lastAccountStatus", statusName); savedAccount = true; } } if(!savedAccount) { String accNodeName = "acc" + Long.toString(System.currentTimeMillis()); String accountPackage = "net.java.sip.communicator.impl.gui.accounts." + accNodeName; configService.setProperty(accountPackage, protocolProvider.getAccountID().getAccountUniqueID()); configService.setProperty( accountPackage + ".lastAccountStatus", statusName); } } /** * Waits for providers to register and then checks for its last status * saved if any and used it to restore its status. * @param evt a <tt>RegistrationStateChangeEvent</tt> which describes the */ @Override public void registrationStateChanged(RegistrationStateChangeEvent evt) { if(!evt.getNewState().equals(RegistrationState.REGISTERED)) return; handleProviderRegistered(evt.getProvider(), true); } /** * Handles registered providers. If provider has a stored last status * publish that status, otherwise we just publish that they * are Online/Available/ * @param pps the provider */ private void handleProviderRegistered(ProtocolProviderService pps, boolean dueToRegistrationStateChanged) { PresenceStatus status = getLastPresenceStatus(pps); if(status == null) { // lets publish just online status = AccountStatusUtils.getOnlineStatus(pps); } if (status != null && status.getStatus() >= PresenceStatus.ONLINE_THRESHOLD) { publishStatusInternal(pps, status, dueToRegistrationStateChanged); } } /** * Publishes the given status to the given presence operation set. */ private class PublishPresenceStatusThread extends Thread { private ProtocolProviderService protocolProvider; private PresenceStatus status; private OperationSetPresence presence; /** * Publishes the given <tt>status</tt> through the given * <tt>presence</tt> operation set. * @param presence the operation set through which we publish the status * @param status the status to publish */ public PublishPresenceStatusThread( ProtocolProviderService protocolProvider, OperationSetPresence presence, PresenceStatus status) { this.protocolProvider = protocolProvider; this.presence = presence; this.status = status; } @Override public void run() { try { presence.publishPresenceStatus(status, ""); } catch (IllegalArgumentException e1) { logger.error("Error - changing status", e1); } catch (IllegalStateException e1) { logger.error("Error - changing status", e1); } catch (OperationFailedException e1) { if (e1.getErrorCode() == OperationFailedException.GENERAL_ERROR) { String msgText = GlobalDisplayDetailsActivator.getResources() .getI18NString( "service.gui.STATUS_CHANGE_GENERAL_ERROR", new String[]{ protocolProvider.getAccountID().getUserID(), protocolProvider.getAccountID().getService()}); GlobalDisplayDetailsActivator.getAlertUIService() .showAlertDialog( GlobalDisplayDetailsActivator.getResources() .getI18NString("service.gui.GENERAL_ERROR"), msgText, e1); } else if (e1.getErrorCode() == OperationFailedException.NETWORK_FAILURE) { String msgText = GlobalDisplayDetailsActivator.getResources() .getI18NString( "service.gui.STATUS_CHANGE_NETWORK_FAILURE", new String[]{ protocolProvider.getAccountID().getUserID(), protocolProvider.getAccountID().getService()}); GlobalDisplayDetailsActivator.getAlertUIService() .showAlertDialog( msgText, GlobalDisplayDetailsActivator.getResources() .getI18NString("service.gui.NETWORK_FAILURE"), e1); } else if (e1.getErrorCode() == OperationFailedException.PROVIDER_NOT_REGISTERED) { String msgText = GlobalDisplayDetailsActivator.getResources() .getI18NString( "service.gui.STATUS_CHANGE_NETWORK_FAILURE", new String[]{ protocolProvider.getAccountID().getUserID(), protocolProvider.getAccountID().getService()}); GlobalDisplayDetailsActivator.getAlertUIService() .showAlertDialog( GlobalDisplayDetailsActivator.getResources() .getI18NString("service.gui.NETWORK_FAILURE"), msgText, e1); } logger.error("Error - changing status", e1); } } } }