initial android support
moved ios specific code into a subfolder created an android folder updated the Install instructions on the readme bumped the version to 0.2.0
This commit is contained in:
parent
4cb6e77504
commit
3ce4b63536
64
README.md
64
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!
|
||||
|
|
|
@ -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;
|
|
@ -4,7 +4,7 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
var UdpSocket = require('./UdpSocket.ios')
|
||||
var UdpSocket = require('./UdpSocket');
|
||||
|
||||
exports.createSocket = function(type) {
|
||||
return new UdpSocket({
|
|
@ -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.+'
|
||||
}
|
|
@ -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 *;
|
||||
#}
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.tradle.react" >
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
</manifest>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<Void, UdpReceiverTask.ReceivedPacket, Void> {
|
||||
private static final String TAG = "UdpReceiverTask";
|
||||
private static final int MAX_UDP_DATAGRAM_LEN = 1024;
|
||||
|
||||
private DatagramSocket mSocket;
|
||||
private WeakReference<OnDataReceivedListener> 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);
|
||||
}
|
||||
}
|
|
@ -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<DatagramPacket, Void, Void> {
|
||||
private static final String TAG = "UdpSenderTask";
|
||||
|
||||
private DatagramSocket mSocket;
|
||||
private WeakReference<OnDataSentListener> 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);
|
||||
}
|
||||
}
|
|
@ -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<UdpSenderTask, Callback> 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);
|
||||
}
|
||||
}
|
|
@ -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<UdpSocketClient> 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<Void, Void>(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<Void, Void>(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<Void, Void>(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<Void, Void>(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<Void, Void>(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<Void, Void>(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<Void, Void>(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);
|
||||
}
|
||||
}
|
|
@ -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<NativeModule> createNativeModules(
|
||||
ReactApplicationContext reactContext) {
|
||||
List<NativeModule> modules = new ArrayList<>();
|
||||
|
||||
modules.add(new UdpSockets(reactContext));
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(
|
||||
ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue