/* * 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 * <tt>OperationSetDesktopSharingClient</tt> which attempts to make it easier * for implementers to provide complete solutions while focusing on * implementation-specific functionality. * * @param <T> * * @author Sebastien Vincent * @author Lyubomir Marinov */ public abstract class AbstractOperationSetDesktopSharingClient <T extends ProtocolProviderService> implements OperationSetDesktopSharingClient { /** * The <tt>CallPeerListener</tt> which listens to modifications in the * properties/state of <tt>CallPeer</tt>. */ private final CallPeerListener callPeerListener = new CallPeerAdapter() { /** * Indicates that a change has occurred in the status of the source * <tt>CallPeer</tt>. * * @param evt the <tt>CallPeerChangeEvent</tt> 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<CallPeer> grantedRemoteControlPeers = new Vector<CallPeer>(); /** * The list of <tt>RemoteControlListener</tt>s to be notified when a change * in remote control access occurs. */ private final List<WeakReference<RemoteControlListener>> listeners = new ArrayList<WeakReference<RemoteControlListener>>(); /** * The <tt>ProtocolProviderService</tt> implementation which created this * instance and for which telephony conferencing services are being provided * by this instance. */ protected final T parentProvider; /** * Initializes a new <tt>AbstractOperationSetDesktopSharing</tt> instance * which is to be provided by a specific <tt>ProtocolProviderService. * * @param parentProvider the <tt>ProtocolProviderService</tt> 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 <tt>RemoteControlListener</tt> to be notified when the remote peer * accepts to give us full control of their desktop. * <p> * The default implementation of * <tt>AbstractOperationSetDesktopSharingClient</tt> adds a * <tt>WeakReference</tt> to the specified <tt>RemoteControlListener</tt> in * order to avoid memory leaks because of code which calls * <tt>addRemoteControlListener</tt> and never calls * <tt>removeRemoteControlListener</tt>. * </p> * * @param listener the <tt>RemoteControlListener</tt> to add */ public void addRemoteControlListener(RemoteControlListener listener) { synchronized (listeners) { Iterator<WeakReference<RemoteControlListener>> 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<RemoteControlListener>(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 <tt>RemoteControlGrantedEvent</tt> to all registered listeners. * * @param peer the <tt>CallPeer</tt> */ 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 <tt>RemoteControlGrantedEvent</tt> to all registered listeners. * * @param peer the <tt>CallPeer</tt> */ 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 <tt>RemoteControlListener</tt>s to be notified of remote * control access changes. * * @return a list of <tt>RemoteControlListener</tt>s to be notifed of remote * control access changes */ protected List<RemoteControlListener> getListeners() { List<RemoteControlListener> listeners; synchronized (this.listeners) { Iterator<WeakReference<RemoteControlListener>> i = this.listeners.iterator(); listeners = new ArrayList<RemoteControlListener>(this.listeners.size()); while (i.hasNext()) { RemoteControlListener l = i.next().get(); if (l == null) i.remove(); else listeners.add(l); } } return listeners; } /** * Removes a <tt>RemoteControlListener</tt> to be notified when remote peer * accept/revoke to give us full control. * * @param listener <tt>RemoteControlListener</tt> to remove */ public void removeRemoteControlListener(RemoteControlListener listener) { synchronized (listeners) { Iterator<WeakReference<RemoteControlListener>> 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 <tt>RemoteControlListener</tt> corresponding to the given * <tt>callPeer</tt>, if it exists. * * @param callPeer the <tt>CallPeer</tt> to get the corresponding * <tt>RemoteControlListener</tt> of * @return the <tt>RemoteControlListener</tt> corresponding to the given * <tt>callPeer</tt>, if it exists; <tt>null</tt>, otherwise */ protected RemoteControlListener getListener(CallPeer callPeer) { String peerID = callPeer.getPeerID(); synchronized (listeners) { Iterator<WeakReference<RemoteControlListener>> 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; } }