Add blob implementation with WebSocket integration

Summary:
This is the first PR from a series of PRs grabbou and me will make to add blob support to React Native. The next PR will include blob support for XMLHttpRequest.

I'd like to get this merged with minimal changes to preserve the attribution. My next PR can contain bigger changes.

Blobs are used to transfer binary data between server and client. Currently React Native lacks a way to deal with binary data. The only thing that comes close is uploading files through a URI.

Current workarounds to transfer binary data includes encoding and decoding them to base64 and and transferring them as string, which is not ideal, since it increases the payload size and the whole payload needs to be sent via the bridge every time changes are made.

The PR adds a way to deal with blobs via a new native module. The blob is constructed on the native side and the data never needs to pass through the bridge. Currently the only way to create a blob is to receive a blob from the server via websocket.

The PR is largely a direct port of https://github.com/silklabs/silk/tree/master/react-native-blobs by philikon into RN (with changes to integrate with RN), and attributed as such.

> **Note:** This is a breaking change for all people running iOS without CocoaPods. You will have to manually add `RCTBlob.xcodeproj` to your `Libraries` and then, add it to Build Phases. Just follow the process of manual linking. We'll also need to document this process in the release notes.

Related discussion - https://github.com/facebook/react-native/issues/11103

- `Image` can't show image when `URL.createObjectURL` is used with large images on Android

The websocket integration can be tested via a simple server,

```js
const fs = require('fs');
const http = require('http');

const WebSocketServer = require('ws').Server;

const wss = new WebSocketServer({
  server: http.createServer().listen(7232),
});

wss.on('connection', (ws) => {
  ws.on('message', (d) => {
    console.log(d);
  });

  ws.send(fs.readFileSync('./some-file'));
});
```

Then on the client,

```js
var ws = new WebSocket('ws://localhost:7232');

ws.binaryType = 'blob';

ws.onerror = (error) => {
  console.error(error);
};

ws.onmessage = (e) => {
  console.log(e.data);
  ws.send(e.data);
};
```

cc brentvatne ide
Closes https://github.com/facebook/react-native/pull/11417

Reviewed By: sahrens

Differential Revision: D5188484

Pulled By: javache

fbshipit-source-id: 6afcbc4d19aa7a27b0dc9d52701ba400e7d7e98f
This commit is contained in:
Philipp von Weitershausen
2017-07-26 08:12:12 -07:00
committed by Facebook Github Bot
parent 91493f6b9d
commit ed903099b4
26 changed files with 1800 additions and 307 deletions

View File

@@ -0,0 +1,22 @@
include_defs("//ReactAndroid/DEFS")
android_library(
name = "blob",
srcs = glob(["**/*.java"]),
visibility = [
"PUBLIC",
],
deps = [
react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"),
react_native_dep("third-party/android/support-annotations:android-support-annotations"),
react_native_dep("third-party/android/support/v4:lib-support-v4"),
react_native_dep("third-party/java/infer-annotations:infer-annotations"),
react_native_dep("third-party/java/jsr-305:jsr-305"),
react_native_dep("third-party/java/okio:okio"),
react_native_target("java/com/facebook/react:react"),
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target("java/com/facebook/react/modules/websocket:websocket"),
],
)

View File

