// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// [Link]
package [Link];
import [Link].*;
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
/**
* Simple Client Socket
* @author moicjarod@[Link] (Jean-Rodolphe Letertre)
* with the help of the work of lizlooney @ [Link] (Liz Looney) and
josmasflores @ [Link] (Jose Dominguez)
* the help of Alexey Brylevskiy for debugging
* and the help of Hossein Amerkashi from AppyBuilder for compatibility with
AppyBuilder
*/
@DesignerComponent(version = 4,
description = "Non-visible component that provides client socket connectivity.",
category = [Link],
nonVisible = true,
iconName = "[Link]
@SimpleObject(external = true)
@UsesPermissions(permissionNames = "[Link]")
public class ClientSocketAI2Ext extends AndroidNonvisibleComponent implements
Component
{
private static final String LOG_TAG = "ClientSocketAI2Ext";
private final Activity activity;
// the socket object
private Socket clientSocket = null;
// the address to connect to
private String serverAddress = "";
// the port to connect to
private String serverPort = "";
// connection timeout to server in ms
private int timeoutms = 2000;
// boolean that indicates the state of the connection, true = connected, false =
not connected
private boolean connectionState = false;
// boolean that indicates the mode used, false = string sent as is, true = String
is considered as hexadecimal data and will be converted before sending
// same behavior is used when receiving data
private boolean hexaStringMode = false;
// boolean to enable debug messages "YailRuntimeError" (true by default to ensure
same behavior as before)
private boolean debugMessages = true;
InputStream inputStream = null;
/**
* Creates a new Client Socket component.
*
* @param container the Form that this component is contained in.
*/
public ClientSocketAI2Ext(ComponentContainer container)
{
super(container.$form());
activity = container.$context();
// compatibility with AppyBuilder (thx Hossein Amerkashi <kkashi01 [at] gmail
[dot] com>)
[Link] policy = new
[Link]().permitAll().build();
[Link](policy);
}
/**
* Method that returns the server's address.
*/
@SimpleProperty(category = [Link], description = "The address
of the server the client will connect to.")
public String ServerAddress()
{
return serverAddress;
}
/**
* Method to specify the server's address
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING)
@SimpleProperty
public void ServerAddress(String address)
{
serverAddress = address;
}
/**
* Method that returns the server's port.
*/
@SimpleProperty(category = [Link], description = "The port of
the server the client will connect to.")
public String ServerPort()
{
return serverPort;
}
/**
* Method to specify the server's port
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING)
@SimpleProperty
public void ServerPort(String port)
{
serverPort = port;
}
/**
* Method that returns the timeout to server in ms
*/
@SimpleProperty(category = [Link], description = "The timeout
to server in ms")
public int TimeoutMs()
{
return timeoutms;
}
/**
* Method to specify the timeout to server in ms
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_INTEGER,
defaultValue="2000")
@SimpleProperty
public void TimeoutMs(int timeout)
{
timeoutms = timeout;
}
/**
* Method that returns the connection state
*/
@SimpleProperty(category = [Link], description = "The state of
the connection - true = connected, false = disconnected")
public boolean ConnectionState()
{
return connectionState;
}
/**
* Method that returns a string containing "\n\r" sequence
*/
@SimpleProperty(category = [Link], description = "returns a
string containing \"\n\r\" sequence")
public String SeqNewLineAndRet()
{
String seq = "";
seq = seq + '\n' + '\r';
return seq;
}
/**
* Method that returns a string containing "\r\n" sequence
*/
@SimpleProperty(category = [Link], description = "returns a
string containing \"\r\n\" sequence")
public String SeqRetAndNewLine()
{
String seq = "";
seq = seq + '\r' + '\n';
return seq;
}
/**
* Method that returns a string containing "\r" sequence
*/
@SimpleProperty(category = [Link], description = "returns a
string containing \"\r\" sequence")
public String SeqRet()
{
String seq = "";
seq = seq + '\r';
return seq;
}
/**
* Method that returns a string containing "\n" sequence
*/
@SimpleProperty(category = [Link], description = "returns a
string containing \"\n\" sequence")
public String SeqNewLine()
{
String seq = "";
seq = seq + '\n';
return seq;
}
/**
* Method that returns the mode (string or hexastring)
*/
@SimpleProperty(category = [Link], description = "The mode of
sending and receiving data.")
public boolean HexaStringMode()
{
return hexaStringMode;
}
/**
* Method to specify the mode (string or hexastring)
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN)
@SimpleProperty
public void HexaStringMode(boolean mode)
{
hexaStringMode = mode;
}
/**
* Method that returns the display of debug messages
*/
@SimpleProperty(category = [Link], description = "The display
of debug messages.")
public boolean DebugMessages()
{
return debugMessages;
}
/**
* Method to specify the display of debug messages
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN,
defaultValue="True")
@SimpleProperty
public void DebugMessages(boolean displayDebugMessages)
{
debugMessages = displayDebugMessages;
}
/**
* Creates the socket, connect to the server and launches the thread to receive
data from server
*/
@SimpleFunction(description = "Tries to connect to the server and launches the
thread for receiving data (blocking until connected or failed)")
public void Connect()
{
if (connectionState == true)
{
if (debugMessages == true)
throw new YailRuntimeError("Connect error, socket connected yet, please
disconnect before reconnect !", "Error");
}
try
{
// connecting the socket
clientSocket = new Socket();
[Link](new InetSocketAddress(serverAddress,
[Link](serverPort)), timeoutms);
connectionState = true;
// begin the receive loop in a new thread
[Link](new Runnable()
{
@Override
public void run()
{
ByteArrayOutputStream byteArrayOutputStream = new
ByteArrayOutputStream(1024);
byte[] buffer = new byte[1024];
int bytesRead = 0;
try
{
// get the input stream and save the data
inputStream = [Link]();
while (true)
{
// test if there is a server problem then close socket properly (thx
Axeley :-))
try
{
bytesRead = [Link](buffer);
if(bytesRead == -1)
break;
}
catch(SocketException e)
{
//throw e;
}
catch(IOException e)
{
if([Link]().indexOf("ETIMEDOUT") >= 0)
break;
//throw e;
}
[Link](buffer, 0, bytesRead);
final String dataReceived;
// hexaStringMode is false, so we don't transform the string received
if (hexaStringMode == false)
{
dataReceived = [Link]("UTF-8");
}
// hexaStringMode is true, so we make a string with each character as
an hexa symbol representing the received message
else
{
int i;
char hexaSymbol1, hexaSymbol2;
String tempData = "";
byte[] byteArray = [Link]();
for (i = 0; i < [Link](); i++)
{
if (((byteArray[i] & 0xF0) >> 4) < 0xA)
// 0 to 9 symbol
hexaSymbol1 = (char)(((byteArray[i] & 0xF0) >> 4) + 0x30);
else
// A to F symbol
hexaSymbol1 = (char)(((byteArray[i] & 0xF0) >> 4) + 0x37);
if ((byteArray[i] & 0x0F) < 0xA)
hexaSymbol2 = (char)((byteArray[i] & 0x0F) + 0x30);
else
hexaSymbol2 = (char)((byteArray[i] & 0x0F) + 0x37);
tempData = tempData + hexaSymbol1 + hexaSymbol2;
}
dataReceived = tempData;
}
// reset of the byteArrayOutputStream to flush the content
[Link]();
// then we send the data to the user using an event
// events must be sent by the main thread (UI)
[Link](new Runnable()
{
@Override
public void run()
{
DataReceived(dataReceived);
}
} );
}
// When we go there, either we have
// - server shutdown
// - disconnection asked (inputstream closed => -1 returned)
// - connection problem
// so, if it is not disconnected yet, we disconnect the socket and
inform the user of it.
if (connectionState == true)
{
Disconnect();
// events must be sent by the main thread (UI)
[Link](new Runnable()
{
@Override
public void run()
{
RemoteConnectionClosed();
}
} );
}
}
catch (SocketException e)
{
Log.e(LOG_TAG, "ERROR_READ", e);
if (debugMessages == true)
throw new YailRuntimeError("Connect error (read 1) " +
[Link](), "Error");
}
catch (IOException e)
{
Log.e(LOG_TAG, "ERROR_READ", e);
if (debugMessages == true)
throw new YailRuntimeError("Connect error (read 2)", "Error");
}
catch (Exception e)
{
connectionState = false;
Log.e(LOG_TAG, "ERROR_READ", e);
if (debugMessages == true)
throw new YailRuntimeError("Connect error (read 3) " +
[Link](), "Error");
}
}
} );
}
catch (SocketException e)
{
Log.e(LOG_TAG, "ERROR_CONNECT", e);
if (debugMessages == true)
throw new YailRuntimeError("Connect error" + [Link](), "Error");
}
catch (Exception e)
{
connectionState = false;
Log.e(LOG_TAG, "ERROR_CONNECT", e);
if (debugMessages == true)
throw new YailRuntimeError("Connect error (Socket Creation, please check Ip
or hostname -> )" + [Link](), "Error");
}
}
/**
* Send data through the socket to the server
*/
@SimpleFunction(description = "Send data to the server")
public void SendData(final String data)
{
if (connectionState == false)
{
if (debugMessages == true)
throw new YailRuntimeError("Send error, socket not connected.", "Error");
}
final byte [] dataToSend;
byte [] dataCopy = [Link]();
if (hexaStringMode == false)
{
//dataSend = new byte [[Link]()];
// if hexaStringMode is false, we send data as is
dataToSend = dataCopy;
}
else
{
// if hexaStringMode is true, we begin to verify we can transcode the symbols
// verify if the data we want to send contains only hexa symbols
int i;
for (i = 0; i < [Link](); i++)
{
if (((dataCopy[i] < 0x30) || (dataCopy[i] > 0x39)) && ((dataCopy[i] < 0x41)
|| (dataCopy[i] > 0x46)) && ((dataCopy[i] < 0x61) || (dataCopy[i] > 0x66)))
if (debugMessages == true)
throw new YailRuntimeError("Send data : hexaStringMode is selected and
non hexa symbol found in send String.", "Error");
}
// verify that the number of symbols is even
if (([Link]() %2) == 1)
{
if (debugMessages == true)
throw new YailRuntimeError("Send data : hexaStringMode is selected and
send String length is odd. Even number of characters needed.", "Error");
}
// if all tests pass, we transcode the data :
dataToSend=new byte[[Link]()/2];
for (i = 0; i < [Link](); i=i+2)
{
byte [] temp1 = new byte [2];
temp1 [0] = dataCopy[i];
temp1 [1] = dataCopy[i+1];
String temp2 = new String (temp1);
dataToSend[i/2]=(byte)[Link](temp2, 16);
}
// those two lines were removed because of a bug sending trailing 0x00 in
hexaString mode
// if 0x00 is really needed, we should add it directly in the string we
want to send
// end of c-type string character
// dataToSend[i/2] = (byte)0x00;
}
// we then send asynchonously the data
[Link](new Runnable()
{
@Override
public void run()
{
try
{
OutputStream out;
out = [Link]();
[Link](dataToSend);
}
catch (SocketException e)
{
Log.e(LOG_TAG, "ERROR_SEND", e);
if (debugMessages == true)
throw new YailRuntimeError("Send data" + [Link](), "Error");
}
catch (Exception e)
{
Log.e(LOG_TAG, "ERROR_UNABLE_TO_SEND_DATA", e);
if (debugMessages == true)
throw new YailRuntimeError("Send Data", "Error");
}
}
} );
}
/**
* Close the socket
*/
@SimpleFunction(description = "Disconnect to the server")
public void Disconnect()
{
if (connectionState == true)
{
connectionState = false;
try
{
// shutdown the input socket,
//[Link]();
//[Link]();
[Link]();
}
catch (SocketException e)
{
// modifications by axeley too :-)
if([Link]().indexOf("ENOTCONN") == -1)
{
Log.e(LOG_TAG, "ERROR_CONNECT", e);
if (debugMessages == true)
throw new YailRuntimeError("Disconnect" + [Link](), "Error");
}
// if not connected, then just ignore the exception
}
catch (IOException e)
{
Log.e(LOG_TAG, "ERROR_CONNECT", e);
if (debugMessages == true)
throw new YailRuntimeError("Disconnect" + [Link](), "Error");
}
catch (Exception e)
{
Log.e(LOG_TAG, "ERROR_CONNECT", e);
if (debugMessages == true)
throw new YailRuntimeError("Disconnect" + [Link](), "Error");
}
finally
{
clientSocket=null;
}
}
else
if (debugMessages == true)
throw new YailRuntimeError("Socket not connected, can't disconnect.",
"Error");
}
/**
* Event indicating that a message has been received
*
* @param data the data sent by the server
*/
@SimpleEvent
public void DataReceived(String data)
{
// invoke the application's "DataReceived" event handler.
[Link](this, "DataReceived", data);
}
/**
* Event indicating that the remote socket closed the connection
*
*/
@SimpleEvent
public void RemoteConnectionClosed()
{
// invoke the application's "RemoteConnectionClosed" event handler.
[Link](this, "RemoteConnectionClosed");
}
}