Java side of NativePlugins

Summary:
Adds abstract classes NativePlugin and TableNativePlugin.

Extend NativePlugin to implement a new NativePlugin type/template.

TableNativePlugin is an example of that. Extend TableNativePlugin to add a particular instance of a table plugin. See the stacked diff for an example of that (newsfeed inspector).

I think I'm going to change the NativePlugin implementation so it uses composition instead of inheriting from FlipperPlugin, so that the concrete subclasses don't get access to the FlipperPlugin primitive methods like onConnect etc. But I don't mind shipping this as is and changing it separately.

Reviewed By: passy

Differential Revision: D14505912

fbshipit-source-id: 0534147112aaf4c5a41d2d3e08de855767b2a010
This commit is contained in:
John Knox
2019-03-28 06:42:07 -07:00
committed by Facebook Github Bot
parent f6f00611d5
commit ff6988ddaf
6 changed files with 325 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
package com.facebook.flipper.nativeplugins;
import com.facebook.flipper.core.FlipperPlugin;
/**
* Subclass of {@link FlipperPlugin} for mobile-defined plugins that conform to a template.
* Implementations should call {@link #NativePlugin(String, String)} to specify which template will
* be used. See {@link com.facebook.flipper.nativeplugins.table.TablePlugin} for an example
* subclass.
*/
public abstract class NativePlugin implements FlipperPlugin {
private final String pluginType;
private final String id;
/**
* Call super() inside subclass constructors to provide the template name and id of the concrete
* plugin instance.
*
* @param pluginType This needs to correspond to a plugin template defined in Flipper.
* @param id This will uniquely
*/
public NativePlugin(final String pluginType, final String id) {
this.pluginType = pluginType;
this.id = id;
}
@Override
public final String getId() {
return "_nativeplugin_" + pluginType + "_" + id;
}
}

View File

@@ -0,0 +1,17 @@
package com.facebook.flipper.nativeplugins.components;
import com.facebook.flipper.core.FlipperArray;
public class Sidebar {
private final FlipperArray.Builder sections = new FlipperArray.Builder();
public Sidebar addSection(SidebarSection section) {
sections.put(section.serialize());
return this;
}
public FlipperArray serialize() {
return sections.build();
}
}

View File

@@ -0,0 +1,8 @@
package com.facebook.flipper.nativeplugins.components;
import com.facebook.flipper.core.FlipperObject;
interface SidebarSection {
FlipperObject serialize();
}

View File

