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",