From 0ee57fe9fec73e02f49896f492eb8dda7a5f41e2 Mon Sep 17 00:00:00 2001 From: "Koen [XII]" Date: Sun, 20 Oct 2024 16:51:39 +0200 Subject: [PATCH] Rewrite Java code to Kotlin Fixes #11 --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/KoenLav/sockets-for-cordova/issues/11?shareId=XXXX-XXXX-XXXX-XXXX). --- .../{Consumer.java => Consumer.kt} | 8 +- .../{Logging.java => Logging.kt} | 14 +- .../socketsforcordova/SocketAdapter.java | 37 --- .../socketsforcordova/SocketAdapter.kt | 21 ++ .../socketsforcordova/SocketAdapterImpl.java | 213 ------------ .../socketsforcordova/SocketAdapterImpl.kt | 142 ++++++++ .../SocketAdapterOptions.java | 127 ------- .../socketsforcordova/SocketAdapterOptions.kt | 11 + .../socketsforcordova/SocketPlugin.java | 311 ------------------ .../socketsforcordova/SocketPlugin.kt | 290 ++++++++++++++++ .../SocketsForCordovaActivity.java | 37 --- .../SocketsForCordovaActivity.kt | 14 + 12 files changed, 489 insertions(+), 736 deletions(-) rename src/android/src/cz/blocshop/socketsforcordova/{Consumer.java => Consumer.kt} (89%) rename src/android/src/cz/blocshop/socketsforcordova/{Logging.java => Logging.kt} (75%) delete mode 100644 src/android/src/cz/blocshop/socketsforcordova/SocketAdapter.java create mode 100644 src/android/src/cz/blocshop/socketsforcordova/SocketAdapter.kt delete mode 100644 src/android/src/cz/blocshop/socketsforcordova/SocketAdapterImpl.java create mode 100644 src/android/src/cz/blocshop/socketsforcordova/SocketAdapterImpl.kt delete mode 100644 src/android/src/cz/blocshop/socketsforcordova/SocketAdapterOptions.java create mode 100644 src/android/src/cz/blocshop/socketsforcordova/SocketAdapterOptions.kt delete mode 100644 src/android/src/cz/blocshop/socketsforcordova/SocketPlugin.java create mode 100644 src/android/src/cz/blocshop/socketsforcordova/SocketPlugin.kt delete mode 100644 src/android/src/cz/blocshop/socketsforcordova/SocketsForCordovaActivity.java create mode 100644 src/android/src/cz/blocshop/socketsforcordova/SocketsForCordovaActivity.kt diff --git a/src/android/src/cz/blocshop/socketsforcordova/Consumer.java b/src/android/src/cz/blocshop/socketsforcordova/Consumer.kt similarity index 89% rename from src/android/src/cz/blocshop/socketsforcordova/Consumer.java rename to src/android/src/cz/blocshop/socketsforcordova/Consumer.kt index 3257831..97c9c34 100644 --- a/src/android/src/cz/blocshop/socketsforcordova/Consumer.java +++ b/src/android/src/cz/blocshop/socketsforcordova/Consumer.kt @@ -15,8 +15,8 @@ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -package cz.blocshop.socketsforcordova; +package cz.blocshop.socketsforcordova -public interface Consumer { - void accept(T t); -} \ No newline at end of file +interface Consumer { + fun accept(t: T) +} diff --git a/src/android/src/cz/blocshop/socketsforcordova/Logging.java b/src/android/src/cz/blocshop/socketsforcordova/Logging.kt similarity index 75% rename from src/android/src/cz/blocshop/socketsforcordova/Logging.java rename to src/android/src/cz/blocshop/socketsforcordova/Logging.kt index b55f948..009f6ff 100644 --- a/src/android/src/cz/blocshop/socketsforcordova/Logging.java +++ b/src/android/src/cz/blocshop/socketsforcordova/Logging.kt @@ -14,13 +14,13 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -package cz.blocshop.socketsforcordova; +package cz.blocshop.socketsforcordova -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.logging.Level +import java.util.logging.Logger -public class Logging { - public static void Error(String klass, String message, Throwable t) { - Logger.getLogger(klass).log(Level.SEVERE, message, t); - } +object Logging { + fun error(klass: String, message: String, t: Throwable) { + Logger.getLogger(klass).log(Level.SEVERE, message, t) + } } diff --git a/src/android/src/cz/blocshop/socketsforcordova/SocketAdapter.java b/src/android/src/cz/blocshop/socketsforcordova/SocketAdapter.java deleted file mode 100644 index a3939c3..0000000 --- a/src/android/src/cz/blocshop/socketsforcordova/SocketAdapter.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2015, Blocshop s.r.o. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by the Blocshop s.r.o.. The name of the - * Blocshop s.r.o. may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -package cz.blocshop.socketsforcordova; - -import org.apache.cordova.CallbackContext; - -import java.io.IOException; -import java.net.SocketException; - - -public interface SocketAdapter { - public void open(String host, int port, int timeout); - public void write(byte[] data, CallbackContext callbackContext); - public void shutdownWrite() throws IOException; - public void close() throws IOException; - public void setOptions(SocketAdapterOptions options) throws SocketException; - public void setOpenEventHandler(Consumer openEventHandler); - public void setOpenErrorEventHandler(Consumer openErrorEventHandler); - public void setDataConsumer(Consumer dataConsumer); - public void setCloseEventHandler(Consumer closeEventHandler); - public void setErrorEventHandler(Consumer errorEventHandler); -} \ No newline at end of file diff --git a/src/android/src/cz/blocshop/socketsforcordova/SocketAdapter.kt b/src/android/src/cz/blocshop/socketsforcordova/SocketAdapter.kt new file mode 100644 index 0000000..e5e214a --- /dev/null +++ b/src/android/src/cz/blocshop/socketsforcordova/SocketAdapter.kt @@ -0,0 +1,21 @@ +package cz.blocshop.socketsforcordova + +import org.apache.cordova.CallbackContext +import java.io.IOException +import java.net.SocketException + +interface SocketAdapter { + fun open(host: String, port: Int, timeout: Int) + fun write(data: ByteArray, callbackContext: CallbackContext) + @Throws(IOException::class) + fun shutdownWrite() + @Throws(IOException::class) + fun close() + @Throws(SocketException::class) + fun setOptions(options: SocketAdapterOptions) + fun setOpenEventHandler(openEventHandler: Consumer) + fun setOpenErrorEventHandler(openErrorEventHandler: Consumer) + fun setDataConsumer(dataConsumer: Consumer) + fun setCloseEventHandler(closeEventHandler: Consumer) + fun setErrorEventHandler(errorEventHandler: Consumer) +} diff --git a/src/android/src/cz/blocshop/socketsforcordova/SocketAdapterImpl.java b/src/android/src/cz/blocshop/socketsforcordova/SocketAdapterImpl.java deleted file mode 100644 index e87170d..0000000 --- a/src/android/src/cz/blocshop/socketsforcordova/SocketAdapterImpl.java +++ /dev/null @@ -1,213 +0,0 @@ -/** - * Copyright (c) 2015, Blocshop s.r.o. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by the Blocshop s.r.o.. The name of the - * Blocshop s.r.o. may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -package cz.blocshop.socketsforcordova; - -import org.apache.cordova.CallbackContext; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.SocketException; -import java.util.Arrays; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - - -public class SocketAdapterImpl implements SocketAdapter { - - private final int INPUT_STREAM_BUFFER_SIZE = 16 * 1024; - private final Socket socket; - - private Consumer openEventHandler; - private Consumer openErrorEventHandler; - private Consumer dataConsumer; - private Consumer closeEventHandler; - private Consumer errorEventHandler; - - private ExecutorService executor; - - public SocketAdapterImpl() { - this.socket = new Socket(); - this.executor = Executors.newSingleThreadExecutor(); - } - - @Override - public void open(final String host, final int port, final int timeout) { - this.executor.submit(new Runnable() { - @Override - public void run() { - try { - socket.setSoTimeout(timeout); - socket.connect(new InetSocketAddress(host, port), 5000); - invokeOpenEventHandler(); - submitReadTask(); - } catch (IOException e) { - Logging.Error(SocketAdapterImpl.class.getName(), "Error during connecting of socket", e.getCause()); - invokeOpenErrorEventHandler(e.getMessage()); - } - } - }); - } - - @Override - public void write(byte[] data, CallbackContext callbackContext) { - new Thread(() -> { - try { - this.socket.getOutputStream().write(data); - callbackContext.success(); - } catch (IOException e) { - e.printStackTrace(); - callbackContext.error(e.toString()); - } - }).start(); - } - - @Override - public void shutdownWrite() throws IOException { - this.socket.shutdownOutput(); - } - - @Override - public void close() throws IOException { - this.socket.close(); - this.invokeCloseEventHandler(false); - } - - @Override - public void setOptions(SocketAdapterOptions options) throws SocketException { - if (options.getKeepAlive() != null) { - this.socket.setKeepAlive(options.getKeepAlive()); - } - if (options.getOobInline() != null) { - this.socket.setOOBInline(options.getOobInline()); - } - if (options.getSoLinger() != null) { - this.socket.setSoLinger(true, options.getSoLinger()); - } - if (options.getSoTimeout() != null) { - this.socket.setSoTimeout(options.getSoTimeout()); - } - if (options.getReceiveBufferSize() != null) { - this.socket.setReceiveBufferSize(options.getReceiveBufferSize()); - } - if (options.getSendBufferSize() != null) { - this.socket.setSendBufferSize(options.getSendBufferSize()); - } - if (options.getTrafficClass() != null) { - this.socket.setTrafficClass(options.getTrafficClass()); - } - } - - @Override - public void setOpenEventHandler(Consumer openEventHandler) { - this.openEventHandler = openEventHandler; - } - - @Override - public void setOpenErrorEventHandler(Consumer openErrorEventHandler) { - this.openErrorEventHandler = openErrorEventHandler; - } - - @Override - public void setDataConsumer(Consumer dataConsumer) { - this.dataConsumer = dataConsumer; - } - - @Override - public void setCloseEventHandler(Consumer closeEventHandler) { - this.closeEventHandler = closeEventHandler; - } - - @Override - public void setErrorEventHandler(Consumer errorEventHandler) { - this.errorEventHandler = errorEventHandler; - } - - private void submitReadTask() { - this.executor.submit(new Runnable() { - @Override - public void run() { - runRead(); - } - }); - } - - private void runRead() { - boolean hasError = false; - try { - runReadLoop(); - } catch (Throwable e) { - Logging.Error(SocketAdapterImpl.class.getName(), "Error during reading of socket input stream", e); - hasError = true; - invokeExceptionHandler(e.getMessage()); - } finally { - try { - socket.close(); - } catch (IOException e) { - Logging.Error(SocketAdapterImpl.class.getName(), "Error during closing of socket", e); - } finally { - invokeCloseEventHandler(hasError); - } - } - } - - private void runReadLoop() throws IOException { - byte[] buffer = new byte[INPUT_STREAM_BUFFER_SIZE]; - int bytesRead = 0; - - while ((bytesRead = socket.getInputStream().read(buffer)) >= 0) { - byte[] data = buffer.length == bytesRead - ? buffer - : Arrays.copyOfRange(buffer, 0, bytesRead); - - this.invokeDataConsumer(data); - } - } - - private void invokeOpenEventHandler() { - if (this.openEventHandler != null) { - this.openEventHandler.accept((Void)null); - } - } - - private void invokeOpenErrorEventHandler(String errorMessage) { - if (this.openErrorEventHandler != null) { - this.openErrorEventHandler.accept(errorMessage); - } - } - - private void invokeDataConsumer(byte[] data) { - if (this.dataConsumer != null) { - this.dataConsumer.accept(data); - } - } - - private void invokeCloseEventHandler(boolean hasError) { - if (this.closeEventHandler != null) { - this.closeEventHandler.accept(hasError); - } - } - - private void invokeExceptionHandler(String errorMessage) { - if (this.errorEventHandler != null) { - this.errorEventHandler.accept(errorMessage); - } - } -} diff --git a/src/android/src/cz/blocshop/socketsforcordova/SocketAdapterImpl.kt b/src/android/src/cz/blocshop/socketsforcordova/SocketAdapterImpl.kt new file mode 100644 index 0000000..d1d5b61 --- /dev/null +++ b/src/android/src/cz/blocshop/socketsforcordova/SocketAdapterImpl.kt @@ -0,0 +1,142 @@ +package cz.blocshop.socketsforcordova + +import org.apache.cordova.CallbackContext +import java.io.IOException +import java.net.InetSocketAddress +import java.net.Socket +import java.net.SocketException +import java.util.Arrays +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class SocketAdapterImpl : SocketAdapter { + private val INPUT_STREAM_BUFFER_SIZE = 16 * 1024 + private val socket: Socket = Socket() + private var openEventHandler: Consumer? = null + private var openErrorEventHandler: Consumer? = null + private var dataConsumer: Consumer? = null + private var closeEventHandler: Consumer? = null + private var errorEventHandler: Consumer? = null + private val executor: ExecutorService = Executors.newSingleThreadExecutor() + + override fun open(host: String, port: Int, timeout: Int) { + executor.submit { + try { + socket.soTimeout = timeout + socket.connect(InetSocketAddress(host, port), 5000) + invokeOpenEventHandler() + submitReadTask() + } catch (e: IOException) { + Logging.Error(SocketAdapterImpl::class.java.name, "Error during connecting of socket", e.cause) + invokeOpenErrorEventHandler(e.message) + } + } + } + + override fun write(data: ByteArray, callbackContext: CallbackContext) { + Thread { + try { + socket.getOutputStream().write(data) + callbackContext.success() + } catch (e: IOException) { + e.printStackTrace() + callbackContext.error(e.toString()) + } + }.start() + } + + @Throws(IOException::class) + override fun shutdownWrite() { + socket.shutdownOutput() + } + + @Throws(IOException::class) + override fun close() { + socket.close() + invokeCloseEventHandler(false) + } + + @Throws(SocketException::class) + override fun setOptions(options: SocketAdapterOptions) { + options.keepAlive?.let { socket.keepAlive = it } + options.oobInline?.let { socket.oobInline = it } + options.soLinger?.let { socket.setSoLinger(true, it) } + options.soTimeout?.let { socket.soTimeout = it } + options.receiveBufferSize?.let { socket.receiveBufferSize = it } + options.sendBufferSize?.let { socket.sendBufferSize = it } + options.trafficClass?.let { socket.trafficClass = it } + } + + override fun setOpenEventHandler(openEventHandler: Consumer) { + this.openEventHandler = openEventHandler + } + + override fun setOpenErrorEventHandler(openErrorEventHandler: Consumer) { + this.openErrorEventHandler = openErrorEventHandler + } + + override fun setDataConsumer(dataConsumer: Consumer) { + this.dataConsumer = dataConsumer + } + + override fun setCloseEventHandler(closeEventHandler: Consumer) { + this.closeEventHandler = closeEventHandler + } + + override fun setErrorEventHandler(errorEventHandler: Consumer) { + this.errorEventHandler = errorEventHandler + } + + private fun submitReadTask() { + executor.submit { runRead() } + } + + private fun runRead() { + var hasError = false + try { + runReadLoop() + } catch (e: Throwable) { + Logging.Error(SocketAdapterImpl::class.java.name, "Error during reading of socket input stream", e) + hasError = true + invokeExceptionHandler(e.message) + } finally { + try { + socket.close() + } catch (e: IOException) { + Logging.Error(SocketAdapterImpl::class.java.name, "Error during closing of socket", e) + } finally { + invokeCloseEventHandler(hasError) + } + } + } + + @Throws(IOException::class) + private fun runReadLoop() { + val buffer = ByteArray(INPUT_STREAM_BUFFER_SIZE) + var bytesRead: Int + while (socket.getInputStream().read(buffer).also { bytesRead = it } >= 0) { + val data = if (buffer.size == bytesRead) buffer else Arrays.copyOfRange(buffer, 0, bytesRead) + invokeDataConsumer(data) + } + } + + private fun invokeOpenEventHandler() { + openEventHandler?.accept(null) + } + + private fun invokeOpenErrorEventHandler(errorMessage: String?) { + openErrorEventHandler?.accept(errorMessage) + } + + private fun invokeDataConsumer(data: ByteArray) { + dataConsumer?.accept(data) + } + + private fun invokeCloseEventHandler(hasError: Boolean) { + closeEventHandler?.accept(hasError) + } + + private fun invokeExceptionHandler(errorMessage: String?) { + errorEventHandler?.accept(errorMessage) + } +} diff --git a/src/android/src/cz/blocshop/socketsforcordova/SocketAdapterOptions.java b/src/android/src/cz/blocshop/socketsforcordova/SocketAdapterOptions.java deleted file mode 100644 index ea43e23..0000000 --- a/src/android/src/cz/blocshop/socketsforcordova/SocketAdapterOptions.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright (c) 2015, Blocshop s.r.o. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by the Blocshop s.r.o.. The name of the - * Blocshop s.r.o. may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -package cz.blocshop.socketsforcordova; - -public class SocketAdapterOptions { - - private Boolean keepAlive; - private Boolean oobInline; - private Integer soLinger; - private Integer soTimeout; - private Integer receiveBufferSize; - private Integer sendBufferSize; - private Integer trafficClass; - - /** - * @return the keepAlive - */ - public Boolean getKeepAlive() { - return keepAlive; - } - - /** - * @param keepAlive the keepAlive to set - */ - public void setKeepAlive(Boolean keepAlive) { - this.keepAlive = keepAlive; - } - - /** - * @return the oobInline - */ - public Boolean getOobInline() { - return oobInline; - } - - /** - * @param oobInline the oobInline to set - */ - public void setOobInline(Boolean oobInline) { - this.oobInline = oobInline; - } - - /** - * @return the soLinger - */ - public Integer getSoLinger() { - return soLinger; - } - - /** - * @param soLinger the soLinger to set - */ - public void setSoLinger(Integer soLinger) { - this.soLinger = soLinger; - } - - /** - * @return the soTimeout - */ - public Integer getSoTimeout() { - return soTimeout; - } - - /** - * @param soTimeout the soTimeout to set - */ - public void setSoTimeout(Integer soTimeout) { - this.soTimeout = soTimeout; - } - - /** - * @return the receiveBufferSize - */ - public Integer getReceiveBufferSize() { - return receiveBufferSize; - } - - /** - * @param receiveBufferSize the receiveBufferSize to set - */ - public void setReceiveBufferSize(Integer receiveBufferSize) { - this.receiveBufferSize = receiveBufferSize; - } - - /** - * @return the sendBufferSize - */ - public Integer getSendBufferSize() { - return sendBufferSize; - } - - /** - * @param sendBufferSize the sendBufferSize to set - */ - public void setSendBufferSize(Integer sendBufferSize) { - this.sendBufferSize = sendBufferSize; - } - - /** - * @return the trafficClass - */ - public Integer getTrafficClass() { - return trafficClass; - } - - /** - * @param trafficClass the trafficClass to set - */ - public void setTrafficClass(Integer trafficClass) { - this.trafficClass = trafficClass; - } -} diff --git a/src/android/src/cz/blocshop/socketsforcordova/SocketAdapterOptions.kt b/src/android/src/cz/blocshop/socketsforcordova/SocketAdapterOptions.kt new file mode 100644 index 0000000..6406719 --- /dev/null +++ b/src/android/src/cz/blocshop/socketsforcordova/SocketAdapterOptions.kt @@ -0,0 +1,11 @@ +package cz.blocshop.socketsforcordova + +class SocketAdapterOptions { + var keepAlive: Boolean? = null + var oobInline: Boolean? = null + var soLinger: Int? = null + var soTimeout: Int? = null + var receiveBufferSize: Int? = null + var sendBufferSize: Int? = null + var trafficClass: Int? = null +} diff --git a/src/android/src/cz/blocshop/socketsforcordova/SocketPlugin.java b/src/android/src/cz/blocshop/socketsforcordova/SocketPlugin.java deleted file mode 100644 index 86d5381..0000000 --- a/src/android/src/cz/blocshop/socketsforcordova/SocketPlugin.java +++ /dev/null @@ -1,311 +0,0 @@ -/** - * Copyright (c) 2015, Blocshop s.r.o. - * All rights reserved. - *

- * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by the Blocshop s.r.o.. The name of the - * Blocshop s.r.o. may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -package cz.blocshop.socketsforcordova; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.cordova.CallbackContext; -import org.apache.cordova.CordovaArgs; -import org.apache.cordova.CordovaPlugin; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import android.annotation.SuppressLint; -import android.util.Log; - -public class SocketPlugin extends CordovaPlugin { - - Map socketAdapters = new HashMap(); - Map socketAdaptersPorts = new HashMap(); - - @Override - public boolean execute(String action, CordovaArgs args, CallbackContext callbackContext) throws JSONException { - - if (action.equals("open")) { - this.open(args, callbackContext); - } else if (action.equals("write")) { - this.write(args, callbackContext); - } else if (action.equals("shutdownWrite")) { - this.shutdownWrite(args, callbackContext); - } else if (action.equals("close")) { - this.close(args, callbackContext); - } else if (action.equals("setOptions")) { - this.setOptions(args, callbackContext); - } else { - callbackContext.error(String.format("SocketPlugin - invalid action:", action)); - return false; - } - return true; - } - - private void open(CordovaArgs args, CallbackContext callbackContext) throws JSONException { - String socketKey = args.getString(0); - String host = args.getString(1); - int port = args.getInt(2); - int timeout = args.getInt(3); - String destination = host + String.valueOf(port); - - Log.d("SocketPlugin", "Open socket plugin"); - - SocketAdapter socketAdapter = new SocketAdapterImpl(); - - socketAdapter.setCloseEventHandler(new CloseEventHandler(socketKey)); - socketAdapter.setDataConsumer(new DataConsumer(socketKey)); - socketAdapter.setErrorEventHandler(new ErrorEventHandler(socketKey)); - socketAdapter.setOpenErrorEventHandler(new OpenErrorEventHandler(callbackContext)); - socketAdapter.setOpenEventHandler(new OpenEventHandler(socketKey, socketAdapter, callbackContext)); - - if (this.socketAdaptersPorts.containsKey(destination)) { - String existsSocketKey = this.socketAdaptersPorts.get(destination); - SocketAdapter existsSocket = this.getSocketAdapter(existsSocketKey); - - try { - if (existsSocket != null) { - existsSocket.close(); - Log.d("SocketPlugin", "Old socket exists. Closing."); - } else { - Log.d("SocketPlugin", "Old socket not exists."); - } - } catch (IOException e) { - Log.d("SocketPlugin", "Old socket closing error: " + e.getMessage()); - } - } - - socketAdapters.put(socketKey, socketAdapter); - socketAdaptersPorts.put(destination, socketKey); - - socketAdapter.open(host, port, timeout); - } - - private void write(CordovaArgs args, CallbackContext callbackContext) throws JSONException { - String socketKey = args.getString(0); - JSONArray data = args.getJSONArray(1); - - byte[] dataBuffer = new byte[data.length()]; - - for (int i = 0; i < dataBuffer.length; i++) { - dataBuffer[i] = (byte) data.getInt(i); - } - - SocketAdapter socket = this.getSocketAdapter(socketKey); - - if (socket != null) { - socket.write(dataBuffer, callbackContext); - } else { - callbackContext.success(); - } - } - - private void shutdownWrite(CordovaArgs args, CallbackContext callbackContext) throws JSONException { - String socketKey = args.getString(0); - - SocketAdapter socket = this.getSocketAdapter(socketKey); - - try { - if (socket != null) { - socket.shutdownWrite(); - } - callbackContext.success(); - } catch (IOException e) { - callbackContext.error(e.toString()); - } - } - - private void close(CordovaArgs args, CallbackContext callbackContext) throws JSONException { - String socketKey = args.getString(0); - - SocketAdapter socket = this.getSocketAdapter(socketKey); - - try { - if (socket != null) { - socket.close(); - } - callbackContext.success(); - } catch (IOException e) { - callbackContext.error(e.toString()); - } - } - - private void setOptions(CordovaArgs args, CallbackContext callbackContext) throws JSONException { - String socketKey = args.getString(0); - JSONObject optionsJSON = args.getJSONObject(1); - - SocketAdapter socket = this.getSocketAdapter(socketKey); - - if (socket != null) { - SocketAdapterOptions options = new SocketAdapterOptions(); - options.setKeepAlive(getBooleanPropertyFromJSON(optionsJSON, "keepAlive")); - options.setOobInline(getBooleanPropertyFromJSON(optionsJSON, "oobInline")); - options.setReceiveBufferSize(getIntegerPropertyFromJSON(optionsJSON, "receiveBufferSize")); - options.setSendBufferSize(getIntegerPropertyFromJSON(optionsJSON, "sendBufferSize")); - options.setSoLinger(getIntegerPropertyFromJSON(optionsJSON, "soLinger")); - options.setSoTimeout(getIntegerPropertyFromJSON(optionsJSON, "soTimeout")); - options.setTrafficClass(getIntegerPropertyFromJSON(optionsJSON, "trafficClass")); - - try { - socket.close(); - callbackContext.success(); - } catch (IOException e) { - callbackContext.error(e.toString()); - } - } - } - - private Boolean getBooleanPropertyFromJSON(JSONObject jsonObject, String propertyName) throws JSONException { - return jsonObject.has(propertyName) ? jsonObject.getBoolean(propertyName) : null; - } - - private Integer getIntegerPropertyFromJSON(JSONObject jsonObject, String propertyName) throws JSONException { - return jsonObject.has(propertyName) ? jsonObject.getInt(propertyName) : null; - } - - private SocketAdapter getSocketAdapter(String socketKey) { - if (!this.socketAdapters.containsKey(socketKey)) { - Log.d("SocketPlugin", "Adapter not exists"); - return null; - } - - return this.socketAdapters.get(socketKey); - } - - private void dispatchEvent(JSONObject jsonEventObject) { - this.webView.sendJavascript(String.format("window.Socket.dispatchEvent(%s);", jsonEventObject.toString())); - } - - private class CloseEventHandler implements Consumer { - private String socketKey; - - public CloseEventHandler(String socketKey) { - this.socketKey = socketKey; - } - - @Override - public void accept(Boolean hasError) { - socketAdapters.remove(this.socketKey); - - try { - JSONObject event = new JSONObject(); - event.put("type", "Close"); - event.put("hasError", hasError.booleanValue()); - event.put("socketKey", this.socketKey); - - dispatchEvent(event); - } catch (JSONException e) { - e.printStackTrace(); - } - } - } - - private class DataConsumer implements Consumer { - private String socketKey; - - public DataConsumer(String socketKey) { - this.socketKey = socketKey; - } - - @SuppressLint("NewApi") - @Override - public void accept(byte[] data) { - try { - JSONObject event = new JSONObject(); - event.put("type", "DataReceived"); - //event.put("data", new JSONArray(data)); NOT SUPPORTED IN API LEVEL LESS THAN 19 - event.put("data", new JSONArray(this.toByteList(data))); - event.put("socketKey", socketKey); - - dispatchEvent(event); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - private List toByteList(byte[] array) { - List byteList = new ArrayList(array.length); - for (int i = 0; i < array.length; i++) { - byteList.add(array[i]); - } - return byteList; - } - } - - private class ErrorEventHandler implements Consumer { - private String socketKey; - - public ErrorEventHandler(String socketKey) { - this.socketKey = socketKey; - } - - @Override - public void accept(String errorMessage) { - try { - JSONObject event = new JSONObject(); - event.put("type", "Error"); - event.put("errorMessage", errorMessage); - event.put("code", 0); - event.put("socketKey", socketKey); - - dispatchEvent(event); - } catch (JSONException e) { - e.printStackTrace(); - } - } - } - - private class OpenErrorEventHandler implements Consumer { - private CallbackContext openCallbackContext; - - public OpenErrorEventHandler(CallbackContext openCallbackContext) { - this.openCallbackContext = openCallbackContext; - } - - @Override - public void accept(String errorMessage) { - try { - JSONObject event = new JSONObject(); - event.put("errorMessage", errorMessage); - event.put("socketKey", "key"); - event.put("code", 0); - this.openCallbackContext.error(event); - } catch (JSONException e) { - e.printStackTrace(); - } - } - } - - private class OpenEventHandler implements Consumer { - private String socketKey; - private SocketAdapter socketAdapter; - private CallbackContext openCallbackContext; - - public OpenEventHandler(String socketKey, SocketAdapter socketAdapter, CallbackContext openCallbackContext) { - this.socketKey = socketKey; - this.socketAdapter = socketAdapter; - this.openCallbackContext = openCallbackContext; - } - - @Override - public void accept(Void voidObject) { - this.openCallbackContext.success(); - } - } -} diff --git a/src/android/src/cz/blocshop/socketsforcordova/SocketPlugin.kt b/src/android/src/cz/blocshop/socketsforcordova/SocketPlugin.kt new file mode 100644 index 0000000..5631c68 --- /dev/null +++ b/src/android/src/cz/blocshop/socketsforcordova/SocketPlugin.kt @@ -0,0 +1,290 @@ +/** + * Copyright (c) 2015, Blocshop s.r.o. + * All rights reserved. + *

+ * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Blocshop s.r.o.. The name of the + * Blocshop s.r.o. may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +package cz.blocshop.socketsforcordova + +import org.apache.cordova.CallbackContext +import org.apache.cordova.CordovaArgs +import org.apache.cordova.CordovaPlugin +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject +import android.annotation.SuppressLint +import android.util.Log +import java.io.IOException +import java.util.ArrayList +import java.util.HashMap + +class SocketPlugin : CordovaPlugin() { + + private val socketAdapters = HashMap() + private val socketAdaptersPorts = HashMap() + + @Throws(JSONException::class) + override fun execute(action: String, args: CordovaArgs, callbackContext: CallbackContext): Boolean { + return when (action) { + "open" -> { + open(args, callbackContext) + true + } + "write" -> { + write(args, callbackContext) + true + } + "shutdownWrite" -> { + shutdownWrite(args, callbackContext) + true + } + "close" -> { + close(args, callbackContext) + true + } + "setOptions" -> { + setOptions(args, callbackContext) + true + } + else -> { + callbackContext.error(String.format("SocketPlugin - invalid action:", action)) + false + } + } + } + + @Throws(JSONException::class) + private fun open(args: CordovaArgs, callbackContext: CallbackContext) { + val socketKey = args.getString(0) + val host = args.getString(1) + val port = args.getInt(2) + val timeout = args.getInt(3) + val destination = host + port.toString() + + Log.d("SocketPlugin", "Open socket plugin") + + val socketAdapter = SocketAdapterImpl() + + socketAdapter.setCloseEventHandler(CloseEventHandler(socketKey)) + socketAdapter.setDataConsumer(DataConsumer(socketKey)) + socketAdapter.setErrorEventHandler(ErrorEventHandler(socketKey)) + socketAdapter.setOpenErrorEventHandler(OpenErrorEventHandler(callbackContext)) + socketAdapter.setOpenEventHandler(OpenEventHandler(socketKey, socketAdapter, callbackContext)) + + if (socketAdaptersPorts.containsKey(destination)) { + val existsSocketKey = socketAdaptersPorts[destination] + val existsSocket = getSocketAdapter(existsSocketKey) + + try { + if (existsSocket != null) { + existsSocket.close() + Log.d("SocketPlugin", "Old socket exists. Closing.") + } else { + Log.d("SocketPlugin", "Old socket not exists.") + } + } catch (e: IOException) { + Log.d("SocketPlugin", "Old socket closing error: " + e.message) + } + } + + socketAdapters[socketKey] = socketAdapter + socketAdaptersPorts[destination] = socketKey + + socketAdapter.open(host, port, timeout) + } + + @Throws(JSONException::class) + private fun write(args: CordovaArgs, callbackContext: CallbackContext) { + val socketKey = args.getString(0) + val data = args.getJSONArray(1) + + val dataBuffer = ByteArray(data.length()) + + for (i in dataBuffer.indices) { + dataBuffer[i] = data.getInt(i).toByte() + } + + val socket = getSocketAdapter(socketKey) + + if (socket != null) { + socket.write(dataBuffer, callbackContext) + } else { + callbackContext.success() + } + } + + @Throws(JSONException::class) + private fun shutdownWrite(args: CordovaArgs, callbackContext: CallbackContext) { + val socketKey = args.getString(0) + + val socket = getSocketAdapter(socketKey) + + try { + socket?.shutdownWrite() + callbackContext.success() + } catch (e: IOException) { + callbackContext.error(e.toString()) + } + } + + @Throws(JSONException::class) + private fun close(args: CordovaArgs, callbackContext: CallbackContext) { + val socketKey = args.getString(0) + + val socket = getSocketAdapter(socketKey) + + try { + socket?.close() + callbackContext.success() + } catch (e: IOException) { + callbackContext.error(e.toString()) + } + } + + @Throws(JSONException::class) + private fun setOptions(args: CordovaArgs, callbackContext: CallbackContext) { + val socketKey = args.getString(0) + val optionsJSON = args.getJSONObject(1) + + val socket = getSocketAdapter(socketKey) + + if (socket != null) { + val options = SocketAdapterOptions() + options.keepAlive = getBooleanPropertyFromJSON(optionsJSON, "keepAlive") + options.oobInline = getBooleanPropertyFromJSON(optionsJSON, "oobInline") + options.receiveBufferSize = getIntegerPropertyFromJSON(optionsJSON, "receiveBufferSize") + options.sendBufferSize = getIntegerPropertyFromJSON(optionsJSON, "sendBufferSize") + options.soLinger = getIntegerPropertyFromJSON(optionsJSON, "soLinger") + options.soTimeout = getIntegerPropertyFromJSON(optionsJSON, "soTimeout") + options.trafficClass = getIntegerPropertyFromJSON(optionsJSON, "trafficClass") + + try { + socket.close() + callbackContext.success() + } catch (e: IOException) { + callbackContext.error(e.toString()) + } + } + } + + @Throws(JSONException::class) + private fun getBooleanPropertyFromJSON(jsonObject: JSONObject, propertyName: String): Boolean? { + return if (jsonObject.has(propertyName)) jsonObject.getBoolean(propertyName) else null + } + + @Throws(JSONException::class) + private fun getIntegerPropertyFromJSON(jsonObject: JSONObject, propertyName: String): Int? { + return if (jsonObject.has(propertyName)) jsonObject.getInt(propertyName) else null + } + + private fun getSocketAdapter(socketKey: String?): SocketAdapter? { + if (!socketAdapters.containsKey(socketKey)) { + Log.d("SocketPlugin", "Adapter not exists") + return null + } + + return socketAdapters[socketKey] + } + + private fun dispatchEvent(jsonEventObject: JSONObject) { + this.webView.sendJavascript(String.format("window.Socket.dispatchEvent(%s);", jsonEventObject.toString())) + } + + private inner class CloseEventHandler(private val socketKey: String) : Consumer { + + override fun accept(hasError: Boolean) { + socketAdapters.remove(socketKey) + + try { + val event = JSONObject() + event.put("type", "Close") + event.put("hasError", hasError) + event.put("socketKey", socketKey) + + dispatchEvent(event) + } catch (e: JSONException) { + e.printStackTrace() + } + } + } + + private inner class DataConsumer(private val socketKey: String) : Consumer { + + @SuppressLint("NewApi") + override fun accept(data: ByteArray) { + try { + val event = JSONObject() + event.put("type", "DataReceived") + //event.put("data", new JSONArray(data)); NOT SUPPORTED IN API LEVEL LESS THAN 19 + event.put("data", JSONArray(toByteList(data))) + event.put("socketKey", socketKey) + + dispatchEvent(event) + } catch (e: JSONException) { + e.printStackTrace() + } + } + + private fun toByteList(array: ByteArray): List { + val byteList = ArrayList(array.size) + for (i in array.indices) { + byteList.add(array[i]) + } + return byteList + } + } + + private inner class ErrorEventHandler(private val socketKey: String) : Consumer { + + override fun accept(errorMessage: String) { + try { + val event = JSONObject() + event.put("type", "Error") + event.put("errorMessage", errorMessage) + event.put("code", 0) + event.put("socketKey", socketKey) + + dispatchEvent(event) + } catch (e: JSONException) { + e.printStackTrace() + } + } + } + + private inner class OpenErrorEventHandler(private val openCallbackContext: CallbackContext) : Consumer { + + override fun accept(errorMessage: String) { + try { + val event = JSONObject() + event.put("errorMessage", errorMessage) + event.put("socketKey", "key") + event.put("code", 0) + openCallbackContext.error(event) + } catch (e: JSONException) { + e.printStackTrace() + } + } + } + + private inner class OpenEventHandler( + private val socketKey: String, + private val socketAdapter: SocketAdapter, + private val openCallbackContext: CallbackContext + ) : Consumer { + + override fun accept(voidObject: Void?) { + openCallbackContext.success() + } + } +} diff --git a/src/android/src/cz/blocshop/socketsforcordova/SocketsForCordovaActivity.java b/src/android/src/cz/blocshop/socketsforcordova/SocketsForCordovaActivity.java deleted file mode 100644 index 9d6e6a6..0000000 --- a/src/android/src/cz/blocshop/socketsforcordova/SocketsForCordovaActivity.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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 cz.blocshop.socketsforcordova; - -import android.os.Bundle; -import org.apache.cordova.*; - -public class SocketsForCordovaActivity extends CordovaActivity -{ - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - super.init(); - // Set by in config.xml - super.loadUrl(Config.getStartUrl()); - //super.loadUrl("file:///android_asset/www/index.html"); - } -} - diff --git a/src/android/src/cz/blocshop/socketsforcordova/SocketsForCordovaActivity.kt b/src/android/src/cz/blocshop/socketsforcordova/SocketsForCordovaActivity.kt new file mode 100644 index 0000000..4b60ff9 --- /dev/null +++ b/src/android/src/cz/blocshop/socketsforcordova/SocketsForCordovaActivity.kt @@ -0,0 +1,14 @@ +package cz.blocshop.socketsforcordova + +import android.os.Bundle +import org.apache.cordova.CordovaActivity + +class SocketsForCordovaActivity : CordovaActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + super.init() + // Set by in config.xml + super.loadUrl(Config.getStartUrl()) + //super.loadUrl("file:///android_asset/www/index.html") + } +}