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:
@@ -8,6 +8,9 @@ if(__engine__ == "rhino"){
|
||||
__importClassOld__(pack);
|
||||
}
|
||||
}
|
||||
var loadJar = function(path){
|
||||
__runtime__.loadJar(path);
|
||||
}
|
||||
}
|
||||
|
||||
var toast = function(text){
|
||||
|
||||
@@ -30,7 +30,6 @@ public abstract class JavaScriptEngineManager {
|
||||
|
||||
private Map<String, Object> mGlobalVariableMap = new HashMap<>();
|
||||
private final Set<JavaScriptEngine> mEngines = new HashSet<>();
|
||||
private boolean mIsStopping = false;
|
||||
private EngineLifecycleCallback mEngineLifecycleCallback;
|
||||
private final ScriptSource INIT_SCRIPT;
|
||||
|
||||
@@ -82,8 +81,6 @@ public abstract class JavaScriptEngineManager {
|
||||
|
||||
void removeEngine(JavaScriptEngine engine) {
|
||||
synchronized (mEngines) {
|
||||
if (mIsStopping)
|
||||
return;
|
||||
mEngines.remove(engine);
|
||||
if(mEngineLifecycleCallback != null){
|
||||
mEngineLifecycleCallback.onEngineRemove(engine);
|
||||
@@ -111,16 +108,11 @@ public abstract class JavaScriptEngineManager {
|
||||
|
||||
public int stopAll() {
|
||||
synchronized (mEngines) {
|
||||
mIsStopping = true;
|
||||
int n = mEngines.size();
|
||||
for (JavaScriptEngine engine : mEngines) {
|
||||
engine.forceStop();
|
||||
if(mEngineLifecycleCallback != null){
|
||||
mEngineLifecycleCallback.onEngineRemove(engine);
|
||||
}
|
||||
}
|
||||
mEngines.clear();
|
||||
mIsStopping = false;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.stardust.autojs.engine;
|
||||
|
||||
import com.stardust.autojs.rhino_android.AndroidContextFactory;
|
||||
import com.stardust.autojs.rhino_android.RhinoAndroidHelper;
|
||||
import com.stardust.autojs.runtime.ScriptStopException;
|
||||
import com.stardust.autojs.script.ScriptSource;
|
||||
|
||||
@@ -9,16 +11,14 @@ import org.mozilla.javascript.ImporterTopLevel;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.mozilla.javascript.ScriptableObject;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/2.
|
||||
*/
|
||||
|
||||
public class RhinoJavaScriptEngine implements JavaScriptEngine {
|
||||
|
||||
static {
|
||||
ContextFactory.initGlobal(new InterruptibleContextFactory());
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
private Scriptable mScriptable;
|
||||
private Thread mThread;
|
||||
@@ -69,7 +69,6 @@ public class RhinoJavaScriptEngine implements JavaScriptEngine {
|
||||
mContext.evaluateString(mScriptable, mEngineManager.getInitScript().getScript(), "<init>", 1, null);
|
||||
}
|
||||
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
@@ -85,23 +84,32 @@ public class RhinoJavaScriptEngine implements JavaScriptEngine {
|
||||
}
|
||||
|
||||
protected Context createContext() {
|
||||
Context context = Context.enter();
|
||||
if (!ContextFactory.hasExplicitGlobal()) {
|
||||
ContextFactory.initGlobal(new InterruptibleAndroidContextFactory(new File(mEngineManager.getContext().getCacheDir(), "classes")));
|
||||
}
|
||||
Context context = new RhinoAndroidHelper(mEngineManager.getContext()).enterContext();
|
||||
context.setOptimizationLevel(-1);
|
||||
context.setLanguageVersion(Context.VERSION_1_7);
|
||||
return context;
|
||||
}
|
||||
|
||||
private static class InterruptibleContextFactory extends ContextFactory {
|
||||
private static class InterruptibleAndroidContextFactory extends AndroidContextFactory {
|
||||
|
||||
public InterruptibleAndroidContextFactory(File cacheDirectory) {
|
||||
super(cacheDirectory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void observeInstructionCount(Context cx, int instructionCount) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
|
||||
throw new ScriptStopException(new InterruptedException());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Context makeContext() {
|
||||
|
||||
Context cx = super.makeContext();
|
||||
cx.setInstructionObserverThreshold(10000);
|
||||
return cx;
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.stardust.autojs.rhino_android;
|
||||
|
||||
import com.android.dx.command.dexer.Main;
|
||||
|
||||
import net.lingala.zip4j.core.ZipFile;
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.model.FileHeader;
|
||||
import net.lingala.zip4j.model.ZipParameters;
|
||||
|
||||
import org.mozilla.javascript.GeneratedClassLoader;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import dalvik.system.DexFile;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/5.
|
||||
*/
|
||||
|
||||
public class AndroidClassLoader extends ClassLoader implements GeneratedClassLoader {
|
||||
|
||||
|
||||
private final ClassLoader parent;
|
||||
private List<DexFile> dx;
|
||||
private final File dexFile;
|
||||
private final File odexOatFile;
|
||||
private final File classFile;
|
||||
|
||||
/**
|
||||
* Create a new instance with the given parent classloader and cache dierctory
|
||||
*
|
||||
* @param parent the parent
|
||||
* @param dir the cache directory
|
||||
*/
|
||||
public AndroidClassLoader(ClassLoader parent, File dir) {
|
||||
this.parent = parent;
|
||||
dx = new ArrayList<>();
|
||||
dexFile = new File(dir, "dex-" + hashCode() + ".jar");
|
||||
odexOatFile = new File(dir, "odex_oat-" + hashCode() + ".tmp");
|
||||
classFile = new File(dir, "class-" + hashCode() + ".jar");
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Class<?> defineClass(String name, byte[] data) {
|
||||
try {
|
||||
final ZipFile zipFile = new ZipFile(classFile);
|
||||
final ZipParameters parameters = new ZipParameters();
|
||||
parameters.setFileNameInZip(name.replace('.', '/') + ".class");
|
||||
parameters.setSourceExternalStream(true);
|
||||
zipFile.addStream(new ByteArrayInputStream(data), parameters);
|
||||
return dexJar().loadClass(name, parent);
|
||||
} catch (IOException | ZipException e) {
|
||||
throw new FatalLoadingException(e);
|
||||
} finally {
|
||||
dexFile.delete();
|
||||
odexOatFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public void loadJar(File jar) throws IOException {
|
||||
try {
|
||||
final ZipFile zipFile = new ZipFile(classFile);
|
||||
final ZipFile jarFile = new ZipFile(jar);
|
||||
//noinspection unchecked
|
||||
for (FileHeader header : (List<FileHeader>) jarFile.getFileHeaders()) {
|
||||
if (!header.isDirectory()) {
|
||||
final ZipParameters parameters = new ZipParameters();
|
||||
parameters.setFileNameInZip(header.getFileName());
|
||||
parameters.setSourceExternalStream(true);
|
||||
zipFile.addStream(jarFile.getInputStream(header), parameters);
|
||||
}
|
||||
}
|
||||
dexJar();
|
||||
} catch (ZipException e) {
|
||||
IOException exception = new IOException();
|
||||
//noinspection UnnecessaryInitCause
|
||||
exception.initCause(e);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
private DexFile dexJar() throws IOException {
|
||||
if (!classFile.exists()) {
|
||||
classFile.createNewFile();
|
||||
}
|
||||
final Main.Arguments arguments = new Main.Arguments();
|
||||
arguments.fileNames = new String[]{classFile.getPath()};
|
||||
arguments.outName = dexFile.getPath();
|
||||
arguments.jarOutput = true;
|
||||
Main.run(arguments);
|
||||
DexFile dex = DexFile.loadDex(dexFile.getPath(), odexOatFile.getPath(), 0);
|
||||
dx.add(dex);
|
||||
return dex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing
|
||||
*
|
||||
* @param aClass ignored
|
||||
*/
|
||||
@Override
|
||||
public void linkClass(Class<?> aClass) {
|
||||
//doesn't make sense on android
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to load a class. This will search all defined classes, all loaded jars and the parent class loader.
|
||||
*
|
||||
* @param name the name of the class to load
|
||||
* @param resolve ignored
|
||||
* @return the class
|
||||
* @throws ClassNotFoundException if the class could not be found in any of the locations
|
||||
*/
|
||||
@Override
|
||||
public Class<?> loadClass(String name, boolean resolve)
|
||||
throws ClassNotFoundException {
|
||||
Class<?> loadedClass = findLoadedClass(name);
|
||||
if (loadedClass == null) {
|
||||
for (DexFile dex : dx) {
|
||||
loadedClass = dex.loadClass(name, parent);
|
||||
if (loadedClass != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (loadedClass == null) {
|
||||
loadedClass = parent.loadClass(name);
|
||||
}
|
||||
}
|
||||
return loadedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Might be thrown in any Rhino method that loads bytecode if the loading failed
|
||||
*/
|
||||
public static class FatalLoadingException extends RuntimeException {
|
||||
FatalLoadingException(Throwable t) {
|
||||
super("Failed to define class", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.stardust.autojs.rhino_android;
|
||||
|
||||
|
||||
import com.stardust.autojs.runtime.ScriptStopException;
|
||||
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.ContextFactory;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/5.
|
||||
*/
|
||||
|
||||
public class AndroidContextFactory extends ContextFactory {
|
||||
private final File cacheDirectory;
|
||||
|
||||
/**
|
||||
* Create a new factory. It will cache generated code in the given directory
|
||||
*
|
||||
* @param cacheDirectory the cache directory
|
||||
*/
|
||||
public AndroidContextFactory(File cacheDirectory) {
|
||||
this.cacheDirectory = cacheDirectory;
|
||||
initApplicationClassLoader(createClassLoader(com.faendir.rhino_android.AndroidContextFactory.class.getClassLoader()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ClassLoader which is able to deal with bytecode
|
||||
*
|
||||
* @param parent the parent of the create classloader
|
||||
* @return a new ClassLoader
|
||||
*/
|
||||
@Override
|
||||
protected AndroidClassLoader createClassLoader(ClassLoader parent) {
|
||||
return new AndroidClassLoader(parent, cacheDirectory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void observeInstructionCount(Context cx, int instructionCount) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new ScriptStopException(new InterruptedException());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Context makeContext() {
|
||||
Context cx = super.makeContext();
|
||||
cx.setInstructionObserverThreshold(10000);
|
||||
return cx;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.stardust.autojs.rhino_android;
|
||||
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.GeneratedClassLoader;
|
||||
import org.mozilla.javascript.SecurityController;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/5.
|
||||
*/
|
||||
|
||||
public class NoSecurityController extends SecurityController implements Serializable {
|
||||
@Override
|
||||
public GeneratedClassLoader createClassLoader(ClassLoader classLoader, Object o) {
|
||||
return Context.getCurrentContext().createClassLoader(classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDynamicSecurityDomain(Object o) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.stardust.autojs.rhino_android;
|
||||
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
|
||||
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.ContextFactory;
|
||||
import org.mozilla.javascript.SecurityController;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/5.
|
||||
*/
|
||||
|
||||
public class RhinoAndroidHelper {
|
||||
|
||||
private final File cacheDirectory;
|
||||
|
||||
/**
|
||||
* Constructs a new helper using the default temporary directory.
|
||||
* Note: It is recommended to use a custom directory, so no permission problems occur.
|
||||
*/
|
||||
public RhinoAndroidHelper() {
|
||||
this(new File(System.getProperty("java.io.tmpdir", "."), "classes"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new helper using a directory in the applications cache.
|
||||
*
|
||||
* @param context any context
|
||||
*/
|
||||
public RhinoAndroidHelper(android.content.Context context) {
|
||||
this(new File(context.getCacheDir(), "classes"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a helper using the specified directory as cache.
|
||||
*
|
||||
* @param cacheDirectory the cache directory to use
|
||||
*/
|
||||
public RhinoAndroidHelper(File cacheDirectory) {
|
||||
this.cacheDirectory = cacheDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* call this instead of {@link Context#enter()}
|
||||
*
|
||||
* @return a context prepared for android
|
||||
*/
|
||||
public Context enterContext() {
|
||||
if (!SecurityController.hasGlobal())
|
||||
SecurityController.initGlobal(new NoSecurityController());
|
||||
return getContextFactory().enterContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Context factory which has to be used on android.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public AndroidContextFactory getContextFactory() {
|
||||
AndroidContextFactory factory;
|
||||
if (!ContextFactory.hasExplicitGlobal()) {
|
||||
factory = new AndroidContextFactory(cacheDirectory);
|
||||
ContextFactory.getGlobalSetter().setContextFactoryGlobal(factory);
|
||||
} else if (!(ContextFactory.getGlobal() instanceof AndroidContextFactory)) {
|
||||
throw new IllegalStateException("Cannot initialize factory for Android Rhino: There is already another factory");
|
||||
} else {
|
||||
factory = (AndroidContextFactory) ContextFactory.getGlobal();
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles all classes in a jar file. They will be available in Rhino.
|
||||
*
|
||||
* @param jar the jar to load
|
||||
* @throws IOException if the jar cannot be read or is invalid or there is a problem with the cache
|
||||
*/
|
||||
public void loadClassJar(File jar) throws IOException {
|
||||
((AndroidClassLoader) getContextFactory().getApplicationClassLoader()).loadJar(jar);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a context prepared for android
|
||||
* @deprecated use {@link #enterContext()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static Context prepareContext() {
|
||||
return new RhinoAndroidHelper().enterContext();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.stardust.autojs.runtime;
|
||||
|
||||
import com.stardust.autojs.runtime.action.ActionPerformAccessibilityDelegate;
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.stardust.automator.AccessibilityEventCommandHost;
|
||||
import com.stardust.automator.simple_action.SimpleActionPerformHost;
|
||||
import com.stardust.view.accessibility.AccessibilityInfoProvider;
|
||||
|
||||
/**
|
||||
@@ -16,7 +19,10 @@ public interface AccessibilityBridge {
|
||||
|
||||
AccessibilityEventCommandHost getCommandHost();
|
||||
|
||||
ActionPerformAccessibilityDelegate getActionPerformHost();
|
||||
SimpleActionPerformHost getActionPerformHost();
|
||||
|
||||
@Nullable
|
||||
AccessibilityService getService();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,15 +6,22 @@ import android.os.Handler;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.stardust.autojs.R;
|
||||
import com.stardust.autojs.runtime.action.ActionAutomator;
|
||||
import com.stardust.autojs.rhino_android.AndroidClassLoader;
|
||||
import com.stardust.autojs.runtime.simple_action.SimpleActionAutomator;
|
||||
import com.stardust.autojs.runtime.api.AppUtils;
|
||||
import com.stardust.autojs.runtime.api.Console;
|
||||
import com.stardust.autojs.runtime.api.UiSelector;
|
||||
import com.stardust.pio.UncheckedIOException;
|
||||
import com.stardust.util.ClipboardUtil;
|
||||
import com.stardust.util.SdkVersionUtil;
|
||||
import com.stardust.util.Shell;
|
||||
import com.stardust.view.accessibility.AccessibilityInfoProvider;
|
||||
|
||||
import org.mozilla.javascript.ContextFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
@@ -35,7 +42,7 @@ public class ScriptRuntime {
|
||||
public Console console;
|
||||
|
||||
@JavascriptField
|
||||
public ActionAutomator automator;
|
||||
public SimpleActionAutomator automator;
|
||||
|
||||
@JavascriptField
|
||||
public AccessibilityInfoProvider info;
|
||||
@@ -47,7 +54,7 @@ public class ScriptRuntime {
|
||||
app = new AppUtils(context);
|
||||
info = accessibilityBridge.getInfoProvider();
|
||||
this.console = console;
|
||||
automator = new ActionAutomator(accessibilityBridge, this);
|
||||
automator = new SimpleActionAutomator(accessibilityBridge, this);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@@ -101,6 +108,15 @@ public class ScriptRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void loadJar(String path) {
|
||||
try {
|
||||
((AndroidClassLoader) ContextFactory.getGlobal().getApplicationClassLoader()).loadJar(new File(path));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void stop() {
|
||||
Thread.interrupted();
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.stardust.autojs.runtime.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);
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.stardust.autojs.runtime.action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public abstract class Action {
|
||||
|
||||
private boolean mValid = true;
|
||||
private Object mResult = false;
|
||||
|
||||
public abstract boolean perform(AccessibilityNodeInfo root);
|
||||
|
||||
public Object getResult() {
|
||||
return mResult;
|
||||
}
|
||||
|
||||
public void setResult(Object result) {
|
||||
mResult = result;
|
||||
}
|
||||
|
||||
public void setValid(boolean valid) {
|
||||
mValid = valid;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return mValid;
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package com.stardust.autojs.runtime.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 Action 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 Action 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 Action 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 Action createScrollMaxAction(int action) {
|
||||
return new ScrollMaxAction(action);
|
||||
}
|
||||
|
||||
public static Action createScrollAction(int action, int i) {
|
||||
return new ScrollAction(action, i);
|
||||
}
|
||||
|
||||
public static Action createActionWithIdFilter(int action, String id) {
|
||||
return new FilterAction.SimpleFilterAction(action, new FilterAction.IdFilter(id));
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package com.stardust.autojs.runtime.action;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.view.accessibility.AccessibilityDelegate;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/21.
|
||||
*/
|
||||
|
||||
public class ActionPerformAccessibilityDelegate implements AccessibilityDelegate {
|
||||
|
||||
private static final String TAG = "ActionPerformDelegate";
|
||||
|
||||
private final Queue<Action> mActions = new LinkedList<>();
|
||||
private Action mCurrentAction;
|
||||
|
||||
public void addAction(Action action) {
|
||||
synchronized (mActions) {
|
||||
mActions.offer(action);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
|
||||
synchronized (mActions) {
|
||||
mCurrentAction = getCurrentAction();
|
||||
if (mCurrentAction == null)
|
||||
return false;
|
||||
AccessibilityNodeInfo root = service.getRootInActiveWindow();
|
||||
if (root == null) {
|
||||
return false;
|
||||
}
|
||||
performAction(root, mCurrentAction);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Action getCurrentAction() {
|
||||
if (isCurrentActionValid()) {
|
||||
return mCurrentAction;
|
||||
}
|
||||
if (mActions.isEmpty())
|
||||
return null;
|
||||
return mActions.poll();
|
||||
}
|
||||
|
||||
private boolean isCurrentActionValid() {
|
||||
return mCurrentAction != null && mCurrentAction.isValid();
|
||||
}
|
||||
|
||||
private void performAction(final AccessibilityNodeInfo root, final Action action) {
|
||||
Log.i(TAG, "perform action:" + action);
|
||||
if (action.perform(root)) {
|
||||
action.setResult(true);
|
||||
onActionPerformed(action);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void onActionPerformed(Action action) {
|
||||
mCurrentAction = null;
|
||||
synchronized (action) {
|
||||
action.notify();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
package com.stardust.autojs.runtime.action;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.RequiresApi;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public interface ActionTarget {
|
||||
|
||||
Action 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 Action 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 Action 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 Action 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 Action createAction(int action, Object... params) {
|
||||
return ActionFactory.createActionWithIdFilter(action, mId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.stardust.autojs.runtime.action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class DepthFirstSearchTargetAction extends SearchTargetAction {
|
||||
|
||||
|
||||
private Able mAble;
|
||||
|
||||
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 = n.getChild(i);
|
||||
if (child == null)
|
||||
continue;
|
||||
AccessibilityNodeInfo node = searchTarget(child);
|
||||
if (node != null)
|
||||
return node;
|
||||
else
|
||||
child.recycle();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
package com.stardust.autojs.runtime.action;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public abstract class FilterAction extends Action {
|
||||
|
||||
|
||||
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 = root.findAccessibilityNodeInfosByText(mText);
|
||||
if (mIndex == -1)
|
||||
return list;
|
||||
if (mIndex >= list.size())
|
||||
return Collections.EMPTY_LIST;
|
||||
return Collections.singletonList(list.get(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 = root.getChild(i);
|
||||
if (child == null)
|
||||
continue;
|
||||
AccessibilityNodeInfo nodeInfo = findAccessibilityNodeInfosByBounds(child);
|
||||
if (nodeInfo != null)
|
||||
return nodeInfo;
|
||||
else
|
||||
child.recycle();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<AccessibilityNodeInfo> findEditable(AccessibilityNodeInfo root) {
|
||||
if (root == null) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
if (root.isEditable()) {
|
||||
return Collections.singletonList(root);
|
||||
}
|
||||
List<AccessibilityNodeInfo> list = new LinkedList<>();
|
||||
for (int i = 0; i < root.getChildCount(); i++) {
|
||||
list.addAll(findEditable(root.getChild(i)));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
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 root.findAccessibilityNodeInfosByViewId(mId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Filter mFilter;
|
||||
|
||||
public FilterAction(Filter filter) {
|
||||
mFilter = filter;
|
||||
}
|
||||
|
||||
public boolean perform(AccessibilityNodeInfo root) {
|
||||
if (root == null)
|
||||
return false;
|
||||
return perform(mFilter.filter(root));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.stardust.autojs.runtime.action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/2/14.
|
||||
*/
|
||||
|
||||
public class GetTextAction extends Action {
|
||||
|
||||
|
||||
@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 = nodeInfo.getChild(i);
|
||||
if (child != null) {
|
||||
getText(child, texts);
|
||||
child.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResult(Object result) {
|
||||
if (result instanceof List)
|
||||
super.setResult(result);
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package com.stardust.autojs.runtime.action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/2/12.
|
||||
*/
|
||||
public class ScrollAction extends Action {
|
||||
|
||||
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 = node.getChild(i);
|
||||
if (child == null)
|
||||
continue;
|
||||
if (!findScrollableNodes(child, list))
|
||||
child.recycle();
|
||||
}
|
||||
return node.isScrollable();
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package com.stardust.autojs.runtime.action;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class ScrollMaxAction extends Action {
|
||||
|
||||
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 = nodeInfo.getChild(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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package com.stardust.autojs.runtime.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;
|
||||
}
|
||||
}
|
||||
return performed;
|
||||
}
|
||||
|
||||
protected void performAction(AccessibilityNodeInfo node) {
|
||||
node.performAction(mAction);
|
||||
}
|
||||
|
||||
public int getAction(){
|
||||
return mAction;
|
||||
}
|
||||
|
||||
public AccessibilityNodeInfo searchTarget(AccessibilityNodeInfo node) {
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.stardust.autojs.runtime.action;
|
||||
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/1/27.
|
||||
*/
|
||||
|
||||
public class SearchUpTargetAction extends SearchTargetAction {
|
||||
|
||||
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;
|
||||
while (node != null && !mAble.isAble(node)) {
|
||||
AccessibilityNodeInfo parent = node.getParent();
|
||||
if (node != n) {
|
||||
node.recycle();
|
||||
}
|
||||
node = parent;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import com.stardust.autojs.runtime.JavascriptInterface;
|
||||
import com.stardust.util.IntentUtil;
|
||||
|
||||
import java.util.List;
|
||||
@@ -21,6 +22,7 @@ public class AppUtils {
|
||||
}
|
||||
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean launchPackage(String packageName) {
|
||||
try {
|
||||
PackageManager packageManager = mContext.getPackageManager();
|
||||
@@ -32,10 +34,12 @@ public class AppUtils {
|
||||
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean launchApp(String appName) {
|
||||
return launchPackage(getPackageName(appName));
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String getPackageName(String appName) {
|
||||
PackageManager packageManager = mContext.getPackageManager();
|
||||
List<ApplicationInfo> installedApplications = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
|
||||
@@ -47,6 +51,7 @@ public class AppUtils {
|
||||
return "";
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean openAppSetting(String packageName) {
|
||||
return IntentUtil.goToAppDetailSettings(mContext, packageName);
|
||||
}
|
||||
|
||||
@@ -2,30 +2,42 @@ package com.stardust.autojs.runtime.api;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.stardust.autojs.runtime.JavascriptInterface;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/2.
|
||||
*/
|
||||
|
||||
public interface Console {
|
||||
|
||||
@JavascriptInterface
|
||||
void i(@Nullable Object o);
|
||||
|
||||
@JavascriptInterface
|
||||
void i(String str);
|
||||
|
||||
@JavascriptInterface
|
||||
void e(String message);
|
||||
|
||||
@JavascriptInterface
|
||||
void e(@Nullable Object o);
|
||||
|
||||
@JavascriptInterface
|
||||
void v(String v);
|
||||
|
||||
@JavascriptInterface
|
||||
void v(@Nullable Object o);
|
||||
|
||||
@JavascriptInterface
|
||||
void log(@Nullable Object o);
|
||||
|
||||
@JavascriptInterface
|
||||
void log(String string);
|
||||
|
||||
@JavascriptInterface
|
||||
void show();
|
||||
|
||||
@JavascriptInterface
|
||||
void clear();
|
||||
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.stardust.autojs.runtime.AccessibilityBridge;
|
||||
import com.stardust.autojs.runtime.JavascriptInterface;
|
||||
import com.stardust.automator.AccessibilityEventCommandHost;
|
||||
import com.stardust.automator.ActionArgument;
|
||||
import com.stardust.automator.UiGlobalSelector;
|
||||
import com.stardust.automator.UiObject;
|
||||
import com.stardust.automator.UiObjectCollection;
|
||||
import com.stardust.automator.filter.DfsFilter;
|
||||
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
|
||||
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
|
||||
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ARGUMENT_COLUMN_INT;
|
||||
@@ -51,7 +53,7 @@ import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.
|
||||
|
||||
public class UiSelector extends UiGlobalSelector {
|
||||
|
||||
private class FindCommand implements AccessibilityEventCommandHost.Command {
|
||||
private class FindCommand extends AccessibilityEventCommandHost.AbstractCommand {
|
||||
|
||||
UiObjectCollection result;
|
||||
|
||||
@@ -62,6 +64,7 @@ public class UiSelector extends UiGlobalSelector {
|
||||
result = findOf(root);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final String TAG = "UiSelector";
|
||||
@@ -73,11 +76,17 @@ public class UiSelector extends UiGlobalSelector {
|
||||
}
|
||||
|
||||
|
||||
@JavascriptInterface
|
||||
public UiObjectCollection find() {
|
||||
ensureAccessibilityServiceEnabled();
|
||||
FindCommand command = new FindCommand();
|
||||
mAccessibilityBridge.getCommandHost().executeAndWaitForEvent(command);
|
||||
return command.result;
|
||||
AccessibilityService service = mAccessibilityBridge.getService();
|
||||
if (service != null) {
|
||||
AccessibilityNodeInfo root = service.getRootInActiveWindow();
|
||||
if (root != null) {
|
||||
return findOf(root);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ensureAccessibilityServiceEnabled() {
|
||||
@@ -85,6 +94,7 @@ public class UiSelector extends UiGlobalSelector {
|
||||
}
|
||||
|
||||
|
||||
@JavascriptInterface
|
||||
@NonNull
|
||||
public UiObjectCollection untilFind() {
|
||||
UiObjectCollection uiObjectCollection;
|
||||
@@ -94,6 +104,7 @@ public class UiSelector extends UiGlobalSelector {
|
||||
return uiObjectCollection;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public UiObject findOne() {
|
||||
return untilFindOne();
|
||||
}
|
||||
@@ -104,6 +115,7 @@ public class UiSelector extends UiGlobalSelector {
|
||||
return new UiObject(collection.get(0).getInfo());
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public UiSelector id(final String id) {
|
||||
if (!id.contains(":")) {
|
||||
addFilter(new DfsFilter() {
|
||||
@@ -120,110 +132,135 @@ public class UiSelector extends UiGlobalSelector {
|
||||
}
|
||||
|
||||
|
||||
public boolean performAction(int action, ActionArgument... arguments) {
|
||||
private boolean performAction(int action, ActionArgument... arguments) {
|
||||
return untilFind().performAction(action, arguments);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean click() {
|
||||
return performAction(ACTION_CLICK);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean longClick() {
|
||||
return performAction(ACTION_LONG_CLICK);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean accessibilityFocus() {
|
||||
return performAction(ACTION_ACCESSIBILITY_FOCUS);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean clearAccessibilityFocus() {
|
||||
return performAction(ACTION_CLEAR_ACCESSIBILITY_FOCUS);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean focus() {
|
||||
return performAction(ACTION_FOCUS);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean clearFocus() {
|
||||
return performAction(ACTION_CLEAR_FOCUS);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean copy() {
|
||||
return performAction(ACTION_COPY);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean paste() {
|
||||
return performAction(ACTION_PASTE);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean select() {
|
||||
return performAction(ACTION_SELECT);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean cut() {
|
||||
return performAction(ACTION_CUT);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean collapse() {
|
||||
return performAction(ACTION_COLLAPSE);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean expand() {
|
||||
return performAction(ACTION_EXPAND);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean dismiss() {
|
||||
return performAction(ACTION_DISMISS);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean show() {
|
||||
return performAction(ACTION_SHOW_ON_SCREEN.getId());
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean scrollForward() {
|
||||
return performAction(ACTION_SCROLL_FORWARD);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean scrollBackward() {
|
||||
return performAction(ACTION_SCROLL_BACKWARD);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean scrollUp() {
|
||||
return performAction(ACTION_SCROLL_UP.getId());
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean scrollDown() {
|
||||
return performAction(ACTION_SCROLL_DOWN.getId());
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean scrollLeft() {
|
||||
return performAction(ACTION_SCROLL_LEFT.getId());
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean scrollRight() {
|
||||
return performAction(ACTION_SCROLL_RIGHT.getId());
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean contextClick() {
|
||||
return performAction(ACTION_CONTEXT_CLICK.getId());
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
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));
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean setText(String text) {
|
||||
return performAction(ACTION_SET_TEXT,
|
||||
new ActionArgument.CharSequenceActionArgument(ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text));
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean setProgress(float value) {
|
||||
return performAction(ACTION_SET_PROGRESS.getId(),
|
||||
new ActionArgument.FloatActionArgument(ACTION_ARGUMENT_PROGRESS_VALUE, value));
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean scrollTo(int row, int column) {
|
||||
return performAction(ACTION_SCROLL_TO_POSITION.getId(),
|
||||
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_ROW_INT, row),
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.stardust.autojs.runtime.action;
|
||||
package com.stardust.autojs.runtime.simple_action;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
@@ -11,14 +12,17 @@ import com.stardust.autojs.runtime.AccessibilityBridge;
|
||||
import com.stardust.autojs.runtime.JavascriptInterface;
|
||||
import com.stardust.autojs.runtime.ScriptRuntime;
|
||||
import com.stardust.automator.AccessibilityEventCommandHost;
|
||||
import com.stardust.automator.simple_action.SimpleAction;
|
||||
import com.stardust.automator.simple_action.ActionFactory;
|
||||
import com.stardust.automator.simple_action.ActionTarget;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/2.
|
||||
*/
|
||||
|
||||
public class ActionAutomator {
|
||||
public class SimpleActionAutomator {
|
||||
|
||||
private static class PerformGlobalActionCommand implements AccessibilityEventCommandHost.Command {
|
||||
private static class PerformGlobalActionCommand extends AccessibilityEventCommandHost.AbstractCommand {
|
||||
|
||||
boolean result;
|
||||
private int mGlobalAction;
|
||||
@@ -31,12 +35,13 @@ public class ActionAutomator {
|
||||
public void execute(AccessibilityService service, AccessibilityEvent event) {
|
||||
result = service.performGlobalAction(mGlobalAction);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private AccessibilityBridge mAccessibilityBridge;
|
||||
private ScriptRuntime mScriptRuntime;
|
||||
|
||||
public ActionAutomator(AccessibilityBridge accessibilityBridge, ScriptRuntime scriptRuntime) {
|
||||
public SimpleActionAutomator(AccessibilityBridge accessibilityBridge, ScriptRuntime scriptRuntime) {
|
||||
mAccessibilityBridge = accessibilityBridge;
|
||||
mScriptRuntime = scriptRuntime;
|
||||
}
|
||||
@@ -159,9 +164,10 @@ public class ActionAutomator {
|
||||
|
||||
private boolean performGlobalAction(final int action) {
|
||||
ensureAccessibilityServiceEnabled();
|
||||
PerformGlobalActionCommand command = new PerformGlobalActionCommand(action);
|
||||
mAccessibilityBridge.getCommandHost().executeAndWaitForEvent(command);
|
||||
return command.result;
|
||||
AccessibilityService service = mAccessibilityBridge.getService();
|
||||
if (service == null)
|
||||
return false;
|
||||
return service.performGlobalAction(action);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@@ -174,17 +180,16 @@ public class ActionAutomator {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T performAction(Action action) {
|
||||
private <T> T performAction(SimpleAction simpleAction) {
|
||||
ensureAccessibilityServiceEnabled();
|
||||
mAccessibilityBridge.getActionPerformHost().addAction(action);
|
||||
synchronized (action) {
|
||||
try {
|
||||
action.wait();
|
||||
} catch (InterruptedException e) {
|
||||
action.setValid(false);
|
||||
mScriptRuntime.stoppedByInterrupted(e);
|
||||
AccessibilityService service = mAccessibilityBridge.getService();
|
||||
if (service != null) {
|
||||
AccessibilityNodeInfo root = service.getRootInActiveWindow();
|
||||
Log.i("Automator", "performAction: " + simpleAction + " root = " + root);
|
||||
if (root != null) {
|
||||
simpleAction.setResult(simpleAction.perform(root));
|
||||
}
|
||||
}
|
||||
return (T) action.getResult();
|
||||
return (T) simpleAction.getResult();
|
||||
}
|
||||
}
|
||||
@@ -66,4 +66,8 @@ public abstract class ScriptSource implements Serializable {
|
||||
return mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mName + ".js";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,4 +23,5 @@ public class StringScriptSource extends ScriptSource {
|
||||
public String getScript() {
|
||||
return mScript;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user