@@ -0,0 +1,180 @@
/**
* Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
*
* <p>This source code is licensed under the BSD-style license found in the LICENSE file in the root
* directory of this source tree. An additional grant of patent rights can be found in the PATENTS
* file in the same directory.
*/
package com.facebook.react.modules.blob;
import android.content.res.Resources;
import android.net.Uri;
import android.support.annotation.Nullable;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.websocket.WebSocketModule;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import okio.ByteString;
@ReactModule(name = BlobModule.NAME)
public class BlobModule extends ReactContextBaseJavaModule {
protected static final String NAME = "BlobModule";
private final Map<String, byte[]> mBlobs = new HashMap<>();
protected final WebSocketModule.ContentHandler mContentHandler =
new WebSocketModule.ContentHandler() {
@Override
public void onMessage(String text, WritableMap params) {
params.putString("data", text);
}
@Override
public void onMessage(ByteString bytes, WritableMap params) {
byte[] data = bytes.toByteArray();
WritableMap blob = Arguments.createMap();
blob.putString("blobId", store(data));
blob.putInt("offset", 0);
blob.putInt("size", data.length);
params.putMap("data", blob);
params.putString("type", "blob");
}
};
public BlobModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return NAME;
}
@Override
@Nullable
public Map getConstants() {
// The application can register BlobProvider as a ContentProvider so that blobs are resolvable.
// If it does, it needs to tell us what authority was used via this string resource.
Resources resources = getReactApplicationContext().getResources();
String packageName = getReactApplicationContext().getPackageName();
int resourceId = resources.getIdentifier("blob_provider_authority", "string", packageName);
if (resourceId == 0) {
return null;
}
return MapBuilder.of(
"BLOB_URI_SCHEME", "content", "BLOB_URI_HOST", resources.getString(resourceId));
}
public String store(byte[] data) {
String blobId = UUID.randomUUID().toString();
store(data, blobId);
return blobId;
}
public void store(byte[] data, String blobId) {
mBlobs.put(blobId, data);
}
public void remove(String blobId) {
mBlobs.remove(blobId);
}
@Nullable
public byte[] resolve(Uri uri) {
String blobId = uri.getLastPathSegment();
int offset = 0;
int size = -1;
String offsetParam = uri.getQueryParameter("offset");
if (offsetParam != null) {
offset = Integer.parseInt(offsetParam, 10);
}
String sizeParam = uri.getQueryParameter("size");
if (sizeParam != null) {
size = Integer.parseInt(sizeParam, 10);
}
return resolve(blobId, offset, size);
}
@Nullable
public byte[] resolve(String blobId, int offset, int size) {
byte[] data = mBlobs.get(blobId);
if (data == null) {
return null;
}
if (size == -1) {
size = data.length - offset;
}
if (offset > 0) {
data = Arrays.copyOfRange(data, offset, offset + size);
}
return data;
}
@Nullable
public byte[] resolve(ReadableMap blob) {
return resolve(blob.getString("blobId"), blob.getInt("offset"), blob.getInt("size"));
}
private WebSocketModule getWebSocketModule() {
return getReactApplicationContext().getNativeModule(WebSocketModule.class);
}
@ReactMethod
public void enableBlobSupport(final int id) {
getWebSocketModule().setContentHandler(id, mContentHandler);
}
@ReactMethod
public void disableBlobSupport(final int id) {
getWebSocketModule().setContentHandler(id, null);
}
@ReactMethod
public void sendBlob(ReadableMap blob, int id) {
byte[] data = resolve(blob.getString("blobId"), blob.getInt("offset"), blob.getInt("size"));
if (data != null) {
getWebSocketModule().sendBinary(ByteString.of(data), id);
} else {
getWebSocketModule().sendBinary((ByteString) null, id);
}
}
@ReactMethod
public void createFromParts(ReadableArray parts, String blobId) {
int totalBlobSize = 0;
ArrayList<ReadableMap> partList = new ArrayList<>(parts.size());
for (int i = 0; i < parts.size(); i++) {
ReadableMap part = parts.getMap(i);
totalBlobSize += part.getInt("size");
partList.add(i, part);
}
ByteBuffer buffer = ByteBuffer.allocate(totalBlobSize);
for (ReadableMap part : partList) {
buffer.put(resolve(part));
}
store(buffer.array(), blobId);
}
@ReactMethod
public void release(String blobId) {
remove(blobId);
}
}

View File

