diff --git a/README.md b/README.md
index 814b531..768cf57 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,61 @@ This module is used by [Tradle](https://github.com/tradle)
npm install --save react-native-udp
```
-* Drag UdpSockets.xcodeproj from node_modules/react-native-udp into your XCode project. Click on the project in XCode, go to Build Phases, then Link Binary With Libraries and add libUdpSockets.a
+### `iOS`
+
+* Drag UdpSockets.xcodeproj from node_modules/react-native-udp/ios into your XCode project.
+
+* Click on the project in XCode, go to Build Phases, then Link Binary With Libraries and add `libUdpSockets.a`
+
+### `Android`
+
+* `android/settings.gradle`
+
+```gradle
+...
+include ':react-native-udp'
+project(':react-native-udp').projectDir = new File(settingsDir, '../node_modules/react-native-udp/android')
+```
+* `android/app/build.gradle`
+
+```gradle
+dependencies {
+ ...
+ compile project(':react-native-udp')
+}
+```
+
+* register module (in MainActivity.java)
+
+```java
+...
+
+import com.tradle.react.*; // <--- import
+
+public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {
+ ...
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mReactRootView = new ReactRootView(this);
+
+ mReactInstanceManager = ReactInstanceManager.builder()
+ .setApplication(getApplication())
+ .setBundleAssetName("index.android.bundle")
+ .setJSMainModuleName("index.android")
+ .addPackage(new MainReactPackage())
+ .addPackage(new UdpSocketsModule()) // <- add here
+ .setUseDeveloperSupport(BuildConfig.DEBUG)
+ .setInitialLifecycleState(LifecycleState.RESUMED)
+ .build();
+
+ mReactRootView.startReactApplication(mReactInstanceManager, "YourProject", null);
+
+ setContentView(mReactRootView);
+ }
+}
+```
Buckle up, Dorothy
@@ -33,19 +87,19 @@ _only if you want to write require('dgram') in your javascript_
### JS
-_see/run index.ios.js for a complete example, but basically it's just like dgram_
+_see/run index.js for a complete example, but basically it's just like dgram_
```js
var dgram = require('dgram')
// OR, if not shimming via package.json "browser" field:
-// var dgram = require('UdpSockets')
+// var dgram = require('UdpSockets')
var socket = dgram.createSocket('udp4')
socket.bind(12345)
socket.once('listening', function() {
var buf = toByteArray('excellent!')
socket.send(buf, 0, buf.length, remotePort, remoteHost, function(err) {
if (err) throw err
-
+
console.log('message was sent')
})
})
@@ -73,4 +127,6 @@ add select tests from node's tests for dgram
[Ellen Katsnelson](https://github.com/pgmemk)
[Tradle, Inc.](https://github.com/tradle/about/wiki)
+[Andy Prock](https://github.com/aprock)
+
PR's welcome!
diff --git a/UdpSocket.ios.js b/UdpSocket.js
similarity index 100%
rename from UdpSocket.ios.js
rename to UdpSocket.js
diff --git a/UdpSockets.android.js b/UdpSockets.android.js
deleted file mode 100644
index 6f35bd5..0000000
--- a/UdpSockets.android.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
- * Stub of UdpSockets for Android.
- *
- * @providesModule UdpSockets
- * @flow
- */
-'use strict';
-
-var warning = require('warning');
-
-var UdpSockets = {
- test: function() {
- warning("Not yet implemented for Android.");
- }
-};
-
-module.exports = UdpSockets;
diff --git a/UdpSockets.ios.js b/UdpSockets.js
similarity index 79%
rename from UdpSockets.ios.js
rename to UdpSockets.js
index f94c64f..e2e3cb6 100644
--- a/UdpSockets.ios.js
+++ b/UdpSockets.js
@@ -4,7 +4,7 @@
* @flow
*/
-var UdpSocket = require('./UdpSocket.ios')
+var UdpSocket = require('./UdpSocket');
exports.createSocket = function(type) {
return new UdpSocket({
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 0000000..447d675
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,31 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.1"
+
+ defaultConfig {
+ minSdkVersion 16
+ targetSdkVersion 22
+ versionCode 1
+ versionName "0.2.0"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ // needed for https://github.com/square/okio/issues/58
+ lintOptions {
+ warning 'InvalidPackage'
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile 'com.android.support:appcompat-v7:23.0.1'
+ compile 'com.facebook.react:react-native:0.11.+'
+}
diff --git a/android/proguard-rules.pro b/android/proguard-rules.pro
new file mode 100644
index 0000000..a92fa17
--- /dev/null
+++ b/android/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a864a45
--- /dev/null
+++ b/android/src/main/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/android/src/main/java/com/tradle/react/UdpErrorUtil.java b/android/src/main/java/com/tradle/react/UdpErrorUtil.java
new file mode 100644
index 0000000..94ad50b
--- /dev/null
+++ b/android/src/main/java/com/tradle/react/UdpErrorUtil.java
@@ -0,0 +1,30 @@
+/**
+ * UdpErrorUtil.java
+ * react-native-udp
+ *
+ * Created by Andy Prock on 9/24/15.
+ */
+
+package com.tradle.react;
+
+import javax.annotation.Nullable;
+
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.WritableMap;
+
+/**
+ * Helper class for udp errors.
+ */
+public class UdpErrorUtil {
+ /**
+ * Create Error object to be passed back to the JS callback.
+ */
+ /* package */ static WritableMap getError(@Nullable String key, String errorMessage) {
+ WritableMap errorMap = Arguments.createMap();
+ errorMap.putString("message", errorMessage);
+ if (key != null) {
+ errorMap.putString("key", key);
+ }
+ return errorMap;
+ }
+}
diff --git a/android/src/main/java/com/tradle/react/UdpReceiverTask.java b/android/src/main/java/com/tradle/react/UdpReceiverTask.java
new file mode 100644
index 0000000..799d493
--- /dev/null
+++ b/android/src/main/java/com/tradle/react/UdpReceiverTask.java
@@ -0,0 +1,116 @@
+/**
+ * UdpReceiverTask.java
+ * react-native-udp
+ *
+ * Created by Andy Prock on 9/24/15.
+ */
+
+package com.tradle.react;
+
+import android.os.AsyncTask;
+import android.util.Base64;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+
+/**
+ * This is a specialized AsyncTask that receives data from a socket in the background, and
+ * notifies it's listener when data is received.
+ */
+public class UdpReceiverTask extends AsyncTask {
+ private static final String TAG = "UdpReceiverTask";
+ private static final int MAX_UDP_DATAGRAM_LEN = 1024;
+
+ private DatagramSocket mSocket;
+ private WeakReference mReceiverListener;
+
+ /**
+ * An {@link AsyncTask} that blocks to receive data from a socket.
+ * Received data is sent via the {@link OnDataReceivedListener}
+ */
+ public UdpReceiverTask(DatagramSocket socket, UdpReceiverTask.OnDataReceivedListener
+ receiverListener) {
+ this.mSocket = socket;
+ this.mReceiverListener = new WeakReference<>(receiverListener);
+ }
+
+ /**
+ * An infinite loop to block and read data from the socket.
+ */
+ @Override
+ protected Void doInBackground(Void... a) {
+ OnDataReceivedListener receiverListener = mReceiverListener.get();
+ try {
+ byte[] lMsg = new byte[MAX_UDP_DATAGRAM_LEN];
+ DatagramPacket dp = new DatagramPacket(lMsg, lMsg.length);
+ while(!isCancelled()){
+ mSocket.receive(dp);
+ publishProgress(new ReceivedPacket(Base64.encodeToString(lMsg, Base64.NO_WRAP),
+ dp.getAddress().getHostAddress(), dp.getPort()));
+ }
+ } catch (IOException ioe) {
+ if (receiverListener != null) {
+ receiverListener.didReceiveError(ioe.getMessage());
+ }
+ } catch (RuntimeException rte) {
+ if (receiverListener != null) {
+ receiverListener.didReceiveRuntimeException(rte);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Send data out to the listener.
+ * @param {@link ReceivedPacket} packet marshalled data
+ */
+ @Override
+ protected void onProgressUpdate(ReceivedPacket... packet) {
+ OnDataReceivedListener receiverListener = mReceiverListener.get();
+ if (receiverListener != null) {
+ receiverListener.didReceiveData(packet[0].base64String, packet[0].address,
+ packet[0].port);
+ }
+ }
+
+ /**
+ * Close if cancelled.
+ */
+ @Override
+ protected void onCancelled(){
+ if (mSocket != null){
+ mSocket.close();
+ }
+ }
+
+ /**
+ * Internal class used to marshall packet data as a progress update.
+ * base64String the data encoded as a base64 string
+ * address the address of the sender
+ * port the port number of the sender
+ */
+ class ReceivedPacket {
+ String base64String;
+ String address;
+ int port;
+
+ ReceivedPacket(String base64String, String address, int port) {
+ this.base64String = base64String;
+ this.address = address;
+ this.port = port;
+ }
+ }
+
+
+ /**
+ * Listener interface for receive events.
+ */
+ public interface OnDataReceivedListener {
+ void didReceiveData(String data, String host, int port);
+ void didReceiveError(String message);
+ void didReceiveRuntimeException(RuntimeException exception);
+ }
+}
diff --git a/android/src/main/java/com/tradle/react/UdpSenderTask.java b/android/src/main/java/com/tradle/react/UdpSenderTask.java
new file mode 100644
index 0000000..643155c
--- /dev/null
+++ b/android/src/main/java/com/tradle/react/UdpSenderTask.java
@@ -0,0 +1,61 @@
+/**
+ * UdpSenderTask.java
+ * react-native-udp
+ *
+ * Created by Andy Prock on 9/24/15.
+ */
+
+package com.tradle.react;
+
+import android.os.AsyncTask;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+
+/**
+ * Specialized AsyncTask that transmits data in the background, and notifies listeners of the result.
+ */
+public class UdpSenderTask extends AsyncTask {
+ private static final String TAG = "UdpSenderTask";
+
+ private DatagramSocket mSocket;
+ private WeakReference mListener;
+
+ public UdpSenderTask(DatagramSocket socket, OnDataSentListener listener) {
+ this.mSocket = socket;
+ this.mListener = new WeakReference<>(listener);
+ }
+
+ @Override
+ protected Void doInBackground(DatagramPacket... params) {
+ OnDataSentListener listener = mListener.get();
+
+ try {
+ mSocket.send(params[0]);
+ if (listener != null) {
+ listener.onDataSent(this);
+ }
+ } catch (IOException e) {
+ if (listener != null) {
+ listener.onDataSentError(this, e.getMessage());
+ }
+ } catch (RuntimeException rte) {
+ if (listener != null) {
+ listener.onDataSentRuntimeException(this, rte);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Callbacks for data send events.
+ */
+ public interface OnDataSentListener {
+ void onDataSent(UdpSenderTask task);
+ void onDataSentError(UdpSenderTask task, String error);
+ void onDataSentRuntimeException(UdpSenderTask task, RuntimeException exception);
+ }
+}
diff --git a/android/src/main/java/com/tradle/react/UdpSocketClient.java b/android/src/main/java/com/tradle/react/UdpSocketClient.java
new file mode 100644
index 0000000..d244515
--- /dev/null
+++ b/android/src/main/java/com/tradle/react/UdpSocketClient.java
@@ -0,0 +1,226 @@
+/**
+ * UdpSocketClient.java
+ * react-native-udp
+ *
+ * Created by Andy Prock on 9/24/15.
+ */
+
+package com.tradle.react;
+
+import android.os.AsyncTask;
+import android.support.annotation.Nullable;
+import android.util.Base64;
+
+import com.facebook.react.bridge.Callback;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.nio.channels.DatagramChannel;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Client class that wraps a sender and a receiver for UDP data.
+ */
+public final class UdpSocketClient implements UdpReceiverTask.OnDataReceivedListener, UdpSenderTask.OnDataSentListener {
+ private final OnDataReceivedListener mReceiverListener;
+ private final OnRuntimeExceptionListener mExceptionListener;
+ private final boolean mReuseAddress;
+ private final Map mPendingSends;
+
+ private DatagramChannel mChannel;
+ private DatagramSocket mSocket;
+ private UdpReceiverTask mReceiverTask;
+
+ private UdpSocketClient(Builder builder) {
+ this.mReceiverListener = builder.receiverListener;
+ this.mExceptionListener = builder.exceptionListener;
+ this.mReuseAddress = builder.reuse;
+ this.mPendingSends = new ConcurrentHashMap<>();
+ }
+
+ /**
+ * Binds to a specific port or address. A random port is used if the address is {@code null}.
+ *
+ * @param port local port to bind to
+ * @param address local address to bind to
+ * @throws IOException
+ * @throws IllegalArgumentException
+ * if the SocketAddress is not supported
+ * @throws SocketException
+ * if the socket is already bound or a problem occurs during
+ * binding.
+ */
+ public void bind(Integer port, @Nullable String address) throws IOException {
+ mChannel = DatagramChannel.open();
+ mChannel.configureBlocking(true);
+ mSocket = mChannel.socket();
+ mReceiverTask = new UdpReceiverTask(mSocket, this);
+
+ SocketAddress socketAddress = null;
+ if (address != null) {
+ socketAddress = new InetSocketAddress(address, port);
+ } else {
+ socketAddress = new InetSocketAddress(port);
+ }
+
+ mSocket.setReuseAddress(mReuseAddress);
+ mSocket.bind(socketAddress);
+
+ // begin listening for data in the background
+ mReceiverTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ /**
+ * Creates a UdpSenderTask, and transmits udp data in the background.
+ *
+ * @param base64String byte array housed in a String.
+ * @param port destination port
+ * @param address destination address
+ * @param callback callback for results
+ * @throws UnknownHostException
+ */
+ public void send(String base64String, Integer port, String address, @Nullable Callback callback) throws UnknownHostException {
+ byte[] data = Base64.decode(base64String, Base64.NO_WRAP);
+ DatagramPacket packet = new DatagramPacket(data, data.length,
+ InetAddress.getByName(address), port);
+ UdpSenderTask task = new UdpSenderTask(mSocket, this);
+ if (callback != null) {
+ synchronized (mPendingSends) {
+ mPendingSends.put(task, callback);
+ }
+ }
+ task.execute(packet);
+ }
+
+ /**
+ * Sets the socket to enable broadcasts.
+ */
+ public void setBroadcast(boolean flag) throws SocketException {
+ if (mSocket != null) {
+ mSocket.setBroadcast(flag);
+ }
+ }
+
+ /**
+ * Shuts down the receiver task, closing the socket.
+ */
+ public void close() throws IOException {
+ if (mReceiverTask != null && !mReceiverTask.isCancelled()) {
+ mReceiverTask.cancel(false);
+ } else if (mChannel.isOpen()) {
+ mChannel.close();
+ }
+ }
+
+ /**
+ * Retransmits the data back a level, attaching {@code this}
+ */
+ @Override
+ public void didReceiveData(String data, String host, int port) {
+ mReceiverListener.didReceiveData(this, data, host, port);
+ }
+
+ /**
+ * Retransmits the error back a level, attaching {@code this}
+ */
+ @Override
+ public void didReceiveError(String message) {
+ mReceiverListener.didReceiveError(this, message);
+ }
+
+ /**
+ * Retransmits the exception back a level, attaching {@code this}
+ */
+ @Override
+ public void didReceiveRuntimeException(RuntimeException exception) {
+ mExceptionListener.didReceiveException(exception);
+ }
+
+ /**
+ * Transmits success to the javascript layer, if a callback is present.
+ */
+ @Override
+ public void onDataSent(UdpSenderTask task) {
+ Callback callback;
+
+ synchronized (mPendingSends) {
+ callback = mPendingSends.get(task);
+ mPendingSends.remove(task);
+ }
+
+ if (callback != null) {
+ callback.invoke();
+ }
+ }
+
+ /**
+ * Transmits an error to the javascript layer, if a callback is present.
+ */
+ @Override
+ public void onDataSentError(UdpSenderTask task, String error) {
+ Callback callback;
+
+ synchronized (mPendingSends) {
+ callback = mPendingSends.get(task);
+ mPendingSends.remove(task);
+ }
+
+ if (callback != null) {
+ callback.invoke(UdpErrorUtil.getError(null, error));
+ }
+ }
+
+ /**
+ * Retransmits the exception back a level, attaching {@code this}
+ */
+ @Override
+ public void onDataSentRuntimeException(UdpSenderTask task, RuntimeException exception) {
+ mExceptionListener.didReceiveException(exception);
+ synchronized (mPendingSends) {
+ mPendingSends.remove(task);
+ }
+ }
+
+
+ public static class Builder {
+ private OnDataReceivedListener receiverListener;
+ private OnRuntimeExceptionListener exceptionListener;
+ private boolean reuse = true;
+
+ public Builder(OnDataReceivedListener receiverListener, OnRuntimeExceptionListener exceptionListener) {
+ this.receiverListener = receiverListener;
+ this.exceptionListener = exceptionListener;
+ }
+
+ public Builder reuseAddress(boolean reuse) {
+ this.reuse = reuse;
+ return this;
+ }
+
+ public UdpSocketClient build() {
+ return new UdpSocketClient(this);
+ }
+ }
+
+ /**
+ * Callback interface for runtime exceptions.
+ */
+ public interface OnRuntimeExceptionListener {
+ void didReceiveException(RuntimeException exception);
+ }
+
+ /**
+ * Callback interface data received events.
+ */
+ public interface OnDataReceivedListener {
+ void didReceiveData(UdpSocketClient client, String data, String host, int port);
+ void didReceiveError(UdpSocketClient client, String message);
+ }
+}
diff --git a/android/src/main/java/com/tradle/react/UdpSockets.java b/android/src/main/java/com/tradle/react/UdpSockets.java
new file mode 100644
index 0000000..2efba57
--- /dev/null
+++ b/android/src/main/java/com/tradle/react/UdpSockets.java
@@ -0,0 +1,277 @@
+/**
+ * UdpSockets.java
+ * react-native-udp
+ *
+ * Created by Andy Prock on 9/24/15.
+ */
+
+package com.tradle.react;
+
+import android.support.annotation.Nullable;
+import android.util.SparseArray;
+
+import com.facebook.common.logging.FLog;
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.Callback;
+import com.facebook.react.bridge.GuardedAsyncTask;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
+import com.facebook.react.bridge.ReactMethod;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.modules.core.DeviceEventManagerModule;
+
+import java.io.IOException;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * The NativeModule in charge of storing active {@link UdpSocketClient}s, and acting as an api layer.
+ */
+public final class UdpSockets extends ReactContextBaseJavaModule
+ implements UdpSocketClient.OnDataReceivedListener, UdpSocketClient.OnRuntimeExceptionListener {
+ private static final String TAG = "UdpSockets";
+
+ private SparseArray mClients = new SparseArray<>();
+ private boolean mShuttingDown = false;
+
+ public UdpSockets(ReactApplicationContext reactContext) {
+ super(reactContext);
+ }
+
+ @Override
+ public String getName() {
+ return TAG;
+ }
+
+ @Override
+ public void initialize() {
+ mShuttingDown = false;
+ }
+
+ @Override
+ public void onCatalystInstanceDestroy() {
+ mShuttingDown = true;
+
+ // serialize on the AsyncTask thread, and block
+ try {
+ new GuardedAsyncTask(getReactApplicationContext()) {
+ @Override
+ protected void doInBackgroundGuarded(Void... params) {
+ for (int i = 0; i < mClients.size(); i++) {
+ try {
+ mClients.valueAt(i).close();
+ } catch (IOException e) {
+ FLog.e(TAG, "exception when shutting down", e);
+ }
+ }
+ mClients.clear();
+ }
+ }.execute().get();
+ } catch (InterruptedException e) {
+ FLog.e(TAG, "onCatalystInstanceDestroy", e);
+ } catch (ExecutionException e) {
+ FLog.e(TAG, "onCatalystInstanceDestroy", e);
+ }
+ }
+
+ /**
+ * Private method to retrieve clients. Must be called from a GuardedAsyncTask for thread-safety.
+ */
+ private UdpSocketClient findClient(final Integer cId, final Callback callback) {
+ final UdpSocketClient client = mClients.get(cId);
+ if (client == null) {
+ if (callback == null) {
+ FLog.e(TAG, "missing callback parameter.");
+ } else {
+ callback.invoke(UdpErrorUtil.getError(null, "no client found with id " + cId), null);
+ }
+ }
+
+ return client;
+ }
+
+ /**
+ * Creates a {@link UdpSocketClient} with the given ID, and options
+ */
+ @ReactMethod
+ public void createSocket(final Integer cId, final ReadableMap options) {
+ new GuardedAsyncTask(getReactApplicationContext()) {
+ @Override
+ protected void doInBackgroundGuarded(Void... params) {
+ if (cId == null) {
+ FLog.e(TAG, "createSocket called with nil id parameter.");
+ return;
+ }
+
+ UdpSocketClient client = mClients.get(cId);
+ if (client != null) {
+ FLog.e(TAG, "createSocket called twice with the same id.");
+ return;
+ }
+
+ UdpSocketClient.Builder builder = new UdpSocketClient.Builder(UdpSockets.this, UdpSockets.this);
+ mClients.put(cId, builder.build());
+ }
+ }.execute();
+ }
+
+ /**
+ * Binds to a given port and address, and begins listening for data.
+ */
+ @ReactMethod
+ public void bind(final Integer cId, final Integer port, final @Nullable String address,
+ final Callback callback) {
+ new GuardedAsyncTask(getReactApplicationContext()) {
+ @Override
+ protected void doInBackgroundGuarded(Void... params) {
+ UdpSocketClient client = findClient(cId, callback);
+ if (client == null) {
+ return;
+ }
+
+ try {
+ client.bind(port, address);
+
+ WritableMap result = Arguments.createMap();
+ result.putString("address", address);
+ result.putInt("port", port);
+
+ callback.invoke(null, result);
+ } catch (SocketException exception) {
+ // Socket is already bound or a problem occurred during binding
+ callback.invoke(UdpErrorUtil.getError(null, exception.getMessage()));
+ } catch (IllegalArgumentException exception) {
+ // SocketAddress is not supported
+ callback.invoke(UdpErrorUtil.getError(null, exception.getMessage()));
+ } catch (IOException exception) {
+ // an exception occurred
+ callback.invoke(UdpErrorUtil.getError(null, exception.getMessage()));
+ }
+ }
+ }.execute();
+ }
+
+ /**
+ * Sends udp data via the {@link UdpSocketClient}
+ */
+ @ReactMethod
+ public void send(final Integer cId, final String base64String,
+ final Integer port, final String address, final Callback callback) {
+ new GuardedAsyncTask(getReactApplicationContext()) {
+ @Override
+ protected void doInBackgroundGuarded(Void... params) {
+ UdpSocketClient client = findClient(cId, callback);
+ if (client == null) {
+ return;
+ }
+
+ try {
+ client.send(base64String, port, address, callback);
+ } catch (UnknownHostException e) {
+ callback.invoke(UdpErrorUtil.getError(null, e.getMessage()));
+ }
+ }
+ }.execute();
+ }
+
+ /**
+ * Closes a specific client's socket, and removes it from the list of known clients.
+ */
+ @ReactMethod
+ public void close(final Integer cId, final Callback callback) {
+ new GuardedAsyncTask(getReactApplicationContext()) {
+ @Override
+ protected void doInBackgroundGuarded(Void... params) {
+ UdpSocketClient client = findClient(cId, callback);
+ if (client == null) {
+ return;
+ }
+
+ try {
+ client.close();
+ callback.invoke();
+ } catch (IOException e) {
+ callback.invoke(UdpErrorUtil.getError(null, e.getMessage()));
+ }
+
+ mClients.remove(cId);
+ }
+ }.execute();
+ }
+
+ /**
+ * Sets the broadcast flag for a given client.
+ */
+ @ReactMethod
+ public void setBroadcast(final Integer cId, final Boolean flag, final Callback callback) {
+ new GuardedAsyncTask(getReactApplicationContext()) {
+ @Override
+ protected void doInBackgroundGuarded(Void... params) {
+ UdpSocketClient client = findClient(cId, callback);
+ if (client == null) {
+ return;
+ }
+
+ try {
+ client.setBroadcast(flag);
+ callback.invoke();
+ } catch (SocketException e) {
+ callback.invoke(UdpErrorUtil.getError(null, e.getMessage()));
+ }
+ }
+ }.execute();
+ }
+
+ /**
+ * Notifies the javascript layer upon data receipt.
+ */
+ @Override
+ public void didReceiveData(final UdpSocketClient socket, final String data, final String host, final int port) {
+ new GuardedAsyncTask(getReactApplicationContext()) {
+ @Override
+ protected void doInBackgroundGuarded(Void... params) {
+ int clientID = -1;
+ for(int i = 0; i < mClients.size(); i++) {
+ clientID = mClients.keyAt(i);
+ // get the object by the key.
+ if (socket.equals(mClients.get(clientID))) {
+ break;
+ }
+ }
+
+ if (clientID == -1) {
+ return;
+ }
+
+ WritableMap eventParams = Arguments.createMap();
+ eventParams.putString("data", data);
+ eventParams.putString("address", host);
+ eventParams.putInt("port", port);
+
+ ReactContext reactContext = UdpSockets.this.getReactApplicationContext();
+ reactContext
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
+ .emit("udp-" + clientID + "-data", eventParams);
+ }
+ }.execute();
+ }
+
+ /**
+ * Logs an error that happened during or prior to data reception.
+ */
+ @Override
+ public void didReceiveError(UdpSocketClient client, String message) {
+ FLog.e(TAG, message);
+ }
+
+ /**
+ * Sends RuntimeExceptions to the application context handler.
+ */
+ @Override
+ public void didReceiveException(RuntimeException exception) {
+ getReactApplicationContext().handleException(exception);
+ }
+}
diff --git a/android/src/main/java/com/tradle/react/UdpSocketsModule.java b/android/src/main/java/com/tradle/react/UdpSocketsModule.java
new file mode 100644
index 0000000..903a64d
--- /dev/null
+++ b/android/src/main/java/com/tradle/react/UdpSocketsModule.java
@@ -0,0 +1,43 @@
+/**
+ * UdpSocketsModule.java
+ * react-native-udp
+ *
+ * Created by Andy Prock on 9/24/15.
+ */
+
+package com.tradle.react;
+
+import com.facebook.react.ReactPackage;
+import com.facebook.react.bridge.JavaScriptModule;
+import com.facebook.react.bridge.NativeModule;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.uimanager.ViewManager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+public final class UdpSocketsModule implements ReactPackage {
+
+ @Override
+ public List createNativeModules(
+ ReactApplicationContext reactContext) {
+ List modules = new ArrayList<>();
+
+ modules.add(new UdpSockets(reactContext));
+
+ return modules;
+ }
+
+ @Override
+ public List> createJSModules() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List createViewManagers(
+ ReactApplicationContext reactContext) {
+ return Collections.emptyList();
+ }
+}
diff --git a/index.ios.js b/index.js
similarity index 100%
rename from index.ios.js
rename to index.js
diff --git a/CocoaAsyncSocket/GCDAsyncUdpSocket.h b/ios/CocoaAsyncSocket/GCDAsyncUdpSocket.h
similarity index 100%
rename from CocoaAsyncSocket/GCDAsyncUdpSocket.h
rename to ios/CocoaAsyncSocket/GCDAsyncUdpSocket.h
diff --git a/CocoaAsyncSocket/GCDAsyncUdpSocket.m b/ios/CocoaAsyncSocket/GCDAsyncUdpSocket.m
similarity index 100%
rename from CocoaAsyncSocket/GCDAsyncUdpSocket.m
rename to ios/CocoaAsyncSocket/GCDAsyncUdpSocket.m
diff --git a/ReactUdp.podspec b/ios/ReactUdp.podspec
similarity index 100%
rename from ReactUdp.podspec
rename to ios/ReactUdp.podspec
diff --git a/UdpSocketClient.h b/ios/UdpSocketClient.h
similarity index 100%
rename from UdpSocketClient.h
rename to ios/UdpSocketClient.h
diff --git a/UdpSocketClient.m b/ios/UdpSocketClient.m
similarity index 100%
rename from UdpSocketClient.m
rename to ios/UdpSocketClient.m
diff --git a/UdpSockets.h b/ios/UdpSockets.h
similarity index 100%
rename from UdpSockets.h
rename to ios/UdpSockets.h
diff --git a/UdpSockets.m b/ios/UdpSockets.m
similarity index 100%
rename from UdpSockets.m
rename to ios/UdpSockets.m
diff --git a/UdpSockets.xcodeproj/project.pbxproj b/ios/UdpSockets.xcodeproj/project.pbxproj
similarity index 100%
rename from UdpSockets.xcodeproj/project.pbxproj
rename to ios/UdpSockets.xcodeproj/project.pbxproj
diff --git a/UdpSockets.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/UdpSockets.xcodeproj/project.xcworkspace/contents.xcworkspacedata
similarity index 100%
rename from UdpSockets.xcodeproj/project.xcworkspace/contents.xcworkspacedata
rename to ios/UdpSockets.xcodeproj/project.xcworkspace/contents.xcworkspacedata
diff --git a/package.json b/package.json
index 807ec07..2ee57d0 100644
--- a/package.json
+++ b/package.json
@@ -2,16 +2,16 @@
"name": "react-native-udp",
"version": "0.1.0",
"description": "node's dgram API for react-native",
- "main": "./UdpSockets.ios.js",
+ "main": "UdpSockets.js",
"scripts": {
"start": "exit 1"
},
"browser": {
- "dgram": "./UdpSockets.ios.js"
+ "dgram": "./UdpSockets.js"
},
"repository": {
"type": "git",
- "url": "https://github.com/tradle/react-native-udp"
+ "url": "git+https://github.com/tradle/react-native-udp.git"
},
"keywords": [
"react-component",
@@ -20,7 +20,8 @@
"dgram",
"udp",
"sockets",
- "ios"
+ "ios",
+ "android"
],
"author": {
"name": "Mark Vayngrib",