From 6a67589c86221faedcebd370824274013be64026 Mon Sep 17 00:00:00 2001 From: Sebastien Vincent Date: Tue, 28 Sep 2010 07:46:45 +0000 Subject: Desktop sharing support for SIP and XMPP. Note that GUI is not ready yet to propose this feature to the users. --- .../impl/protocol/jabber/CallJabberImpl.java | 35 +- .../jabber/CallPeerMediaHandlerJabberImpl.java | 52 +++ .../impl/protocol/jabber/JabberActivator.java | 28 +- ...OperationSetDesktopSharingClientJabberImpl.java | 187 ++++++++ ...OperationSetDesktopSharingServerJabberImpl.java | 473 +++++++++++++++++++++ .../OperationSetVideoTelephonyJabberImpl.java | 4 +- .../jabber/ProtocolProviderServiceJabberImpl.java | 18 + .../jabber/extensions/inputevt/InputEvtAction.java | 70 +++ .../jabber/extensions/inputevt/InputEvtIQ.java | 142 +++++++ .../extensions/inputevt/InputEvtIQProvider.java | 85 ++++ .../inputevt/RemoteControlExtension.java | 194 +++++++++ .../inputevt/RemoteControlExtensionProvider.java | 402 +++++++++++++++++ .../extensions/jingle/InputEvtPacketExtension.java | 37 ++ .../jabber/extensions/jingle/JingleIQProvider.java | 9 +- .../protocol/jabber/jabber.provider.manifest.mf | 1 + 15 files changed, 1732 insertions(+), 5 deletions(-) create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingClientJabberImpl.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingServerJabberImpl.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtAction.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQ.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQProvider.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtension.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtensionProvider.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/InputEvtPacketExtension.java (limited to 'src/net/java/sip/communicator/impl/protocol/jabber') diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java index 9088e91..eaf84af 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java @@ -36,6 +36,12 @@ public class CallJabberImpl extends MediaAwareCall< private final OperationSetBasicTelephonyJabberImpl parentOpSet; /** + * Indicates if the CallPeer will support inputevt + * extension (i.e. will be able to be remote-controlled). + */ + private boolean localInputEvtAware = false; + + /** * Crates a CallJabberImpl instance belonging to sourceProvider and * associated with the jingle session with the specified jingleSID. * If this call corresponds to an incoming jingle session then the jingleSID @@ -57,6 +63,26 @@ public class CallJabberImpl extends MediaAwareCall< } /** + * Enable or disable inputevt support (remote control). + * + * @param enable new state of inputevt support + */ + public void setLocalInputEvtAware(boolean enable) + { + localInputEvtAware = enable; + } + + /** + * Returns if the call support inputevt (remote control). + * + * @return true if the call support inputevt, false otherwise + */ + public boolean getLocalInputEvtAware() + { + return localInputEvtAware; + } + + /** * Creates a new call peer and sends a RINGING response. * * @param jingleIQ the {@link JingleIQ} that created the session. @@ -132,6 +158,8 @@ public class CallJabberImpl extends MediaAwareCall< /* enable video if it is a videocall */ callPeer.getMediaHandler().setLocalVideoTransmissionEnabled( localVideoAllowed); + /* enable remote-control if it is a desktop sharing session */ + callPeer.getMediaHandler().setLocalInputEvtAware(localInputEvtAware); //set call state to connecting so that the user interface would start //playing the tones. we do that here because we may be harvesting @@ -151,8 +179,13 @@ public class CallJabberImpl extends MediaAwareCall< * @throws OperationFailedException if problem occurred during message * generation or network problem */ - public void modifyVideoContent(boolean allowed) throws OperationFailedException + public void modifyVideoContent(boolean allowed) + throws OperationFailedException { + if(logger.isInfoEnabled()) + logger.info(allowed ? "Start local video streaming" : + "Stop local video streaming"); + for(CallPeerJabberImpl peer : getCallPeersVector()) { peer.sendModifyVideoContent(allowed); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java index e03c4ba..1ab2ae3 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java @@ -61,6 +61,12 @@ public class CallPeerMediaHandlerJabberImpl private boolean remotelyOnHold = false; /** + * Indicates if the CallPeer will support inputevt + * extension (i.e. will be able to be remote-controlled). + */ + private boolean localInputEvtAware = false; + + /** * Creates a new handler that will be managing media streams for * peer. * @@ -97,6 +103,16 @@ public class CallPeerMediaHandlerJabberImpl } /** + * Enable or disable inputevt support (remote-control). + * + * @param enable new state of inputevt support + */ + public void setLocalInputEvtAware(boolean enable) + { + localInputEvtAware = enable; + } + + /** * Get the remote content of a specific content type (like audio or video). * * @param contentType content type name @@ -300,6 +316,14 @@ public class CallPeerMediaHandlerJabberImpl } } + // got an content which have inputevt, it means that peer requests + // a desktop sharing session so tell it we support inputevt + if(content.getChildExtensionsOfType( + InputEvtPacketExtension.class) != null) + { + ourContent.addChildExtension(new InputEvtPacketExtension()); + } + answerContentList.add(ourContent); localContentMap.put(content.getName(), ourContent); @@ -395,6 +419,22 @@ public class CallPeerMediaHandlerJabberImpl // create the corresponding stream... initStream(ourContent.getName(), connector, dev, format, target, direction, rtpExtensions); + + // if remote peer requires inputevt, notify UI to capture mouse + // and keyboard events + if(ourContent.getChildExtensionsOfType( + InputEvtPacketExtension.class) != null) + { + OperationSetDesktopSharingClientJabberImpl client = + (OperationSetDesktopSharingClientJabberImpl) + this.getPeer().getProtocolProvider().getOperationSet( + OperationSetDesktopSharingClient.class); + + if(client != null) + { + client.fireRemoteControlGranted(); + } + } } return sessAccept; } @@ -562,6 +602,18 @@ public class CallPeerMediaHandlerJabberImpl } } + /* we request a desktop sharing session so add the inputevt + * extension in the "video" content + */ + RtpDescriptionPacketExtension description + = JingleUtils.getRtpDescription(content); + if(description.getMedia().equals( + MediaType.VIDEO.toString()) && localInputEvtAware) + { + content.addChildExtension( + new InputEvtPacketExtension()); + } + mediaDescs.add(content); } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java b/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java index 7e7f723..94181b6 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java @@ -10,6 +10,7 @@ import java.util.*; import net.java.sip.communicator.service.configuration.*; import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.hid.*; import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.netaddr.*; import net.java.sip.communicator.service.protocol.*; @@ -58,7 +59,8 @@ public class JabberActivator /** * The Jabber protocol provider factory. */ - private static ProtocolProviderFactoryJabberImpl jabberProviderFactory = null; + private static ProtocolProviderFactoryJabberImpl + jabberProviderFactory = null; /** * The UriHandler implementation that we use to handle "xmpp:" URIs @@ -77,6 +79,11 @@ public class JabberActivator private static ResourceManagementService resourcesService = null; /** + * A reference to the currently valid HIDService instance. + */ + private static HIDService hidService = null; + + /** * Called when this bundle is started so the Framework can perform the * bundle-specific activities necessary to start this bundle. * @@ -274,4 +281,23 @@ public class JabberActivator } return networkAddressManagerService; } + + /** + * Returns a reference to HIDService implementation currently + * registered in the bundle context or null if no such implementation was + * found + * + * @return a currently valid implementation of the HIDService + */ + public static HIDService getHIDService() + { + if(hidService == null) + { + ServiceReference hidReference = + bundleContext.getServiceReference( + HIDService.class.getName()); + hidService = (HIDService)bundleContext.getService(hidReference); + } + return hidService; + } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingClientJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingClientJabberImpl.java new file mode 100644 index 0000000..190ddc7 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingClientJabberImpl.java @@ -0,0 +1,187 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.List; // disambiguation + +import org.jivesoftware.smack.packet.*; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; + +/** + * Implements all desktop sharing client-side related functions for Jabber + * protocol. + * + * @author Sebastien Vincent + */ +public class OperationSetDesktopSharingClientJabberImpl + implements OperationSetDesktopSharingClient +{ + /** + * Our class logger. + */ + private static final Logger logger = + Logger.getLogger(OperationSetDesktopSharingClientJabberImpl.class); + + /** + * The Jabber ProtocolProviderService implementation which created + * this instance and for which telephony conferencing services are being + * provided by this instance. + */ + private final ProtocolProviderServiceJabberImpl parentProvider; + + /** + * List of listeners to be notified when a change occurred in remote control + * access. + */ + private List listeners = + new ArrayList(); + + /** + * Initializes a new OperationSetDesktopSharingClientJabberImpl. + * + * @param parentProvider the Jabber ProtocolProviderService + * implementation which has requested the creation of the new instance and + * for which the new instance is to provide desktop sharing. + */ + public OperationSetDesktopSharingClientJabberImpl( + ProtocolProviderServiceJabberImpl parentProvider) + { + this.parentProvider = parentProvider; + } + + /** + * Fire a RemoteControlGrantedEvent to all registered listeners. + */ + public void fireRemoteControlGranted() + { + RemoteControlGrantedEvent event = new RemoteControlGrantedEvent(this); + + for (RemoteControlListener l : listeners) + { + l.remoteControlGranted(event); + } + } + + /** + * Fire a RemoteControlGrantedEvent to all registered listeners. + */ + public void fireRemoteControlRevoked() + { + RemoteControlRevokedEvent event = new RemoteControlRevokedEvent(this); + + for (RemoteControlListener l : listeners) + { + l.remoteControlRevoked(event); + } + } + + /** + * Add a RemoteControlListener to be notified when remote peer + * accept to give us full control. + * + * @param listener RemoteControlListener to add + */ + public void addRemoteControlListener(RemoteControlListener listener) + { + if (logger.isInfoEnabled()) + logger.info("Enable remote control"); + + if (!listeners.contains(listener)) + { + listeners.add(listener); + } + } + + /** + * Remove 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) + { + if (logger.isInfoEnabled()) + logger.info("Disable remote control"); + + if (listeners.contains(listener)) + { + listeners.remove(listener); + } + } + + /** + * Send a keyboard notification. + * + * @param callPeer CallPeer that will be notified + * @param event KeyEvent received and that will be send to remote + * peer + */ + public void sendKeyboardEvent(CallPeer callPeer, KeyEvent event) + { + RemoteControlExtension payload = new RemoteControlExtension(event); + InputEvtIQ inputIQ = new InputEvtIQ(); + + inputIQ.setAction(InputEvtAction.NOTIFY); + inputIQ.setType(IQ.Type.SET); + inputIQ.setFrom(parentProvider.getOurJID()); + inputIQ.setTo(callPeer.getAddress()); + inputIQ.addRemoteControl(payload); + parentProvider.getConnection().sendPacket(inputIQ); + } + + /** + * Send a mouse notification. + * + * @param callPeer CallPeer that will be notified + * @param event MouseEvent received and that will be send to remote + * peer + */ + public void sendMouseEvent(CallPeer callPeer, MouseEvent event) + { + RemoteControlExtension payload = new RemoteControlExtension(event); + InputEvtIQ inputIQ = new InputEvtIQ(); + + inputIQ.setAction(InputEvtAction.NOTIFY); + inputIQ.setType(IQ.Type.SET); + inputIQ.setFrom(parentProvider.getOurJID()); + inputIQ.setTo(callPeer.getAddress()); + inputIQ.addRemoteControl(payload); + parentProvider.getConnection().sendPacket(inputIQ); + } + + /** + * Send a mouse notification for specific "moved" MouseEvent. As + * controller computer could have smaller desktop that controlled ones, we + * should take care to send the percentage of point x and point y. + * + * @param callPeer CallPeer that will be notified + * @param event MouseEvent received and that will be send to remote + * peer + * @param videoPanelSize size of the panel that contains video + */ + public void sendMouseEvent(CallPeer callPeer, MouseEvent event, + Dimension videoPanelSize) + { + RemoteControlExtension payload = new RemoteControlExtension(event, + videoPanelSize); + InputEvtIQ inputIQ = new InputEvtIQ(); + + inputIQ.setAction(InputEvtAction.NOTIFY); + inputIQ.setType(IQ.Type.SET); + inputIQ.setFrom(parentProvider.getOurJID()); + inputIQ.setTo(callPeer.getAddress()); + inputIQ.addRemoteControl(payload); + parentProvider.getConnection().sendPacket(inputIQ); + } +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingServerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingServerJabberImpl.java new file mode 100644 index 0000000..093c195 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingServerJabberImpl.java @@ -0,0 +1,473 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber; + +import java.util.*; +import java.util.List; // disambiguation + +import java.awt.*; +import java.awt.event.*; + +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.filter.*; +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smackx.packet.*; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt.*; +import net.java.sip.communicator.service.hid.*; +import net.java.sip.communicator.service.neomedia.*; +import net.java.sip.communicator.service.neomedia.format.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; + +/** + * Implements all desktop sharing server-side related functions for Jabber + * protocol. + * + * @author Sebastien Vincent + */ +public class OperationSetDesktopSharingServerJabberImpl + extends OperationSetDesktopStreamingJabberImpl + implements OperationSetDesktopSharingServer, + RegistrationStateChangeListener, + PacketListener, + PacketFilter +{ + /** + * Our class logger. + */ + private static final Logger logger = Logger + .getLogger(OperationSetDesktopSharingServerJabberImpl.class); + + /** + * 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 (remoteControlEnabled && state != null && + (state.equals(CallPeerState.DISCONNECTED) || + state.equals(CallPeerState.FAILED))) + { + disableRemoteControl(evt.getSourceCallPeer()); + } + else if(state != null && state.equals(CallPeerState.CONNECTED)) + { + /* connected peer */ + enableRemoteControl(evt.getSourceCallPeer()); + } + } + }; + + /** + * If the remote control is authorized and thus enabled. + */ + private boolean remoteControlEnabled = false; + + /** + * HID service that will regenerates keyboard and mouse events received in + * Jabber messages. + */ + private HIDService hidService = null; + + /** + * List of callPeers for the desktop sharing session. + */ + private List callPeers = new ArrayList(); + + /** + * Video panel size. + */ + private Dimension size = null; + + /** + * Initializes a new OperationSetDesktopSharingJabberImpl instance + * which builds upon the telephony-related functionality of a specific + * OperationSetBasicTelephonyJabberImpl. + * + * @param basicTelephony the OperationSetBasicTelephonyJabberImpl + * the new extension should build upon + */ + public OperationSetDesktopSharingServerJabberImpl( + OperationSetBasicTelephonyJabberImpl basicTelephony) + { + super(basicTelephony); + + parentProvider.addRegistrationStateChangeListener(this); + hidService = JabberActivator.getHIDService(); + } + + /** + * Create a new video call and invite the specified CallPeer to it. + * + * @param uri the address of the callee that we should invite to a new + * call. + * @return CallPeer the CallPeer that will represented by the + * specified uri. All following state change events will be delivered + * through that call peer. The Call that this peer is a member + * of could be retrieved from the CallParticipatn instance with the use + * of the corresponding method. + * @throws OperationFailedException with the corresponding code if we fail + * to create the video call. + */ + @Override + public Call createVideoCall(String uri) + throws OperationFailedException + { + CallJabberImpl call = (CallJabberImpl)super.createVideoCall(uri); + CallPeerJabberImpl callPeer = call.getCallPeers().next(); + callPeer.addCallPeerListener(callPeerListener); + + size = (((VideoMediaFormat)call.getDefaultDevice( + MediaType.VIDEO).getFormat()).getSize()); + return call; + } + + /** + * Create a new video call and invite the specified CallPeer to it. + * + * @param callee the address of the callee that we should invite to a new + * call. + * @return CallPeer the CallPeer that will represented by the + * specified uri. All following state change events will be delivered + * through that call peer. The Call that this peer is a member + * of could be retrieved from the CallParticipatn instance with the use + * of the corresponding method. + * @throws OperationFailedException with the corresponding code if we fail + * to create the video call. + */ + @Override + public Call createVideoCall(Contact callee) throws OperationFailedException + { + CallJabberImpl call = (CallJabberImpl)super.createVideoCall(callee); + CallPeerJabberImpl callPeer = call.getCallPeers().next(); + callPeer.addCallPeerListener(callPeerListener); + + size = (((VideoMediaFormat)call.getDefaultDevice( + MediaType.VIDEO).getFormat()).getSize()); + return call; + } + + /** + * Check if the remote part supports Jingle video. + * + * @param calleeAddress Contact address + * @return true if contact support Jingle video, false otherwise + * + * @throws OperationFailedException with the corresponding code if we fail + * to create the video call. + */ + protected Call createOutgoingVideoCall(String calleeAddress) + throws OperationFailedException + { + boolean supported = false; + String fullCalleeURI = null; + + if (calleeAddress.indexOf('/') > 0) + { + fullCalleeURI = calleeAddress; + } + else + { + fullCalleeURI = parentProvider.getConnection() + .getRoster().getPresence(calleeAddress).getFrom(); + } + + if (logger.isInfoEnabled()) + logger.info("creating outgoing desktop sharing call..."); + + DiscoverInfo di = null; + try + { + // check if the remote client supports inputevt (remote control) + di = parentProvider.getDiscoveryManager() + .discoverInfo(fullCalleeURI); + + if (di.containsFeature(InputEvtIQ.NAMESPACE)) + { + if (logger.isInfoEnabled()) + logger.info(fullCalleeURI + ": remote-control supported"); + + supported = true; + } + else + { + if (logger.isInfoEnabled()) + logger.info(fullCalleeURI + + ": remote-control not supported!"); + + /* XXX fail or not ? */ + /* + throw new OperationFailedException( + "Failed to create a true desktop sharing.\n" + + fullCalleeURI + " does not support inputevt", + OperationFailedException.INTERNAL_ERROR); + */ + } + } + catch (XMPPException ex) + { + logger.warn("could not retrieve info for " + fullCalleeURI, ex); + } + + if (parentProvider.getConnection() == null) + { + throw new OperationFailedException( + "Failed to create OutgoingJingleSession.\n" + + "we don't have a valid XMPPConnection." + , OperationFailedException.INTERNAL_ERROR); + } + + CallJabberImpl call = new CallJabberImpl(basicTelephony); + /* enable video */ + call.setLocalVideoAllowed(true, getMediaUseCase()); + /* enable remote-control */ + call.setLocalInputEvtAware(supported); + + return basicTelephony.createOutgoingCall(call, calleeAddress); + } + + /** + * Enable desktop remote control. Local desktop can now regenerates keyboard + * and mouse events received from peer. + * + * @param callPeer call peer that will take control on local computer + */ + public void enableRemoteControl(CallPeer callPeer) + { + if(logger.isInfoEnabled()) + logger.info("Enable remote control"); + + CallJabberImpl call = (CallJabberImpl)callPeer.getCall(); + if(call.getLocalInputEvtAware()) + { + remoteControlEnabled = true; + + if(!callPeers.contains(callPeer.getAddress())) + { + callPeers.add(callPeer.getAddress()); + } + } + } + + /** + * Disable desktop remote control. Local desktop stop regenerates keyboard + * and mouse events received from peer. + * + * @param callPeer call peer that will stop controlling on local computer + */ + public void disableRemoteControl(CallPeer callPeer) + { + if(logger.isInfoEnabled()) + logger.info("Disable remote control"); + + remoteControlEnabled = false; + + if(callPeers.contains(callPeer.getAddress())) + { + callPeers.remove(callPeer.getAddress()); + } + } + + /** + * Implementation of method registrationStateChange from + * interface RegistrationStateChangeListener for setting up (or down) + * our InputEvtManager when an XMPPConnection is available + * + * @param evt the event received + */ + public void registrationStateChanged(RegistrationStateChangeEvent evt) + { + if ((evt.getNewState() == RegistrationState.REGISTERING)) + { + /* listen to specific inputevt IQ */ + parentProvider.getConnection().addPacketListener(this, this); + } + } + + /** + * Handles incoming inputevt packets and passes them to the corresponding + * method based on their action. + * + * @param packet the packet to process. + */ + public void processPacket(Packet packet) + { + //this is not supposed to happen because of the filter ... but still + if (!(packet instanceof InputEvtIQ)) + return; + + InputEvtIQ inputIQ = (InputEvtIQ)packet; + + if(inputIQ.getAction() != InputEvtAction.NOTIFY) + { + return; + } + + /* do not waste time to parse packet if remote control is not enabled */ + if(!remoteControlEnabled) + { + return; + } + + //first ack all "set" requests. + if(inputIQ.getType() == IQ.Type.SET) + { + IQ ack = IQ.createResultIQ(inputIQ); + parentProvider.getConnection().sendPacket(ack); + } + + if(!callPeers.contains(inputIQ.getFrom())) + { + return; + } + + for(RemoteControlExtension p : inputIQ.getRemoteControls()) + { + ComponentEvent evt = p.getEvent(); + processComponentEvent(evt); + } + } + + /** + * Tests whether or not the specified packet should be handled by this + * operation set. This method is called by smack prior to packet delivery + * and it would only accept InputEvtIQs. + * + * @param packet the packet to test. + * @return true if and only if packet passes the filter. + */ + public boolean accept(Packet packet) + { + //we only handle InputEvtIQ-s + if(!(packet instanceof InputEvtIQ)) + return false; + + return true; + } + + /** + * Process an ComponentEvent received from remote peer. + * + * @param event ComponentEvent that will be regenerated on local + * computer + */ + public void processComponentEvent(ComponentEvent event) + { + if(event == null) + { + return; + } + + if(event instanceof KeyEvent) + { + processKeyboardEvent((KeyEvent)event); + } + else if(event instanceof MouseEvent) + { + processMouseEvent((MouseEvent)event); + } + } + + /** + * Process keyboard notification received from remote peer. + * + * @param event KeyboardEvent that will be regenerated on local + * computer + */ + public void processKeyboardEvent(KeyEvent event) + { + /* ignore command if remote control is not enabled otherwise regenerates + * event on the computer + */ + if (hidService != null) + { + int keycode = 0; + + /* process immediately a "key-typed" event via press/release */ + if(event.getKeyChar() != 0 && event.getID() == KeyEvent.KEY_TYPED) + { + hidService.keyPress(event.getKeyChar()); + hidService.keyRelease(event.getKeyChar()); + return; + } + + keycode = event.getKeyCode(); + + if(keycode == 0) + { + return; + } + + switch(event.getID()) + { + case KeyEvent.KEY_PRESSED: + hidService.keyPress(keycode); + break; + case KeyEvent.KEY_RELEASED: + hidService.keyRelease(keycode); + break; + default: + break; + } + } + } + + /** + * Process mouse notification received from remote peer. + * + * @param event MouseEvent that will be regenerated on local + * computer + */ + public void processMouseEvent(MouseEvent event) + { + /* ignore command if remote control is not enabled otherwise regenerates + * event on the computer + */ + if (hidService != null) + { + switch(event.getID()) + { + case MouseEvent.MOUSE_PRESSED: + hidService.mousePress(event.getModifiers()); + break; + case MouseEvent.MOUSE_RELEASED: + hidService.mouseRelease(event.getModifiers()); + break; + case MouseEvent.MOUSE_MOVED: + /* x and y position are sent in percentage but we multiply + * by 1000 in depacketizer because we cannot passed the size + * to the Provider + */ + int x = ((event.getX() * size.width) / 1000); + int y = ((event.getY() * size.height) / 1000); + hidService.mouseMove(x, y); + break; + case MouseEvent.MOUSE_WHEEL: + MouseWheelEvent evt = (MouseWheelEvent)event; + hidService.mouseWheel(evt.getWheelRotation()); + break; + default: + break; + } + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java index 0b6fc00..cfb7c0e 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java @@ -110,10 +110,10 @@ public class OperationSetVideoTelephonyJabberImpl * @param calleeAddress Contact address * @return true if contact support Jingle video, false otherwise * - * @exception OperationFailedException with the corresponding code if we fail + * @throws OperationFailedException with the corresponding code if we fail * to create the video call. */ - private Call createOutgoingVideoCall(String calleeAddress) + protected Call createOutgoingVideoCall(String calleeAddress) throws OperationFailedException { if (logger.isInfoEnabled()) diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java index a606c6a..a8f29d4 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java @@ -18,6 +18,7 @@ import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.protocol.jabberconstants.*; import net.java.sip.communicator.util.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt.*; import net.java.sip.communicator.impl.protocol.jabber.sasl.*; import net.java.sip.communicator.service.gui.*; @@ -1078,6 +1079,11 @@ public class ProtocolProviderServiceJabberImpl JingleIQ.NAMESPACE, new JingleIQProvider()); + // register our input event provider + providerManager.addIQProvider(InputEvtIQ.ELEMENT_NAME, + InputEvtIQ.NAMESPACE, + new InputEvtIQProvider()); + //initialize the telephony operation set //until we actually finish jingle, we'll have a clumsy way of //enabling it through a system property. @@ -1104,6 +1110,15 @@ public class ProtocolProviderServiceJabberImpl OperationSetDesktopStreaming.class, new OperationSetDesktopStreamingJabberImpl(basicTelephony)); + // initialize desktop sharing OperationSets + addSupportedOperationSet( + OperationSetDesktopSharingServer.class, + new OperationSetDesktopSharingServerJabberImpl( + basicTelephony)); + addSupportedOperationSet( + OperationSetDesktopSharingClient.class, + new OperationSetDesktopSharingClientJabberImpl(this)); + // Add Jingle features to supported features. supportedFeatures.add(URN_XMPP_JINGLE); supportedFeatures.add(URN_XMPP_JINGLE_RTP); @@ -1113,6 +1128,9 @@ public class ProtocolProviderServiceJabberImpl supportedFeatures.add(URN_XMPP_JINGLE_RTP_AUDIO); supportedFeatures.add(URN_XMPP_JINGLE_RTP_VIDEO); supportedFeatures.add(URN_XMPP_JINGLE_RTP_ZRTP); + + /* add extension to support remote control */ + supportedFeatures.add(InputEvtIQ.NAMESPACE); } // OperationSetContactCapabilities diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtAction.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtAction.java new file mode 100644 index 0000000..e15f321 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtAction.java @@ -0,0 +1,70 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt; + +/** + * Enumeration about the possible actions for an InputEvt IQ. + * + * @author Sebastien Vincent + */ +public enum InputEvtAction +{ + /** + * The notify action. + */ + NOTIFY("notify"); + + /** + * The name of this direction. + */ + private final String actionName; + + /** + * Creates a InputEvtAction instance with the specified name. + * + * @param actionName the name of the InputEvtAction we'd like + * to create. + */ + private InputEvtAction(String actionName) + { + this.actionName = actionName; + } + + /** + * Returns the name of this InputEvtAction. The name returned by + * this method is meant for use directly in the XMPP XML string. + * + * @return Returns the name of this InputEvtAction. + */ + @Override + public String toString() + { + return actionName; + } + + /** + * Returns a InputEvtAction value corresponding to the specified + * inputActionStr. + * + * @param inputActionStr the action String that we'd like to + * parse. + * @return a InputEvtAction value corresponding to the specified + * inputActionStr. + * @throws IllegalArgumentException in case inputActionStr is + * not valid + */ + public static InputEvtAction parseString(String inputActionStr) + throws IllegalArgumentException + { + for (InputEvtAction value : values()) + if (value.toString().equals(inputActionStr)) + return value; + + throw new IllegalArgumentException( + inputActionStr + " is not a valid Input action"); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQ.java new file mode 100644 index 0000000..c506401 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQ.java @@ -0,0 +1,142 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt; + +import java.util.*; + +import org.jivesoftware.smack.packet.*; + +/** + * Input event IQ. It is used to transfer key and mouse events through XMPP. + * + * @author Sebastien Vincent + */ +public class InputEvtIQ extends IQ { + + /** + * The namespace that input event belongs to. + */ + public static final String NAMESPACE = + "http://sip-communicator.org/protocol/inputevt"; + + /** + * The name of the element that contains the input event data. + */ + public static final String ELEMENT_NAME = "inputevt"; + + /** + * The name of the argument that contains the input action value. + */ + public static final String ACTION_ATTR_NAME = "action"; + + /** + * Action of this InputIQ. + */ + private InputEvtAction action = null; + + /** + * List of remote-control elements. + */ + private List remoteControls = + new ArrayList(); + + /** + * Constructor. + */ + public InputEvtIQ() + { + } + + /** + * Get the XML representation of the IQ. + * + * @return XML representation of the IQ + */ + @Override + public String getChildElementXML() + { + StringBuilder bldr = new StringBuilder("<" + ELEMENT_NAME); + + bldr.append(" xmlns='" + NAMESPACE + "'"); + + bldr.append(" " + ACTION_ATTR_NAME + "='" + getAction() + "'"); + + if(remoteControls.size() > 0) + { + bldr.append(">"); + + for(RemoteControlExtension p : remoteControls) + bldr.append(p.toXML()); + + bldr.append(""); + } + else + { + bldr.append("/>"); + } + + return bldr.toString(); + } + + /** + * Sets the value of this element's action attribute. The value of + * the 'action' attribute MUST be one of the values enumerated here. If an + * entity receives a value not defined here, it MUST ignore the attribute + * and MUST return a bad-request error to the sender. There is no + * default value for the 'action' attribute. + * + * @param action the value of the action attribute. + */ + public void setAction(InputEvtAction action) + { + this.action = action; + } + + /** + * Returns the value of this element's action attribute. The value + * of the 'action' attribute MUST be one of the values enumerated here. If + * an entity receives a value not defined here, it MUST ignore the attribute + * and MUST return a bad-request error to the sender. There is no + * default value for the 'action' attribute. + * + * @return the value of the action attribute. + */ + public InputEvtAction getAction() + { + return action; + } + + /** + * Add a remote-control extension. + * + * @param item remote-control extension + */ + public void addRemoteControl(RemoteControlExtension item) + { + remoteControls.add(item); + } + + /** + * Remove a remote-control extension. + * + * @param item remote-control extension + */ + public void removeRemoteControl(RemoteControlExtension item) + { + remoteControls.remove(item); + } + + /** + * Get the RemoteControlExtension list of this IQ. + * + * @return list of RemoteControlExtension + */ + public List getRemoteControls() + { + return remoteControls; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQProvider.java new file mode 100644 index 0000000..c2ea6a5 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQProvider.java @@ -0,0 +1,85 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt; + +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.provider.*; + +import org.xmlpull.v1.*; + +/** + * An implementation of a InputEvt IQ provider that parses incoming Input IQs. + * + * @author Sebastien Vincent + */ +public class InputEvtIQProvider implements IQProvider +{ + /** + * Constructs a new InputEvtIQ provider. + */ + public InputEvtIQProvider() + { +/* + ProviderManager providerManager = ProviderManager.getInstance(); + + providerManager.addExtensionProvider( + InputExtensionProvider.ELEMENT_REMOTE_CONTROL, + InputExtensionProvider.NAMESPACE, + new InputExtensionProvider()); +*/ + } + + /** + * Parse the Input IQ sub-document and returns the corresponding + * InputEvtIQ. + * + * @param parser XML parser + * @return InputEvtIQ + * @throws Exception if something goes wrong during parsing + */ + public IQ parseIQ(XmlPullParser parser) throws Exception + { + InputEvtIQ inputIQ = new InputEvtIQ(); + boolean done = false; + RemoteControlExtensionProvider provider = new RemoteControlExtensionProvider(); + InputEvtAction action = InputEvtAction.parseString(parser + .getAttributeValue("", InputEvtIQ.ACTION_ATTR_NAME)); + + inputIQ.setAction(action); + + int eventType; + String elementName; + + while (!done) + { + eventType = parser.next(); + elementName = parser.getName(); + + if (eventType == XmlPullParser.START_TAG) + { + // + if (elementName.equals( + RemoteControlExtensionProvider.ELEMENT_REMOTE_CONTROL)) + { + RemoteControlExtension item = + (RemoteControlExtension)provider.parseExtension(parser); + inputIQ.addRemoteControl(item); + } + } + + if (eventType == XmlPullParser.END_TAG) + { + if (parser.getName().equals(InputEvtIQ.ELEMENT_NAME)) + { + done = true; + } + } + } + + return inputIQ; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtension.java new file mode 100644 index 0000000..9b9f7c5 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtension.java @@ -0,0 +1,194 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt; + +import java.awt.*; +import java.awt.event.*; + +import org.jivesoftware.smack.packet.*; + +/** + * This class implements input event extension. + * + * @author Sebastien Vincent + */ +public class RemoteControlExtension + implements PacketExtension +{ + /** + * AWT event that represents our RemoteControlExtension. + */ + private final ComponentEvent event; + + /** + * Size of the panel that contains video + */ + private final Dimension videoPanelSize; + + /** + * Constructor. + * + */ + public RemoteControlExtension() + { + videoPanelSize = null; + event = null; + } + + /** + * Constructor. + * + * @param videoPanelSize size of the panel that contains video + */ + public RemoteControlExtension(Dimension videoPanelSize) + { + this.videoPanelSize = videoPanelSize; + this.event = null; + } + + /** + * Constructor. + * + * @param event AWT event + */ + public RemoteControlExtension(ComponentEvent event) + { + this.event = event; + this.videoPanelSize = null; + } + + /** + * Constructor. + * + * @param videoPanelSize size of the panel that contains video + * @param event AWT event + */ + public RemoteControlExtension(InputEvent event, + Dimension videoPanelSize) + { + this.videoPanelSize = videoPanelSize; + this.event = event; + } + + /** + * Get ComponentEvent that represents our + * InputExtensionItem. + * + * @return AWT ComponentEvent + */ + public ComponentEvent getEvent() + { + return event; + } + + /** + * Get the element name of the PacketExtension. + * + * @return "remote-control" + */ + public String getElementName() + { + return RemoteControlExtensionProvider.ELEMENT_REMOTE_CONTROL; + } + + /** + * Returns the XML namespace of the extension sub-packet root element. + * The namespace is always "http://sip-communicator.org/protocol/inputevt". + * + * @return the XML namespace of the packet extension. + */ + public String getNamespace() + { + return RemoteControlExtensionProvider.NAMESPACE; + } + + /** + * Get the XML representation. + * + * @return XML representation of the item + */ + public String toXML() + { + String ret = null; + + if(event == null) + { + return null; + } + + if(event instanceof MouseEvent) + { + MouseEvent e = (MouseEvent)event; + + switch(e.getID()) + { + case MouseEvent.MOUSE_DRAGGED: + case MouseEvent.MOUSE_MOVED: + if(videoPanelSize != null) + { + Point p = e.getPoint(); + double x = (p.getX() / videoPanelSize.width); + double y = (p.getY() / videoPanelSize.height); + ret = RemoteControlExtensionProvider.getMouseMovedXML(x, y); + } + break; + case MouseEvent.MOUSE_WHEEL: + MouseWheelEvent ew = (MouseWheelEvent)e; + ret = RemoteControlExtensionProvider.getMouseWheelXML( + ew.getWheelRotation()); + break; + case MouseEvent.MOUSE_PRESSED: + ret = RemoteControlExtensionProvider.getMousePressedXML( + e.getModifiers()); + break; + case MouseEvent.MOUSE_RELEASED: + ret = RemoteControlExtensionProvider.getMouseReleasedXML( + e.getModifiers()); + break; + default: + break; + } + } + else if(event instanceof KeyEvent) + { + KeyEvent e = (KeyEvent)event; + int keycode = e.getKeyCode(); + int key = e.getKeyChar(); + + if(key != KeyEvent.CHAR_UNDEFINED) + { + keycode = e.getKeyChar(); + } + else + { + keycode = e.getKeyCode(); + } + + if(keycode == 0) + { + return null; + } + + switch(e.getID()) + { + case KeyEvent.KEY_PRESSED: + ret = RemoteControlExtensionProvider.getKeyPressedXML(keycode); + break; + case KeyEvent.KEY_RELEASED: + ret = RemoteControlExtensionProvider.getKeyReleasedXML(keycode); + break; + case KeyEvent.KEY_TYPED: + ret = RemoteControlExtensionProvider.getKeyTypedXML(keycode); + break; + default: + break; + } + } + + return ret; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtensionProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtensionProvider.java new file mode 100644 index 0000000..95394ed --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtensionProvider.java @@ -0,0 +1,402 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt; + +import java.awt.*; +import java.awt.event.*; + +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.provider.*; +import org.xmlpull.v1.*; + +/** + * This class parses incoming remote-control XML element and extracts + * input events such as keyboard and mouse ones. + * + * @author Sebastien Vincent + */ +public class RemoteControlExtensionProvider + implements PacketExtensionProvider +{ + /** + * The name of the remote-info XML element remote-control. + */ + public static final String ELEMENT_REMOTE_CONTROL = "remote-control"; + + /** + * The name of the remote-info XML element mouse-move. + */ + public static final String ELEMENT_MOUSE_MOVE = "mouse-move"; + + /** + * The name of the remote-info XML element mouse-wheel. + */ + public static final String ELEMENT_MOUSE_WHEEL = "mouse-wheel"; + + /** + * The name of the remote-info XML element mouse-press. + */ + public static final String ELEMENT_MOUSE_PRESS = "mouse-press"; + + /** + *The name of the remote-info XML element mouse-release. + */ + public static final String ELEMENT_MOUSE_RELEASE = "mouse-release"; + + /** + * The name of the remote-info XML element key-press. + */ + public static final String ELEMENT_KEY_PRESS = "key-press"; + + /** + * The name of the remote-info XML element key-release. + */ + public static final String ELEMENT_KEY_RELEASE = "key-release"; + + /** + * The name of the remote-info XML element key-type. + */ + public static final String ELEMENT_KEY_TYPE = "key-type"; + + /** + * Namespace of this extension. + */ + public static final String NAMESPACE = + "http://sip-communicator.org/protocol/inputevt"; + + /** + * Component to be used in custom generated MouseEvent and + * KeyEvent. + */ + private static final Component component = new Canvas(); + + /** + * Constructor. + */ + public RemoteControlExtensionProvider() + { + } + + /** + * Parses the extension and returns a PacketExtension. + * + * @param parser XML parser + * @return a PacketExtension that represents a remote-control + * element. + * @throws Exception if an error occurs during XML parsing + */ + public PacketExtension parseExtension(XmlPullParser parser) + throws Exception + { + RemoteControlExtension result = null; + boolean done = false; + + while (!done) + { + try + { + int eventType = parser.next(); + + if (eventType == XmlPullParser.START_TAG) + { + if(parser.getName().equals(ELEMENT_MOUSE_MOVE)) + { + String attr = parser.getAttributeValue("", "x"); + String attr2 = parser.getAttributeValue("", "y"); + if(attr != null && attr2 != null) + { + int x = (int)(Double. + parseDouble(attr) * 1000); + int y = (int)(Double. + parseDouble(attr2) * 1000); + + MouseEvent me = new MouseEvent(component, + MouseEvent.MOUSE_MOVED, + System.currentTimeMillis(), + 0, x, y, 0, false, 0); + + result = new RemoteControlExtension(me); + continue; + } + } + + if(parser.getName().equals(ELEMENT_MOUSE_WHEEL)) + { + String attr = parser.getAttributeValue("", "notch"); + if(attr != null) + { + MouseWheelEvent me = new MouseWheelEvent( + component, MouseEvent.MOUSE_WHEEL, + System.currentTimeMillis(), + 0, 0, 0, 0, false, 0, 0, + Integer.parseInt(attr)); + + + result = new RemoteControlExtension(me); + continue; + } + } + + if(parser.getName().equals(ELEMENT_MOUSE_PRESS)) + { + String attr = parser.getAttributeValue("", "btns"); + if(attr != null) + { + MouseEvent me = new MouseEvent(component, + MouseEvent.MOUSE_PRESSED, + System.currentTimeMillis(), + Integer.parseInt(attr), + 0, 0, 0, false, 0); + + result = new RemoteControlExtension(me); + continue; + } + } + + if(parser.getName().equals(ELEMENT_MOUSE_RELEASE)) + { + String attr = parser.getAttributeValue("", "btns"); + if(attr != null) + { + MouseEvent me = new MouseEvent(component, + MouseEvent.MOUSE_RELEASED, + System.currentTimeMillis(), + Integer.parseInt(attr), + 0, 0, 0, false, 0); + + result = new RemoteControlExtension(me); + continue; + } + } + + if(parser.getName().equals(ELEMENT_KEY_PRESS)) + { + String attr = parser.getAttributeValue("", "keycode"); + if(attr != null) + { + KeyEvent ke = new KeyEvent(component, + KeyEvent.KEY_PRESSED, + System.currentTimeMillis(), + 0, + Integer.parseInt(attr), + (char)0); + + result = new RemoteControlExtension(ke); + continue; + } + } + + if(parser.getName().equals(ELEMENT_KEY_RELEASE)) + { + String attr = parser.getAttributeValue("", "keycode"); + if(attr != null) + { + KeyEvent ke = new KeyEvent(component, + KeyEvent.KEY_RELEASED, + System.currentTimeMillis(), + 0, + Integer.parseInt(attr), + (char)0); + + result = new RemoteControlExtension(ke); + continue; + } + } + + if(parser.getName().equals(ELEMENT_KEY_TYPE)) + { + String attr = parser.getAttributeValue("", "keychar"); + if(attr != null) + { + KeyEvent ke = new KeyEvent(component, + KeyEvent.KEY_TYPED, + System.currentTimeMillis(), + 0, + 0, + (char)Integer.parseInt(attr)); + + result = new RemoteControlExtension(ke); + continue; + } + } + } + else if (eventType == XmlPullParser.END_TAG) + { + if (parser.getName().equals( + RemoteControlExtensionProvider.ELEMENT_REMOTE_CONTROL)) + { + done = true; + } + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + if(result == null) + { + /* we are not allowed to return null otherwise the parser goes + * crazy + */ + result = new RemoteControlExtension(new ComponentEvent(component, 0)); + } + + return result; + } + + /** + * Appends a specific array of Strings to a specific + * StringBuffer. + * + * @param stringBuffer the StringBuffer to append the specified + * strings to + * @param strings the String values to be appended to the specified + * stringBuffer + */ + private static void append(StringBuffer stringBuffer, String... strings) + { + for (String str : strings) + stringBuffer.append(str); + } + + /** + * Build a key-press remote-control XML element. + * + * @param keycode keyboard's code + * @return raw XML bytes + */ + public static String getKeyPressedXML(int keycode) + { + StringBuffer xml = new StringBuffer(); + + append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE + + "\">"); + // + append(xml, "<", RemoteControlExtensionProvider.ELEMENT_KEY_PRESS); + append(xml, " keycode=\"", Integer.toString(keycode), "\"/>"); + append(xml, ""); + return xml.toString(); + } + + /** + * Build a key-release remote-control XML element. + * + * @param keycode keyboard's code + * @return raw XML bytes + */ + public static String getKeyReleasedXML(int keycode) + { + StringBuffer xml = new StringBuffer(); + + append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE + + "\">"); + // + append(xml, "<", RemoteControlExtensionProvider.ELEMENT_KEY_RELEASE); + append(xml, " keycode=\"", Integer.toString(keycode), "\"/>"); + append(xml, ""); + return xml.toString(); + } + + /** + * Build a key-typed remote-control XML element. + * + * @param keycode keyboard's code + * @return raw XML bytes + */ + public static String getKeyTypedXML(int keycode) + { + StringBuffer xml = new StringBuffer(); + + append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE + + "\">"); + // + append(xml, "<", RemoteControlExtensionProvider.ELEMENT_KEY_TYPE); + append(xml, " keychar=\"", Integer.toString(keycode), "\"/>"); + append(xml, ""); + return xml.toString(); + } + + /** + * Build a mouse-press remote-control XML element. + * + * @param btns button mask + * @return raw XML bytes + */ + public static String getMousePressedXML(int btns) + { + StringBuffer xml = new StringBuffer(); + + append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE + + "\">"); + // + append(xml, "<", RemoteControlExtensionProvider.ELEMENT_MOUSE_PRESS); + append(xml, " btns=\"", Integer.toString(btns), "\"/>"); + append(xml, ""); + return xml.toString(); + } + + /** + * Build a remote-info mouse-release remote-control XML element. + * + * @param btns button mask + * @return raw XML bytes + */ + public static String getMouseReleasedXML(int btns) + { + StringBuffer xml = new StringBuffer(); + + append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE + + "\">"); + // + append(xml, "<", RemoteControlExtensionProvider.ELEMENT_MOUSE_RELEASE); + append(xml, " btns=\"", Integer.toString(btns), "\"/>"); + append(xml, ""); + return xml.toString(); + } + + /** + * Build a remote-info mouse-move remote-control XML element. + * + * @param x x position of the mouse + * @param y y position of the mouse + * @return raw XML bytes + */ + public static String getMouseMovedXML(double x, double y) + { + StringBuffer xml = new StringBuffer(); + + append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE + + "\">"); + // + append(xml, "<", RemoteControlExtensionProvider.ELEMENT_MOUSE_MOVE); + append(xml, " x=\"", Double.toString(x), "\" y=\"", Double.toString(y), + "\"/>"); + append(xml, ""); + return xml.toString(); + } + + /** + * Build a remote-info mouse-wheel remote-control XML element. + * + * @param notch wheel notch + * @return raw XML bytes + */ + public static String getMouseWheelXML(int notch) + { + StringBuffer xml = new StringBuffer(); + + append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE + + "\">"); + // + append(xml, "<", RemoteControlExtensionProvider.ELEMENT_MOUSE_WHEEL); + append(xml, " notch=\"", Integer.toString(notch), "\"/>"); + append(xml, ""); + return xml.toString(); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/InputEvtPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/InputEvtPacketExtension.java new file mode 100644 index 0000000..7abced8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/InputEvtPacketExtension.java @@ -0,0 +1,37 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; + +/** + * Represents the content inputevt element that may be find in + * content part of a Jingle media negociation. + * + * @author Sebastien Vincent + */ +public class InputEvtPacketExtension extends AbstractPacketExtension +{ + /** + * Name of the XML element representing the extension. + */ + public final static String ELEMENT_NAME = "inputevt"; + + /** + * Namespace.. + */ + public final static String NAMESPACE = + "http://sip-communicator.org/protocol/inputevt"; + + /** + * Constructs a new inputevt extension. + */ + public InputEvtPacketExtension() + { + super(NAMESPACE, ELEMENT_NAME); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java index 921b940..910f4e8 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java @@ -5,6 +5,7 @@ * See terms of license at gnu.org. */ package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle; + import java.util.logging.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.*; @@ -12,7 +13,6 @@ import net.java.sip.communicator.impl.protocol.jabber.extensions.*; import org.jivesoftware.smack.provider.*; import org.xmlpull.v1.XmlPullParser; - /** * An implementation of a Jingle IQ provider that parses incoming Jingle IQs. * @@ -107,6 +107,13 @@ public class JingleIQProvider implements IQProvider IceUdpTransportPacketExtension.NAMESPACE, new DefaultPacketExtensionProvider( RemoteCandidatePacketExtension.class)); + + //inputevt provider + providerManager.addExtensionProvider( + InputEvtPacketExtension.ELEMENT_NAME, + InputEvtPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider( + InputEvtPacketExtension.class)); } /** diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf index 5ff04de..2037109 100755 --- a/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf +++ b/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf @@ -41,6 +41,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.neomedia.device, net.java.sip.communicator.service.neomedia.event, net.java.sip.communicator.service.neomedia.format, + net.java.sip.communicator.service.hid, net.java.sip.communicator.service.netaddr, net.java.sip.communicator.service.argdelegation, net.java.sip.communicator.service.gui, -- cgit v1.1