@@ -0,0 +1,99 @@
/**
* Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
*
* <p>This source code is licensed under the BSD-style license found in the LICENSE file in the root
* directory of this source tree. An additional grant of patent rights can be found in the PATENTS
* file in the same directory.
*/
package com.facebook.react.modules.blob;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.support.annotation.Nullable;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.bridge.ReactContext;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
public final class BlobProvider extends ContentProvider {
@Override
public boolean onCreate() {
return true;
}
@Override
public @Nullable Cursor query(
Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
@Override
public @Nullable String getType(Uri uri) {
return null;
}
@Override
public @Nullable Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
if (!mode.equals("r")) {
throw new FileNotFoundException("Cannot open " + uri.toString() + " in mode '" + mode + "'");
}
BlobModule blobModule = null;
Context context = getContext().getApplicationContext();
if (context instanceof ReactApplication) {
ReactNativeHost host = ((ReactApplication) context).getReactNativeHost();
ReactContext reactContext = host.getReactInstanceManager().getCurrentReactContext();
blobModule = reactContext.getNativeModule(BlobModule.class);
}
if (blobModule == null) {
throw new RuntimeException("No blob module associated with BlobProvider");
}
byte[] data = blobModule.resolve(uri);
if (data == null) {
throw new FileNotFoundException("Cannot open " + uri.toString() + ", blob not found.");
}
ParcelFileDescriptor[] pipe;
try {
pipe = ParcelFileDescriptor.createPipe();
} catch (IOException exception) {
return null;
}
ParcelFileDescriptor readSide = pipe[0];
ParcelFileDescriptor writeSide = pipe[1];
OutputStream outputStream = new ParcelFileDescriptor.AutoCloseOutputStream(writeSide);
try {
outputStream.write(data);
outputStream.close();
} catch (IOException exception) {
return null;
}
return readSide;
}
}

View File

