Make it possible to write JS only plugins
Summary: This diff is part of the bigger task T60496135 This diff is based on D18706643, extracting only the react native module parts It implements the entire Android client api for JavaScript, so that there is feature parity. However this implementation is happy path only, and edge cases will be handled in separate diffs Reviewed By: jknoxville Differential Revision: D19310265 fbshipit-source-id: 589716fe059952bdde98df84ed250c5c6feaa118
This commit is contained in:
committed by
Facebook Github Bot
parent
3fab1f8fd6
commit
c7158f4517
@@ -0,0 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.facebook.flipper.reactnative">
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.flipper.reactnative;
|
||||
|
||||
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||
import com.facebook.flipper.core.FlipperArray;
|
||||
import com.facebook.flipper.core.FlipperClient;
|
||||
import com.facebook.flipper.core.FlipperConnection;
|
||||
import com.facebook.flipper.core.FlipperObject;
|
||||
import com.facebook.flipper.core.FlipperPlugin;
|
||||
import com.facebook.flipper.core.FlipperReceiver;
|
||||
import com.facebook.flipper.core.FlipperResponder;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
|
||||
@ReactModule(name = FlipperModule.NAME)
|
||||
public class FlipperModule extends ReactContextBaseJavaModule {
|
||||
|
||||
public static final String NAME = "Flipper";
|
||||
|
||||
private final ReactApplicationContext reactContext;
|
||||
private final FlipperClient flipperClient;
|
||||
private final Map<String, FlipperConnection> connections;
|
||||
private final Map<String, FlipperResponder> responders;
|
||||
private final AtomicLong responderId = new AtomicLong();
|
||||
|
||||
public FlipperModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.reactContext = reactContext;
|
||||
this.flipperClient = AndroidFlipperClient.getInstanceIfInitialized();
|
||||
this.connections = new ConcurrentHashMap<>();
|
||||
this.responders = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void registerPlugin(
|
||||
final String pluginId,
|
||||
final Boolean inBackground,
|
||||
final Callback onConnect,
|
||||
final Callback onDisconnect) {
|
||||
FlipperPlugin plugin =
|
||||
new FlipperPlugin() {
|
||||
@Override
|
||||
public String getId() {
|
||||
return pluginId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnect(FlipperConnection connection) throws Exception {
|
||||
FlipperModule.this.connections.put(pluginId, connection);
|
||||
onConnect.invoke();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect() throws Exception {
|
||||
FlipperModule.this.connections.remove(pluginId);
|
||||
onDisconnect.invoke();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean runInBackground() {
|
||||
return inBackground;
|
||||
}
|
||||
};
|
||||
this.flipperClient.addPlugin(plugin);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void send(String pluginId, String method, String data) {
|
||||
// Optimization: throwing raw strings around to the desktop would probably avoid some double
|
||||
// parsing...
|
||||
Object parsedData = FlipperModule.parseJSON(data);
|
||||
FlipperConnection connection = this.connections.get(pluginId);
|
||||
if (parsedData instanceof FlipperArray) {
|
||||
connection.send(method, (FlipperArray) parsedData);
|
||||
} else {
|
||||
connection.send(method, (FlipperObject) parsedData);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void reportErrorWithMetadata(String pluginId, String reason, String stackTrace) {
|
||||
this.connections.get(pluginId).reportErrorWithMetadata(reason, stackTrace);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void reportError(String pluginId, String error) {
|
||||
this.connections.get(pluginId).reportError(new Error(error));
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void subscribe(String pluginId, String method, final Callback callback) {
|
||||
this.connections
|
||||
.get(pluginId)
|
||||
.receive(
|
||||
method,
|
||||
new FlipperReceiver() {
|
||||
|
||||
@Override
|
||||
public void onReceive(FlipperObject params, FlipperResponder responder)
|
||||
throws Exception {
|
||||
String id = String.valueOf(FlipperModule.this.responderId.incrementAndGet());
|
||||
FlipperModule.this.responders.put(id, responder);
|
||||
callback.invoke(params.toJsonString(), id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void respondSuccess(String responderId, String data) {
|
||||
FlipperResponder responder = FlipperModule.this.responders.remove(responderId);
|
||||
if (data == null) {
|
||||
responder.success();
|
||||
} else {
|
||||
Object parsedData = FlipperModule.parseJSON(data);
|
||||
if (parsedData instanceof FlipperArray) {
|
||||
responder.success((FlipperArray) parsedData);
|
||||
} else {
|
||||
responder.success((FlipperObject) parsedData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void respondError(String responderId, String data) {
|
||||
FlipperResponder responder = FlipperModule.this.responders.remove(responderId);
|
||||
Object parsedData = FlipperModule.parseJSON(data);
|
||||
if (parsedData instanceof FlipperArray) {
|
||||
responder.success((FlipperArray) parsedData);
|
||||
} else {
|
||||
responder.success((FlipperObject) parsedData);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object /* FlipperArray | FlipperObject */ parseJSON(String json) {
|
||||
// returns either a FlipperObject or Flipper array, pending the data
|
||||
try {
|
||||
JSONTokener tokener = new JSONTokener(json);
|
||||
if (tokener.nextClean() == '[') {
|
||||
tokener.back();
|
||||
return new FlipperArray(new JSONArray(tokener));
|
||||
} else {
|
||||
tokener.back();
|
||||
return new FlipperObject(new JSONObject(tokener));
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.flipper.reactnative;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class FlipperPackage implements ReactPackage {
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
return Arrays.<NativeModule>asList(new FlipperModule(reactContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user