Change the way of action pefrom(no longer wait for event), add loading jar file support, fix simple action memory leak
This commit is contained in:
@@ -3,6 +3,7 @@ package com.stardust.automator;
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityDelegate;
|
||||
|
||||
@@ -21,6 +22,24 @@ public class AccessibilityEventCommandHost implements AccessibilityDelegate {
|
||||
public interface Command {
|
||||
|
||||
void execute(AccessibilityService service, AccessibilityEvent event);
|
||||
|
||||
boolean isValid();
|
||||
|
||||
void setValid(boolean valid);
|
||||
}
|
||||
|
||||
public abstract static class AbstractCommand implements Command {
|
||||
|
||||
private boolean mValid;
|
||||
|
||||
public synchronized boolean isValid() {
|
||||
return mValid;
|
||||
}
|
||||
|
||||
public synchronized void setValid(boolean valid) {
|
||||
mValid = valid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final String TAG = "CommandHostDelegate";
|
||||
@@ -31,8 +50,8 @@ public class AccessibilityEventCommandHost implements AccessibilityDelegate {
|
||||
|
||||
|
||||
private final Queue<Command> mCommands = new LinkedList<>();
|
||||
private Executor mExecutor = Executors.newFixedThreadPool(5);
|
||||
private int mRunMode = 0;
|
||||
private Executor mExecutor = Executors.newSingleThreadExecutor();
|
||||
private int mRunMode = RUN_MODE_THREAD_POOL;
|
||||
|
||||
@Override
|
||||
public boolean onAccessibilityEvent(final AccessibilityService service, final AccessibilityEvent event) {
|
||||
@@ -42,20 +61,24 @@ public class AccessibilityEventCommandHost implements AccessibilityDelegate {
|
||||
}
|
||||
while (!mCommands.isEmpty()) {
|
||||
final Command command = mCommands.poll();
|
||||
executeCommand(command, service, event);
|
||||
if (command.isValid()) {
|
||||
executeCommand(command, service, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void executeCommand(final Command command, final AccessibilityService service, final AccessibilityEvent event) {
|
||||
Runnable r = new Runnable() {
|
||||
final Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!command.isValid()) {
|
||||
return;
|
||||
}
|
||||
Log.v(TAG, "executing " + command);
|
||||
command.execute(service, event);
|
||||
synchronized (command) {
|
||||
Log.v(TAG, "notify " + mCommands.size() + " commands");
|
||||
command.notify();
|
||||
}
|
||||
}
|
||||
@@ -78,6 +101,7 @@ public class AccessibilityEventCommandHost implements AccessibilityDelegate {
|
||||
try {
|
||||
command.wait();
|
||||
} catch (InterruptedException e) {
|
||||
command.setValid(false);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.stardust.automator;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class GC {
|
||||
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.stardust.automator;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class UiBridge {
|
||||
|
||||
|
||||
public UiBridge() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import com.stardust.automator.filter.TextFilter;
|
||||
import com.stardust.automator.filter.ClassNameFilter;
|
||||
import com.stardust.automator.filter.DescFilter;
|
||||
import com.stardust.automator.filter.ListFilter;
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
@@ -360,6 +361,15 @@ public class UiGlobalSelector {
|
||||
return UiObjectCollection.of(list);
|
||||
}
|
||||
|
||||
public UiObjectCollection findOf(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo node) {
|
||||
List<AccessibilityNodeInfo> list = new ArrayList<>();
|
||||
list.add(node);
|
||||
for (ListFilter filter : mFilters) {
|
||||
list = filter.filter(list);
|
||||
}
|
||||
return UiObjectCollection.of(list);
|
||||
}
|
||||
|
||||
public UiObject findOneOf(AccessibilityNodeInfo node) {
|
||||
// TODO: 2017/3/9 优化
|
||||
return new UiObject(findOf(node).get(0).getInfo());
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package com.stardust.automator;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CONTEXT_CLICK;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_DOWN;
|
||||
@@ -21,10 +26,17 @@ import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.
|
||||
|
||||
public class UiObject extends AccessibilityNodeInfoCompat {
|
||||
|
||||
private AccessibilityNodeInfoAllocator mAllocator = null;
|
||||
|
||||
public UiObject(Object info) {
|
||||
super(info);
|
||||
}
|
||||
|
||||
public UiObject(Object info, AccessibilityNodeInfoAllocator allocator) {
|
||||
super(info);
|
||||
mAllocator = allocator;
|
||||
}
|
||||
|
||||
public UiObject parent() {
|
||||
return new UiObject(getParent().getInfo());
|
||||
}
|
||||
@@ -49,6 +61,22 @@ public class UiObject extends AccessibilityNodeInfoCompat {
|
||||
return UiObjectCollection.ofCompat(list);
|
||||
}
|
||||
|
||||
public int childCount() {
|
||||
return getChildCount();
|
||||
}
|
||||
|
||||
public Rect bounds() {
|
||||
return AccessibilityNodeInfoHelper.getBoundsInScreen(this);
|
||||
}
|
||||
|
||||
public Rect boundsInParent() {
|
||||
return AccessibilityNodeInfoHelper.getBoundsInParent(this);
|
||||
}
|
||||
|
||||
public int drawingOrder() {
|
||||
return getDrawingOrder();
|
||||
}
|
||||
|
||||
public String id() {
|
||||
return getViewIdResourceName();
|
||||
}
|
||||
@@ -188,4 +216,33 @@ public class UiObject extends AccessibilityNodeInfoCompat {
|
||||
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_COLUMN_INT, column));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeInfoCompat getChild(int index) {
|
||||
if (mAllocator == null)
|
||||
return super.getChild(index);
|
||||
return mAllocator.getChild(this, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeInfoCompat getParent() {
|
||||
if (mAllocator == null)
|
||||
return super.getParent();
|
||||
return mAllocator.getParent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(String text) {
|
||||
if (mAllocator == null)
|
||||
return super.findAccessibilityNodeInfosByText(text);
|
||||
return mAllocator.findAccessibilityNodeInfosByText(this, text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(String viewId) {
|
||||
if (mAllocator == null)
|
||||
return super.findAccessibilityNodeInfosByViewId(viewId);
|
||||
return mAllocator.findAccessibilityNodeInfosByViewId(this, viewId);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -34,7 +36,7 @@ public abstract class DfsFilter implements ListFilter, Filter {
|
||||
|
||||
private void filterChildren(AccessibilityNodeInfo parent, List<AccessibilityNodeInfo> list) {
|
||||
for (int i = 0; i < parent.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo child = parent.getChild(i);
|
||||
AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(parent, i);
|
||||
if (child == null)
|
||||
continue;
|
||||
boolean included = isIncluded(child);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.stardust.automator.simple_action;
|
||||
|
||||
import android.util.SparseArray;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.util.SparseArrayEntries;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public interface Able {
|
||||
|
||||
SparseArray<Able> ABLE_MAP = new SparseArrayEntries<Able>()
|
||||
.entry(AccessibilityNodeInfo.ACTION_CLICK, new Able() {
|
||||
@Override
|
||||
public boolean isAble(AccessibilityNodeInfo nodeInfo) {
|
||||
return nodeInfo.isClickable();
|
||||
}
|
||||
})
|
||||
.entry(AccessibilityNodeInfo.ACTION_LONG_CLICK, new Able() {
|
||||
@Override
|
||||
public boolean isAble(AccessibilityNodeInfo nodeInfo) {
|
||||
return nodeInfo.isLongClickable();
|
||||
}
|
||||
})
|
||||
.entry(AccessibilityNodeInfo.ACTION_FOCUS, new Able() {
|
||||
@Override
|
||||
public boolean isAble(AccessibilityNodeInfo nodeInfo) {
|
||||
return nodeInfo.isFocusable();
|
||||
}
|
||||
})
|
||||
.entry(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD, new Able() {
|
||||
@Override
|
||||
public boolean isAble(AccessibilityNodeInfo nodeInfo) {
|
||||
return nodeInfo.isScrollable();
|
||||
}
|
||||
})
|
||||
.entry(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, new Able() {
|
||||
@Override
|
||||
public boolean isAble(AccessibilityNodeInfo nodeInfo) {
|
||||
return nodeInfo.isScrollable();
|
||||
}
|
||||
})
|
||||
.sparseArray();
|
||||
|
||||
boolean isAble(AccessibilityNodeInfo node);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.stardust.automator.simple_action;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.util.MapEntries;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class ActionFactory {
|
||||
|
||||
private static Map<Integer, Object> searchUpAction = new MapEntries<Integer, Object>()
|
||||
.entry(AccessibilityNodeInfo.ACTION_CLICK, null)
|
||||
.entry(AccessibilityNodeInfo.ACTION_LONG_CLICK, null)
|
||||
.entry(AccessibilityNodeInfo.ACTION_SELECT, null)
|
||||
.entry(AccessibilityNodeInfo.ACTION_FOCUS, null)
|
||||
.entry(AccessibilityNodeInfo.ACTION_SELECT, null)
|
||||
.entry(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD, null)
|
||||
.entry(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, null)
|
||||
.map();
|
||||
|
||||
public static SimpleAction createActionWithTextFilter(int action, String text, int index) {
|
||||
if (searchUpAction.containsKey(action))
|
||||
return new SearchUpTargetAction(action, new FilterAction.TextFilter(text, index));
|
||||
else
|
||||
return new DepthFirstSearchTargetAction(action, new FilterAction.TextFilter(text, index));
|
||||
}
|
||||
|
||||
public static SimpleAction createActionWithBoundsFilter(int action, Rect rect) {
|
||||
if (searchUpAction.containsKey(action))
|
||||
return new SearchUpTargetAction(action, new FilterAction.BoundsFilter(rect));
|
||||
else
|
||||
return new DepthFirstSearchTargetAction(action, new FilterAction.BoundsFilter(rect));
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public static SimpleAction createActionWithEditableFilter(int action, int index, final String text) {
|
||||
return new SearchTargetAction(action, new FilterAction.EditableFilter(index)) {
|
||||
|
||||
@Override
|
||||
protected void performAction(AccessibilityNodeInfo node) {
|
||||
Bundle args = new Bundle();
|
||||
args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
|
||||
node.performAction(getAction(), args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static SimpleAction createScrollMaxAction(int action) {
|
||||
return new ScrollMaxAction(action);
|
||||
}
|
||||
|
||||
public static SimpleAction createScrollAction(int action, int i) {
|
||||
return new ScrollAction(action, i);
|
||||
}
|
||||
|
||||
public static SimpleAction createActionWithIdFilter(int action, String id) {
|
||||
return new FilterAction.SimpleFilterAction(action, new FilterAction.IdFilter(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.stardust.automator.simple_action;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.RequiresApi;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public interface ActionTarget {
|
||||
|
||||
SimpleAction createAction(int action, Object... params);
|
||||
|
||||
class TextActionTarget implements ActionTarget {
|
||||
|
||||
String mText;
|
||||
int mIndex;
|
||||
|
||||
public TextActionTarget(String text, int i) {
|
||||
mText = text;
|
||||
mIndex = i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleAction createAction(int action, Object... params) {
|
||||
return ActionFactory.createActionWithTextFilter(action, mText, mIndex);
|
||||
}
|
||||
}
|
||||
|
||||
class BoundsActionTarget implements ActionTarget {
|
||||
|
||||
Rect mBoundsInRect;
|
||||
|
||||
public BoundsActionTarget(Rect rect) {
|
||||
mBoundsInRect = rect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleAction createAction(int action, Object... params) {
|
||||
return ActionFactory.createActionWithBoundsFilter(action, mBoundsInRect);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
class EditableActionTarget implements ActionTarget {
|
||||
private int mIndex;
|
||||
|
||||
public EditableActionTarget(int index) {
|
||||
mIndex = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleAction createAction(int action, Object... params) {
|
||||
return ActionFactory.createActionWithEditableFilter(action, mIndex, params[0].toString());
|
||||
}
|
||||
}
|
||||
|
||||
class IdActionTarget implements ActionTarget {
|
||||
private String mId;
|
||||
|
||||
public IdActionTarget(String id) {
|
||||
mId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleAction createAction(int action, Object... params) {
|
||||
return ActionFactory.createActionWithIdFilter(action, mId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.stardust.automator.simple_action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class DepthFirstSearchTargetAction extends SearchTargetAction {
|
||||
|
||||
|
||||
private Able mAble;
|
||||
private static final long TIME_LIMIT = 500;
|
||||
|
||||
public DepthFirstSearchTargetAction(int action, Filter filter) {
|
||||
super(action, filter);
|
||||
mAble = Able.ABLE_MAP.get(action);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeInfo searchTarget(AccessibilityNodeInfo n) {
|
||||
if (n == null)
|
||||
return null;
|
||||
if (mAble.isAble(n))
|
||||
return n;
|
||||
for (int i = 0; i < n.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(n, i);
|
||||
if (child == null)
|
||||
continue;
|
||||
AccessibilityNodeInfo node = searchTarget(child);
|
||||
if (node != null)
|
||||
return node;
|
||||
else
|
||||
child.recycle();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package com.stardust.automator.simple_action;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public abstract class FilterAction extends SimpleAction {
|
||||
|
||||
|
||||
public interface Filter {
|
||||
|
||||
List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root);
|
||||
}
|
||||
|
||||
public static class TextFilter implements Filter {
|
||||
|
||||
String mText;
|
||||
int mIndex;
|
||||
|
||||
public TextFilter(String text, int index) {
|
||||
mText = text;
|
||||
mIndex = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root) {
|
||||
List<AccessibilityNodeInfo> list = AccessibilityNodeInfoAllocator.getGlobal().findAccessibilityNodeInfosByText(root, mText);
|
||||
if (mIndex == -1)
|
||||
return list;
|
||||
if (mIndex >= list.size())
|
||||
return Collections.EMPTY_LIST;
|
||||
return Collections.singletonList(list.get(mIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TextFilter{" +
|
||||
"mText='" + mText + '\'' +
|
||||
", mIndex=" + mIndex +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class BoundsFilter implements Filter {
|
||||
|
||||
Rect mBoundsInScreen;
|
||||
|
||||
public BoundsFilter(Rect boundsInScreen) {
|
||||
mBoundsInScreen = boundsInScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root) {
|
||||
return Collections.singletonList(findAccessibilityNodeInfosByBounds(root));
|
||||
}
|
||||
|
||||
private AccessibilityNodeInfo findAccessibilityNodeInfosByBounds(AccessibilityNodeInfo root) {
|
||||
if (root == null)
|
||||
return null;
|
||||
Rect rect = new Rect();
|
||||
root.getBoundsInScreen(rect);
|
||||
if (rect.equals(mBoundsInScreen)) {
|
||||
return root;
|
||||
}
|
||||
for (int i = 0; i < root.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(root, i);
|
||||
if (child == null)
|
||||
continue;
|
||||
AccessibilityNodeInfo nodeInfo = findAccessibilityNodeInfosByBounds(child);
|
||||
if (nodeInfo != null)
|
||||
return nodeInfo;
|
||||
else
|
||||
child.recycle();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BoundsFilter{" +
|
||||
"mBoundsInScreen=" + mBoundsInScreen +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class EditableFilter implements Filter {
|
||||
|
||||
private int mIndex;
|
||||
|
||||
public EditableFilter(int index) {
|
||||
mIndex = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root) {
|
||||
List<AccessibilityNodeInfo> editableList = findEditable(root);
|
||||
if (mIndex == -1)
|
||||
return editableList;
|
||||
if (mIndex >= editableList.size())
|
||||
return Collections.EMPTY_LIST;
|
||||
return Collections.singletonList(editableList.get(mIndex));
|
||||
}
|
||||
|
||||
public static List<AccessibilityNodeInfo> findEditable(AccessibilityNodeInfo root) {
|
||||
if (root == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (root.isEditable()) {
|
||||
return Collections.singletonList(root);
|
||||
}
|
||||
List<AccessibilityNodeInfo> list = new LinkedList<>();
|
||||
for (int i = 0; i < root.getChildCount(); i++) {
|
||||
list.addAll(findEditable(AccessibilityNodeInfoAllocator.getGlobal().getChild(root, i)));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EditableFilter{" +
|
||||
"mIndex=" + mIndex +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class IdFilter implements Filter {
|
||||
|
||||
private final String mId;
|
||||
|
||||
public IdFilter(String id) {
|
||||
this.mId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root) {
|
||||
return AccessibilityNodeInfoAllocator.getGlobal().findAccessibilityNodeInfosByViewId(root, mId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IdFilter{" +
|
||||
"mId='" + mId + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Filter mFilter;
|
||||
|
||||
public FilterAction(Filter filter) {
|
||||
mFilter = filter;
|
||||
}
|
||||
|
||||
public boolean perform(AccessibilityNodeInfo root) {
|
||||
if (root == null)
|
||||
return false;
|
||||
List<AccessibilityNodeInfo> list = mFilter.filter(root);
|
||||
boolean succeed = perform(list);
|
||||
AccessibilityNodeInfoAllocator.recycleList(root, list);
|
||||
return succeed;
|
||||
}
|
||||
|
||||
public abstract boolean perform(List<AccessibilityNodeInfo> nodes);
|
||||
|
||||
public static class SimpleFilterAction extends FilterAction {
|
||||
|
||||
private int mAction;
|
||||
|
||||
public SimpleFilterAction(int action, Filter filter) {
|
||||
super(filter);
|
||||
mAction = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean perform(List<AccessibilityNodeInfo> nodes) {
|
||||
if (nodes == null || nodes.isEmpty())
|
||||
return false;
|
||||
boolean succeed = true;
|
||||
for (AccessibilityNodeInfo nodeInfo : nodes) {
|
||||
if (!nodeInfo.performAction(mAction)) {
|
||||
succeed = false;
|
||||
}
|
||||
}
|
||||
return succeed;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FilterAction{" +
|
||||
"mFilter=" + mFilter +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.stardust.automator.simple_action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/2/14.
|
||||
*/
|
||||
|
||||
public class GetTextAction extends SimpleAction {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean perform(AccessibilityNodeInfo root) {
|
||||
List<String> texts = new ArrayList<>();
|
||||
getText(root, texts);
|
||||
super.setResult(texts);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void getText(AccessibilityNodeInfo nodeInfo, List<String> texts) {
|
||||
CharSequence text = nodeInfo.getText();
|
||||
if (text != null && text.length() != 0) {
|
||||
texts.add(text.toString());
|
||||
}
|
||||
for (int i = 0; i < nodeInfo.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(nodeInfo, i);
|
||||
if (child != null) {
|
||||
getText(child, texts);
|
||||
child.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResult(Object result) {
|
||||
if (result instanceof List)
|
||||
super.setResult(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.stardust.automator.simple_action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/2/12.
|
||||
*/
|
||||
public class ScrollAction extends SimpleAction {
|
||||
|
||||
private int mIndex, mAction;
|
||||
|
||||
public ScrollAction(int action, int i) {
|
||||
mAction = action;
|
||||
mIndex = i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean perform(AccessibilityNodeInfo root) {
|
||||
List<AccessibilityNodeInfo> scrollableNodes = findScrollableNodes(root);
|
||||
boolean result = mIndex < scrollableNodes.size() && scrollableNodes.get(mIndex).performAction(mAction);
|
||||
recycle(scrollableNodes, root);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void recycle(List<AccessibilityNodeInfo> list, AccessibilityNodeInfo root) {
|
||||
for (AccessibilityNodeInfo nodeInfo : list) {
|
||||
if (nodeInfo != root)
|
||||
nodeInfo.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
private List<AccessibilityNodeInfo> findScrollableNodes(AccessibilityNodeInfo root) {
|
||||
List<AccessibilityNodeInfo> list = new ArrayList<>();
|
||||
findScrollableNodes(root, list);
|
||||
return list;
|
||||
}
|
||||
|
||||
private static boolean findScrollableNodes(AccessibilityNodeInfo node, List<AccessibilityNodeInfo> list) {
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
if (node.isScrollable()) {
|
||||
list.add(node);
|
||||
}
|
||||
for (int i = 0; i < node.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(node, i);
|
||||
if (child == null)
|
||||
continue;
|
||||
if (!findScrollableNodes(child, list))
|
||||
child.recycle();
|
||||
}
|
||||
return node.isScrollable();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.stardust.automator.simple_action;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class ScrollMaxAction extends SimpleAction {
|
||||
|
||||
private static final String TAG = ScrollMaxAction.class.getSimpleName();
|
||||
private int mScrollAction;
|
||||
private AccessibilityNodeInfo mMaxScrollableNode;
|
||||
private AccessibilityNodeInfo mRootNode;
|
||||
|
||||
public ScrollMaxAction(int scrollAction) {
|
||||
mScrollAction = scrollAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean perform(AccessibilityNodeInfo rootNodeInfo) {
|
||||
reset();
|
||||
mRootNode = rootNodeInfo;
|
||||
findMaxScrollableNodeInfo(rootNodeInfo);
|
||||
boolean result = mMaxScrollableNode != null && mMaxScrollableNode.performAction(mScrollAction);
|
||||
reset();
|
||||
return result;
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
if (mMaxScrollableNode != null && mMaxScrollableNode != mRootNode) {
|
||||
mMaxScrollableNode.recycle();
|
||||
}
|
||||
mMaxScrollableNode = mRootNode = null;
|
||||
}
|
||||
|
||||
private void findMaxScrollableNodeInfo(AccessibilityNodeInfo nodeInfo) {
|
||||
if (nodeInfo == null)
|
||||
return;
|
||||
if (nodeInfo.isScrollable()) {
|
||||
if (mMaxScrollableNode == null) {
|
||||
mMaxScrollableNode = nodeInfo;
|
||||
} else if (getAreaInScreen(mMaxScrollableNode) < getAreaInScreen(nodeInfo)) {
|
||||
if (mMaxScrollableNode != mRootNode)
|
||||
mMaxScrollableNode.recycle();
|
||||
mMaxScrollableNode = nodeInfo;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < nodeInfo.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(nodeInfo, i);
|
||||
if (child != null) {
|
||||
findMaxScrollableNodeInfo(child);
|
||||
if (mMaxScrollableNode != child) {
|
||||
child.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long getAreaInScreen(AccessibilityNodeInfo nodeInfo) {
|
||||
Rect rect = new Rect();
|
||||
nodeInfo.getBoundsInScreen(rect);
|
||||
long area = ((long) rect.width()) * rect.height();
|
||||
Log.v(TAG, "area=" + area);
|
||||
return area;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.stardust.automator.simple_action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public abstract class SearchTargetAction extends FilterAction {
|
||||
|
||||
private int mAction;
|
||||
|
||||
public SearchTargetAction(int action, Filter filter) {
|
||||
super(filter);
|
||||
mAction = action;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean perform(List<AccessibilityNodeInfo> nodes) {
|
||||
boolean performed = false;
|
||||
for (AccessibilityNodeInfo node : nodes) {
|
||||
node = searchTarget(node);
|
||||
if (node != null) {
|
||||
performAction(node);
|
||||
performed = true;
|
||||
node.recycle();
|
||||
}
|
||||
}
|
||||
return performed;
|
||||
}
|
||||
|
||||
protected void performAction(AccessibilityNodeInfo node) {
|
||||
node.performAction(mAction);
|
||||
}
|
||||
|
||||
public int getAction() {
|
||||
return mAction;
|
||||
}
|
||||
|
||||
public AccessibilityNodeInfo searchTarget(AccessibilityNodeInfo node) {
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SearchTargetAction{" +
|
||||
"mAction=" + mAction + ", " +
|
||||
super.toString() +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.stardust.automator.simple_action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class SearchUpTargetAction extends SearchTargetAction {
|
||||
|
||||
private static final String TAG = SearchUpTargetAction.class.getSimpleName();
|
||||
private static final int LOOP_MAX = 20;
|
||||
private Able mAble;
|
||||
|
||||
public SearchUpTargetAction(int action, Filter filter) {
|
||||
super(action, filter);
|
||||
mAble = Able.ABLE_MAP.get(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeInfo searchTarget(AccessibilityNodeInfo n) {
|
||||
AccessibilityNodeInfo node = n;
|
||||
int i = 0;
|
||||
List<AccessibilityNodeInfo> list = new ArrayList<>();
|
||||
while (node != null && !mAble.isAble(node)) {
|
||||
i++;
|
||||
if (i > LOOP_MAX) {
|
||||
node.recycle();
|
||||
return null;
|
||||
}
|
||||
AccessibilityNodeInfo parent = AccessibilityNodeInfoAllocator.getGlobal().getParent(node);
|
||||
list.add(node);
|
||||
node = parent;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SearchUpTargetAction{" +
|
||||
"mAble=" + mAble + ", " +
|
||||
super.toString() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.stardust.automator.simple_action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public abstract class SimpleAction {
|
||||
|
||||
private boolean mValid = true;
|
||||
private Object mResult = false;
|
||||
|
||||
public abstract boolean perform(AccessibilityNodeInfo root);
|
||||
|
||||
public synchronized Object getResult() {
|
||||
return mResult;
|
||||
}
|
||||
|
||||
public synchronized void setResult(Object result) {
|
||||
mResult = result;
|
||||
}
|
||||
|
||||
public synchronized void setValid(boolean valid) {
|
||||
mValid = valid;
|
||||
}
|
||||
|
||||
public synchronized boolean isValid() {
|
||||
return mValid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.stardust.automator.simple_action;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
|
||||
import com.stardust.view.accessibility.AccessibilityDelegate;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/21.
|
||||
*/
|
||||
|
||||
public class SimpleActionPerformHost implements AccessibilityDelegate {
|
||||
|
||||
private static final String TAG = "ActionPerformDelegate";
|
||||
|
||||
private BlockingQueue<SimpleAction> mSimpleAction = new ArrayBlockingQueue<>(1);
|
||||
private Executor mExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
public void addToActionQueueAndWait(SimpleAction simpleAction) throws InterruptedException {
|
||||
mSimpleAction.put(simpleAction);
|
||||
synchronized (simpleAction) {
|
||||
simpleAction.wait();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
|
||||
SimpleAction action = mSimpleAction.poll();
|
||||
if (action == null)
|
||||
return false;
|
||||
AccessibilityNodeInfo root = service.getRootInActiveWindow();
|
||||
if (root == null) {
|
||||
return false;
|
||||
}
|
||||
performAction(root, action);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void performAction(AccessibilityNodeInfo root, SimpleAction simpleAction) {
|
||||
mExecutor.execute(new SimpleActionPerformRunnable(root, simpleAction));
|
||||
}
|
||||
|
||||
|
||||
private synchronized void onActionPerformed(SimpleAction simpleAction) {
|
||||
AccessibilityNodeInfoAllocator.getGlobal().recycleAll();
|
||||
synchronized (simpleAction) {
|
||||
simpleAction.notify();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class SimpleActionPerformRunnable implements Runnable {
|
||||
|
||||
private final SimpleAction mSimpleAction;
|
||||
private final AccessibilityNodeInfo mRoot;
|
||||
|
||||
SimpleActionPerformRunnable(AccessibilityNodeInfo root, SimpleAction simpleAction) {
|
||||
mRoot = root;
|
||||
mSimpleAction = simpleAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!mSimpleAction.isValid()) {
|
||||
return;
|
||||
}
|
||||
Log.i(TAG, "perform simpleAction: " + mSimpleAction);
|
||||
if (mSimpleAction.perform(mRoot)) {
|
||||
mSimpleAction.setResult(true);
|
||||
onActionPerformed(mSimpleAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.stardust.view.accessibility;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/22.
|
||||
*/
|
||||
|
||||
public class AccessibilityAllocator {
|
||||
|
||||
|
||||
private List<AccessibilityNodeInfo> mAccessibilityNodeInfoList = new Vector<>();
|
||||
|
||||
public AccessibilityNodeInfo getChild(AccessibilityNodeInfo parent, int i) {
|
||||
return add(parent.getChild(i));
|
||||
}
|
||||
|
||||
private AccessibilityNodeInfo add(AccessibilityNodeInfo nodeInfo) {
|
||||
mAccessibilityNodeInfoList.add(nodeInfo);
|
||||
return nodeInfo;
|
||||
}
|
||||
|
||||
public int recycleAll() {
|
||||
int recycled = 0;
|
||||
for (AccessibilityNodeInfo nodeInfo : mAccessibilityNodeInfoList) {
|
||||
try {
|
||||
nodeInfo.recycle();
|
||||
recycled++;
|
||||
} catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
return recycled;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.stardust.view.accessibility;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/2/14.
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityDelegate;
|
||||
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
package com.stardust.view.accessibility;
|
||||
|
||||
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.automator.BuildConfig;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/22.
|
||||
*/
|
||||
|
||||
public class AccessibilityNodeInfoAllocator {
|
||||
|
||||
public static final AccessibilityNodeInfoAllocator NONE = new NoOpAllocator();
|
||||
|
||||
private static final String TAG = "AccessibilityAllocator";
|
||||
private static final boolean DEBUG = BuildConfig.DEBUG;
|
||||
|
||||
|
||||
private static final AccessibilityNodeInfoAllocator GLOBAL = new AccessibilityNodeInfoAllocator();
|
||||
|
||||
public static AccessibilityNodeInfoAllocator getGlobal() {
|
||||
return GLOBAL;
|
||||
}
|
||||
|
||||
private Map<AccessibilityNodeInfo, String> mAccessibilityNodeInfoList = new HashMap<>();
|
||||
|
||||
public AccessibilityNodeInfo getChild(AccessibilityNodeInfo parent, int i) {
|
||||
return add(parent.getChild(i));
|
||||
}
|
||||
|
||||
public AccessibilityNodeInfoCompat getChild(AccessibilityNodeInfoCompat parent, int i) {
|
||||
AccessibilityNodeInfoCompat compat = parent.getChild(i);
|
||||
add((AccessibilityNodeInfo) compat.getInfo());
|
||||
return compat;
|
||||
}
|
||||
|
||||
public AccessibilityNodeInfo getParent(AccessibilityNodeInfo n) {
|
||||
return add(n.getParent());
|
||||
}
|
||||
|
||||
|
||||
public AccessibilityNodeInfoCompat getParent(AccessibilityNodeInfoCompat n) {
|
||||
AccessibilityNodeInfoCompat compat = n.getParent();
|
||||
add((AccessibilityNodeInfo) compat.getInfo());
|
||||
return compat;
|
||||
}
|
||||
|
||||
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(AccessibilityNodeInfo root, String text) {
|
||||
List<AccessibilityNodeInfo> list = root.findAccessibilityNodeInfosByText(text);
|
||||
addAll(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(AccessibilityNodeInfoCompat root, String text) {
|
||||
List<AccessibilityNodeInfoCompat> list = root.findAccessibilityNodeInfosByText(text);
|
||||
addAll(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(AccessibilityNodeInfo root, String id) {
|
||||
List<AccessibilityNodeInfo> list = root.findAccessibilityNodeInfosByViewId(id);
|
||||
addAll(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(AccessibilityNodeInfoCompat root, String id) {
|
||||
List<AccessibilityNodeInfoCompat> list = root.findAccessibilityNodeInfosByViewId(id);
|
||||
addAll(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
public void recycle(AccessibilityNodeInfo nodeInfo) {
|
||||
nodeInfo.recycle();
|
||||
mAccessibilityNodeInfoList.remove(nodeInfo);
|
||||
}
|
||||
|
||||
public void recycle(AccessibilityNodeInfoCompat nodeInfo) {
|
||||
recycle((AccessibilityNodeInfo) nodeInfo.getInfo());
|
||||
}
|
||||
|
||||
public int recycleAll() {
|
||||
int notRecycledCount = 0;
|
||||
int size = mAccessibilityNodeInfoList.size();
|
||||
for (Map.Entry<AccessibilityNodeInfo, String> pair : mAccessibilityNodeInfoList.entrySet()) {
|
||||
try {
|
||||
pair.getKey().recycle();
|
||||
notRecycledCount++;
|
||||
if (DEBUG)
|
||||
Log.w(TAG, pair.getValue());
|
||||
} catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
Log.i(TAG, "Total: " + size + " Not recycled: " + notRecycledCount);
|
||||
return notRecycledCount;
|
||||
}
|
||||
|
||||
private AccessibilityNodeInfo add(AccessibilityNodeInfo nodeInfo) {
|
||||
String stackTrace = DEBUG ? Arrays.toString(Thread.currentThread().getStackTrace()) : null;
|
||||
mAccessibilityNodeInfoList.put(nodeInfo, stackTrace);
|
||||
return nodeInfo;
|
||||
}
|
||||
|
||||
private void addAll(Collection<?> nodeInfos) {
|
||||
String stackTrace = DEBUG ? Arrays.toString(Thread.currentThread().getStackTrace()) : null;
|
||||
for (Object nodeInfo : nodeInfos) {
|
||||
if (nodeInfo instanceof AccessibilityNodeInfo) {
|
||||
mAccessibilityNodeInfoList.put((AccessibilityNodeInfo) nodeInfo, stackTrace);
|
||||
} else if (nodeInfo instanceof AccessibilityNodeInfoCompat) {
|
||||
mAccessibilityNodeInfoList.put((AccessibilityNodeInfo) ((AccessibilityNodeInfoCompat) nodeInfo).getInfo(), stackTrace);
|
||||
} else {
|
||||
throw new IllegalArgumentException("nodeInfo: " + nodeInfo + " nodeInfos:" + nodeInfos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void recycleList(AccessibilityNodeInfo root, List<AccessibilityNodeInfo> list) {
|
||||
for (AccessibilityNodeInfo nodeInfo : list) {
|
||||
if (nodeInfo != root) {
|
||||
nodeInfo.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class NoOpAllocator extends AccessibilityNodeInfoAllocator {
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeInfoCompat getParent(AccessibilityNodeInfoCompat n) {
|
||||
return n.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeInfo getParent(AccessibilityNodeInfo n) {
|
||||
return n.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeInfoCompat getChild(AccessibilityNodeInfoCompat parent, int i) {
|
||||
return parent.getChild(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeInfo getChild(AccessibilityNodeInfo parent, int i) {
|
||||
return parent.getChild(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(AccessibilityNodeInfoCompat root, String id) {
|
||||
return root.findAccessibilityNodeInfosByViewId(id);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(AccessibilityNodeInfo root, String id) {
|
||||
return root.findAccessibilityNodeInfosByViewId(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(AccessibilityNodeInfo root, String text) {
|
||||
return root.findAccessibilityNodeInfosByText(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(AccessibilityNodeInfoCompat root, String text) {
|
||||
return root.findAccessibilityNodeInfosByText(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle(AccessibilityNodeInfo nodeInfo) {
|
||||
super.recycle(nodeInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle(AccessibilityNodeInfoCompat nodeInfo) {
|
||||
super.recycle(nodeInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int recycleAll() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.stardust.view.accessibility;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.automator.UiObject;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/6.
|
||||
*/
|
||||
@@ -45,4 +48,16 @@ public class AccessibilityNodeInfoHelper {
|
||||
nodeInfo.getBoundsInScreen(rect);
|
||||
return rect;
|
||||
}
|
||||
|
||||
public static Rect getBoundsInScreen(AccessibilityNodeInfoCompat nodeInfo) {
|
||||
Rect rect = new Rect();
|
||||
nodeInfo.getBoundsInScreen(rect);
|
||||
return rect;
|
||||
}
|
||||
|
||||
public static Rect getBoundsInParent(AccessibilityNodeInfoCompat nodeInfo) {
|
||||
Rect rect = new Rect();
|
||||
nodeInfo.getBoundsInParent(rect);
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user