@@ -9,16 +9,6 @@
package com.facebook.react.modules.websocket;
import javax.annotation.Nullable;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
@@ -34,7 +24,14 @@ import com.facebook.react.common.ReactConstants;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.modules.network.ForwardingCookieHandler;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
@@ -43,9 +40,16 @@ import okhttp3.WebSocketListener;
import okio.ByteString;
@ReactModule(name = "WebSocketModule", hasConstants = false)
public class WebSocketModule extends ReactContextBaseJavaModule {
public final class WebSocketModule extends ReactContextBaseJavaModule {
public interface ContentHandler {
void onMessage(String text, WritableMap params);
void onMessage(ByteString byteString, WritableMap params);
}
private final Map<Integer, WebSocket> mWebSocketConnections = new HashMap<>();
private final Map<Integer, ContentHandler> mContentHandlers = new HashMap<>();
private ReactContext mReactContext;
private ForwardingCookieHandler mCookieHandler;
@@ -67,6 +71,14 @@ public class WebSocketModule extends ReactContextBaseJavaModule {
return "WebSocketModule";
}
public void setContentHandler(final int id, final ContentHandler contentHandler) {
if (contentHandler != null) {
mContentHandlers.put(id, contentHandler);
} else {
mContentHandlers.remove(id);
}
}
@ReactMethod
public void connect(
final String url,
@@ -79,9 +91,7 @@ public class WebSocketModule extends ReactContextBaseJavaModule {
.readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read
.build();
Request.Builder builder = new Request.Builder()
.tag(id)
.url(url);
Request.Builder builder = new Request.Builder().tag(id).url(url);
String cookie = getCookie(url);
if (cookie != null) {
@@ -124,49 +134,65 @@ public class WebSocketModule extends ReactContextBaseJavaModule {
}
}
client.newWebSocket(builder.build(), new WebSocketListener() {
client.newWebSocket(
builder.build(),
new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
mWebSocketConnections.put(id, webSocket);
WritableMap params = Arguments.createMap();
params.putInt("id", id);
sendEvent("websocketOpen", params);
}
@Override
public void onOpen(WebSocket webSocket, Response response) {
mWebSocketConnections.put(id, webSocket);
WritableMap params = Arguments.createMap();
params.putInt("id", id);
sendEvent("websocketOpen", params);
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
WritableMap params = Arguments.createMap();
params.putInt("id", id);
params.putInt("code", code);
params.putString("reason", reason);
sendEvent("websocketClosed", params);
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
WritableMap params = Arguments.createMap();
params.putInt("id", id);
params.putInt("code", code);
params.putString("reason", reason);
sendEvent("websocketClosed", params);
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
notifyWebSocketFailed(id, t.getMessage());
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
notifyWebSocketFailed(id, t.getMessage());
}
@Override
public void onMessage(WebSocket webSocket, String text) {
WritableMap params = Arguments.createMap();
params.putInt("id", id);
params.putString("data", text);
params.putString("type", "text");
sendEvent("websocketMessage", params);
}
@Override
public void onMessage(WebSocket webSocket, String text) {
WritableMap params = Arguments.createMap();
params.putInt("id", id);
params.putString("type", "text");
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
String text = bytes.base64();
WritableMap params = Arguments.createMap();
params.putInt("id", id);
params.putString("data", text);
params.putString("type", "binary");
sendEvent("websocketMessage", params);
}
});
ContentHandler contentHandler = mContentHandlers.get(id);
if (contentHandler != null) {
contentHandler.onMessage(text, params);
} else {
params.putString("data", text);
}
sendEvent("websocketMessage", params);
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
WritableMap params = Arguments.createMap();
params.putInt("id", id);
params.putString("type", "binary");
ContentHandler contentHandler = mContentHandlers.get(id);
if (contentHandler != null) {
contentHandler.onMessage(bytes, params);
} else {
String text = bytes.base64();
params.putString("data", text);
}
sendEvent("websocketMessage", params);
}
});
// Trigger shutdown of the dispatcher's executor so this process can exit cleanly
client.dispatcher().executorService().shutdown();
@@ -183,6 +209,7 @@ public class WebSocketModule extends ReactContextBaseJavaModule {
try {
client.close(code, reason);
mWebSocketConnections.remove(id);
mContentHandlers.remove(id);
} catch (Exception e) {
FLog.e(
ReactConstants.TAG,
@@ -219,6 +246,19 @@ public class WebSocketModule extends ReactContextBaseJavaModule {
}
}
public void sendBinary(ByteString byteString, int id) {
WebSocket client = mWebSocketConnections.get(id);
if (client == null) {
// This is a programmer error
throw new RuntimeException("Cannot send a message. Unknown WebSocket id " + id);
}
try {
client.send(byteString);
} catch (Exception e) {
notifyWebSocketFailed(id, e.getMessage());
}
}
@ReactMethod
public void ping(int id) {
WebSocket client = mWebSocketConnections.get(id);
@@ -243,10 +283,9 @@ public class WebSocketModule extends ReactContextBaseJavaModule {
/**
* Get the default HTTP(S) origin for a specific WebSocket URI
*
* @param String uri
* @param uri
* @return A string of the endpoint converted to HTTP protocol (http[s]://host[:port])
*/
private static String getDefaultOrigin(String uri) {
try {
String defaultOrigin;
@@ -280,7 +319,7 @@ public class WebSocketModule extends ReactContextBaseJavaModule {
/**
* Get the cookie for a specific domain
*
* @param String uri
* @param uri
* @return The cookie header or null if none is set
*/
private String getCookie(String uri) {

View File

@@ -23,6 +23,7 @@ android_library(
react_native_target("java/com/facebook/react/module/model:model"),
react_native_target("java/com/facebook/react/modules/accessibilityinfo:accessibilityinfo"),
react_native_target("java/com/facebook/react/modules/appstate:appstate"),
react_native_target("java/com/facebook/react/modules/blob:blob"),
react_native_target("java/com/facebook/react/modules/camera:camera"),
react_native_target("java/com/facebook/react/modules/clipboard:clipboard"),
react_native_target("java/com/facebook/react/modules/core:core"),

View File

@@ -13,7 +13,6 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.facebook.react.LazyReactPackage;
import com.facebook.react.animated.NativeAnimatedModule;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.ModuleSpec;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
@@ -30,6 +29,7 @@ import com.facebook.react.flat.RCTVirtualTextManager;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import com.facebook.react.modules.accessibilityinfo.AccessibilityInfoModule;
import com.facebook.react.modules.appstate.AppStateModule;
import com.facebook.react.modules.blob.BlobModule;
import com.facebook.react.modules.camera.CameraRollManager;
import com.facebook.react.modules.camera.ImageEditingManager;
import com.facebook.react.modules.camera.ImageStoreManager;
@@ -74,12 +74,10 @@ import com.facebook.react.views.toolbar.ReactToolbarManager;
import com.facebook.react.views.view.ReactViewManager;
import com.facebook.react.views.viewpager.ReactViewPagerManager;
import com.facebook.react.views.webview.ReactWebViewManager;
import javax.inject.Provider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.inject.Provider;
/**
* Package defining basic modules and view managers.
@@ -101,150 +99,207 @@ public class MainReactPackage extends LazyReactPackage {
@Override
public List<ModuleSpec> getNativeModules(final ReactApplicationContext context) {
return Arrays.asList(
new ModuleSpec(AccessibilityInfoModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new AccessibilityInfoModule(context);
}
}),
new ModuleSpec(AppStateModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new AppStateModule(context);
}
}),
new ModuleSpec(AsyncStorageModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new AsyncStorageModule(context);
}
}),
new ModuleSpec(CameraRollManager.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new CameraRollManager(context);
}
}),
new ModuleSpec(ClipboardModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ClipboardModule(context);
}
}),
new ModuleSpec(DatePickerDialogModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new DatePickerDialogModule(context);
}
}),
new ModuleSpec(DialogModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new DialogModule(context);
}
}),
new ModuleSpec(FrescoModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new FrescoModule(context, true, mConfig != null ? mConfig.getFrescoConfig() : null);
}
}),
new ModuleSpec(I18nManagerModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new I18nManagerModule(context);
}
}),
new ModuleSpec(ImageEditingManager.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ImageEditingManager(context);
}
}),
new ModuleSpec(ImageLoaderModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ImageLoaderModule(context);
}
}),
new ModuleSpec(ImageStoreManager.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ImageStoreManager(context);
}
}),
new ModuleSpec(IntentModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new IntentModule(context);
}
}),
new ModuleSpec(LocationModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new LocationModule(context);
}
}),
new ModuleSpec(NativeAnimatedModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new NativeAnimatedModule(context);
}
}),
new ModuleSpec(NetworkingModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new NetworkingModule(context);
}
}),
new ModuleSpec(NetInfoModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new NetInfoModule(context);
}
}),
new ModuleSpec(PermissionsModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new PermissionsModule(context);
}
}),
new ModuleSpec(ShareModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ShareModule(context);
}
}),
new ModuleSpec(StatusBarModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new StatusBarModule(context);
}
}),
new ModuleSpec(TimePickerDialogModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new TimePickerDialogModule(context);
}
}),
new ModuleSpec(ToastModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ToastModule(context);
}
}),
new ModuleSpec(VibrationModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new VibrationModule(context);
}
}),
new ModuleSpec(WebSocketModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new WebSocketModule(context);
}
}));
new ModuleSpec(
AccessibilityInfoModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new AccessibilityInfoModule(context);
}
}),
new ModuleSpec(
AppStateModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new AppStateModule(context);
}
}),
new ModuleSpec(
BlobModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new BlobModule(context);
}
}),
new ModuleSpec(
AsyncStorageModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new AsyncStorageModule(context);
}
}),
new ModuleSpec(
CameraRollManager.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new CameraRollManager(context);
}
}),
new ModuleSpec(
ClipboardModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ClipboardModule(context);
}
}),
new ModuleSpec(
DatePickerDialogModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new DatePickerDialogModule(context);
}
}),
new ModuleSpec(
DialogModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new DialogModule(context);
}
}),
new ModuleSpec(
FrescoModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new FrescoModule(
context, true, mConfig != null ? mConfig.getFrescoConfig() : null);
}
}),
new ModuleSpec(
I18nManagerModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new I18nManagerModule(context);
}
}),
new ModuleSpec(
ImageEditingManager.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ImageEditingManager(context);
}
}),
new ModuleSpec(
ImageLoaderModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ImageLoaderModule(context);
}
}),
new ModuleSpec(
ImageStoreManager.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ImageStoreManager(context);
}
}),
new ModuleSpec(
IntentModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new IntentModule(context);
}
}),
new ModuleSpec(
LocationModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new LocationModule(context);
}
}),
new ModuleSpec(
NativeAnimatedModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new NativeAnimatedModule(context);
}
}),
new ModuleSpec(
NetworkingModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new NetworkingModule(context);
}
}),
new ModuleSpec(
NetInfoModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new NetInfoModule(context);
}
}),
new ModuleSpec(
PermissionsModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new PermissionsModule(context);
}
}),
new ModuleSpec(
ShareModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ShareModule(context);
}
}),
new ModuleSpec(
StatusBarModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new StatusBarModule(context);
}
}),
new ModuleSpec(
TimePickerDialogModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new TimePickerDialogModule(context);
}
}),
new ModuleSpec(
ToastModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ToastModule(context);
}
}),
new ModuleSpec(
VibrationModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new VibrationModule(context);
}
}),
new ModuleSpec(
WebSocketModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new WebSocketModule(context);
}
}));
}
@Override