Extract TouchOverlayView to new class

Summary:
Before this diff, `TouchOverlayView` would be an inner non-static class,so it'd be difficult to track memory ownership for it.

It also made `InspectorFlipperPlugin` longer and harder to read.

Reviewed By: cekkaewnumchai

Differential Revision: D22285744

fbshipit-source-id: 6fdd8c33a07be6ab900ebb28a8c3ebf3761fb598
This commit is contained in:
Paco Estevez Garcia
2020-07-01 11:03:58 -07:00
committed by Facebook GitHub Bot
parent c1d9527406
commit d871ce0a04
3 changed files with 232 additions and 182 deletions

View File

@@ -9,8 +9,6 @@ package com.facebook.flipper.plugins.inspector;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEvent;
@@ -24,11 +22,8 @@ import com.facebook.flipper.core.FlipperReceiver;
import com.facebook.flipper.core.FlipperResponder; import com.facebook.flipper.core.FlipperResponder;
import com.facebook.flipper.plugins.common.MainThreadFlipperReceiver; import com.facebook.flipper.plugins.common.MainThreadFlipperReceiver;
import com.facebook.flipper.plugins.inspector.descriptors.ApplicationDescriptor; import com.facebook.flipper.plugins.inspector.descriptors.ApplicationDescriptor;
import com.facebook.flipper.plugins.inspector.descriptors.utils.AccessibilityUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Stack;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class InspectorFlipperPlugin implements FlipperPlugin { public class InspectorFlipperPlugin implements FlipperPlugin {
@@ -37,7 +32,7 @@ public class InspectorFlipperPlugin implements FlipperPlugin {
private DescriptorMapping mDescriptorMapping; private DescriptorMapping mDescriptorMapping;
private ObjectTracker mObjectTracker; private ObjectTracker mObjectTracker;
private String mHighlightedId; private String mHighlightedId;
private TouchOverlayView mTouchOverlay; TouchOverlayView mTouchOverlay;
private FlipperConnection mConnection; private FlipperConnection mConnection;
private @Nullable List<ExtensionCommand> mExtensionCommands; private @Nullable List<ExtensionCommand> mExtensionCommands;
private boolean mShowLithoAccessibilitySettings; private boolean mShowLithoAccessibilitySettings;
@@ -373,7 +368,18 @@ public class InspectorFlipperPlugin implements FlipperPlugin {
if (root != null) { if (root != null) {
if (active) { if (active) {
mTouchOverlay = new TouchOverlayView(root.getContext()); mTouchOverlay =
new TouchOverlayView(root.getContext(), mConnection, mApplication) {
@Override
protected String trackObject(Object obj) throws Exception {
return InspectorFlipperPlugin.this.trackObject(obj);
}
@Override
protected NodeDescriptor<Object> descriptorForObject(Object obj) {
return InspectorFlipperPlugin.this.descriptorForObject(obj);
}
};
root.addView(mTouchOverlay); root.addView(mTouchOverlay);
root.bringChildToFront(mTouchOverlay); root.bringChildToFront(mTouchOverlay);
} else { } else {
@@ -413,175 +419,6 @@ public class InspectorFlipperPlugin implements FlipperPlugin {
} }
}; };
class TouchOverlayView extends View implements HiddenNode {
public TouchOverlayView(Context context) {
super(context);
setBackgroundColor(BoundsDrawable.COLOR_HIGHLIGHT_CONTENT);
}
@Override
public boolean onHoverEvent(MotionEvent event) {
// if in layout inspector and talkback is running, override the first click to locate the
// clicked view
if (mConnection != null
&& AccessibilityUtil.isTalkbackEnabled(getContext())
&& event.getPointerCount() == 1) {
FlipperObject params =
new FlipperObject.Builder()
.put("type", "usage")
.put("eventName", "accessibility:clickToInspectTalkbackRunning")
.build();
mConnection.send("track", params);
final int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_HOVER_ENTER:
{
event.setAction(MotionEvent.ACTION_DOWN);
}
break;
case MotionEvent.ACTION_HOVER_MOVE:
{
event.setAction(MotionEvent.ACTION_MOVE);
}
break;
case MotionEvent.ACTION_HOVER_EXIT:
{
event.setAction(MotionEvent.ACTION_UP);
}
break;
}
return onTouchEvent(event);
}
// otherwise use the default
return super.onHoverEvent(event);
}
@Override
public boolean onTouchEvent(final MotionEvent event) {
if (event.getAction() != MotionEvent.ACTION_UP) {
return true;
}
new ErrorReportingRunnable(mConnection) {
@Override
public void runOrThrow() throws Exception {
hitTest((int) event.getX(), (int) event.getY());
}
}.run();
return true;
}
}
private Pair<Touch, Stack<FlipperObject.Builder>> createTouch(
final int touchX, final int touchY, final boolean ax) throws Exception {
final Stack<FlipperObject.Builder> objStack = new Stack<>();
objStack.push(new FlipperObject.Builder());
final Stack<Object> nodes = new Stack<>();
nodes.push(mApplication);
final Touch touch =
new Touch() {
int x = touchX;
int y = touchY;
@Override
public void finish() {}
@Override
public void continueWithOffset(
final int childIndex, final int offsetX, final int offsetY) {
final Touch touch = this;
new ErrorReportingRunnable(mConnection) {
@Override
protected void runOrThrow() throws Exception {
Object nextNode;
final Object currNode = nodes.peek();
x -= offsetX;
y -= offsetY;
if (ax) {
nextNode =
assertNotNull(
descriptorForObject(currNode).getAXChildAt(currNode, childIndex));
} else {
nextNode =
assertNotNull(descriptorForObject(currNode).getChildAt(currNode, childIndex));
}
nodes.push(nextNode);
final String nodeID = trackObject(nextNode);
final NodeDescriptor<Object> descriptor = descriptorForObject(nextNode);
objStack.push(new FlipperObject.Builder());
if (ax) {
descriptor.axHitTest(nextNode, touch);
} else {
descriptor.hitTest(nextNode, touch);
}
x += offsetX;
y += offsetY;
nodes.pop();
final FlipperObject objTree = objStack.pop().build();
objStack.peek().put(nodeID, objTree);
}
}.run();
}
@Override
public boolean containedIn(int l, int t, int r, int b) {
return x >= l && x <= r && y >= t && y <= b;
}
};
return new Pair<>(touch, objStack);
}
// This is mainly for backward compatibility
private FlipperArray getPathFromTree(FlipperObject tree) {
final FlipperArray.Builder pathBuilder = new FlipperArray.Builder();
FlipperObject subtree = tree;
Iterator<String> it = subtree.keys();
while (it.hasNext()) {
final String key = it.next();
pathBuilder.put(key);
subtree = subtree.getObject(key);
it = subtree.keys();
}
return pathBuilder.build();
}
void hitTest(final int touchX, final int touchY) throws Exception {
final NodeDescriptor<Object> descriptor = descriptorForObject(mApplication);
FlipperObject treeObj;
Pair<Touch, Stack<FlipperObject.Builder>> pair = createTouch(touchX, touchY, false);
descriptor.hitTest(mApplication, pair.first);
treeObj = new FlipperObject.Builder().put(trackObject(mApplication), pair.second.pop()).build();
mConnection.send(
"select",
new FlipperObject.Builder()
.put("tree", treeObj)
.put("path", getPathFromTree(treeObj))
.build());
pair = createTouch(touchX, touchY, true);
descriptor.axHitTest(mApplication, pair.first);
treeObj = new FlipperObject.Builder().put(trackObject(mApplication), pair.second.pop()).build();
mConnection.send(
"selectAX",
new FlipperObject.Builder()
.put("tree", treeObj)
.put("path", getPathFromTree(treeObj))
.build());
}
private void setHighlighted( private void setHighlighted(
final String id, final boolean highlighted, final boolean isAlignmentMode) throws Exception { final String id, final boolean highlighted, final boolean isAlignmentMode) throws Exception {
final Object obj = mObjectTracker.get(id); final Object obj = mObjectTracker.get(id);
@@ -745,7 +582,7 @@ public class InspectorFlipperPlugin implements FlipperPlugin {
.build(); .build();
} }
private String trackObject(Object obj) throws Exception { String trackObject(Object obj) throws Exception {
final NodeDescriptor<Object> descriptor = descriptorForObject(obj); final NodeDescriptor<Object> descriptor = descriptorForObject(obj);
final String id = descriptor.getId(obj); final String id = descriptor.getId(obj);
final Object curr = mObjectTracker.get(id); final Object curr = mObjectTracker.get(id);
@@ -756,12 +593,12 @@ public class InspectorFlipperPlugin implements FlipperPlugin {
return id; return id;
} }
private NodeDescriptor<Object> descriptorForObject(Object obj) { NodeDescriptor<Object> descriptorForObject(Object obj) {
final Class c = assertNotNull(obj).getClass(); final Class c = assertNotNull(obj).getClass();
return (NodeDescriptor<Object>) mDescriptorMapping.descriptorForClass(c); return (NodeDescriptor<Object>) mDescriptorMapping.descriptorForClass(c);
} }
private static Object assertNotNull(@Nullable Object o) { static Object assertNotNull(@Nullable Object o) {
if (o == null) { if (o == null) {
throw new RuntimeException("Unexpected null value"); throw new RuntimeException("Unexpected null value");
} }

View File

@@ -0,0 +1,202 @@
/*
* 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.inspector;
import android.content.Context;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import com.facebook.flipper.core.ErrorReportingRunnable;
import com.facebook.flipper.core.FlipperArray;
import com.facebook.flipper.core.FlipperConnection;
import com.facebook.flipper.core.FlipperObject;
import com.facebook.flipper.plugins.inspector.descriptors.utils.AccessibilityUtil;
import java.util.Iterator;
import java.util.Stack;
abstract class TouchOverlayView extends View implements HiddenNode {
private final FlipperConnection mConnection;
private final ApplicationWrapper mApplication;
public TouchOverlayView(
Context context, FlipperConnection connection, ApplicationWrapper wrapper) {
super(context);
mConnection = connection;
mApplication = wrapper;
setBackgroundColor(BoundsDrawable.COLOR_HIGHLIGHT_CONTENT);
}
protected abstract String trackObject(Object obj) throws Exception;
protected abstract NodeDescriptor<Object> descriptorForObject(Object obj);
@Override
public boolean onHoverEvent(MotionEvent event) {
// if in layout inspector and talkback is running, override the first click to locate the
// clicked view
if (mConnection != null
&& AccessibilityUtil.isTalkbackEnabled(getContext())
&& event.getPointerCount() == 1) {
FlipperObject params =
new FlipperObject.Builder()
.put("type", "usage")
.put("eventName", "accessibility:clickToInspectTalkbackRunning")
.build();
mConnection.send("track", params);
final int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_HOVER_ENTER:
{
event.setAction(MotionEvent.ACTION_DOWN);
}
break;
case MotionEvent.ACTION_HOVER_MOVE:
{
event.setAction(MotionEvent.ACTION_MOVE);
}
break;
case MotionEvent.ACTION_HOVER_EXIT:
{
event.setAction(MotionEvent.ACTION_UP);
}
break;
}
return onTouchEvent(event);
}
// otherwise use the default
return super.onHoverEvent(event);
}
@Override
public boolean onTouchEvent(final MotionEvent event) {
if (event.getAction() != MotionEvent.ACTION_UP) {
return true;
}
new ErrorReportingRunnable(mConnection) {
@Override
public void runOrThrow() throws Exception {
hitTest((int) event.getX(), (int) event.getY());
}
}.run();
return true;
}
void hitTest(final int touchX, final int touchY) throws Exception {
final NodeDescriptor<Object> descriptor = descriptorForObject(mApplication);
FlipperObject treeObj;
Pair<Touch, Stack<FlipperObject.Builder>> pair = createTouch(touchX, touchY, false);
descriptor.hitTest(mApplication, pair.first);
treeObj = new FlipperObject.Builder().put(trackObject(mApplication), pair.second.pop()).build();
mConnection.send(
"select",
new FlipperObject.Builder()
.put("tree", treeObj)
.put("path", getPathFromTree(treeObj))
.build());
pair = createTouch(touchX, touchY, true);
descriptor.axHitTest(mApplication, pair.first);
treeObj = new FlipperObject.Builder().put(trackObject(mApplication), pair.second.pop()).build();
mConnection.send(
"selectAX",
new FlipperObject.Builder()
.put("tree", treeObj)
.put("path", getPathFromTree(treeObj))
.build());
}
private Pair<Touch, Stack<FlipperObject.Builder>> createTouch(
final int touchX, final int touchY, final boolean ax) throws Exception {
final Stack<FlipperObject.Builder> objStack = new Stack<>();
objStack.push(new FlipperObject.Builder());
final Stack<Object> nodes = new Stack<>();
nodes.push(mApplication);
final Touch touch =
new Touch() {
int x = touchX;
int y = touchY;
@Override
public void finish() {}
@Override
public void continueWithOffset(
final int childIndex, final int offsetX, final int offsetY) {
final Touch touch = this;
new ErrorReportingRunnable(mConnection) {
@Override
protected void runOrThrow() throws Exception {
Object nextNode;
final Object currNode = nodes.peek();
x -= offsetX;
y -= offsetY;
if (ax) {
nextNode =
InspectorFlipperPlugin.assertNotNull(
descriptorForObject(currNode).getAXChildAt(currNode, childIndex));
} else {
nextNode =
InspectorFlipperPlugin.assertNotNull(
descriptorForObject(currNode).getChildAt(currNode, childIndex));
}
nodes.push(nextNode);
final String nodeID = trackObject(nextNode);
final NodeDescriptor<Object> descriptor = descriptorForObject(nextNode);
objStack.push(new FlipperObject.Builder());
if (ax) {
descriptor.axHitTest(nextNode, touch);
} else {
descriptor.hitTest(nextNode, touch);
}
x += offsetX;
y += offsetY;
nodes.pop();
final FlipperObject objTree = objStack.pop().build();
objStack.peek().put(nodeID, objTree);
}
}.run();
}
@Override
public boolean containedIn(int l, int t, int r, int b) {
return x >= l && x <= r && y >= t && y <= b;
}
};
return new Pair<>(touch, objStack);
}
// This is mainly for backward compatibility
private FlipperArray getPathFromTree(FlipperObject tree) {
final FlipperArray.Builder pathBuilder = new FlipperArray.Builder();
FlipperObject subtree = tree;
Iterator<String> it = subtree.keys();
while (it.hasNext()) {
final String key = it.next();
pathBuilder.put(key);
subtree = subtree.getObject(key);
it = subtree.keys();
}
return pathBuilder.build();
}
}

View File

@@ -22,7 +22,6 @@ import com.facebook.flipper.core.FlipperArray;
import com.facebook.flipper.core.FlipperConnection; import com.facebook.flipper.core.FlipperConnection;
import com.facebook.flipper.core.FlipperDynamic; import com.facebook.flipper.core.FlipperDynamic;
import com.facebook.flipper.core.FlipperObject; import com.facebook.flipper.core.FlipperObject;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin.TouchOverlayView;
import com.facebook.flipper.plugins.inspector.descriptors.ApplicationDescriptor; import com.facebook.flipper.plugins.inspector.descriptors.ApplicationDescriptor;
import com.facebook.flipper.testing.FlipperConnectionMock; import com.facebook.flipper.testing.FlipperConnectionMock;
import com.facebook.flipper.testing.FlipperResponderMock; import com.facebook.flipper.testing.FlipperResponderMock;
@@ -242,7 +241,6 @@ public class InspectorFlipperPluginTest {
final InspectorFlipperPlugin plugin = final InspectorFlipperPlugin plugin =
new InspectorFlipperPlugin(mApp, mDescriptorMapping, null); new InspectorFlipperPlugin(mApp, mDescriptorMapping, null);
final FlipperConnectionMock connection = new FlipperConnectionMock(); final FlipperConnectionMock connection = new FlipperConnectionMock();
plugin.onConnect(connection);
final TestNode one = new TestNode(); final TestNode one = new TestNode();
one.id = "1"; one.id = "1";
@@ -263,7 +261,20 @@ public class InspectorFlipperPluginTest {
root.children.add(three); root.children.add(three);
mApplicationDescriptor.root = root; mApplicationDescriptor.root = root;
plugin.hitTest(10, 10); TouchOverlayView view =
new TouchOverlayView(mApp.getApplication(), connection, mApp) {
@Override
protected String trackObject(Object obj) throws Exception {
return plugin.trackObject(obj);
}
@Override
protected NodeDescriptor<Object> descriptorForObject(Object obj) {
return plugin.descriptorForObject(obj);
}
};
view.hitTest(10, 10);
assertThat( assertThat(
connection.sent.get("select"), connection.sent.get("select"),