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:
committed by
Facebook GitHub Bot
parent
c1d9527406
commit
d871ce0a04
@@ -9,8 +9,6 @@ package com.facebook.flipper.plugins.inspector;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.util.Pair;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
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.plugins.common.MainThreadFlipperReceiver;
|
||||
import com.facebook.flipper.plugins.inspector.descriptors.ApplicationDescriptor;
|
||||
import com.facebook.flipper.plugins.inspector.descriptors.utils.AccessibilityUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class InspectorFlipperPlugin implements FlipperPlugin {
|
||||
@@ -37,7 +32,7 @@ public class InspectorFlipperPlugin implements FlipperPlugin {
|
||||
private DescriptorMapping mDescriptorMapping;
|
||||
private ObjectTracker mObjectTracker;
|
||||
private String mHighlightedId;
|
||||
private TouchOverlayView mTouchOverlay;
|
||||
TouchOverlayView mTouchOverlay;
|
||||
private FlipperConnection mConnection;
|
||||
private @Nullable List<ExtensionCommand> mExtensionCommands;
|
||||
private boolean mShowLithoAccessibilitySettings;
|
||||
@@ -373,7 +368,18 @@ public class InspectorFlipperPlugin implements FlipperPlugin {
|
||||
|
||||
if (root != null) {
|
||||
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.bringChildToFront(mTouchOverlay);
|
||||
} 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(
|
||||
final String id, final boolean highlighted, final boolean isAlignmentMode) throws Exception {
|
||||
final Object obj = mObjectTracker.get(id);
|
||||
@@ -745,7 +582,7 @@ public class InspectorFlipperPlugin implements FlipperPlugin {
|
||||
.build();
|
||||
}
|
||||
|
||||
private String trackObject(Object obj) throws Exception {
|
||||
String trackObject(Object obj) throws Exception {
|
||||
final NodeDescriptor<Object> descriptor = descriptorForObject(obj);
|
||||
final String id = descriptor.getId(obj);
|
||||
final Object curr = mObjectTracker.get(id);
|
||||
@@ -756,12 +593,12 @@ public class InspectorFlipperPlugin implements FlipperPlugin {
|
||||
return id;
|
||||
}
|
||||
|
||||
private NodeDescriptor<Object> descriptorForObject(Object obj) {
|
||||
NodeDescriptor<Object> descriptorForObject(Object obj) {
|
||||
final Class c = assertNotNull(obj).getClass();
|
||||
return (NodeDescriptor<Object>) mDescriptorMapping.descriptorForClass(c);
|
||||
}
|
||||
|
||||
private static Object assertNotNull(@Nullable Object o) {
|
||||
static Object assertNotNull(@Nullable Object o) {
|
||||
if (o == null) {
|
||||
throw new RuntimeException("Unexpected null value");
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import com.facebook.flipper.core.FlipperArray;
|
||||
import com.facebook.flipper.core.FlipperConnection;
|
||||
import com.facebook.flipper.core.FlipperDynamic;
|
||||
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.testing.FlipperConnectionMock;
|
||||
import com.facebook.flipper.testing.FlipperResponderMock;
|
||||
@@ -242,7 +241,6 @@ public class InspectorFlipperPluginTest {
|
||||
final InspectorFlipperPlugin plugin =
|
||||
new InspectorFlipperPlugin(mApp, mDescriptorMapping, null);
|
||||
final FlipperConnectionMock connection = new FlipperConnectionMock();
|
||||
plugin.onConnect(connection);
|
||||
|
||||
final TestNode one = new TestNode();
|
||||
one.id = "1";
|
||||
@@ -263,7 +261,20 @@ public class InspectorFlipperPluginTest {
|
||||
root.children.add(three);
|
||||
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(
|
||||
connection.sent.get("select"),
|
||||
|
||||
Reference in New Issue
Block a user