Move Litho Flipper Plugin to Litho

Summary:
The current ownership and release model prevents cleanly releasing new versions of the plugin while the code is split between two repos.

This diff moves the flipper-litho plugin to the Litho repository. You'll find new releases as `litho-editor-flipper` that'll be reexported via gradle's `api` to the old artifact.

Reviewed By: colriot

Differential Revision: D23962234

fbshipit-source-id: 7884423342904219ae9b41632c6df90cda870798
This commit is contained in:
Paco Estevez Garcia
2020-10-07 15:40:47 -07:00
committed by Facebook GitHub Bot
parent 08fee986bd
commit 51293406d6
11 changed files with 3 additions and 1465 deletions

View File

@@ -21,8 +21,8 @@ android {
compileOnly deps.lithoAnnotations compileOnly deps.lithoAnnotations
implementation project(':android') implementation project(':android')
implementation deps.lithoCore implementation deps.lithoCore
implementation deps.lithoEditorCore api deps.lithoEditorCore
implementation(deps.lithoEditorFlipper) { api(deps.lithoEditorFlipper) {
exclude group:'com.facebook.flipper', module:'flipper' exclude group:'com.facebook.flipper', module:'flipper'
} }
implementation deps.lithoSectionsDebug implementation deps.lithoSectionsDebug

View File

@@ -1,126 +0,0 @@
/*
* 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.plugins.litho;
import static com.facebook.flipper.plugins.inspector.InspectorValue.Type.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import com.facebook.flipper.core.FlipperObject;
import com.facebook.flipper.plugins.inspector.InspectorValue;
import com.facebook.flipper.plugins.inspector.Named;
import com.facebook.litho.StateContainer;
import com.facebook.litho.annotations.Prop;
import com.facebook.litho.annotations.State;
import com.facebook.litho.drawable.ComparableColorDrawable;
import com.facebook.litho.editor.flipper.FlipperEditor;
import java.lang.reflect.Field;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
public class DataUtils {
static List<Named<FlipperObject>> getPropData(Object node) throws Exception {
final FlipperObject.Builder props = new FlipperObject.Builder();
List<Named<FlipperObject>> data = new ArrayList<>();
boolean hasProps = false;
for (Field f : node.getClass().getDeclaredFields()) {
f.setAccessible(true);
final Prop annotation = f.getAnnotation(Prop.class);
if (annotation != null) {
if (f.get(node) != null
&& PropWithInspectorSection.class.isAssignableFrom(f.get(node).getClass())) {
final AbstractMap.SimpleEntry<String, String> datum =
((PropWithInspectorSection) f.get(node)).getFlipperLayoutInspectorSection();
if (datum != null) {
data.add(new Named<>(datum.getKey(), new FlipperObject(datum.getValue())));
}
}
switch (annotation.resType()) {
case COLOR:
props.put(f.getName(), f.get(node) == null ? "null" : fromColor((Integer) f.get(node)));
break;
case DRAWABLE:
props.put(
f.getName(), f.get(node) == null ? "null" : fromDrawable((Drawable) f.get(node)));
break;
default:
if (f.get(node) != null
&& PropWithDescription.class.isAssignableFrom(f.get(node).getClass())) {
final Object description =
((PropWithDescription) f.get(node)).getFlipperLayoutInspectorPropDescription();
// Treat the description as immutable for now, because it's a "translation" of the
// actual prop,
// mutating them is not going to change the original prop.
if (description instanceof Map<?, ?>) {
final Map<?, ?> descriptionMap = (Map<?, ?>) description;
for (Map.Entry<?, ?> entry : descriptionMap.entrySet()) {
props.put(entry.getKey().toString(), InspectorValue.immutable(entry.getValue()));
}
} else {
props.put(f.getName(), InspectorValue.immutable(description));
}
} else {
props.put(f.getName(), FlipperEditor.makeFlipperField(node, f));
}
break;
}
hasProps = true;
}
}
if (hasProps) {
data.add(new Named<>("Props", props.build()));
}
return data;
}
@Nullable
static FlipperObject getStateData(StateContainer stateContainer) {
if (stateContainer == null) {
return null;
}
final FlipperObject.Builder state = new FlipperObject.Builder();
boolean hasState = false;
for (Field f : stateContainer.getClass().getDeclaredFields()) {
f.setAccessible(true);
final State annotation = f.getAnnotation(State.class);
if (annotation != null) {
state.put(f.getName(), FlipperEditor.makeFlipperField(stateContainer, f));
hasState = true;
}
}
return hasState ? state.build() : null;
}
static InspectorValue fromDrawable(Drawable d) {
int color = 0;
if (d instanceof ColorDrawable) {
color = ((ColorDrawable) d).getColor();
} else if (d instanceof ComparableColorDrawable) {
color = ((ComparableColorDrawable) d).getColor();
}
return InspectorValue.mutable(Color, color);
}
static InspectorValue fromColor(int color) {
return InspectorValue.mutable(Color, color);
}
}

View File

@@ -1,598 +0,0 @@
/*
* 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.plugins.litho;
import static com.facebook.flipper.plugins.inspector.InspectorValue.Type.Boolean;
import static com.facebook.flipper.plugins.inspector.InspectorValue.Type.Enum;
import static com.facebook.flipper.plugins.inspector.InspectorValue.Type.Number;
import static com.facebook.flipper.plugins.inspector.InspectorValue.Type.Picker;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.core.util.Pair;
import com.facebook.flipper.core.FlipperDynamic;
import com.facebook.flipper.core.FlipperObject;
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;
import com.facebook.litho.Component;
import com.facebook.litho.DebugComponent;
import com.facebook.litho.DebugLayoutNode;
import com.facebook.litho.LithoView;
import com.facebook.litho.StateContainer;
import com.facebook.litho.editor.flipper.FlipperEditor;
import com.facebook.yoga.YogaAlign;
import com.facebook.yoga.YogaDirection;
import com.facebook.yoga.YogaEdge;
import com.facebook.yoga.YogaFlexDirection;
import com.facebook.yoga.YogaJustify;
import com.facebook.yoga.YogaPositionType;
import com.facebook.yoga.YogaValue;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
public class DebugComponentDescriptor extends NodeDescriptor<DebugComponent> {
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[], Pair<SetDataOperations.FlipperValueHint, FlipperDynamic>>>
overrides = mOverrides.get(key);
if (overrides == null) {
return;
}
for (Pair<String[], Pair<SetDataOperations.FlipperValueHint, FlipperDynamic>> override :
overrides) {
if (override.first[0].equals("Props")) {
applyReflectiveOverride(
component, override.first, override.second.first, override.second.second);
}
}
}
@Override
public void applyStateOverrides(String key, StateContainer stateContainer) {
final List<Pair<String[], Pair<SetDataOperations.FlipperValueHint, FlipperDynamic>>>
overrides = mOverrides.get(key);
if (overrides == null) {
return;
}
for (Pair<String[], Pair<SetDataOperations.FlipperValueHint, FlipperDynamic>> override :
overrides) {
if (override.first[0].equals("State")) {
applyReflectiveOverride(
stateContainer, override.first, override.second.first, override.second.second);
}
}
}
@Override
public void applyLayoutOverrides(String key, DebugLayoutNode node) {
final List<Pair<String[], Pair<SetDataOperations.FlipperValueHint, FlipperDynamic>>>
overrides = mOverrides.get(key);
if (overrides == null) {
return;
}
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.second);
} catch (Exception ignored) {
}
}
}
}
};
@Override
public void init(DebugComponent node) {
// We rely on the LithoView being invalidated when a component hierarchy changes.
}
@Override
public String getId(DebugComponent node) {
return node.getGlobalKey();
}
@Override
public String getName(DebugComponent node) throws Exception {
NodeDescriptor componentDescriptor = descriptorForClass(node.getComponent().getClass());
if (componentDescriptor.getClass() != ObjectDescriptor.class) {
return componentDescriptor.getName(node.getComponent());
}
return node.getComponent().getSimpleName();
}
@Override
public int getChildCount(DebugComponent node) {
if (node.getMountedView() != null || node.getMountedDrawable() != null) {
return 1;
} else {
return node.getChildComponents().size();
}
}
@Override
public Object getChildAt(DebugComponent node, int index) {
final View mountedView = node.getMountedView();
final Drawable mountedDrawable = node.getMountedDrawable();
if (mountedView != null) {
return mountedView;
} else if (mountedDrawable != null) {
return mountedDrawable;
} else {
return node.getChildComponents().get(index);
}
}
@Override
public List<Named<FlipperObject>> getData(DebugComponent node) throws Exception {
NodeDescriptor componentDescriptor = descriptorForClass(node.getComponent().getClass());
if (componentDescriptor.getClass() != ObjectDescriptor.class) {
return componentDescriptor.getData(node.getComponent());
}
final List<Named<FlipperObject>> data = new ArrayList<>();
final FlipperObject layoutData = getLayoutData(node);
if (layoutData != null) {
data.add(new Named<>("Layout", layoutData));
}
final List<Named<FlipperObject>> propData = getPropData(node);
if (propData != null) {
data.addAll(propData);
}
final FlipperObject stateData = getStateData(node);
if (stateData != null) {
data.add(new Named<>("State", stateData));
}
data.add(
new Named<>(
"Theme", ContextDescriptorUtils.themeData(node.getContext().getAndroidContext())));
return data;
}
@Nullable
private static FlipperObject getLayoutData(DebugComponent node) {
final DebugLayoutNode layout = node.getLayoutNode();
if (layout == null) {
return null;
}
final FlipperObject.Builder data = new FlipperObject.Builder();
data.put("<PLAYGROUND>", InspectorValue.immutable("https://yogalayout.com/playground/"));
data.put("background", DataUtils.fromDrawable(layout.getBackground()));
data.put("foreground", DataUtils.fromDrawable(layout.getForeground()));
data.put(
"direction",
InspectorValue.mutable(
Picker,
new InspectorValue.Picker(
enumToSet(YogaDirection.values()), layout.getLayoutDirection().name())));
data.put(
"flex-direction",
InspectorValue.mutable(
Picker,
new InspectorValue.Picker(
enumToSet(YogaFlexDirection.values()), layout.getFlexDirection().name())));
data.put(
"justify-content",
InspectorValue.mutable(
Picker,
new InspectorValue.Picker(
enumToSet(YogaJustify.values()), layout.getJustifyContent().name())));
data.put(
"align-items",
InspectorValue.mutable(
Picker,
new InspectorValue.Picker(
enumToSet(YogaAlign.values()), layout.getAlignItems().name())));
data.put(
"align-self",
InspectorValue.mutable(
Picker,
new InspectorValue.Picker(
enumToSet(YogaAlign.values()), layout.getAlignSelf().name())));
data.put(
"align-content",
InspectorValue.mutable(
Picker,
new InspectorValue.Picker(
enumToSet(YogaAlign.values()), layout.getAlignContent().name())));
data.put(
"position-type",
InspectorValue.mutable(
Picker,
new InspectorValue.Picker(
enumToSet(YogaPositionType.values()), layout.getPositionType().name())));
data.put("flex-grow", fromFloat(layout.getFlexGrow()));
data.put("flex-shrink", fromFloat(layout.getFlexShrink()));
data.put("flex-basis", fromYogaValue(layout.getFlexBasis()));
data.put("width", fromYogaValue(layout.getWidth()));
data.put("min-width", fromYogaValue(layout.getMinWidth()));
data.put("max-width", fromYogaValue(layout.getMaxWidth()));
data.put("height", fromYogaValue(layout.getHeight()));
data.put("min-height", fromYogaValue(layout.getMinHeight()));
data.put("max-height", fromYogaValue(layout.getMaxHeight()));
data.put("aspect-ratio", fromFloat(layout.getAspectRatio()));
data.put(
"margin",
new FlipperObject.Builder()
.put("left", fromYogaValue(layout.getMargin(YogaEdge.LEFT)))
.put("top", fromYogaValue(layout.getMargin(YogaEdge.TOP)))
.put("right", fromYogaValue(layout.getMargin(YogaEdge.RIGHT)))
.put("bottom", fromYogaValue(layout.getMargin(YogaEdge.BOTTOM)))
.put("start", fromYogaValue(layout.getMargin(YogaEdge.START)))
.put("end", fromYogaValue(layout.getMargin(YogaEdge.END)))
.put("horizontal", fromYogaValue(layout.getMargin(YogaEdge.HORIZONTAL)))
.put("vertical", fromYogaValue(layout.getMargin(YogaEdge.VERTICAL)))
.put("all", fromYogaValue(layout.getMargin(YogaEdge.ALL))));
data.put(
"padding",
new FlipperObject.Builder()
.put("left", fromYogaValue(layout.getPadding(YogaEdge.LEFT)))
.put("top", fromYogaValue(layout.getPadding(YogaEdge.TOP)))
.put("right", fromYogaValue(layout.getPadding(YogaEdge.RIGHT)))
.put("bottom", fromYogaValue(layout.getPadding(YogaEdge.BOTTOM)))
.put("start", fromYogaValue(layout.getPadding(YogaEdge.START)))
.put("end", fromYogaValue(layout.getPadding(YogaEdge.END)))
.put("horizontal", fromYogaValue(layout.getPadding(YogaEdge.HORIZONTAL)))
.put("vertical", fromYogaValue(layout.getPadding(YogaEdge.VERTICAL)))
.put("all", fromYogaValue(layout.getPadding(YogaEdge.ALL))));
data.put(
"border",
new FlipperObject.Builder()
.put("left", fromFloat(layout.getBorderWidth(YogaEdge.LEFT)))
.put("top", fromFloat(layout.getBorderWidth(YogaEdge.TOP)))
.put("right", fromFloat(layout.getBorderWidth(YogaEdge.RIGHT)))
.put("bottom", fromFloat(layout.getBorderWidth(YogaEdge.BOTTOM)))
.put("start", fromFloat(layout.getBorderWidth(YogaEdge.START)))
.put("end", fromFloat(layout.getBorderWidth(YogaEdge.END)))
.put("horizontal", fromFloat(layout.getBorderWidth(YogaEdge.HORIZONTAL)))
.put("vertical", fromFloat(layout.getBorderWidth(YogaEdge.VERTICAL)))
.put("all", fromFloat(layout.getBorderWidth(YogaEdge.ALL))));
data.put(
"position",
new FlipperObject.Builder()
.put("left", fromYogaValue(layout.getPosition(YogaEdge.LEFT)))
.put("top", fromYogaValue(layout.getPosition(YogaEdge.TOP)))
.put("right", fromYogaValue(layout.getPosition(YogaEdge.RIGHT)))
.put("bottom", fromYogaValue(layout.getPosition(YogaEdge.BOTTOM)))
.put("start", fromYogaValue(layout.getPosition(YogaEdge.START)))
.put("end", fromYogaValue(layout.getPosition(YogaEdge.END)))
.put("horizontal", fromYogaValue(layout.getPosition(YogaEdge.HORIZONTAL)))
.put("vertical", fromYogaValue(layout.getPosition(YogaEdge.VERTICAL)))
.put("all", fromYogaValue(layout.getPosition(YogaEdge.ALL))));
data.put("hasViewOutput", InspectorValue.immutable(Boolean, layout.hasViewOutput()));
if (layout.hasViewOutput()) {
data.put("alpha", fromFloat(layout.getAlpha()));
data.put("scale", fromFloat(layout.getScale()));
data.put("rotation", fromFloat(layout.getRotation()));
}
return data.build();
}
private static <E extends Enum<E>> HashSet<String> enumToSet(Enum<E>[] enums) {
final HashSet<String> names = new HashSet<>();
for (Enum<E> aEnum : enums) {
names.add(aEnum.name());
}
return names;
}
@Nullable
private static List<Named<FlipperObject>> getPropData(DebugComponent node) throws Exception {
if (node.canResolve()) {
return null;
}
final Component component = node.getComponent();
return DataUtils.getPropData(component);
}
@Nullable
private static FlipperObject getStateData(DebugComponent node) {
return DataUtils.getStateData(node.getStateContainer());
}
@Override
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, new Pair<>(kind, value)));
node.setOverrider(mOverrider);
node.rerender();
}
@Override
public List<Named<String>> getAttributes(DebugComponent node) {
final List<Named<String>> attributes = new ArrayList<>();
final String key = node.getKey();
final String testKey = node.getTestKey();
if (key != null && key.trim().length() > 0) {
attributes.add(new Named<>("key", key));
}
if (testKey != null && testKey.trim().length() > 0) {
attributes.add(new Named<>("testKey", testKey));
}
return attributes;
}
@Override
public FlipperObject getExtraInfo(DebugComponent node) {
FlipperObject.Builder extraInfo = new FlipperObject.Builder();
final NodeDescriptor descriptor = descriptorForClass(View.class);
final View hostView = node.getComponentHost();
final View lithoView = node.getLithoView();
if (hostView != null) {
try {
extraInfo.put("linkedNode", descriptor.getId(hostView));
} catch (Exception ignored) {
// doesn't have linked node descriptor
}
} else if (lithoView != null) {
try {
extraInfo.put("linkedNode", descriptor.getId(lithoView)).put("expandWithParent", true);
} catch (Exception ignored) {
// doesn't add linked node descriptor
}
}
final FlipperObject.Builder metaData = new FlipperObject.Builder();
metaData.put("className", node.getComponent().getClass().getName());
metaData.put("framework", "LITHO");
extraInfo.put("metaData", metaData);
return extraInfo.build();
}
@Override
public void setHighlighted(DebugComponent node, boolean selected, boolean isAlignmentMode) {
final LithoView lithoView = node.getLithoView();
if (lithoView == null) {
return;
}
if (!selected) {
HighlightedOverlay.removeHighlight(lithoView);
return;
}
final DebugLayoutNode layout = node.getLayoutNode();
final boolean hasNode = layout != null;
final Rect margin;
if (!node.isRoot()) {
margin =
new Rect(
hasNode ? (int) layout.getResultMargin(YogaEdge.START) : 0,
hasNode ? (int) layout.getResultMargin(YogaEdge.TOP) : 0,
hasNode ? (int) layout.getResultMargin(YogaEdge.END) : 0,
hasNode ? (int) layout.getResultMargin(YogaEdge.BOTTOM) : 0);
} else {
// Margin not applied if you're at the root
margin = new Rect();
}
final Rect padding =
new Rect(
hasNode ? (int) layout.getResultPadding(YogaEdge.START) : 0,
hasNode ? (int) layout.getResultPadding(YogaEdge.TOP) : 0,
hasNode ? (int) layout.getResultPadding(YogaEdge.END) : 0,
hasNode ? (int) layout.getResultPadding(YogaEdge.BOTTOM) : 0);
final Rect contentBounds = node.getBoundsInLithoView();
HighlightedOverlay.setHighlighted(lithoView, margin, padding, contentBounds, isAlignmentMode);
}
@Override
public void hitTest(DebugComponent node, Touch touch) {
boolean finish = true;
for (int i = getChildCount(node) - 1; i >= 0; i--) {
final Object child = getChildAt(node, i);
if (child instanceof DebugComponent) {
final DebugComponent componentChild = (DebugComponent) child;
final Rect bounds = componentChild.getBounds();
if (touch.containedIn(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
touch.continueWithOffset(i, bounds.left, bounds.top);
finish = false;
}
} else if (child instanceof View || child instanceof Drawable) {
// Components can only mount one view or drawable and its bounds are the same as the
// hosting component.
touch.continueWithOffset(i, 0, 0);
finish = false;
}
}
if (finish) touch.finish();
}
@Override
public String getDecoration(DebugComponent node) throws Exception {
if (node.getComponent() != null) {
NodeDescriptor componentDescriptor = descriptorForClass(node.getComponent().getClass());
if (componentDescriptor.getClass() != ObjectDescriptor.class) {
return componentDescriptor.getDecoration(node.getComponent());
}
}
return "litho";
}
@Override
public boolean matches(String query, DebugComponent node) throws Exception {
NodeDescriptor descriptor = descriptorForClass(Object.class);
return descriptor.matches(query, node) || getId(node).equals(query);
}
private static void applyLayoutOverride(
DebugLayoutNode node, String[] path, FlipperDynamic value) {
switch (path[0]) {
case "background":
node.setBackgroundColor(value.asInt());
break;
case "foreground":
node.setForegroundColor(value.asInt());
break;
case "direction":
node.setLayoutDirection(YogaDirection.valueOf(value.asString().toUpperCase()));
break;
case "flex-direction":
node.setFlexDirection(YogaFlexDirection.valueOf(value.asString().toUpperCase()));
break;
case "justify-content":
node.setJustifyContent(YogaJustify.valueOf(value.asString().toUpperCase()));
break;
case "align-items":
node.setAlignItems(YogaAlign.valueOf(value.asString().toUpperCase()));
break;
case "align-self":
node.setAlignSelf(YogaAlign.valueOf(value.asString().toUpperCase()));
break;
case "align-content":
node.setAlignContent(YogaAlign.valueOf(value.asString().toUpperCase()));
break;
case "position-type":
node.setPositionType(YogaPositionType.valueOf(value.asString().toUpperCase()));
break;
case "flex-grow":
node.setFlexGrow(value.asFloat());
break;
case "flex-shrink":
node.setFlexShrink(value.asFloat());
break;
case "flex-basis":
node.setFlexBasis(YogaValue.parse(value.asString()));
break;
case "width":
node.setWidth(YogaValue.parse(value.asString()));
break;
case "min-width":
node.setMinWidth(YogaValue.parse(value.asString()));
break;
case "max-width":
node.setMaxWidth(YogaValue.parse(value.asString()));
break;
case "height":
node.setHeight(YogaValue.parse(value.asString()));
break;
case "min-height":
node.setMinHeight(YogaValue.parse(value.asString()));
break;
case "max-height":
node.setMaxHeight(YogaValue.parse(value.asString()));
break;
case "aspect-ratio":
node.setAspectRatio(value.asFloat());
break;
case "margin":
node.setMargin(edgeFromString(path[1]), YogaValue.parse(value.asString()));
break;
case "padding":
node.setPadding(edgeFromString(path[1]), YogaValue.parse(value.asString()));
break;
case "border":
node.setBorderWidth(edgeFromString(path[1]), value.asFloat());
break;
case "position":
node.setPosition(edgeFromString(path[1]), YogaValue.parse(value.asString()));
break;
case "alpha":
node.setAlpha(value.asFloat());
break;
case "scale":
node.setScale(value.asFloat());
break;
case "rotation":
node.setRotation(value.asFloat());
break;
}
}
private static YogaEdge edgeFromString(String s) {
return YogaEdge.valueOf(s.toUpperCase());
}
// The path follows the pattern (Props|State)/field/(field|index)*
private static void applyReflectiveOverride(
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, hint, dynamic);
} catch (Exception ignored) {
}
}
private static InspectorValue fromFloat(float f) {
if (Float.isNaN(f)) {
return InspectorValue.mutable(Enum, "undefined");
}
return InspectorValue.mutable(Number, f);
}
static InspectorValue fromYogaValue(YogaValue v) {
// TODO add support for Type.Dimension or similar
return InspectorValue.mutable(Enum, v.toString());
}
}

View File

@@ -1,238 +0,0 @@
/*
* 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.plugins.litho;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import androidx.core.view.MarginLayoutParamsCompat;
import androidx.core.view.ViewCompat;
import com.facebook.flipper.core.ErrorReportingRunnable;
import com.facebook.flipper.core.FlipperDynamic;
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;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
public class DebugSectionDescriptor extends NodeDescriptor<DebugSection> {
@Override
public void invalidate(final DebugSection debugSection) {
super.invalidate(debugSection);
new ErrorReportingRunnable(mConnection) {
@Override
protected void runOrThrow() throws Exception {
for (int i = 0; i < getChildCount(debugSection); i++) {
Object child = getChildAt(debugSection, i);
if (child instanceof DebugSection) {
invalidate((DebugSection) child);
}
}
}
}.run();
}
@Override
public void init(DebugSection node) throws Exception {}
@Override
public String getId(DebugSection node) throws Exception {
return node.getGlobalKey();
}
@Override
public String getName(DebugSection node) throws Exception {
return node.getName();
}
@Override
public int getChildCount(DebugSection node) throws Exception {
return node.getSectionChildren().size();
}
@Override
public Object getChildAt(DebugSection node, int index) throws Exception {
return node.getSectionChildren().get(index);
}
@Override
public List<Named<FlipperObject>> getData(DebugSection node) throws Exception {
// TODO T39526148 add changeset info
final List<Named<FlipperObject>> data = new ArrayList<>();
final List<Named<FlipperObject>> propData = getPropData(node);
if (propData != null) {
data.addAll(propData);
}
final FlipperObject stateData = getStateData(node);
if (stateData != null) {
data.add(new Named<>("State", stateData));
}
return data;
}
private static @Nullable List<Named<FlipperObject>> getPropData(DebugSection node)
throws Exception {
final Section section = node.getSection();
return DataUtils.getPropData(section);
}
private static @Nullable FlipperObject getStateData(DebugSection node) {
return DataUtils.getStateData(node.getStateContainer());
}
@Override
public void setValue(
DebugSection node,
String[] path,
@Nullable SetDataOperations.FlipperValueHint kind,
FlipperDynamic value)
throws Exception {
// TODO T39526148
}
@Override
public List<Named<String>> getAttributes(DebugSection node) throws Exception {
// TODO T39526148
final List<Named<String>> attrs = new ArrayList<>();
return attrs;
}
@Override
public FlipperObject getExtraInfo(DebugSection node) {
FlipperObject.Builder extraInfo = new FlipperObject.Builder();
final FlipperObject.Builder metaData = new FlipperObject.Builder();
metaData.put("className", node.getSection().getClass().getName());
metaData.put("framework", "LITHO");
extraInfo.put("metaData", metaData);
return extraInfo.build();
}
@Override
public void setHighlighted(DebugSection node, boolean selected, boolean isAlignmentMode)
throws Exception {
final int childCount = getChildCount(node);
if (node.isDiffSectionSpec()) {
for (int i = 0; i < childCount; i++) {
final View view = (View) getChildAt(node, i);
highlightChildView(view, selected, isAlignmentMode);
}
} else {
for (int i = 0; i < childCount; i++) {
final Object child = getChildAt(node, i);
final NodeDescriptor descriptor = descriptorForClass(child.getClass());
descriptor.setHighlighted(child, selected, isAlignmentMode);
}
}
}
// This is similar to the implementation in ViewDescriptor but doesn't
// target the parent view.
private void highlightChildView(View node, boolean selected, boolean isAlignmentMode) {
if (!selected) {
HighlightedOverlay.removeHighlight(node);
return;
}
final Rect padding =
new Rect(
ViewCompat.getPaddingStart(node),
node.getPaddingTop(),
ViewCompat.getPaddingEnd(node),
node.getPaddingBottom());
final Rect margin;
final ViewGroup.LayoutParams params = node.getLayoutParams();
if (params instanceof ViewGroup.MarginLayoutParams) {
final ViewGroup.MarginLayoutParams marginParams = (ViewGroup.MarginLayoutParams) params;
margin =
new Rect(
MarginLayoutParamsCompat.getMarginStart(marginParams),
marginParams.topMargin,
MarginLayoutParamsCompat.getMarginEnd(marginParams),
marginParams.bottomMargin);
} else {
margin = new Rect();
}
final int left = node.getLeft();
final int top = node.getTop();
final Rect contentBounds = new Rect(left, top, left + node.getWidth(), top + node.getHeight());
contentBounds.offset(-left, -top);
HighlightedOverlay.setHighlighted(node, margin, padding, contentBounds, false);
}
@Override
public void hitTest(DebugSection node, Touch touch) throws Exception {
final int childCount = getChildCount(node);
// For a DiffSectionSpec, check if child view to see if the touch is in its bounds.
// For a GroupSectionSpec, check the bounds of the entire section.
boolean finish = true;
if (node.isDiffSectionSpec()) {
for (int i = 0; i < childCount; i++) {
View child = (View) getChildAt(node, i);
int left = child.getLeft() + (int) child.getTranslationX();
int top = (child.getTop() + (int) child.getTranslationY());
int right = (child.getRight() + (int) child.getTranslationX());
int bottom = (child.getBottom() + (int) child.getTranslationY());
final boolean hit = touch.containedIn(left, top, right, bottom);
if (hit) {
touch.continueWithOffset(i, left, top);
finish = false;
}
}
} else {
for (int i = 0; i < childCount; i++) {
DebugSection child = (DebugSection) getChildAt(node, i);
Rect bounds = child.getBounds();
final boolean hit = touch.containedIn(bounds.left, bounds.top, bounds.right, bounds.bottom);
if (hit) {
touch.continueWithOffset(i, 0, 0);
finish = false;
}
}
}
if (finish) touch.finish();
}
@Override
public String getDecoration(DebugSection node) throws Exception {
// TODO T39526148
return null;
}
@Override
public boolean matches(String query, DebugSection node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(Object.class);
return descriptor.matches(query, node);
}
@Override
public int getAXChildCount(DebugSection node) {
return 0;
}
}

View File

@@ -1,89 +0,0 @@
/*
* 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.plugins.litho;
import android.view.View;
import android.view.ViewGroup;
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.plugins.common.MainThreadFlipperReceiver;
import com.facebook.flipper.plugins.inspector.ApplicationWrapper;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.inspector.ObjectTracker;
import com.facebook.litho.LithoView;
import java.util.Stack;
public final class GenerateLithoAccessibilityRenderExtensionCommand
implements InspectorFlipperPlugin.ExtensionCommand {
@Override
public String command() {
return "forceLithoAXRender";
}
@Override
public FlipperReceiver receiver(final ObjectTracker tracker, final FlipperConnection connection) {
return new MainThreadFlipperReceiver() {
@Override
public void onReceiveOnMainThread(
final FlipperObject params, final FlipperResponder responder) throws Exception {
final String applicationId = params.getString("applicationId");
// check that the application is valid
if (applicationId == null) {
return;
}
final Object obj = tracker.get(applicationId);
if (obj != null && !(obj instanceof ApplicationWrapper)) {
return;
}
final ApplicationWrapper applicationWrapper = ((ApplicationWrapper) obj);
final boolean forceLithoAXRender = params.getBoolean("forceLithoAXRender");
final boolean prevForceLithoAXRender = Boolean.getBoolean("is_accessibility_enabled");
// nothing has changed, so return
if (forceLithoAXRender == prevForceLithoAXRender) {
return;
}
// change property and rerender
System.setProperty("is_accessibility_enabled", forceLithoAXRender + "");
forceRerenderAllLithoViews(forceLithoAXRender, applicationWrapper);
}
};
}
private void forceRerenderAllLithoViews(
boolean forceLithoAXRender, ApplicationWrapper applicationWrapper) {
// iterate through tree and rerender all litho views
Stack<ViewGroup> lithoViewSearchStack = new Stack<>();
for (View root : applicationWrapper.getViewRoots()) {
if (root instanceof ViewGroup) {
lithoViewSearchStack.push((ViewGroup) root);
}
}
while (!lithoViewSearchStack.isEmpty()) {
ViewGroup v = lithoViewSearchStack.pop();
if (v instanceof LithoView) {
((LithoView) v).rerenderForAccessibility(forceLithoAXRender);
} else {
for (int i = 0; i < v.getChildCount(); i++) {
View child = v.getChildAt(i);
if (child instanceof ViewGroup) {
lithoViewSearchStack.push((ViewGroup) child);
}
}
}
}
}
}

View File

@@ -1,28 +0,0 @@
/*
* 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.plugins.litho;
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.litho.DebugComponent;
import com.facebook.litho.LithoView;
import com.facebook.litho.sections.debug.DebugSection;
import com.facebook.litho.widget.LithoRecylerView;
public final class LithoFlipperDescriptors {
public static void add(DescriptorMapping descriptorMapping) {
descriptorMapping.register(LithoView.class, new LithoViewDescriptor());
descriptorMapping.register(DebugComponent.class, new DebugComponentDescriptor());
}
public static void addWithSections(DescriptorMapping descriptorMapping) {
add(descriptorMapping);
descriptorMapping.register(LithoRecylerView.class, new LithoRecyclerViewDescriptor());
descriptorMapping.register(DebugSection.class, new DebugSectionDescriptor());
}
}

View File

@@ -1,179 +0,0 @@
/*
* 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.plugins.litho;
import android.view.View;
import android.view.ViewGroup;
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> {
@Override
public void invalidate(final LithoRecylerView node) {
super.invalidate(node);
new com.facebook.flipper.core.ErrorReportingRunnable(mConnection) {
@Override
protected void runOrThrow() throws Exception {
final Object child;
child = getChildAt(node, 0);
if (child instanceof DebugSection) {
DebugSection childSection = (DebugSection) child;
final NodeDescriptor descriptor = descriptorForClass(DebugSection.class);
descriptor.invalidate(childSection);
}
}
}.run();
}
@Override
public void init(final LithoRecylerView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
descriptor.init(node);
}
@Override
public String getId(LithoRecylerView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getId(node);
}
@Override
public String getName(LithoRecylerView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getName(node);
}
@Override
public int getChildCount(LithoRecylerView node) throws Exception {
// TODO T39526148 this might not always be true when using the RecyclerBinder manually.
return 1;
}
@Override
public int getAXChildCount(LithoRecylerView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getAXChildCount(node);
}
@Override
public Object getChildAt(LithoRecylerView node, int index) throws Exception {
// TODO T39526148 account for the case above
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
int count = descriptor.getChildCount(node);
final List<View> childrenViews = new ArrayList<>();
for (int i = 0; i < count; i++) {
childrenViews.add((View) descriptor.getChildAt(node, i));
}
return DebugSection.getRootInstance(childrenViews);
}
@Override
public Object getAXChildAt(LithoRecylerView node, int index) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getAXChildAt(node, index);
}
@Override
public List<Named<FlipperObject>> getData(LithoRecylerView node) throws Exception {
final List<Named<FlipperObject>> props = new ArrayList<>();
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
props.addAll(descriptor.getData(node));
return props;
}
@Override
public List<Named<FlipperObject>> getAXData(LithoRecylerView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getAXData(node);
}
@Override
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, kind, value);
}
@Override
public List<Named<String>> getAttributes(LithoRecylerView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getAttributes(node);
}
@Override
public FlipperObject getExtraInfo(LithoRecylerView node) {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getExtraInfo(node);
}
@Override
public void hitTest(LithoRecylerView node, Touch touch) throws Exception {
touch.continueWithOffset(0, 0, 0);
}
@Override
public void axHitTest(LithoRecylerView node, Touch touch) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
descriptor.axHitTest(node, touch);
}
@Override
public String getAXName(LithoRecylerView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getAXName(node);
}
@Override
public List<Named<String>> getAXAttributes(LithoRecylerView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getAXAttributes(node);
}
@Override
public void setHighlighted(LithoRecylerView node, boolean selected, boolean isAlignmentMode)
throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
descriptor.setHighlighted(node, selected, isAlignmentMode);
}
@Override
public String getDecoration(LithoRecylerView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getDecoration(node);
}
@Override
public String getAXDecoration(LithoRecylerView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getAXDecoration(node);
}
@Override
public boolean matches(String query, LithoRecylerView node) throws Exception {
NodeDescriptor descriptor = descriptorForClass(Object.class);
return descriptor.matches(query, node);
}
}

View File

@@ -1,175 +0,0 @@
/*
* 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.plugins.litho;
import android.graphics.Rect;
import android.view.ViewGroup;
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;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
public class LithoViewDescriptor extends NodeDescriptor<LithoView> {
@Override
public void init(LithoView node) throws Exception {
node.setOnDirtyMountListener(
new LithoView.OnDirtyMountListener() {
@Override
public void onDirtyMount(LithoView view) {
invalidate(view);
invalidateAX(view);
}
});
}
@Override
public String getId(LithoView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getId(node);
}
@Override
public String getName(LithoView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getName(node);
}
@Override
public String getAXName(LithoView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getAXName(node);
}
@Override
public int getChildCount(LithoView node) {
return DebugComponent.getRootInstance(node) == null ? 0 : 1;
}
@Override
public int getAXChildCount(LithoView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getAXChildCount(node);
}
@Override
public Object getChildAt(LithoView node, int index) {
return DebugComponent.getRootInstance(node);
}
@Override
public @Nullable Object getAXChildAt(LithoView node, int index) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getChildAt(node, index);
}
@Override
public List<Named<FlipperObject>> getData(LithoView node) throws Exception {
final List<Named<FlipperObject>> props = new ArrayList<>();
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
final Rect mountedBounds = node.getPreviousMountBounds();
props.add(
0,
new Named<>(
"LithoView",
new FlipperObject.Builder()
.put(
"mountbounds",
new FlipperObject.Builder()
.put("left", mountedBounds.left)
.put("top", mountedBounds.top)
.put("right", mountedBounds.right)
.put("bottom", mountedBounds.bottom))
.build()));
props.addAll(descriptor.getData(node));
return props;
}
@Override
public List<Named<FlipperObject>> getAXData(LithoView node) throws Exception {
final List<Named<FlipperObject>> props = new ArrayList<>();
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
props.addAll(descriptor.getAXData(node));
return props;
}
@Override
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, kind, value);
}
@Override
public List<Named<String>> getAttributes(LithoView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getAttributes(node);
}
@Override
public List<Named<String>> getAXAttributes(LithoView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getAXAttributes(node);
}
@Override
public FlipperObject getExtraInfo(LithoView node) {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getExtraInfo(node);
}
@Override
public void setHighlighted(LithoView node, boolean selected, boolean isAlignmentMode)
throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
descriptor.setHighlighted(node, selected, isAlignmentMode);
}
@Override
public void hitTest(LithoView node, Touch touch) {
touch.continueWithOffset(0, 0, 0);
}
@Override
public void axHitTest(LithoView node, Touch touch) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
descriptor.axHitTest(node, touch);
}
@Override
public String getDecoration(LithoView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getDecoration(node);
}
@Override
public String getAXDecoration(LithoView node) throws Exception {
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
return descriptor.getAXDecoration(node);
}
@Override
public boolean matches(String query, LithoView node) throws Exception {
NodeDescriptor descriptor = descriptorForClass(Object.class);
return descriptor.matches(query, node);
}
}

View File

@@ -1,13 +0,0 @@
/*
* 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.plugins.litho;
public interface PropWithDescription {
Object getFlipperLayoutInspectorPropDescription();
}

View File

@@ -1,16 +0,0 @@
/*
* 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.plugins.litho;
import java.util.AbstractMap;
import javax.annotation.CheckForNull;
public interface PropWithInspectorSection {
@CheckForNull
AbstractMap.SimpleEntry<String, String> getFlipperLayoutInspectorSection();
}

View File

@@ -16,13 +16,13 @@ import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping; import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.leakcanary.LeakCanaryFlipperPlugin; import com.facebook.flipper.plugins.leakcanary.LeakCanaryFlipperPlugin;
import com.facebook.flipper.plugins.litho.LithoFlipperDescriptors;
import com.facebook.flipper.plugins.navigation.NavigationFlipperPlugin; import com.facebook.flipper.plugins.navigation.NavigationFlipperPlugin;
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin.SharedPreferencesDescriptor; import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin.SharedPreferencesDescriptor;
import com.facebook.litho.config.ComponentsConfiguration; import com.facebook.litho.config.ComponentsConfiguration;
import com.facebook.litho.editor.flipper.LithoFlipperDescriptors;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;