Add: floating window, layout inspector
This commit is contained in:
12
automator/src/main/AndroidManifest.xml
Normal file
12
automator/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
|
||||
package="com.stardust.automator"
|
||||
>
|
||||
|
||||
<application
|
||||
android:supportsRtl="true"
|
||||
>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.stardust.automator;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityDelegate;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class AccessibilityEventCommandHost implements AccessibilityDelegate {
|
||||
|
||||
public interface Command {
|
||||
|
||||
void execute(AccessibilityService service, AccessibilityEvent event);
|
||||
}
|
||||
|
||||
public static final AccessibilityEventCommandHost instance = new AccessibilityEventCommandHost();
|
||||
|
||||
private static final String TAG = "CommandHostDelegate";
|
||||
|
||||
private final Queue<Command> mCommands = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
|
||||
synchronized (mCommands) {
|
||||
if (!mCommands.isEmpty()) {
|
||||
Log.v(TAG, "execute " + mCommands.size() + " commands");
|
||||
}
|
||||
while (!mCommands.isEmpty()) {
|
||||
Command command = mCommands.poll();
|
||||
command.execute(service, event);
|
||||
synchronized (command) {
|
||||
command.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public void executeAndWaitForEvent(Command command) {
|
||||
synchronized (mCommands) {
|
||||
mCommands.offer(command);
|
||||
}
|
||||
synchronized (command) {
|
||||
try {
|
||||
command.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.stardust.automator;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public abstract class ActionArgument {
|
||||
|
||||
protected final String mKey;
|
||||
|
||||
private ActionArgument(String key) {
|
||||
mKey = key;
|
||||
}
|
||||
|
||||
public abstract void putIn(Bundle bundle);
|
||||
|
||||
public static class IntActionArgument extends ActionArgument {
|
||||
|
||||
private final int mInt;
|
||||
|
||||
public IntActionArgument(String name, int i) {
|
||||
super(name);
|
||||
mInt = i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIn(Bundle bundle) {
|
||||
bundle.putInt(mKey, mInt);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CharSequenceActionArgument extends ActionArgument {
|
||||
|
||||
private final CharSequence mCharSequence;
|
||||
|
||||
public CharSequenceActionArgument(String name, CharSequence charSequence) {
|
||||
super(name);
|
||||
mCharSequence = charSequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIn(Bundle bundle) {
|
||||
bundle.putCharSequence(mKey, mCharSequence);
|
||||
}
|
||||
}
|
||||
|
||||
public static class FloatActionArgument extends ActionArgument {
|
||||
private final float mFloat;
|
||||
|
||||
public FloatActionArgument(String name, float value) {
|
||||
super(name);
|
||||
mFloat = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIn(Bundle bundle) {
|
||||
bundle.putFloat(mKey, mFloat);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
automator/src/main/java/com/stardust/automator/GC.java
Normal file
12
automator/src/main/java/com/stardust/automator/GC.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.stardust.automator;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class GC {
|
||||
|
||||
|
||||
}
|
||||
15
automator/src/main/java/com/stardust/automator/UiBridge.java
Normal file
15
automator/src/main/java/com/stardust/automator/UiBridge.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.stardust.automator;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class UiBridge {
|
||||
|
||||
|
||||
public UiBridge() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
package com.stardust.automator;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.automator.filter.BooleanFilter;
|
||||
import com.stardust.automator.filter.BoundsFilter;
|
||||
import com.stardust.automator.filter.IdFilter;
|
||||
import com.stardust.automator.filter.PackageNameFilter;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.*;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CONTEXT_CLICK;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_DOWN;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_LEFT;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_RIGHT;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_TO_POSITION;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_PROGRESS;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SHOW_ON_SCREEN;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/8.
|
||||
*/
|
||||
|
||||
public class UiGlobalSelector {
|
||||
|
||||
private Queue<ListFilter> mFilters = new LinkedList<>();
|
||||
|
||||
//// 第一类筛选条件
|
||||
|
||||
public UiGlobalSelector id(String id) {
|
||||
mFilters.add(IdFilter.equals(id));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector idContains(String str) {
|
||||
mFilters.add(IdFilter.contains(str));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector idStartsWith(String prefix) {
|
||||
mFilters.add(IdFilter.startsWith(prefix));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector idEndsWith(String suffix) {
|
||||
mFilters.add(IdFilter.endsWith(suffix));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector idMatches(String regex) {
|
||||
mFilters.add(IdFilter.matches(regex));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector text(String text) {
|
||||
mFilters.add(TextFilter.equals(text));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector textContains(String str) {
|
||||
mFilters.add(TextFilter.contains(str));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector textStartsWith(String prefix) {
|
||||
mFilters.add(TextFilter.startsWith(prefix));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector textEndsWith(String suffix) {
|
||||
mFilters.add(TextFilter.endsWith(suffix));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector textMatches(String regex) {
|
||||
mFilters.add(TextFilter.matches(regex));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector desc(String desc) {
|
||||
mFilters.add(DescFilter.equals(desc));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector descContains(String str) {
|
||||
mFilters.add(DescFilter.contains(str));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector descStartsWith(String prefix) {
|
||||
mFilters.add(DescFilter.startsWith(prefix));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector descEndsWith(String suffix) {
|
||||
mFilters.add(DescFilter.endsWith(suffix));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector descMatches(String regex) {
|
||||
mFilters.add(DescFilter.matches(regex));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector className(String className) {
|
||||
mFilters.add(ClassNameFilter.equals(className));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector classNameContains(String str) {
|
||||
mFilters.add(ClassNameFilter.contains(str));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector classNameStartsWith(String prefix) {
|
||||
mFilters.add(ClassNameFilter.startsWith(prefix));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector classNameEndsWith(String suffix) {
|
||||
mFilters.add(ClassNameFilter.endsWith(suffix));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector classNameMatches(String regex) {
|
||||
mFilters.add(ClassNameFilter.matches(regex));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector packageName(String packageName) {
|
||||
mFilters.add(PackageNameFilter.equals(packageName));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector packageNameContains(String str) {
|
||||
mFilters.add(PackageNameFilter.contains(str));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector packageNameStartsWith(String prefix) {
|
||||
mFilters.add(PackageNameFilter.startsWith(prefix));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector packageNameEndsWith(String suffix) {
|
||||
mFilters.add(PackageNameFilter.endsWith(suffix));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector packageNameMatches(String regex) {
|
||||
mFilters.add(PackageNameFilter.matches(regex));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector bounds(int l, int t, int r, int b) {
|
||||
mFilters.add(new BoundsFilter(new Rect(l, t, r, b), BoundsFilter.TYPE_EQUALS));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector boundsInside(int l, int t, int r, int b) {
|
||||
mFilters.add(new BoundsFilter(new Rect(l, t, r, b), BoundsFilter.TYPE_INSIDE));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector boundsInParent(int l, int t, int r, int b) {
|
||||
mFilters.add(new BoundsFilter(new Rect(l, t, r, b), BoundsFilter.TYPE_PARENT));
|
||||
return this;
|
||||
}
|
||||
|
||||
//// 第二类筛选条件 -able
|
||||
|
||||
public UiGlobalSelector checkable(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.CHECKABLE, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector checked(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.CHECKED, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector focusable(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.FOCUSABLE, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector focused(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.FOCUSED, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector visibleToUser(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.VISIBLE_TO_USER, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector accessibilityFocused(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.ACCESSIBILITY_FOCUSED, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector selected(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.SELECTED, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector clickable(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.CLICKABLE, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector longClickable(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.LONG_CLICKABLE, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector enabled(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.ENABLED, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector password(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.PASSWORD, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector scrollable(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.SCROLLABLE, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector editable(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.EDITABLE, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector contentInvalid(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.CONTENT_INVALID, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector contextClickable(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.CONTEXT_CLICKABLE, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector multiLine(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.MULTI_LINE, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiGlobalSelector dismissable(boolean b) {
|
||||
mFilters.add(BooleanFilter.get(BooleanFilter.DISMISSABLE, b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public UiObjectCollection findOf(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());
|
||||
}
|
||||
|
||||
public UiGlobalSelector addFilter(ListFilter filter) {
|
||||
mFilters.add(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
184
automator/src/main/java/com/stardust/automator/UiObject.java
Normal file
184
automator/src/main/java/com/stardust/automator/UiObject.java
Normal file
@@ -0,0 +1,184 @@
|
||||
package com.stardust.automator;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CONTEXT_CLICK;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_DOWN;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_LEFT;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_RIGHT;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_TO_POSITION;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_PROGRESS;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SHOW_ON_SCREEN;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class UiObject extends AccessibilityNodeInfoCompat {
|
||||
|
||||
public UiObject(Object info) {
|
||||
super(info);
|
||||
}
|
||||
|
||||
public UiObject parent() {
|
||||
return new UiObject(getParent().getInfo());
|
||||
}
|
||||
|
||||
public UiObject child(int i) {
|
||||
return new UiObject(getChild(i).getInfo());
|
||||
}
|
||||
|
||||
public UiObjectCollection children() {
|
||||
ArrayList<AccessibilityNodeInfoCompat> list = new ArrayList<>(getChildCount());
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
list.add(getChild(i));
|
||||
}
|
||||
return UiObjectCollection.ofCompat(list);
|
||||
}
|
||||
|
||||
public String id() {
|
||||
return getViewIdResourceName();
|
||||
}
|
||||
|
||||
public CharSequence text() {
|
||||
return getText();
|
||||
}
|
||||
|
||||
public CharSequence desc() {
|
||||
return getContentDescription();
|
||||
}
|
||||
|
||||
public CharSequence className() {
|
||||
return getClassName();
|
||||
}
|
||||
|
||||
public CharSequence packageName() {
|
||||
|
||||
return getPackageName();
|
||||
}
|
||||
|
||||
public boolean performAction(int action, ActionArgument... arguments) {
|
||||
Bundle bundle = argumentsToBundle(arguments);
|
||||
return performAction(action, bundle);
|
||||
}
|
||||
|
||||
private static Bundle argumentsToBundle(ActionArgument[] arguments) {
|
||||
Bundle bundle = new Bundle();
|
||||
for (ActionArgument arg : arguments) {
|
||||
arg.putIn(bundle);
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
public boolean click() {
|
||||
return performAction(ACTION_CLICK);
|
||||
}
|
||||
|
||||
public boolean longClick() {
|
||||
return performAction(ACTION_LONG_CLICK);
|
||||
}
|
||||
|
||||
public boolean accessibilityFocus() {
|
||||
return performAction(ACTION_ACCESSIBILITY_FOCUS);
|
||||
}
|
||||
|
||||
public boolean clearAccessibilityFocus() {
|
||||
return performAction(ACTION_CLEAR_ACCESSIBILITY_FOCUS);
|
||||
}
|
||||
|
||||
public boolean focus() {
|
||||
return performAction(ACTION_FOCUS);
|
||||
}
|
||||
|
||||
public boolean clearFocus() {
|
||||
return performAction(ACTION_CLEAR_FOCUS);
|
||||
}
|
||||
|
||||
public boolean copy() {
|
||||
return performAction(ACTION_COPY);
|
||||
}
|
||||
|
||||
public boolean paste() {
|
||||
return performAction(ACTION_PASTE);
|
||||
}
|
||||
|
||||
public boolean select() {
|
||||
return performAction(ACTION_SELECT);
|
||||
}
|
||||
|
||||
public boolean cut() {
|
||||
return performAction(ACTION_CUT);
|
||||
}
|
||||
|
||||
public boolean collapse() {
|
||||
return performAction(ACTION_COLLAPSE);
|
||||
}
|
||||
|
||||
public boolean expand() {
|
||||
return performAction(ACTION_EXPAND);
|
||||
}
|
||||
|
||||
public boolean dismiss() {
|
||||
return performAction(ACTION_DISMISS);
|
||||
}
|
||||
|
||||
public boolean show() {
|
||||
return performAction(ACTION_SHOW_ON_SCREEN.getId());
|
||||
}
|
||||
|
||||
public boolean scrollForward() {
|
||||
return performAction(ACTION_SCROLL_FORWARD);
|
||||
}
|
||||
|
||||
public boolean scrollBackward() {
|
||||
return performAction(ACTION_SCROLL_BACKWARD);
|
||||
}
|
||||
|
||||
public boolean scrollUp() {
|
||||
return performAction(ACTION_SCROLL_UP.getId());
|
||||
}
|
||||
|
||||
public boolean scrollDown() {
|
||||
return performAction(ACTION_SCROLL_DOWN.getId());
|
||||
}
|
||||
|
||||
public boolean scrollLeft() {
|
||||
return performAction(ACTION_SCROLL_LEFT.getId());
|
||||
}
|
||||
|
||||
public boolean scrollRight() {
|
||||
return performAction(ACTION_SCROLL_RIGHT.getId());
|
||||
}
|
||||
|
||||
public boolean contextClick() {
|
||||
return performAction(ACTION_CONTEXT_CLICK.getId());
|
||||
}
|
||||
|
||||
public boolean setSelection(int s, int e) {
|
||||
return performAction(ACTION_SET_SELECTION,
|
||||
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_SELECTION_START_INT, s),
|
||||
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_SELECTION_END_INT, e));
|
||||
}
|
||||
|
||||
public boolean setText(String text) {
|
||||
return performAction(ACTION_SET_TEXT,
|
||||
new ActionArgument.CharSequenceActionArgument(ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text));
|
||||
}
|
||||
|
||||
public boolean setProgress(float value) {
|
||||
return performAction(ACTION_SET_PROGRESS.getId(),
|
||||
new ActionArgument.FloatActionArgument(ACTION_ARGUMENT_PROGRESS_VALUE, value));
|
||||
}
|
||||
|
||||
public boolean scrollTo(int row, int column) {
|
||||
return performAction(ACTION_SCROLL_TO_POSITION.getId(),
|
||||
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_ROW_INT, row),
|
||||
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_COLUMN_INT, column));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package com.stardust.automator;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.util.Consumer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.*;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CONTEXT_CLICK;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_DOWN;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_LEFT;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_RIGHT;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_TO_POSITION;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_PROGRESS;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SHOW_ON_SCREEN;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class UiObjectCollection {
|
||||
|
||||
|
||||
public static UiObjectCollection ofCompat(List<AccessibilityNodeInfoCompat> list) {
|
||||
return new UiObjectCollection(list);
|
||||
}
|
||||
|
||||
public static UiObjectCollection of(List<AccessibilityNodeInfo> list) {
|
||||
List<AccessibilityNodeInfoCompat> compatList = new ArrayList<>(list.size());
|
||||
for (AccessibilityNodeInfo nodeInfo : list) {
|
||||
compatList.add(new AccessibilityNodeInfoCompat(nodeInfo));
|
||||
}
|
||||
return new UiObjectCollection(compatList);
|
||||
}
|
||||
|
||||
private List<AccessibilityNodeInfoCompat> mNodes;
|
||||
private UiObject[] mUiObjects;
|
||||
|
||||
|
||||
private UiObjectCollection(List<AccessibilityNodeInfoCompat> list) {
|
||||
mNodes = list;
|
||||
}
|
||||
|
||||
public boolean performAction(int action) {
|
||||
boolean fail = false;
|
||||
for (AccessibilityNodeInfoCompat node : mNodes) {
|
||||
if (!node.performAction(action)) {
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
return !fail;
|
||||
}
|
||||
|
||||
public boolean performAction(int action, ActionArgument... arguments) {
|
||||
boolean fail = false;
|
||||
Bundle bundle = argumentsToBundle(arguments);
|
||||
for (AccessibilityNodeInfoCompat node : mNodes) {
|
||||
if (!node.performAction(action, bundle)) {
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
return !fail;
|
||||
}
|
||||
|
||||
private Bundle argumentsToBundle(ActionArgument[] arguments) {
|
||||
Bundle bundle = new Bundle();
|
||||
for (ActionArgument arg : arguments) {
|
||||
arg.putIn(bundle);
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
public boolean click() {
|
||||
return performAction(ACTION_CLICK);
|
||||
}
|
||||
|
||||
public boolean longClick() {
|
||||
return performAction(ACTION_LONG_CLICK);
|
||||
}
|
||||
|
||||
public boolean accessibilityFocus() {
|
||||
return performAction(ACTION_ACCESSIBILITY_FOCUS);
|
||||
}
|
||||
|
||||
public boolean clearAccessibilityFocus() {
|
||||
return performAction(ACTION_CLEAR_ACCESSIBILITY_FOCUS);
|
||||
}
|
||||
|
||||
public boolean focus() {
|
||||
return performAction(ACTION_FOCUS);
|
||||
}
|
||||
|
||||
public boolean clearFocus() {
|
||||
return performAction(ACTION_CLEAR_FOCUS);
|
||||
}
|
||||
|
||||
public boolean copy() {
|
||||
return performAction(ACTION_COPY);
|
||||
}
|
||||
|
||||
public boolean paste() {
|
||||
return performAction(ACTION_PASTE);
|
||||
}
|
||||
|
||||
public boolean select() {
|
||||
return performAction(ACTION_SELECT);
|
||||
}
|
||||
|
||||
public boolean cut() {
|
||||
return performAction(ACTION_CUT);
|
||||
}
|
||||
|
||||
public boolean collapse() {
|
||||
return performAction(ACTION_COLLAPSE);
|
||||
}
|
||||
|
||||
public boolean expand() {
|
||||
return performAction(ACTION_EXPAND);
|
||||
}
|
||||
|
||||
public boolean dismiss() {
|
||||
return performAction(ACTION_DISMISS);
|
||||
}
|
||||
|
||||
public boolean show() {
|
||||
return performAction(ACTION_SHOW_ON_SCREEN.getId());
|
||||
}
|
||||
|
||||
public boolean scrollForward() {
|
||||
return performAction(ACTION_SCROLL_FORWARD);
|
||||
}
|
||||
|
||||
public boolean scrollBackward() {
|
||||
return performAction(ACTION_SCROLL_BACKWARD);
|
||||
}
|
||||
|
||||
public boolean scrollUp() {
|
||||
return performAction(ACTION_SCROLL_UP.getId());
|
||||
}
|
||||
|
||||
public boolean scrollDown() {
|
||||
return performAction(ACTION_SCROLL_DOWN.getId());
|
||||
}
|
||||
|
||||
public boolean scrollLeft() {
|
||||
return performAction(ACTION_SCROLL_LEFT.getId());
|
||||
}
|
||||
|
||||
public boolean scrollRight() {
|
||||
return performAction(ACTION_SCROLL_RIGHT.getId());
|
||||
}
|
||||
|
||||
public boolean contextClick() {
|
||||
return performAction(ACTION_CONTEXT_CLICK.getId());
|
||||
}
|
||||
|
||||
public boolean setSelection(int s, int e) {
|
||||
return performAction(ACTION_SET_SELECTION,
|
||||
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_SELECTION_START_INT, s),
|
||||
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_SELECTION_END_INT, e));
|
||||
}
|
||||
|
||||
public boolean setText(CharSequence text) {
|
||||
return performAction(ACTION_SET_TEXT,
|
||||
new ActionArgument.CharSequenceActionArgument(ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text));
|
||||
}
|
||||
|
||||
public boolean setProgress(float value) {
|
||||
return performAction(ACTION_SET_PROGRESS.getId(),
|
||||
new ActionArgument.FloatActionArgument(ACTION_ARGUMENT_PROGRESS_VALUE, value));
|
||||
|
||||
}
|
||||
|
||||
public boolean scrollTo(int row, int column) {
|
||||
return performAction(ACTION_SCROLL_TO_POSITION.getId(),
|
||||
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_ROW_INT, row),
|
||||
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_COLUMN_INT, column));
|
||||
}
|
||||
|
||||
public UiObject get(int i) {
|
||||
ensureUiObjectAt(i);
|
||||
return mUiObjects[i];
|
||||
}
|
||||
|
||||
private void ensureUiObjectAt(int i) {
|
||||
if (mUiObjects == null) {
|
||||
mUiObjects = new UiObject[mNodes.size()];
|
||||
}
|
||||
if (mUiObjects[i] == null) {
|
||||
mUiObjects[i] = new UiObject(mNodes.get(i).getInfo());
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return mNodes.size();
|
||||
}
|
||||
|
||||
public UiObjectCollection each(Consumer<UiObject> consumer) {
|
||||
for (int i = 0; i < mNodes.size(); i++) {
|
||||
ensureUiObjectAt(i);
|
||||
consumer.accept(mUiObjects[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class BooleanFilter extends DfsFilter {
|
||||
|
||||
private static Map<BooleanSupplier, BooleanFilter[]> cache = new HashMap<>();
|
||||
|
||||
|
||||
public static BooleanFilter get(BooleanSupplier supplier, boolean b) {
|
||||
BooleanFilter[] booleanFilters = cache.get(supplier);
|
||||
if (booleanFilters == null) {
|
||||
booleanFilters = new BooleanFilter[2];
|
||||
cache.put(supplier, booleanFilters);
|
||||
}
|
||||
int i = b ? 1 : 0;
|
||||
if (booleanFilters[i] == null) {
|
||||
booleanFilters[i] = new BooleanFilter(supplier, b);
|
||||
}
|
||||
return booleanFilters[i];
|
||||
}
|
||||
|
||||
public interface BooleanSupplier {
|
||||
|
||||
boolean get(AccessibilityNodeInfo node);
|
||||
|
||||
}
|
||||
|
||||
public static final BooleanSupplier CHECKABLE = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isCheckable();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier CHECKED = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isChecked();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier FOCUSABLE = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isFocusable();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier FOCUSED = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isFocused();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier VISIBLE_TO_USER = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isVisibleToUser();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier ACCESSIBILITY_FOCUSED = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isAccessibilityFocused();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier SELECTED = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isSelected();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier CLICKABLE = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isClickable();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier LONG_CLICKABLE = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isLongClickable();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier ENABLED = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isEnabled();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public static final BooleanSupplier PASSWORD = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isPassword();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier SCROLLABLE = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isScrollable();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier EDITABLE = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isEditable();
|
||||
}
|
||||
};
|
||||
public static final BooleanSupplier CONTENT_INVALID = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isContentInvalid();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier CONTEXT_CLICKABLE = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isContextClickable();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier MULTI_LINE = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isMultiLine();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier DISMISSABLE = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isDismissable();
|
||||
}
|
||||
};
|
||||
|
||||
public static final BooleanSupplier importantForAccessibility = new BooleanSupplier() {
|
||||
|
||||
@Override
|
||||
public boolean get(AccessibilityNodeInfo node) {
|
||||
return node.isImportantForAccessibility();
|
||||
}
|
||||
};
|
||||
|
||||
private BooleanSupplier mBooleanSupplier;
|
||||
private boolean mExceptedValue;
|
||||
|
||||
public BooleanFilter(BooleanSupplier booleanSupplier, boolean exceptedValue) {
|
||||
mBooleanSupplier = booleanSupplier;
|
||||
mExceptedValue = exceptedValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isIncluded(AccessibilityNodeInfo nodeInfo) {
|
||||
return nodeInfo != null && mBooleanSupplier.get(nodeInfo) == mExceptedValue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoHelper;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class BoundsFilter extends DfsFilter {
|
||||
|
||||
public static final int TYPE_EQUALS = 0;
|
||||
public static final int TYPE_INSIDE = 1;
|
||||
public static final int TYPE_PARENT = 2;
|
||||
|
||||
private Rect mBounds;
|
||||
private int mType;
|
||||
|
||||
public BoundsFilter(Rect bounds, int type) {
|
||||
mBounds = bounds;
|
||||
mType = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isIncluded(AccessibilityNodeInfo nodeInfo) {
|
||||
if (mType == TYPE_PARENT) {
|
||||
return AccessibilityNodeInfoHelper.getBoundsInParent(nodeInfo).equals(mBounds);
|
||||
}
|
||||
Rect boundsInScreen = AccessibilityNodeInfoHelper.getBoundsInScreen(nodeInfo);
|
||||
if (mType == TYPE_EQUALS)
|
||||
return boundsInScreen.equals(mBounds);
|
||||
return mBounds.contains(boundsInScreen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class ClassNameFilter {
|
||||
|
||||
private static final KeyGetter CLASS_NAME_GETTER = new KeyGetter() {
|
||||
@Override
|
||||
public String getKey(AccessibilityNodeInfo nodeInfo) {
|
||||
CharSequence charSequence = nodeInfo.getClassName();
|
||||
return charSequence == null ? null : charSequence.toString();
|
||||
}
|
||||
};
|
||||
|
||||
public static ListFilter equals(String text) {
|
||||
if (!text.contains(".")) {
|
||||
text = "android.widget." + text;
|
||||
}
|
||||
return new EqualsFilter(text, CLASS_NAME_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter contains(String str) {
|
||||
return new ContainsFilter(str, CLASS_NAME_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter startsWith(String prefix) {
|
||||
return new StartsWithFilter(prefix, CLASS_NAME_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter endsWith(String suffix) {
|
||||
return new EndsWithFilter(suffix, CLASS_NAME_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter matches(String regex) {
|
||||
return new MatchesFilter(regex, CLASS_NAME_GETTER);
|
||||
}
|
||||
|
||||
private ClassNameFilter() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class ContainsFilter extends DfsFilter {
|
||||
private final KeyGetter mKeyGetter;
|
||||
private final String mContains;
|
||||
|
||||
ContainsFilter(String contains, KeyGetter keyGetter) {
|
||||
mContains = contains;
|
||||
mKeyGetter = keyGetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isIncluded(AccessibilityNodeInfo nodeInfo) {
|
||||
String key = mKeyGetter.getKey(nodeInfo);
|
||||
return key != null && key.contains(mContains);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class DescFilter {
|
||||
|
||||
private static final KeyGetter DESC_GETTER = new KeyGetter() {
|
||||
@Override
|
||||
public String getKey(AccessibilityNodeInfo nodeInfo) {
|
||||
CharSequence charSequence = nodeInfo.getContentDescription();
|
||||
return charSequence == null ? null : charSequence.toString();
|
||||
}
|
||||
};
|
||||
|
||||
public static ListFilter equals(String text) {
|
||||
return new EqualsFilter(text, DESC_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter contains(String str) {
|
||||
return new ContainsFilter(str, DESC_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter startsWith(String prefix) {
|
||||
return new StartsWithFilter(prefix, DESC_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter endsWith(String suffix) {
|
||||
return new EndsWithFilter(suffix, DESC_GETTER);
|
||||
}
|
||||
public static ListFilter matches(String regex) {
|
||||
return new MatchesFilter(regex, DESC_GETTER);
|
||||
}
|
||||
|
||||
private DescFilter(){
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public abstract class DfsFilter implements ListFilter, Filter {
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfo> filter(List<AccessibilityNodeInfo> nodes) {
|
||||
ArrayList<AccessibilityNodeInfo> list = new ArrayList<>();
|
||||
for (AccessibilityNodeInfo node : nodes) {
|
||||
if (isIncluded(node)) {
|
||||
list.add(node);
|
||||
}
|
||||
filterChildren(node, list);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo node) {
|
||||
ArrayList<AccessibilityNodeInfo> list = new ArrayList<>();
|
||||
if (isIncluded(node)) {
|
||||
list.add(node);
|
||||
}
|
||||
filterChildren(node, list);
|
||||
return list;
|
||||
}
|
||||
|
||||
private void filterChildren(AccessibilityNodeInfo parent, List<AccessibilityNodeInfo> list) {
|
||||
for (int i = 0; i < parent.getChildCount(); i++) {
|
||||
AccessibilityNodeInfo child = parent.getChild(i);
|
||||
if (child == null)
|
||||
continue;
|
||||
boolean included = isIncluded(child);
|
||||
if (included) {
|
||||
list.add(child);
|
||||
}
|
||||
filterChildren(child, list);
|
||||
if (!included) {
|
||||
child.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean isIncluded(AccessibilityNodeInfo nodeInfo);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class EndsWithFilter extends DfsFilter {
|
||||
|
||||
private final String mSuffix;
|
||||
private final KeyGetter mKeyGetter;
|
||||
|
||||
public EndsWithFilter(String suffix, KeyGetter keyGetter) {
|
||||
mSuffix = suffix;
|
||||
mKeyGetter = keyGetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isIncluded(AccessibilityNodeInfo nodeInfo) {
|
||||
String key = mKeyGetter.getKey(nodeInfo);
|
||||
return key != null && key.endsWith(mSuffix);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class EqualsFilter extends DfsFilter {
|
||||
|
||||
private String mText;
|
||||
private KeyGetter mKeyGetter;
|
||||
|
||||
public EqualsFilter(String text, KeyGetter getter) {
|
||||
mText = text;
|
||||
mKeyGetter = getter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isIncluded(AccessibilityNodeInfo nodeInfo) {
|
||||
String key = mKeyGetter.getKey(nodeInfo);
|
||||
if(key != null){
|
||||
return key.equals(mText);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public interface Filter {
|
||||
|
||||
List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo node);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class IdFilter extends ListFilter.Default {
|
||||
|
||||
private static final KeyGetter ID_GETTER = new KeyGetter() {
|
||||
|
||||
@Override
|
||||
public String getKey(AccessibilityNodeInfo nodeInfo) {
|
||||
return nodeInfo.getViewIdResourceName();
|
||||
}
|
||||
};
|
||||
|
||||
public static IdFilter equals(String id) {
|
||||
return new IdFilter(id);
|
||||
}
|
||||
|
||||
public static StartsWithFilter startsWith(String prefix) {
|
||||
return new StartsWithFilter(prefix, ID_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter endsWith(String suffix) {
|
||||
return new EndsWithFilter(suffix, ID_GETTER);
|
||||
}
|
||||
|
||||
public static ContainsFilter contains(String contains) {
|
||||
return new ContainsFilter(contains, ID_GETTER);
|
||||
}
|
||||
|
||||
public static MatchesFilter matches(String regex) {
|
||||
return new MatchesFilter(regex, ID_GETTER);
|
||||
}
|
||||
|
||||
private String mId;
|
||||
|
||||
private IdFilter(String id) {
|
||||
mId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo node) {
|
||||
return node.findAccessibilityNodeInfosByViewId(mId);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public interface KeyGetter {
|
||||
|
||||
String getKey(AccessibilityNodeInfo nodeInfo);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public interface ListFilter {
|
||||
|
||||
List<AccessibilityNodeInfo> filter(List<AccessibilityNodeInfo> nodes);
|
||||
|
||||
abstract class Default implements Filter, ListFilter {
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfo> filter(List<AccessibilityNodeInfo> nodes) {
|
||||
List<AccessibilityNodeInfo> list = new ArrayList<>();
|
||||
for (AccessibilityNodeInfo node : nodes) {
|
||||
list.addAll(filter(node));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class MatchesFilter extends DfsFilter {
|
||||
|
||||
private final String mRegex;
|
||||
private final KeyGetter mKeyGetter;
|
||||
|
||||
MatchesFilter(String regex, KeyGetter keyGetter) {
|
||||
mRegex = regex;
|
||||
mKeyGetter = keyGetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isIncluded(AccessibilityNodeInfo nodeInfo) {
|
||||
String key = mKeyGetter.getKey(nodeInfo);
|
||||
return key != null && key.matches(mRegex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class PackageNameFilter {
|
||||
|
||||
private static final KeyGetter PACKAGE_NAME_GETTER = new KeyGetter() {
|
||||
@Override
|
||||
public String getKey(AccessibilityNodeInfo nodeInfo) {
|
||||
CharSequence charSequence = nodeInfo.getPackageName();
|
||||
return charSequence == null ? null : charSequence.toString();
|
||||
}
|
||||
};
|
||||
|
||||
public static ListFilter equals(String text) {
|
||||
return new EqualsFilter(text, PACKAGE_NAME_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter contains(String str) {
|
||||
return new ContainsFilter(str, PACKAGE_NAME_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter startsWith(String prefix) {
|
||||
return new StartsWithFilter(prefix, PACKAGE_NAME_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter endsWith(String suffix) {
|
||||
return new EndsWithFilter(suffix, PACKAGE_NAME_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter matches(String regex) {
|
||||
return new MatchesFilter(regex, PACKAGE_NAME_GETTER);
|
||||
}
|
||||
|
||||
private PackageNameFilter() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class StartsWithFilter extends DfsFilter {
|
||||
|
||||
private final String mPrefix;
|
||||
private final KeyGetter mKeyGetter;
|
||||
|
||||
public StartsWithFilter(String prefix, KeyGetter keyGetter) {
|
||||
mPrefix = prefix;
|
||||
mKeyGetter = keyGetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isIncluded(AccessibilityNodeInfo nodeInfo) {
|
||||
String key = mKeyGetter.getKey(nodeInfo);
|
||||
return key != null && key.startsWith(mPrefix);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.stardust.automator.filter;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class TextFilter extends ListFilter.Default {
|
||||
|
||||
private static final KeyGetter TEXT_GETTER = new KeyGetter() {
|
||||
@Override
|
||||
public String getKey(AccessibilityNodeInfo nodeInfo) {
|
||||
CharSequence charSequence = nodeInfo.getText();
|
||||
return charSequence == null ? null : charSequence.toString();
|
||||
}
|
||||
};
|
||||
|
||||
public static ListFilter equals(String text) {
|
||||
return new EqualsFilter(text, TEXT_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter contains(String str) {
|
||||
return new TextFilter(str);
|
||||
}
|
||||
|
||||
public static ListFilter startsWith(String prefix) {
|
||||
return new StartsWithFilter(prefix, TEXT_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter endsWith(String suffix) {
|
||||
return new EndsWithFilter(suffix, TEXT_GETTER);
|
||||
}
|
||||
|
||||
public static ListFilter matches(String regex) {
|
||||
return new MatchesFilter(regex, TEXT_GETTER);
|
||||
}
|
||||
|
||||
private String mText;
|
||||
|
||||
private TextFilter(String text) {
|
||||
mText = text;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo node) {
|
||||
return node.findAccessibilityNodeInfosByText(mText);
|
||||
}
|
||||
}
|
||||
11
automator/src/main/java/com/stardust/util/Consumer.java
Normal file
11
automator/src/main/java/com/stardust/util/Consumer.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package com.stardust.util;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/10.
|
||||
*/
|
||||
|
||||
public interface Consumer<T> {
|
||||
|
||||
void accept(T t);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.stardust.util;
|
||||
|
||||
import android.util.SparseArray;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/26.
|
||||
*/
|
||||
|
||||
public class SparseArrayEntries<E> {
|
||||
|
||||
private SparseArray<E> mSparseArray = new SparseArray<>();
|
||||
|
||||
public SparseArrayEntries<E> entry(int key, E value) {
|
||||
mSparseArray.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SparseArray<E> sparseArray() {
|
||||
return mSparseArray;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.stardust.view.accessibility;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/2/14.
|
||||
*/
|
||||
|
||||
public interface AccessibilityDelegate {
|
||||
|
||||
boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.stardust.view.accessibility;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/6.
|
||||
*/
|
||||
|
||||
public class AccessibilityNodeInfoHelper {
|
||||
|
||||
/**
|
||||
* Returns the node's bounds clipped to the size of the display
|
||||
*
|
||||
* @param node
|
||||
* @param width pixel width of the display
|
||||
* @param height pixel height of the display
|
||||
* @return null if node is null, else a Rect containing visible bounds
|
||||
*/
|
||||
public static Rect getVisibleBoundsInScreen(AccessibilityNodeInfo node, int width, int height) {
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
// targeted node's bounds
|
||||
Rect nodeRect = new Rect();
|
||||
node.getBoundsInScreen(nodeRect);
|
||||
|
||||
Rect displayRect = new Rect();
|
||||
displayRect.top = 0;
|
||||
displayRect.left = 0;
|
||||
displayRect.right = width;
|
||||
displayRect.bottom = height;
|
||||
boolean intersect = nodeRect.intersect(displayRect);
|
||||
return nodeRect;
|
||||
}
|
||||
|
||||
public static Rect getBoundsInParent(AccessibilityNodeInfo nodeInfo) {
|
||||
Rect rect = new Rect();
|
||||
nodeInfo.getBoundsInParent(rect);
|
||||
return rect;
|
||||
}
|
||||
|
||||
public static Rect getBoundsInScreen(AccessibilityNodeInfo nodeInfo) {
|
||||
Rect rect = new Rect();
|
||||
nodeInfo.getBoundsInScreen(rect);
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.stardust.view.accessibility;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/26.
|
||||
*/
|
||||
|
||||
public class AccessibilityServiceUtils {
|
||||
|
||||
public static void goToAccessibilitySetting(Context context) {
|
||||
context.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
}
|
||||
|
||||
public static boolean isAccessibilityServiceEnabled(Context context, Class<? extends AccessibilityService> accessibilityService) {
|
||||
ComponentName expectedComponentName = new ComponentName(context, accessibilityService);
|
||||
|
||||
String enabledServicesSetting = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
|
||||
if (enabledServicesSetting == null)
|
||||
return false;
|
||||
|
||||
TextUtils.SimpleStringSplitter colonSplitter = new TextUtils.SimpleStringSplitter(':');
|
||||
colonSplitter.setString(enabledServicesSetting);
|
||||
|
||||
while (colonSplitter.hasNext()) {
|
||||
String componentNameString = colonSplitter.next();
|
||||
ComponentName enabledService = ComponentName.unflattenFromString(componentNameString);
|
||||
|
||||
if (enabledService != null && enabledService.equals(expectedComponentName))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
2
automator/src/main/res/values/strings.xml
Normal file
2
automator/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<resources>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user