@@ -0,0 +1,26 @@
package com.facebook.flipper.nativeplugins.table;
public class TableMetadata {
final TablePlugin.Column[] mColumns;
private TableMetadata(TablePlugin.Column[] columns) {
if (columns == null) {
throw new IllegalArgumentException("columns must not be null");
}
this.mColumns = columns;
}
public static class Builder {
private TablePlugin.Column[] columns;
public Builder columns(TablePlugin.Column... columns) {
this.columns = columns;
return this;
}
public TableMetadata build() {
return new TableMetadata(columns);
}
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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.nativeplugins.table;
import com.facebook.flipper.core.FlipperArray;
import com.facebook.flipper.core.FlipperConnection;
import com.facebook.flipper.core.FlipperObject;
import com.facebook.flipper.core.FlipperReceiver;
import com.facebook.flipper.core.FlipperResponder;
import com.facebook.flipper.nativeplugins.NativePlugin;
import java.util.List;
public abstract class TablePlugin extends NativePlugin {
public static class Column {
public final String id;
final String displayName;
final String displayWidth;
final boolean showByDefault;
final boolean isFilterable;
Column(
String id,
String displayName,
String displayWidth,
boolean showByDefault,
boolean isFilterable) {
if (id == null) {
throw new IllegalArgumentException("id must not be null");
}
if (displayName == null) {
throw new IllegalArgumentException("displayName must not be null");
}
this.id = id;
this.displayName = displayName;
this.displayWidth = displayWidth;
this.showByDefault = showByDefault;
this.isFilterable = isFilterable;
}
public static class Builder {
private final String id;
private String displayName;
private String displayWidth;
private boolean showByDefault = true;
private boolean isFilterable = false;
public Builder(String id) {
this.id = id;
}
public Builder displayName(String displayName) {
this.displayName = displayName;
return this;
}
public Builder displayWidthPx(int displayWidth) {
this.displayWidth = Integer.toString(displayWidth);
return this;
}
public Builder displayWidthPercent(int displayWidth) {
this.displayWidth = Integer.toString(displayWidth) + "%";
return this;
}
public Builder showByDefault(boolean showByDefault) {
this.showByDefault = showByDefault;
return this;
}
public Builder isFilterable(boolean isFilterable) {
this.isFilterable = isFilterable;
return this;
}
public Column build() {
return new Column(id, displayName, displayWidth, showByDefault, isFilterable);
}
}
}
private FlipperConnection mConnection;
public TablePlugin(final String id) {
super("Table", id);
}
@Override
public final void onConnect(FlipperConnection connection) {
this.mConnection = connection;
connection.receive(
"getMetadata",
new FlipperReceiver() {
@Override
public void onReceive(FlipperObject params, FlipperResponder responder) throws Exception {
final FlipperObject.Builder columns = new FlipperObject.Builder();
final FlipperObject.Builder columnSizes = new FlipperObject.Builder();
final FlipperArray.Builder columnOrder = new FlipperArray.Builder();
final FlipperArray.Builder filterableColumns = new FlipperArray.Builder();
for (Column c : getMetadata().mColumns) {
columns.put(c.id, new FlipperObject.Builder().put("value", c.displayName).build());
columnSizes.put(c.id, c.displayWidth);
columnOrder.put(
new FlipperObject.Builder().put("key", c.id).put("visible", c.showByDefault));
if (c.isFilterable) {
filterableColumns.put(c.id);
}
}
responder.success(
new FlipperObject.Builder()
.put("columns", columns.build())
.put("columnSizes", columnSizes.build())
.put("columnOrder", columnOrder.build())
.put("filterableColumns", filterableColumns.build())
.build());
}
});
}
protected abstract void onConnected();
protected abstract void onDisconnected();
protected final void updateRows(List<? extends TableRow> rows) {
final FlipperArray.Builder array = new FlipperArray.Builder();
for (TableRow r : rows) {
array.put(r.serialize());
}
this.mConnection.send("updateRows", array.build());
}
public abstract TableMetadata getMetadata();
public List<? extends TableRow> getRows() {
throw new UnsupportedOperationException(
"getRows not implemented in "
+ getClass().getSimpleName()
+ ". Perhaps this is a streaming plugin?");
}
@Override
public void onDisconnect() throws Exception {
this.onDisconnected();
}
@Override
public final boolean runInBackground() {
return false;
}
}

View File

@@ -0,0 +1,86 @@
package com.facebook.flipper.nativeplugins.table;
import com.facebook.flipper.core.FlipperObject;
import com.facebook.flipper.nativeplugins.components.Sidebar;
import java.util.Map;
public abstract class TableRow {
interface Value {
FlipperObject serialize();
}
public static class StringValue implements Value {
private String val;
public StringValue(String s) {
this.val = s;
}
@Override
public FlipperObject serialize() {
return new FlipperObject.Builder().put("type", "string").put("value", val).build();
}
}
public static class IntValue implements Value {
private int val;
public IntValue(int i) {
this.val = i;
}
@Override
public FlipperObject serialize() {
return new FlipperObject.Builder().put("type", "int").put("value", val).build();
}
}
public static class TimeValue implements Value {
private long millis;
public TimeValue(long millis) {
this.millis = millis;
}
@Override
public FlipperObject serialize() {
return new FlipperObject.Builder().put("type", "time").put("value", millis).build();
}
}
public static class DurationValue implements Value {
private long millis;
public DurationValue(long millis) {
this.millis = millis;
}
@Override
public FlipperObject serialize() {
return new FlipperObject.Builder().put("type", "duration").put("value", millis).build();
}
}
final String id;
final Map<TablePlugin.Column, ? extends Value> values;
final Sidebar sidebar;
public TableRow(String id, Map<TablePlugin.Column, ? extends Value> values, Sidebar sidebar) {
this.id = id;
this.values = values;
this.sidebar = sidebar;
}
FlipperObject serialize() {
FlipperObject.Builder columnsObject = new FlipperObject.Builder();
for (Map.Entry<TablePlugin.Column, ? extends Value> e : values.entrySet()) {
columnsObject.put(e.getKey().id, e.getValue().serialize());
}
columnsObject.put("id", id);
return new FlipperObject.Builder()
.put("columns", columnsObject.build())
.put("sidebar", sidebar.serialize())
.put("id", id)
.build();
}
}