Add support for tagged messages on the Layout Inspector

Summary:
Related diff [CK]: D23243009

This diff adds support for a protocol for layout messages where the type is recursively encoded as:

```
{
 kind: "type",
 data: ???
}
```

The meat of the diff is on FlipperEditor.java, SetDataOperations.java and InspectorFlipperPlugin.java. The others are there just for a change on an interface.

We check if the message adheres to the new encoding, otherwise we fall back to the old behavior. If it's the new encoding, the message is traversed recursively flattening the types to EditorValue using the type hints provided.

Reviewed By: muraziz

Differential Revision: D23243009

fbshipit-source-id: 0f313455885930f3beaaadb66f3bf394f109ea23
This commit is contained in:
Paco Estevez Garcia
2020-08-28 08:49:51 -07:00
committed by Facebook GitHub Bot
parent 19b5b65081
commit ff3584e2e0
23 changed files with 323 additions and 49 deletions

View File

@@ -22,6 +22,7 @@ import com.facebook.flipper.plugins.inspector.HighlightedOverlay;
import com.facebook.flipper.plugins.inspector.InspectorValue;
import com.facebook.flipper.plugins.inspector.Named;
import com.facebook.flipper.plugins.inspector.NodeDescriptor;
import com.facebook.flipper.plugins.inspector.SetDataOperations;
import com.facebook.flipper.plugins.inspector.Touch;
import com.facebook.flipper.plugins.inspector.descriptors.ObjectDescriptor;
import com.facebook.flipper.plugins.inspector.descriptors.utils.ContextDescriptorUtils;
@@ -48,51 +49,61 @@ import javax.annotation.Nullable;
public class DebugComponentDescriptor extends NodeDescriptor<DebugComponent> {
private Map<String, List<Pair<String[], FlipperDynamic>>> mOverrides = new HashMap<>();
private Map<
String, List<Pair<String[], Pair<SetDataOperations.FlipperValueHint, FlipperDynamic>>>>
mOverrides = new HashMap<>();
private DebugComponent.Overrider mOverrider =
new DebugComponent.Overrider() {
@Override
public void applyComponentOverrides(String key, Component component) {
final List<Pair<String[], FlipperDynamic>> overrides = mOverrides.get(key);
final List<Pair<String[], Pair<SetDataOperations.FlipperValueHint, FlipperDynamic>>>
overrides = mOverrides.get(key);
if (overrides == null) {
return;
}
for (Pair<String[], FlipperDynamic> override : overrides) {
for (Pair<String[], Pair<SetDataOperations.FlipperValueHint, FlipperDynamic>> override :
overrides) {
if (override.first[0].equals("Props")) {
applyReflectiveOverride(component, override.first, override.second);
applyReflectiveOverride(
component, override.first, override.second.first, override.second.second);
}
}
}
@Override
public void applyStateOverrides(String key, StateContainer stateContainer) {
final List<Pair<String[], FlipperDynamic>> overrides = mOverrides.get(key);
final List<Pair<String[], Pair<SetDataOperations.FlipperValueHint, FlipperDynamic>>>
overrides = mOverrides.get(key);
if (overrides == null) {
return;
}
for (Pair<String[], FlipperDynamic> override : overrides) {
for (Pair<String[], Pair<SetDataOperations.FlipperValueHint, FlipperDynamic>> override :
overrides) {
if (override.first[0].equals("State")) {
applyReflectiveOverride(stateContainer, override.first, override.second);
applyReflectiveOverride(
stateContainer, override.first, override.second.first, override.second.second);
}
}
}
@Override
public void applyLayoutOverrides(String key, DebugLayoutNode node) {
final List<Pair<String[], FlipperDynamic>> overrides = mOverrides.get(key);
final List<Pair<String[], Pair<SetDataOperations.FlipperValueHint, FlipperDynamic>>>
overrides = mOverrides.get(key);
if (overrides == null) {
return;
}
for (Pair<String[], FlipperDynamic> override : overrides) {
for (Pair<String[], Pair<SetDataOperations.FlipperValueHint, FlipperDynamic>> override :
overrides) {
if (override.first[0].equals("Layout")) {
try {
applyLayoutOverride(
node,
Arrays.copyOfRange(override.first, 1, override.first.length),
override.second);
override.second.second);
} catch (Exception ignored) {
}
}
@@ -327,13 +338,18 @@ public class DebugComponentDescriptor extends NodeDescriptor<DebugComponent> {
}
@Override
public void setValue(DebugComponent node, String[] path, FlipperDynamic value) {
List<Pair<String[], FlipperDynamic>> overrides = mOverrides.get(node.getGlobalKey());
public void setValue(
DebugComponent node,
String[] path,
@Nullable SetDataOperations.FlipperValueHint kind,
FlipperDynamic value) {
List<Pair<String[], Pair<SetDataOperations.FlipperValueHint, FlipperDynamic>>> overrides =
mOverrides.get(node.getGlobalKey());
if (overrides == null) {
overrides = new ArrayList<>();
mOverrides.put(node.getGlobalKey(), overrides);
}
overrides.add(new Pair<>(path, value));
overrides.add(new Pair<>(path, new Pair<>(kind, value)));
node.setOverrider(mOverrider);
node.rerender();
@@ -554,11 +570,13 @@ public class DebugComponentDescriptor extends NodeDescriptor<DebugComponent> {
// The path follows the pattern (Props|State)/field/(field|index)*
private static void applyReflectiveOverride(
Object o, final String[] path, final FlipperDynamic dynamic) {
Object o,
final String[] path,
@Nullable SetDataOperations.FlipperValueHint hint,
final FlipperDynamic dynamic) {
try {
final Field field = o.getClass().getDeclaredField(path[1]);
FlipperEditor.updateComponent(path, field, o, dynamic);
FlipperEditor.updateComponent(path, field, o, hint, dynamic);
} catch (Exception ignored) {
}
}

View File

@@ -18,6 +18,7 @@ import com.facebook.flipper.core.FlipperObject;
import com.facebook.flipper.plugins.inspector.HighlightedOverlay;
import com.facebook.flipper.plugins.inspector.Named;
import com.facebook.flipper.plugins.inspector.NodeDescriptor;
import com.facebook.flipper.plugins.inspector.SetDataOperations;
import com.facebook.flipper.plugins.inspector.Touch;
import com.facebook.litho.sections.Section;
import com.facebook.litho.sections.debug.DebugSection;
@@ -96,7 +97,12 @@ public class DebugSectionDescriptor extends NodeDescriptor<DebugSection> {
}
@Override
public void setValue(DebugSection node, String[] path, FlipperDynamic value) throws Exception {
public void setValue(
DebugSection node,
String[] path,
@Nullable SetDataOperations.FlipperValueHint kind,
FlipperDynamic value)
throws Exception {
// TODO T39526148
}

View File

@@ -7,11 +7,13 @@
package com.facebook.flipper.plugins.litho;
import androidx.core.util.Pair;
import com.facebook.flipper.core.FlipperArray;
import com.facebook.flipper.core.FlipperDynamic;
import com.facebook.flipper.core.FlipperObject;
import com.facebook.flipper.core.FlipperValue;
import com.facebook.flipper.plugins.inspector.InspectorValue;
import com.facebook.flipper.plugins.inspector.SetDataOperations;
import com.facebook.litho.editor.EditorRegistry;
import com.facebook.litho.editor.model.EditorArray;
import com.facebook.litho.editor.model.EditorBool;
@@ -20,7 +22,10 @@ import com.facebook.litho.editor.model.EditorShape;
import com.facebook.litho.editor.model.EditorString;
import com.facebook.litho.editor.model.EditorValue;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
@@ -53,7 +58,36 @@ public class FlipperEditor {
* chain into the object. Fields retain their name, positions in an array use their index.
*/
public static @Nullable Boolean updateComponent(
String[] path, Field field, Object o, FlipperDynamic dynamic) {
String[] path,
Field field,
Object o,
final @Nullable SetDataOperations.FlipperValueHint hint,
FlipperDynamic dynamic) {
EditorValue edit = parseEditorValue(hint, dynamic);
for (int i = path.length - 1; i > 0; i--) {
HashMap<String, EditorValue> content = new HashMap<>();
content.put(path[i], edit);
edit = EditorValue.shape(content);
}
return EditorRegistry.write(field.getType(), field, o, edit);
}
/**
* Layout Plugin supports a protocol that tags the type of all messages. This enables support for
* heterogeneous Maps and Arrays.
*
* @param hint type hint for the FlipperDynamic parameter
* @param dynamic The value produced by the Flipper user
* @return an EditorValue extracted from dynamic
*/
private static EditorValue parseEditorValue(
@Nullable SetDataOperations.FlipperValueHint hint, FlipperDynamic dynamic) {
// TODO(festevezga) - Remove educated guess when the Layout Plugin is updated to produce tagged
// messages
return hint == null ? guessEditorValue(dynamic) : extractEditorValue(hint, dynamic);
}
private static EditorValue guessEditorValue(FlipperDynamic dynamic) {
Object raw = dynamic.raw();
EditorValue edit;
if (raw instanceof String) {
@@ -65,12 +99,59 @@ public class FlipperEditor {
} else {
edit = EditorValue.string(raw.toString());
}
for (int i = path.length - 1; i > 0; i--) {
HashMap<String, EditorValue> content = new HashMap<>();
content.put(path[i], edit);
edit = EditorValue.shape(content);
return edit;
}
/**
* This method flattens the Layout Editor messages using the type hints, recursively.
*
* @param hint type hint for the FlipperDynamic parameter
* @param dynamic The value produced by the Flipper user
* @return an EditorValue extracted from dynamic
*/
private static EditorValue extractEditorValue(
SetDataOperations.FlipperValueHint hint, FlipperDynamic dynamic) {
switch (hint) {
case STRING:
return EditorValue.string(dynamic.asString());
case NUMBER:
return EditorValue.number(dynamic.asDouble());
case OBJECT:
return EditorValue.shape(parseObject(dynamic.asObject()));
case ARRAY:
return EditorValue.array(parseArray(dynamic.asArray()));
case NULL:
// TODO(festevezga) - add support for null
return EditorValue.string("null");
default:
// Java switch isn't exhaustive before Java 13
return EditorValue.string("If you see this, report an error to the Flipper repository");
}
return EditorRegistry.write(field.getType(), field, o, edit);
}
private static Map<String, EditorValue> parseObject(FlipperObject flipperObject) {
final Iterator<String> keys = flipperObject.keys();
final Map<String, EditorValue> values = new HashMap<>();
while (keys.hasNext()) {
final String field = keys.next();
final FlipperObject object = flipperObject.getObject(field);
final Pair<SetDataOperations.FlipperValueHint, FlipperDynamic> value =
SetDataOperations.parseLayoutEditorMessage(object);
values.put(field, parseEditorValue(value.first, value.second));
}
return values;
}
private static List<EditorValue> parseArray(FlipperArray flipperArray) {
ArrayList<EditorValue> values = new ArrayList<>();
for (int i = 0; i < flipperArray.length(); i++) {
final FlipperObject object = flipperArray.getObject(i);
final Pair<SetDataOperations.FlipperValueHint, FlipperDynamic> value =
SetDataOperations.parseLayoutEditorMessage(object);
values.add(parseEditorValue(value.first, value.second));
}
return values;
}
/** Converts into one of FlipperValue, FlipperObject, or FlipperArray */

View File

@@ -13,11 +13,13 @@ import com.facebook.flipper.core.FlipperDynamic;
import com.facebook.flipper.core.FlipperObject;
import com.facebook.flipper.plugins.inspector.Named;
import com.facebook.flipper.plugins.inspector.NodeDescriptor;
import com.facebook.flipper.plugins.inspector.SetDataOperations;
import com.facebook.flipper.plugins.inspector.Touch;
import com.facebook.litho.sections.debug.DebugSection;
import com.facebook.litho.widget.LithoRecylerView;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
public class LithoRecyclerViewDescriptor extends NodeDescriptor<LithoRecylerView> {
@@ -105,10 +107,14 @@ public class LithoRecyclerViewDescriptor extends NodeDescriptor<LithoRecylerView
}
@Override
public void setValue(LithoRecylerView node, String[] path, FlipperDynamic value)
public void setValue(
LithoRecylerView node,
String[] path,
@Nullable SetDataOperations.FlipperValueHint kind,
FlipperDynamic value)
throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
descriptor.setValue(node, path, value);
descriptor.setValue(node, path, kind, value);
}
@Override

View File

@@ -13,6 +13,7 @@ import com.facebook.flipper.core.FlipperDynamic;
import com.facebook.flipper.core.FlipperObject;
import com.facebook.flipper.plugins.inspector.Named;
import com.facebook.flipper.plugins.inspector.NodeDescriptor;
import com.facebook.flipper.plugins.inspector.SetDataOperations;
import com.facebook.flipper.plugins.inspector.Touch;
import com.facebook.litho.DebugComponent;
import com.facebook.litho.LithoView;
@@ -108,9 +109,14 @@ public class LithoViewDescriptor extends NodeDescriptor<LithoView> {
}
@Override
public void setValue(LithoView node, String[] path, FlipperDynamic value) throws Exception {
public void setValue(
LithoView node,
String[] path,
@Nullable SetDataOperations.FlipperValueHint kind,
FlipperDynamic value)
throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
descriptor.setValue(node, path, value);
descriptor.setValue(node, path, kind, value);
}
@Override