/* * 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 java.lang.ref.*; import java.util.*; import net.java.sip.communicator.service.protocol.event.*; /** * Represents a default/base implementation of * OperationSetDesktopSharingClient which attempts to make it easier * for implementers to provide complete solutions while focusing on * implementation-specific functionality. * * @param * * @author Sebastien Vincent * @author Lyubomir Marinov */ public abstract class AbstractOperationSetDesktopSharingClient implements OperationSetDesktopSharingClient { /** * The CallPeerListener which listens to modifications in the * properties/state of CallPeer. */ private final CallPeerListener callPeerListener = new CallPeerAdapter() { /** * Indicates that a change has occurred in the status of the source * CallPeer. * * @param evt the CallPeerChangeEvent instance containing the * source event as well as its previous and its new status */ @Override public void peerStateChanged(CallPeerChangeEvent evt) { CallPeer peer = evt.getSourceCallPeer(); CallPeerState state = peer.getState(); if(state != null && (state.equals(CallPeerState.DISCONNECTED) || state.equals(CallPeerState.FAILED))) { removesNullAndRevokedControlPeer(peer.getPeerID()); removeRemoteControlListener(getListener(peer)); } } }; /** * List of the granted remote control peers for this client. Used to * remember granted remote control peers, when the granted event is fired * before the corresponding UI listener registration. */ private Vector grantedRemoteControlPeers = new Vector(); /** * The list of RemoteControlListeners to be notified when a change * in remote control access occurs. */ private final List> listeners = new ArrayList>(); /** * The ProtocolProviderService implementation which created this * instance and for which telephony conferencing services are being provided * by this instance. */ protected final T parentProvider; /** * Initializes a new AbstractOperationSetDesktopSharing instance * which is to be provided by a specific ProtocolProviderService. * * @param parentProvider the ProtocolProviderService implementation * which is creating the new instance and for which telephony conferencing * services are being provided by this instance */ protected AbstractOperationSetDesktopSharingClient(T parentProvider) { this.parentProvider = parentProvider; } /** * Adds a RemoteControlListener to be notified when the remote peer * accepts to give us full control of their desktop. *

* The default implementation of * AbstractOperationSetDesktopSharingClient adds a * WeakReference to the specified RemoteControlListener in * order to avoid memory leaks because of code which calls * addRemoteControlListener and never calls * removeRemoteControlListener. *

* * @param listener the RemoteControlListener to add */ public void addRemoteControlListener(RemoteControlListener listener) { synchronized (listeners) { Iterator> i = listeners.iterator(); boolean contains = false; while (i.hasNext()) { RemoteControlListener l = i.next().get(); if (l == null) i.remove(); else if (l.equals(listener)) contains = true; } if (!contains) { listeners.add( new WeakReference(listener)); listener.getCallPeer().addCallPeerListener(callPeerListener); } } // Notifies the new listener if the corresponding peer has already been // granted to remotely control the shared desktop. CallPeer peer = listener.getCallPeer(); // Removes the null peers from the granted remote control peer list. // If the corresponding peer was in the granted list, then this peer has // already been granted and we must call the remoteControlGranted // function for this listener. if(this.removesNullAndRevokedControlPeer(peer.getPeerID()) != -1) listener.remoteControlGranted(new RemoteControlGrantedEvent(peer)); } /** * Fires a RemoteControlGrantedEvent to all registered listeners. * * @param peer the CallPeer */ public void fireRemoteControlGranted(CallPeer peer) { RemoteControlListener listener = getListener(peer); if(listener != null) { listener.remoteControlGranted(new RemoteControlGrantedEvent(peer)); } // The UI has not created the listener yet, then we need to store the // information taht this peer has alreayd been granted. else { // Removes all previous instance of this peer. this.removesNullAndRevokedControlPeer(peer.getPeerID()); // Adds the peer to the granted remote control peer list. synchronized(this.grantedRemoteControlPeers) { this.grantedRemoteControlPeers.add(peer); } } } /** * Fires a RemoteControlGrantedEvent to all registered listeners. * * @param peer the CallPeer */ public void fireRemoteControlRevoked(CallPeer peer) { RemoteControlListener listener = getListener(peer); if(listener != null) { listener.remoteControlRevoked(new RemoteControlRevokedEvent(peer)); } // Removes the peer from the granted remote control peer list. this.removesNullAndRevokedControlPeer(peer.getPeerID()); } /** * Gets a list of RemoteControlListeners to be notified of remote * control access changes. * * @return a list of RemoteControlListeners to be notifed of remote * control access changes */ protected List getListeners() { List listeners; synchronized (this.listeners) { Iterator> i = this.listeners.iterator(); listeners = new ArrayList(this.listeners.size()); while (i.hasNext()) { RemoteControlListener l = i.next().get(); if (l == null) i.remove(); else listeners.add(l); } } return listeners; } /** * Removes a RemoteControlListener to be notified when remote peer * accept/revoke to give us full control. * * @param listener RemoteControlListener to remove */ public void removeRemoteControlListener(RemoteControlListener listener) { synchronized (listeners) { Iterator> i = listeners.iterator(); while (i.hasNext()) { RemoteControlListener l = i.next().get(); if ((l == null) || l.equals(listener)) i.remove(); } } } /** * Removes null and the peer corresponding to the revokedPeerID from the * granted control peer list. * * @param revokedPeerID The ID of the revoked peer. May be null to only * clear null instances from the granted control peer list. * * @return The index corresponding to the revokedPeerID entry. -1 if the * revoked PeerID is null, or if the revokedPeerID is not found and removed. */ private int removesNullAndRevokedControlPeer(String revokedPeerID) { int index = -1; synchronized(this.grantedRemoteControlPeers) { CallPeer peer; for(int i = 0; i < this.grantedRemoteControlPeers.size(); ++i) { peer = this.grantedRemoteControlPeers.get(i); if(peer == null || peer.getPeerID().equals(revokedPeerID)) { this.grantedRemoteControlPeers.remove(i); index = i; --i; } } } return index; } /** * Returns the RemoteControlListener corresponding to the given * callPeer, if it exists. * * @param callPeer the CallPeer to get the corresponding * RemoteControlListener of * @return the RemoteControlListener corresponding to the given * callPeer, if it exists; null, otherwise */ protected RemoteControlListener getListener(CallPeer callPeer) { String peerID = callPeer.getPeerID(); synchronized (listeners) { Iterator> i = listeners.iterator(); while (i.hasNext()) { RemoteControlListener l = i.next().get(); if (l == null) i.remove(); else if (peerID.equals(l.getCallPeer().getPeerID())) return l; } } return null; } }