Java tutorial
/** * Squidy Interaction Library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * Squidy Interaction Library is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Squidy Interaction Library. If not, see * <http://www.gnu.org/licenses/>. * * 2009 Human-Computer Interaction Group, University of Konstanz. * <http://hci.uni-konstanz.de> * * Please contact info@squidy-lib.de or visit our website * <http://www.squidy-lib.de> for further information. */ package org.squidy.nodes; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.imageio.ImageIO; import javax.jmdns.JmDNS; import javax.jmdns.ServiceInfo; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlType; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.squidy.SquidyException; import org.squidy.common.util.ReflectionUtil; import org.squidy.manager.ProcessException; import org.squidy.manager.controls.CheckBox; import org.squidy.manager.controls.TextField; import org.squidy.manager.data.DataConstant; import org.squidy.manager.data.IData; import org.squidy.manager.data.Processor; import org.squidy.manager.data.Property; import org.squidy.manager.data.impl.DataButton; import org.squidy.manager.data.impl.DataPosition2D; import org.squidy.manager.model.AbstractNode; import org.squidy.manager.protocol.osc.OSCListener; import org.squidy.manager.protocol.osc.OSCServer; import com.apple.dnssd.DNSSD; import com.apple.dnssd.DNSSDException; import com.apple.dnssd.DNSSDRegistration; import com.apple.dnssd.DNSSDService; import com.apple.dnssd.RegisterListener; import com.illposed.osc.Endian; import com.illposed.osc.OSCMessage; /** * <code>iPhone</code>. * * <pre> * Date: May 25, 2008 * Time: 4:54:02 PM * </pre> * * @author Roman Rädle, <a * href="mailto:Roman.Raedle@uni-konstanz.de">Roman. * Raedle@uni-konstanz.de</a>, University of Konstanz * @version $Id: iPhone.java 772 2011-09-16 15:39:44Z raedle $ * @since 1.0.0 * @since * 1.0.0 * @since 1.0 */ @XmlType(name = "Apple iPhone / iPod Touch") @Processor(types = { Processor.Type.INPUT, Processor.Type.OUTPUT }, name = "iPhone", icon = "/org/squidy/nodes/image/48x48/iphone.png", description = "/org/squidy/nodes/html/iPhone.html", tags = { "Apple", "iPhone", "Smartphone", "touch", "multi-touch", "multitouch" }) public class iPhone extends AbstractNode implements RegisterListener { // Logger to log info, error, debug,... messages. private static final Log LOG = LogFactory.getLog(iPhone.class); // Data constants sent from Squidy reference implementation on the Apple // iPhone. public static final DataConstant TOUCHES_BEGAN = DataConstant.get(Boolean.class, "TOUCHES_BEGAN"); public static final DataConstant TOUCHES_MOVED = DataConstant.get(Boolean.class, "TOUCHES_MOVED"); public static final DataConstant TOUCHES_ENDED = DataConstant.get(Boolean.class, "TOUCHES_ENDED"); public static final DataConstant TOUCHES_CANCELLED = DataConstant.get(Boolean.class, "TOUCHES_CANCELLED"); public static final DataConstant TAP_COUNT = DataConstant.get(Integer.class, "TAP_COUNT"); public static final DataConstant HEADING_X = DataConstant.get(String.class, "HEADING_X"); public static final DataConstant HEADING_Y = DataConstant.get(String.class, "HEADING_Y"); public static final DataConstant HEADING_Z = DataConstant.get(String.class, "HEADING_Z"); public static final DataConstant HEADING_MAGNETIC = DataConstant.get(String.class, "MAGNETIC_HEADING"); public static final String SERVICE_TYPE_UDP = "_squidy-client-app._udp."; // public static final String SERVICE_TYPE_TCP = "_squidy-client-app._tcp"; // ################################################################################ // BEGIN OF PROPERTIES // ################################################################################ @XmlAttribute(name = "service-name") @Property(name = "Service name", description = "Service name under which an iPhone can connect to the current iPhone node.") @TextField private String serviceName = "Squidy iPhone"; /** * @return the serviceName */ public final String getServiceName() { return serviceName; } /** * @param serviceName * the serviceName to set */ public final void setServiceName(String serviceName) { this.serviceName = serviceName; if (isProcessing()) { stopDNSSDService(); startDNSSDService(); } } @XmlAttribute(name = "port") @Property(name = "Port", group = "Connection Settings") @TextField private int port = 1919; /** * @return the port */ public final int getPort() { return port; } /** * @param port * the port to set */ public final void setPort(int port) { this.port = port; if (isProcessing()) { stopOSCServer(); startOSCServer(); } } @XmlAttribute(name = "should-release-buttons") @Property(name = "Should release buttons", group = "Options", description = "Whether the iPhone node should release buttons or not.") @CheckBox private boolean releaseButtonOnSingleTouch = true; /** * @return the releaseButtonOnSingleTouch */ public final boolean isReleaseButtonOnSingleTouch() { return releaseButtonOnSingleTouch; } /** * @param releaseButtonOnSingleTouch * the releaseButtonOnSingleTouch to set */ public final void setReleaseButtonOnSingleTouch(boolean releaseButtonOnSingleTouch) { this.releaseButtonOnSingleTouch = releaseButtonOnSingleTouch; } // ################################################################################ // END OF PROPERTIES // ################################################################################ // ################################################################################ // BEGIN OF DNS SD REGISTER LISTENER // ################################################################################ /* * (non-Javadoc) * * @seecom.apple.dnssd.RegisterListener#serviceRegistered(com.apple.dnssd. * DNSSDRegistration, int, java.lang.String, java.lang.String, * java.lang.String) */ public void serviceRegistered(DNSSDRegistration dnssdRegistration, int flags, String serviceName, String regType, String domain) { if (LOG.isDebugEnabled()) { LOG.debug("Registered dns sd service " + SERVICE_TYPE_UDP + " [flags=" + flags + ",serviceName=" + serviceName + ",regType=" + regType + ",domain=" + domain + "]"); } this.dnssdService = dnssdRegistration; } /* * (non-Javadoc) * * @see * com.apple.dnssd.BaseListener#operationFailed(com.apple.dnssd.DNSSDService * , int) */ public void operationFailed(DNSSDService dnssdService, int errorCode) { publishFailure(new SquidyException( "Registration of dns sd service " + SERVICE_TYPE_UDP + " failed [error code=" + errorCode + "]")); this.dnssdService = dnssdService; // Stopping dns sd service. stopDNSSDService(); } // ################################################################################ // END OF DNS SD REGISTER LISTENER // ################################################################################ private DNSSDService dnssdService; // private JmDNS jmdns; // private ServiceInfo info; // The OSC server to receive iPhone data. private OSCServer oscServer; private ServerSocket tcpServer; private Map<Socket, DataOutputStream> outputStreams = new ConcurrentHashMap<Socket, DataOutputStream>(); // public iPhone() { // try { // jmdns = JmDNS.create(); // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // } /* (non-Javadoc) * @see org.squidy.manager.model.AbstractNode#onStart() */ @Override public void onStart() throws ProcessException { // Registering dns sd service. startDNSSDService(); startOSCServer(); startTCPServer(); } /* (non-Javadoc) * @see org.squidy.manager.model.AbstractNode#onStop() */ @Override public void onStop() throws ProcessException { stopTCPServer(); stopOSCServer(); // Stopping dns sd service. stopDNSSDService(); } /** * */ private void startDNSSDService() { // try { // jmdns = JmDNS.create(); // info = ServiceInfo.create(SERVICE_TYPE_UDP, serviceName, port, ""); // jmdns.registerService(info); // } catch (IOException e1) { // // TODO Auto-generated catch block // e1.printStackTrace(); // } try { dnssdService = DNSSD.register(serviceName, SERVICE_TYPE_UDP, port, this); } catch (DNSSDException e) { // TODO [RR]: Uncomment if Bonjour has to be installed. // publishFailure(e); if (LOG.isErrorEnabled()) { LOG.error(e); } } catch (Error e) { if (LOG.isWarnEnabled()) { LOG.warn(e); } } } /** * */ private void stopDNSSDService() { // jmdns.unregisterService(info); if (dnssdService != null) { dnssdService.stop(); dnssdService = null; if (LOG.isDebugEnabled()) { LOG.debug("Stopped dns sd service " + SERVICE_TYPE_UDP); } } } /** * */ private void startOSCServer() { oscServer = new OSCServer(port, Endian.LITTLE_ENDIAN); oscServer.addOSCListener(OSCServer.STANDARD_SQUIDY_OSC_ADDRESS, new DataObjectReceiver()); oscServer.addOSCListener("/squidy/remote", new DataObjectReceiverLegacy()); oscServer.startListening(); } /** * */ private void stopOSCServer() { if (oscServer != null) { oscServer.stopListening(); oscServer.close(); oscServer = null; } } /** * */ private void startTCPServer() { new Thread() { @Override public void run() { try { tcpServer = new ServerSocket(port + 1); while (isProcessing()) { Socket client = tcpServer.accept(); client.setTcpNoDelay(true); outputStreams.put(client, new DataOutputStream(client.getOutputStream())); } } catch (IOException e) { // publishFailure(e); } } }.start(); } /** * */ private void stopTCPServer() { if (tcpServer != null) { try { for (DataOutputStream outputStream : outputStreams.values()) { outputStream.close(); } for (Socket client : outputStreams.keySet()) { client.close(); } outputStreams.clear(); tcpServer.close(); } catch (IOException e) { publishFailure(e); } } } private boolean paletteVisible = false; /** * @param dataButton * @return */ public IData process(DataButton dataButton) { if (dataButton.getFlag()) { try { // if (!paletteVisible) { showButton(20, 20, 0, ImageIO.read(iPhone.class.getResource("/mouse.png"))); showButton(80, 20, 1, ImageIO.read(iPhone.class.getResource("/pen_red.png"))); showButton(140, 20, 2, ImageIO.read(iPhone.class.getResource("/pen_blue.png"))); // paletteVisible = true; // } // else { // paletteVisible = false; // } } catch (IOException e) { publishFailure(e); } } return dataButton; } /** * @param image */ public void showButton(int x, int y, int actionType, BufferedImage image) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(image, "png", baos); byte[] bytes = baos.toByteArray(); System.out.println("BYTES TO READ: " + bytes.length); for (Socket client : outputStreams.keySet()) { try { DataOutputStream outputStream = outputStreams.get(client); outputStream.writeInt(x); outputStream.writeInt(y); outputStream.writeInt(actionType); outputStream.writeInt(bytes.length); outputStream.write(bytes); outputStream.flush(); } catch (SocketException e) { outputStreams.remove(client); } } } catch (IOException e) { e.printStackTrace(); publishFailure(e); } } /** * <code>DataObjectReceiver</code>. * * <pre> * Date: Dec 14, 2009 * Time: 7:58:30 PM * </pre> * * * @author * Roman Rdle * <a href="mailto:Roman.Raedle@uni-konstanz.de">Roman.Raedle@uni-konstanz.de</a> * Human-Computer Interaction Group * University of Konstanz * * @version $Id: iPhone.java 772 2011-09-16 15:39:44Z raedle $ * @since 1.0.0 */ class DataObjectReceiver implements OSCListener { /* * (non-Javadoc) * * @see org.squidy.manager.protocol.osc.OSCListener#handleMessages * (com.illposed.osc.OSCMessage[]) */ public void handleMessages(OSCMessage[] messages) { List<IData> datas = new ArrayList<IData>(); for (OSCMessage message : messages) { Object[] arguments = message.getArguments(); IData data = ReflectionUtil.createInstance((String) arguments[0]); Object[] rawData = new Object[arguments.length - 1]; System.arraycopy(arguments, 1, rawData, 0, rawData.length); try { data.deserialize(rawData); } catch (Exception e) { if (LOG.isErrorEnabled()) { LOG.error("Could not deserialize data object of type " + data.getClass().getName() + " [cause=" + e.getMessage() + "]"); } } if (data instanceof DataPosition2D) { long timestamp = System.currentTimeMillis(); data.setTimestamp(timestamp); // if (releaseButtonOnSingleTouch) { // if (messages.length == 1) { // int tapCount = (Integer) data.getAttribute(TAP_COUNT); // for (int i = 0; i < tapCount; i++) { // // if (data.hasAttribute(TOUCHES_ENDED)) { // datas.add(new DataButton(iPhone.class, // DataButton.BUTTON_1, true)); // datas.add(new DataButton(iPhone.class, // DataButton.BUTTON_1, false)); // } // } // } // } } datas.add(data); } // Publish received data. publish(datas); } } @Deprecated class DataObjectReceiverLegacy implements OSCListener { /* * (non-Javadoc) * * @see org.squidy.manager.protocol.osc.OSCListener#handleMessages * (com.illposed.osc.OSCMessage[]) */ public void handleMessages(OSCMessage[] messages) { List<IData> datas = new ArrayList<IData>(); for (OSCMessage message : messages) { Object[] arguments = message.getArguments(); IData data = ReflectionUtil.createInstance((String) arguments[0]); Object[] rawData = new Object[arguments.length - 2]; System.arraycopy(arguments, 1, rawData, 0, 1); System.arraycopy(arguments, 3, rawData, 1, rawData.length - 1); try { data.deserialize(rawData); } catch (Exception e) { if (LOG.isErrorEnabled()) { LOG.error("Could not deserialize data object of type " + data.getClass().getName() + " [cause=" + e.getMessage() + "]"); } } if (data instanceof DataPosition2D) { long timestamp = System.currentTimeMillis(); data.setTimestamp(timestamp); if (releaseButtonOnSingleTouch) { if (messages.length == 1) { if (data.hasAttribute(TAP_COUNT)) { int tapCount = (Integer) data.getAttribute(TAP_COUNT); for (int i = 0; i < tapCount; i++) { if (data.hasAttribute(TOUCHES_ENDED)) { datas.add(new DataButton(iPhone.class, DataButton.BUTTON_1, true)); datas.add(new DataButton(iPhone.class, DataButton.BUTTON_1, false)); } } } } } } datas.add(data); } // Publish received data. publish(datas); } } }