/* * 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.service.protocol; import static net.java.sip.communicator.service.protocol.OperationSetBasicTelephony.*; import java.util.*; import java.util.regex.*; import net.java.sip.communicator.service.calendar.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; import org.jitsi.service.configuration.*; import org.osgi.framework.*; /** * Imposes the policy to have one call in progress i.e. to put existing calls on * hold when a new call enters in progress. * * @author Lyubomir Marinov * @author Damian Minkov * @author Hristo Terezov */ public class SingleCallInProgressPolicy { /** * Account property to enable per account rejecting calls if the account * presence is in DND or OnThePhone status. */ private static final String ACCOUNT_PROPERTY_REJECT_IN_CALL_ON_DND = "RejectIncomingCallsWhenDnD"; /** * Our class logger */ private static final Logger logger = Logger.getLogger(SingleCallInProgressPolicy.class); /** * The name of the configuration property which specifies whether call * waiting is disabled i.e. whether it should reject new incoming calls when * there are other calls already in progress. */ private static final String PNAME_CALL_WAITING_DISABLED = "net.java.sip.communicator.impl.protocol.CallWaitingDisabled"; /** * The name of the configuration property which specifies whether * <tt>OnThePhoneStatusPolicy</tt> is enabled i.e. whether it should set the * presence statuses of online accounts to "On the phone" when at * least one <tt>Call</tt> is in progress. */ private static final String PNAME_ON_THE_PHONE_STATUS_ENABLED = "net.java.sip.communicator.impl.protocol.OnThePhoneStatusPolicy" + ".enabled"; /** * Global property which will enable rejecting incoming calls for all * accounts, if the account is in DND or OnThePhone status. */ private static final String PNAME_REJECT_IN_CALL_ON_DND = "net.java.sip.communicator.impl.protocol." + ACCOUNT_PROPERTY_REJECT_IN_CALL_ON_DND; /** * The name of the configuration property which specifies whether * <tt>SingleCallInProgressPolicy</tt> is enabled i.e. whether it should put * existing calls on hold when a new call enters in progress. */ private static final String PNAME_SINGLE_CALL_IN_PROGRESS_POLICY_ENABLED = "net.java.sip.communicator.impl.protocol.SingleCallInProgressPolicy" + ".enabled"; /** * The <tt>BundleContext</tt> to the Calls of which this policy applies. */ private final BundleContext bundleContext; /** * The <tt>Call</tt>s this policy manages i.e. put on hold when one of them * enters in progress. */ private final List<Call> calls = new ArrayList<Call>(); /** * The listener utilized by this policy to discover new <tt>Call</tt> and * track their in-progress state. */ private final SingleCallInProgressPolicyListener listener = new SingleCallInProgressPolicyListener(); /** * The implementation of the policy to have the presence statuses of online * accounts (i.e. registered <tt>ProtocolProviderService</tt>s) set to * "On the phone" when at least one <tt>Call</tt> is in progress. */ private final OnThePhoneStatusPolicy onThePhoneStatusPolicy = new OnThePhoneStatusPolicy(); /** * Initializes a new <tt>SingleCallInProgressPolicy</tt> instance which * will apply to the <tt>Call</tt>s of a specific <tt>BundleContext</tt>. * * @param bundleContext the <tt>BundleContext</tt> to the <tt>Call<tt>s of * which the new policy should apply */ public SingleCallInProgressPolicy(BundleContext bundleContext) { this.bundleContext = bundleContext; this.bundleContext.addServiceListener(listener); } /** * Registers a specific <tt>Call</tt> with this policy in order to have the * rules of the latter apply to the former. * * @param call the <tt>Call</tt> to register with this policy in order to * have the rules of the latter apply to the former */ private void addCallListener(Call call) { if(logger.isTraceEnabled()) { logger.trace("Add call change listener"); } synchronized (calls) { if (!calls.contains(call)) { CallState callState = call.getCallState(); if ((callState != null) && !callState.equals(CallState.CALL_ENDED)) { calls.add(call); } } } call.addCallChangeListener(listener); } /** * Registers a specific <tt>OperationSetBasicTelephony</tt> with this policy * in order to have the rules of the latter apply to the <tt>Call</tt>s * created by the former. * * @param telephony the <tt>OperationSetBasicTelephony</tt> to register with * this policy in order to have the rules of the latter apply to the * <tt>Call</tt>s created by the former */ private void addOperationSetBasicTelephonyListener( OperationSetBasicTelephony<? extends ProtocolProviderService> telephony) { if(logger.isTraceEnabled()) { logger.trace("Call listener added to provider."); } telephony.addCallListener(listener); } /** * Handles changes in the state of a <tt>Call</tt> this policy applies to in * order to detect when new calls become in-progress and when the other * calls should be put on hold. * * @param ev a <tt>CallChangeEvent</tt> value which describes the * <tt>Call</tt> and the change in its state */ private void callStateChanged(CallChangeEvent ev) { Call call = ev.getSourceCall(); if(logger.isTraceEnabled()) { logger.trace("Call state changed."); } if (CallState.CALL_INITIALIZATION.equals(ev.getOldValue()) && CallState.CALL_IN_PROGRESS.equals(call.getCallState()) && ProtocolProviderActivator .getConfigurationService() .getBoolean( PNAME_SINGLE_CALL_IN_PROGRESS_POLICY_ENABLED, true)) { CallConference conference = call.getConference(); synchronized (calls) { for (Call otherCall : calls) { if (!call.equals(otherCall) && CallState.CALL_IN_PROGRESS.equals( otherCall.getCallState())) { /* * Only put on hold calls which are visually distinctive * from the specified call i.e. do not put on hold calls * which participate in the same telephony conference as * the specified call. */ boolean putOnHold; CallConference otherConference = otherCall.getConference(); if (conference == null) putOnHold = (otherConference == null); else putOnHold = (conference != otherConference); if (putOnHold) putOnHold(otherCall); } } } } /* * Forward to onThePhoneStatusPolicy for which we are proxying the * Call-related events. */ onThePhoneStatusPolicy.callStateChanged(ev); } /** * Performs end-of-life cleanup associated with this instance e.g. removes * added listeners. */ public void dispose() { bundleContext.removeServiceListener(listener); } /** * Handles the start and end of the <tt>Call</tt>s this policy applies to in * order to have them or stop having them put the other existing calls on * hold when the former change their states to * <tt>CallState.CALL_IN_PROGRESS</tt>. * <p> * Also handles call rejection via "busy here" according to the call policy. * </p> * * @param type one of {@link CallEvent#CALL_ENDED}, * {@link CallEvent#CALL_INITIATED} and {@link CallEvent#CALL_RECEIVED} * which describes the type of the event to be handled * @param ev a <tt>CallEvent</tt> value which describes the change and the * <tt>Call</tt> associated with it */ private void handleCallEvent(int type, CallEvent ev) { Call call = ev.getSourceCall(); if(logger.isTraceEnabled()) { logger.trace("Call event fired."); } switch (type) { case CallEvent.CALL_ENDED: removeCallListener(call); break; case CallEvent.CALL_INITIATED: case CallEvent.CALL_RECEIVED: addCallListener(call); break; } /* * Forward to onThePhoneStatusPolicy for which we are proxying the * Call-related events. */ onThePhoneStatusPolicy.handleCallEvent(type, ev); } /** * Notifies this instance that an incoming <tt>Call</tt> has been received. * * @param ev a <tt>CallEvent</tt> which describes the received incoming * <tt>Call</tt> */ private void incomingCallReceived(CallEvent ev) { Call call = ev.getSourceCall(); // check whether we should hangup this call saying we are busy // already on call if (CallState.CALL_INITIALIZATION.equals(call.getCallState())) { ConfigurationService config = ProtocolProviderActivator.getConfigurationService(); if (config.getBoolean(PNAME_CALL_WAITING_DISABLED, false)) { boolean rejectCallWithBusyHere = false; synchronized (calls) { for (Call otherCall : calls) { if (!call.equals(otherCall) && CallState.CALL_IN_PROGRESS.equals( otherCall.getCallState())) { rejectCallWithBusyHere = true; break; } } } if (rejectCallWithBusyHere) { rejectCallWithBusyHere(call); return; } } ProtocolProviderService provider = call.getProtocolProvider(); if (config.getBoolean(PNAME_REJECT_IN_CALL_ON_DND, false) || provider.getAccountID().getAccountPropertyBoolean( ACCOUNT_PROPERTY_REJECT_IN_CALL_ON_DND, false)) { OperationSetPresence presence = provider.getOperationSet(OperationSetPresence.class); // if our provider has no presence op set, lets search for // connected provider which will have if(presence == null) { // There is no presence OpSet. Let's check the connected // CUSAX provider if available OperationSetCusaxUtils cusaxOpSet = provider.getOperationSet( OperationSetCusaxUtils.class); if(cusaxOpSet != null) { ProtocolProviderService linkedCusaxProvider = cusaxOpSet.getLinkedCusaxProvider(); if(linkedCusaxProvider != null) { // we found the provider, let's take its presence // opset presence = linkedCusaxProvider.getOperationSet( OperationSetPresence.class); } } } if(presence != null) { int presenceStatus = (presence == null) ? PresenceStatus.AVAILABLE_THRESHOLD : presence.getPresenceStatus().getStatus(); // between AVAILABLE and EXTENDED AWAY (>20, <= 31) are // the busy statuses as DND and On the phone if (presenceStatus > PresenceStatus.ONLINE_THRESHOLD && presenceStatus <= PresenceStatus.EXTENDED_AWAY_THRESHOLD) { rejectCallWithBusyHere(call); return; } } } } handleCallEvent(CallEvent.CALL_RECEIVED, ev); } /** * Puts the <tt>CallPeer</tt>s of a specific <tt>Call</tt> on hold. * * @param call the <tt>Call</tt> the <tt>CallPeer</tt>s of which are to be * put on hold */ private void putOnHold(Call call) { OperationSetBasicTelephony<?> telephony = call.getProtocolProvider().getOperationSet( OperationSetBasicTelephony.class); if (telephony != null) { for (Iterator<? extends CallPeer> peerIter = call.getCallPeers(); peerIter.hasNext();) { CallPeer peer = peerIter.next(); CallPeerState peerState = peer.getState(); if (!CallPeerState.DISCONNECTED.equals(peerState) && !CallPeerState.FAILED.equals(peerState) && !CallPeerState.isOnHold(peerState)) { try { telephony.putOnHold(peer); } catch (OperationFailedException ex) { logger.error("Failed to put " + peer + " on hold.", ex); } } } } } /** * Rejects a <tt>call</tt> with busy here code. * * @param call the call to reject. */ private void rejectCallWithBusyHere(Call call) { // We're interested in one-to-one incoming calls. if(call.getCallPeerCount() == 1) { CallPeer peer = call.getCallPeers().next(); OperationSetBasicTelephony<?> telephony = call.getProtocolProvider().getOperationSet( OperationSetBasicTelephony.class); if (telephony != null) { try { telephony.hangupCallPeer( peer, HANGUP_REASON_BUSY_HERE, null); } catch (OperationFailedException ex) { logger.error("Failed to reject " + peer, ex); } } } } /** * Unregisters a specific <tt>Call</tt> from this policy in order to have * the rules of the latter no longer applied to the former. * * @param call the <tt>Call</tt> to unregister from this policy in order to * have the rules of the latter no longer apply to the former */ private void removeCallListener(Call call) { if(logger.isTraceEnabled()) { logger.trace("Remove call change listener."); } call.removeCallChangeListener(listener); synchronized (calls) { calls.remove(call); } } /** * Unregisters a specific <tt>OperationSetBasicTelephony</tt> from this * policy in order to have the rules of the latter no longer apply to the * <tt>Call</tt>s created by the former. * * @param telephony the <tt>OperationSetBasicTelephony</tt> to unregister * from this policy in order to have the rules of the latter apply to the * <tt>Call</tt>s created by the former */ private void removeOperationSetBasicTelephonyListener( OperationSetBasicTelephony<? extends ProtocolProviderService> telephony) { telephony.removeCallListener(listener); } /** * Handles the registering and unregistering of * <tt>OperationSetBasicTelephony</tt> instances in order to apply or * unapply the rules of this policy to the <tt>Call</tt>s originating from * them. * * @param ev a <tt>ServiceEvent</tt> value which described a change in an * OSGi service and which is to be examined for the registering or * unregistering of a <tt>ProtocolProviderService</tt> and thus a * <tt>OperationSetBasicTelephony</tt> */ private void serviceChanged(ServiceEvent ev) { Object service = bundleContext.getService(ev.getServiceReference()); if (service instanceof ProtocolProviderService) { if(logger.isTraceEnabled()) { logger.trace("Protocol provider service changed."); } OperationSetBasicTelephony<?> telephony = ((ProtocolProviderService) service).getOperationSet( OperationSetBasicTelephony.class); if (telephony != null) { switch (ev.getType()) { case ServiceEvent.REGISTERED: addOperationSetBasicTelephonyListener(telephony); break; case ServiceEvent.UNREGISTERING: removeOperationSetBasicTelephonyListener(telephony); break; } } else { if(logger.isTraceEnabled()) { logger.trace("The protocol provider service doesn't support " + "telephony."); } } } } /** * Implements the policy to have the presence statuses of online accounts * (i.e. registered <tt>ProtocolProviderService</tt>s) set to * "On the phone" when at least one <tt>Call</tt> is in progress. * * @author Lyubomir Marinov */ private class OnThePhoneStatusPolicy { /** * The regular expression which removes whitespace from the * <tt>statusName</tt> property value of <tt>PresenceStatus</tt> * instances in order to recognize the <tt>PresenceStatus</tt> which * represents "On the phone". */ private final Pattern presenceStatusNameWhitespace = Pattern.compile("\\p{Space}"); /** * The <tt>PresenceStatus</tt>es of <tt>ProtocolProviderService</tt>s * before they were changed to "On the phone" remembered so * that they can be restored after the last <tt>Call</tt> in progress * ends. */ private final Map<ProtocolProviderService,PresenceStatus> presenceStatuses = Collections.synchronizedMap( new WeakHashMap<ProtocolProviderService,PresenceStatus>()); /** * Notifies this instance that the <tt>callState</tt> of a specific * <tt>Call</tt> has changed. * * @param ev a <tt>CallChangeEvent</tt> which represents the details of * the notification such as the affected <tt>Call</tt> and its old and * new <tt>CallState</tt>s */ public void callStateChanged(CallChangeEvent ev) { if(logger.isTraceEnabled()) { logger.trace("Call state changed.[2]"); } Call call = ev.getSourceCall(); Object oldCallState = ev.getOldValue(); Object newCallState = call.getCallState(); if ((CallState.CALL_INITIALIZATION.equals(oldCallState) && CallState.CALL_IN_PROGRESS.equals(newCallState)) || (CallState.CALL_IN_PROGRESS.equals(oldCallState) && CallState.CALL_ENDED.equals(newCallState))) { run(); } else { if(logger.isTraceEnabled()) { logger.trace("Not applicable call state."); } } } /** * Finds the first <tt>PresenceStatus</tt> among the set of * <tt>PresenceStatus</tt>es supported by a specific * <tt>OperationSetPresence</tt> which represents * "On the phone". * * @param presence the <tt>OperationSetPresence</tt> which represents * the set of supported <tt>PresenceStatus</tt>es * @return the first <tt>PresenceStatus</tt> among the set of * <tt>PresenceStatus</tt>es supported by <tt>presence</tt> which * represents "On the phone" if such a <tt>PresenceStatus</tt> * was found; otherwise, <tt>null</tt> */ private PresenceStatus findOnThePhonePresenceStatus( OperationSetPresence presence) { for (Iterator<PresenceStatus> i = presence.getSupportedStatusSet(); i.hasNext();) { PresenceStatus presenceStatus = i.next(); if (presenceStatusNameWhitespace .matcher(presenceStatus.getStatusName()) .replaceAll("") .equalsIgnoreCase("OnThePhone")) { return presenceStatus; } } return null; } private PresenceStatus forgetPresenceStatus(ProtocolProviderService pps) { return presenceStatuses.remove(pps); } private void forgetPresenceStatuses() { presenceStatuses.clear(); } /** * Notifies this instance that a new outgoing <tt>Call</tt> was * initiated, an incoming <tt>Call</tt> was received or an existing * <tt>Call</tt> ended. * * @param type one of {@link CallEvent#CALL_ENDED}, * {@link CallEvent#CALL_INITIATED} and {@link CallEvent#CALL_RECEIVED} * which describes the type of the event to be handled * @param ev a <tt>CallEvent</tt> value which describes the change and * the <tt>Call</tt> associated with it */ public void handleCallEvent(int type, CallEvent ev) { run(); } /** * Determines whether there is at least one existing <tt>Call</tt> which * is currently in progress i.e. determines whether the local user is * currently on the phone. * * @return <tt>true</tt> if there is at least one existing <tt>Call</tt> * which is currently in progress i.e. if the local user is currently on * the phone; otherwise, <tt>false</tt> */ private boolean isOnThePhone() { synchronized (calls) { for (Call call : calls) { if (CallState.CALL_IN_PROGRESS.equals(call.getCallState())) return true; } } return false; } /** * Invokes * {@link OperationSetPresence#publishPresenceStatus(PresenceStatus, * String)} on a specific <tt>OperationSetPresence</tt> with a specific * <tt>PresenceStatus</tt> and catches any exceptions. * * @param presence the <tt>OperationSetPresence</tt> on which the method * is to be invoked * @param presenceStatus the <tt>PresenceStatus</tt> to provide as the * respective method argument value */ private void publishPresenceStatus( OperationSetPresence presence, PresenceStatus presenceStatus) { try { presence.publishPresenceStatus(presenceStatus, null); } catch (Throwable t) { if (t instanceof InterruptedException) Thread.currentThread().interrupt(); else if (t instanceof ThreadDeath) throw (ThreadDeath) t; } } private PresenceStatus rememberPresenceStatus( ProtocolProviderService pps, PresenceStatus presenceStatus) { return presenceStatuses.put(pps, presenceStatus); } /** * Finds the first <tt>PresenceStatus</tt> among the set of * <tt>PresenceStatus</tt>es supported by a specific * <tt>OperationSetPresence</tt> which represents * "In meeting". * * @param presence the <tt>OperationSetPresence</tt> which represents * the set of supported <tt>PresenceStatus</tt>es * @return the first <tt>PresenceStatus</tt> among the set of * <tt>PresenceStatus</tt>es supported by <tt>presence</tt> which * represents "In meeting" if such a <tt>PresenceStatus</tt> * was found; otherwise, <tt>null</tt> */ private PresenceStatus findInMeetingPresenceStatus( OperationSetPresence presence) { for (Iterator<PresenceStatus> i = presence.getSupportedStatusSet(); i.hasNext();) { PresenceStatus presenceStatus = i.next(); if (presenceStatusNameWhitespace .matcher(presenceStatus.getStatusName()) .replaceAll("") .equalsIgnoreCase("InAMeeting")) { return presenceStatus; } } return null; } /** * Applies this policy to the current state of the application. */ private void run() { if(logger.isTraceEnabled()) { logger.trace("On the phone status policy run."); } if (!ProtocolProviderActivator.getConfigurationService().getBoolean( PNAME_ON_THE_PHONE_STATUS_ENABLED, false)) { if(logger.isTraceEnabled()) { logger.trace("On the phone status is not enabled."); } forgetPresenceStatuses(); return; } ServiceReference[] ppsRefs; try { ppsRefs = bundleContext.getServiceReferences( ProtocolProviderService.class.getName(), null); } catch (InvalidSyntaxException ise) { if(logger.isTraceEnabled()) { logger.trace("Can't access protocol providers refences."); } ppsRefs = null; } if ((ppsRefs == null) || (ppsRefs.length == 0)) { forgetPresenceStatuses(); } else { boolean isOnThePhone = isOnThePhone(); CalendarService calendar = ProtocolProviderActivator.getCalendarService(); if(!isOnThePhone && calendar != null && calendar.onThePhoneStatusChanged(presenceStatuses)) { if(logger.isTraceEnabled()) { logger.trace("We are not on the phone."); } forgetPresenceStatuses(); return; } for (ServiceReference ppsRef : ppsRefs) { ProtocolProviderService pps = (ProtocolProviderService) bundleContext.getService(ppsRef); if (pps == null) { if(logger.isTraceEnabled()) { logger.trace("Provider is null."); } continue; } OperationSetPresence presence = pps.getOperationSet(OperationSetPresence.class); if (presence == null) { if(logger.isTraceEnabled()) { logger.trace("Presence is null."); } /* * "On the phone" is a PresenceStatus so it is available * only to accounts which support presence in the first * place. */ forgetPresenceStatus(pps); } else if (pps.isRegistered()) { if(logger.isTraceEnabled()) { logger.trace("Provider is registered."); } PresenceStatus onThePhonePresenceStatus = findOnThePhonePresenceStatus(presence); if (onThePhonePresenceStatus == null) { if(logger.isTraceEnabled()) { logger.trace("Can't find on the phone status."); } /* * If do not know how to define "On the phone" for * an OperationSetPresence, then we'd better not * mess with it in the first place. */ forgetPresenceStatus(pps); } else if (isOnThePhone) { if(logger.isTraceEnabled()) { logger.trace( "Setting the status to on the phone."); } PresenceStatus presenceStatus = presence.getPresenceStatus(); if (presenceStatus == null) { if(logger.isTraceEnabled()) { logger.trace("Presence status is null."); } /* * It is strange that an OperationSetPresence * does not have a PresenceStatus so it may be * safer to not mess with it. */ forgetPresenceStatus(pps); } else if (!onThePhonePresenceStatus.equals( presenceStatus)) { if(logger.isTraceEnabled()) { logger.trace( "On the phone status is published."); } publishPresenceStatus( presence, onThePhonePresenceStatus); if(presenceStatus.equals( findInMeetingPresenceStatus(presence)) && calendar != null) { Map<ProtocolProviderService,PresenceStatus> statuses = calendar.getRememberedStatuses(); for(ProtocolProviderService provider : statuses.keySet()) rememberPresenceStatus(provider, statuses.get(provider)); } else if (onThePhonePresenceStatus.equals( presence.getPresenceStatus())) { rememberPresenceStatus(pps, presenceStatus); } else { forgetPresenceStatus(pps); } } else { if(logger.isTraceEnabled()) { logger.trace( "Currently the status is on the phone."); } } } else { if(logger.isTraceEnabled()) { logger.trace("Unset on the phone status."); } PresenceStatus presenceStatus = forgetPresenceStatus(pps); if ((presenceStatus != null) && onThePhonePresenceStatus.equals( presence.getPresenceStatus())) { if(logger.isTraceEnabled()) { logger.trace("Unset on the phone status.[2]"); } publishPresenceStatus(presence, presenceStatus); } } } else { if(logger.isTraceEnabled()) { logger.trace("Protocol provider is not registered"); } /* * Offline accounts do not get their PresenceStatus * modified for the purposes of "On the phone". */ forgetPresenceStatus(pps); } } } } } /** * Implements the listeners interfaces used by this policy. * * @author Lyubomir Marinov */ private class SingleCallInProgressPolicyListener implements CallChangeListener, CallListener, ServiceListener { /** * Stops tracking the state of a specific <tt>Call</tt> and no longer * tries to put it on hold when it ends. * * @see CallListener#callEnded(CallEvent) */ public void callEnded(CallEvent ev) { SingleCallInProgressPolicy.this.handleCallEvent( CallEvent.CALL_ENDED, ev); } /** * Does nothing because adding <tt>CallPeer<tt>s to <tt>Call</tt>s isn't * related to the policy to put existing calls on hold when a new call * becomes in-progress and just implements <tt>CallChangeListener</tt>. * * @see CallChangeListener#callPeerAdded(CallPeerEvent) */ public void callPeerAdded(CallPeerEvent ev) { /* * Not of interest, just implementing CallChangeListener in which * only #callStateChanged(CallChangeEvent) is of interest. */ } /** * Does nothing because removing <tt>CallPeer<tt>s to <tt>Call</tt>s * isn't related to the policy to put existing calls on hold when a new * call becomes in-progress and just implements * <tt>CallChangeListener</tt>. * * @see CallChangeListener#callPeerRemoved(CallPeerEvent) */ public void callPeerRemoved(CallPeerEvent ev) { /* * Not of interest, just implementing CallChangeListener in which * only #callStateChanged(CallChangeEvent) is of interest. */ } /** * Upon a <tt>Call</tt> changing its state to * <tt>CallState.CALL_IN_PROGRESS</tt>, puts the other existing * <tt>Call</tt>s on hold. * * @param ev the <tt>CallChangeEvent</tt> that we are to deliver. * * @see CallChangeListener#callStateChanged(CallChangeEvent) */ public void callStateChanged(CallChangeEvent ev) { // we are interested only in CALL_STATE_CHANGEs if (ev.getEventType().equals(CallChangeEvent.CALL_STATE_CHANGE)) SingleCallInProgressPolicy.this.callStateChanged(ev); } /** * Remembers an incoming <tt>Call</tt> so that it can put the other * existing <tt>Call</tt>s on hold when it changes its state to * <tt>CallState.CALL_IN_PROGRESS</tt>. * * @see CallListener#incomingCallReceived(CallEvent) */ public void incomingCallReceived(CallEvent ev) { SingleCallInProgressPolicy.this.incomingCallReceived(ev); } /** * Remembers an outgoing <tt>Call</tt> so that it can put the other * existing <tt>Call</tt>s on hold when it changes its state to * <tt>CallState.CALL_IN_PROGRESS</tt>. * * @see CallListener#outgoingCallCreated(CallEvent) */ public void outgoingCallCreated(CallEvent ev) { SingleCallInProgressPolicy.this.handleCallEvent( CallEvent.CALL_INITIATED, ev); } /** * Starts/stops tracking the new <tt>Call</tt>s originating from a * specific <tt>ProtocolProviderService</tt> when it * registers/unregisters in order to take them into account when putting * existing calls on hold upon a new call entering its in-progress * state. * * @param ev the <tt>ServiceEvent</tt> event describing a change in the * state of a service registration which may be a * <tt>ProtocolProviderService</tt> supporting * <tt>OperationSetBasicTelephony</tt> and thus being able to create new * <tt>Call</tt>s */ public void serviceChanged(ServiceEvent ev) { if(logger.isTraceEnabled()) { logger.trace("Service changed."); } SingleCallInProgressPolicy.this.serviceChanged(ev); } } }