新增 特性continuation

This commit is contained in:
hyb1996
2018-12-09 14:19:33 +08:00
parent 941cdccf31
commit 8b907d31c8
18 changed files with 349 additions and 151 deletions

View File

@@ -2,23 +2,22 @@ var global = this;
runtime.init();
(function(){
(function () {
//重定向importClass使得其支持字符串参数
global.importClass =
(function(){
var __importClass__ = importClass;
return function(pack){
if(typeof(pack) == "string"){
__importClass__(Packages[pack]);
}else{
__importClass__(pack);
(function () {
var __importClass__ = importClass;
return function (pack) {
if (typeof (pack) == "string") {
__importClass__(Packages[pack]);
} else {
__importClass__(pack);
}
}
}
})();
})();
//初始化不依赖环境的模块
global.Promise = require('promise.js');
global.JSON = require('__json2__.js');
global.util = require('__util__.js');
global.device = runtime.device;
@@ -27,26 +26,32 @@ runtime.init();
runtime.bridges.setBridges(require('__bridges__.js'));
//一些内部函数
global.__asGlobal__ = function(obj, functions){
global.__asGlobal__ = function (obj, functions) {
var len = functions.length;
for(var i = 0; i < len; i++) {
for (var i = 0; i < len; i++) {
var funcName = functions[i];
if(obj[funcName]){
global[funcName] = obj[funcName].bind(obj);
var func = obj[funcName]
if (!func) {
continue;
}
(function (obj, funcName, func) {
global[funcName] = function () {
return func.apply(obj, arguments);
};
})(obj, funcName, func);
}
}
global.__exitIfError__ = function(action, defReturnValue){
try{
return action();
}catch(err){
if(err instanceof java.lang.Throwable){
global.__exitIfError__ = function (action, defReturnValue) {
try {
return action();
} catch (err) {
if (err instanceof java.lang.Throwable) {
exit(err);
}else if(err instanceof Error){
} else if (err instanceof Error) {
exit(new org.mozilla.javascript.EvaluatorException(err.name + ": " + err.message, err.fileName, err.lineNumber));
//new java.lang.RuntimeException(err.name + ": " + err.message + "\n" + err.stack));
}else{
} else {
exit();
}
return defReturnValue;
@@ -56,17 +61,22 @@ runtime.init();
//初始化全局函数
require("__globals__")(runtime, global);
//初始化一般模块
(function(scope){
(function (scope) {
var modules = ['app', 'automator', 'console', 'dialogs', 'io', 'selector', 'shell', 'web', 'ui',
"images", "timers", "threads", "events", "engines", "RootAutomator", "http", "storages", "floaty",
"sensors", "media", "plugins"];
"sensors", "media", "plugins", "continuation"];
var len = modules.length;
for(var i = 0; i < len; i++) {
for (var i = 0; i < len; i++) {
var m = modules[i];
scope[m] = require('__' + m + '__')(scope.runtime, scope);
}
})(global);
global.Promise = require('promise.js');
global.Promise.prototype.await = function () {
return continuation.await(this);
}
importClass(android.view.KeyEvent);
importClass(com.stardust.autojs.core.util.Shell);
importClass(android.graphics.Paint);

View File

@@ -1,100 +1,107 @@
module.exports = function(runtime, scope){
module.exports = function (runtime, scope) {
importPackage(Packages["okhttp3"]);
importClass(com.stardust.autojs.core.http.MutableOkHttp);
var http = {};
http.__okhttp__ = new MutableOkHttp();
http.get = function(url, options, callback){
http.get = function (url, options, callback) {
options = options || {};
options.method = "GET";
return http.request(url, options, callback);
}
http.client = function(){
http.client = function () {
return http.__okhttp__.client();
}
http.post = function(url, data, options, callback){
http.post = function (url, data, options, callback) {
options = options || {};
options.method = "POST";
options.contentType = options.contentType || "application/x-www-form-urlencoded";
if(data){
if (data) {
fillPostData(options, data);
}
return http.request(url, options, callback);
}
http.postJson = function(url, data, options, callback){
options = options || {};
options.contentType = "application/json";
return http.post(url, data, options, callback);
http.postJson = function (url, data, options, callback) {
options = options || {};
options.contentType = "application/json";
return http.post(url, data, options, callback);
}
http.postMultipart = function(url, files, options, callback){
options = options || {};
options.method = "POST";
options.contentType = "multipart/form-data";
options.files = files;
return http.request(url, options, callback);
http.postMultipart = function (url, files, options, callback) {
options = options || {};
options.method = "POST";
options.contentType = "multipart/form-data";
options.files = files;
return http.request(url, options, callback);
}
http.request = function(url, options, callback){
if(!callback && ui.isUiThread()){
throw new Error("不能在ui线程执行网络操作请加上回调函数的参数callback或在子线程执行");
http.request = function (url, options, callback) {
var cont = null;
if (!callback && ui.isUiThread()) {
cont = continuation.create();
}
var call = http.client().newCall(http.buildRequest(url, options));
if(!callback){
if (!callback && !cont) {
return wrapResponse(call.execute());
}
call.enqueue(new Callback({
onResponse: function(call, res){
callback(wrapResponse(res));
onResponse: function (call, res) {
res = wrapResponse(res);
cont && cont.resume(res);
callback && callback(res);
},
onFailure: function(call, ex){
callback(null, ex);
onFailure: function (call, ex) {
cont && cont.resumeError(ex);
callback && callback(null, ex);
}
}));
if(cont) {
return cont.await();
}
}
http.buildRequest = function(url, options){
http.buildRequest = function (url, options) {
var r = new Request.Builder();
if(!url.startsWith("http://") && !url.startsWith("https://")){
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "http://" + url;
}
r.url(url);
if(options.headers){
if (options.headers) {
setHeaders(r, options.headers);
}
if(options.body){
if (options.body) {
r.method(options.method, parseBody(options, options.body));
}else if(options.files){
} else if (options.files) {
r.method(options.method, parseMultipart(options.files));
}else {
} else {
r.method(options.method, null);
}
return r.build();
}
function parseMultipart(files){
function parseMultipart(files) {
var builder = new MultipartBody.Builder()
.setType(MultipartBody.FORM);
for(var key in files){
if(!files.hasOwnProperty(key)){
for (var key in files) {
if (!files.hasOwnProperty(key)) {
continue;
}
var value = files[key];
if(typeof(value) == 'string'){
if (typeof (value) == 'string') {
builder.addFormDataPart(key, value);
continue;
}
var path, mimeType, fileName;
if(typeof(value.getPath) == 'function'){
if (typeof (value.getPath) == 'function') {
path = value.getPath();
}else if(value.length == 2){
} else if (value.length == 2) {
fileName = value[0];
path = value[1];
}else if(value.length >= 3){
} else if (value.length >= 3) {
fileName = value[0];
mimeType = value[1]
path = value[2];
@@ -107,77 +114,77 @@ module.exports = function(runtime, scope){
return builder.build();
}
function parseMimeType(ext){
if(ext.length == 0){
function parseMimeType(ext) {
if (ext.length == 0) {
return "application/octet-stream";
}
return android.webkit.MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext)
|| "application/octet-stream";
}
function fillPostData(options, data){
if(options.contentType == "application/x-www-form-urlencoded"){
function fillPostData(options, data) {
if (options.contentType == "application/x-www-form-urlencoded") {
var b = new FormBody.Builder();
for(var key in data){
if(data.hasOwnProperty(key)){
for (var key in data) {
if (data.hasOwnProperty(key)) {
b.add(key, data[key]);
}
}
options.body = b.build();
}else if(options.contentType == "application/json"){
} else if (options.contentType == "application/json") {
options.body = JSON.stringify(data);
}else{
} else {
options.body = data;
}
}
function setHeaders(r, headers){
for(var key in headers){
if(headers.hasOwnProperty(key)){
let value = headers[key];
if(Array.isArray(value)){
value.forEach(v => {
r.header(key, v);
});
}else{
r.header(key, value);
function setHeaders(r, headers) {
for (var key in headers) {
if (headers.hasOwnProperty(key)) {
let value = headers[key];
if (Array.isArray(value)) {
value.forEach(v => {
r.header(key, v);
});
} else {
r.header(key, value);
}
}
}
}
}
function parseBody(options, body){
if(typeof(body) == "string"){
function parseBody(options, body) {
if (typeof (body) == "string") {
body = RequestBody.create(MediaType.parse(options.contentType), body);
}else if(body instanceof RequestBody){
} else if (body instanceof RequestBody) {
return body;
}else{
} else {
body = new RequestBody({
contentType: function(){
return MediaType.parse(options.contentType);
},
writeTo: body
});
contentType: function () {
return MediaType.parse(options.contentType);
},
writeTo: body
});
}
return body;
}
function wrapResponse(res){
function wrapResponse(res) {
var r = {};
r.statusCode = res.code();
r.statusMessage = res.message();
var headers = res.headers();
r.headers = {};
for(var i = 0; i < headers.size(); i++){
for (var i = 0; i < headers.size(); i++) {
let name = headers.name(i);
let value = headers.value(i);
if(r.headers.hasOwnProperty(name)){
if (r.headers.hasOwnProperty(name)) {
let origin = r.headers[name];
if(!Array.isArray(origin)){
if (!Array.isArray(origin)) {
r.headers[name] = [origin];
}
r.headers[name].push(value);
}else{
} else {
r.headers[name] = value;
}
}
@@ -185,7 +192,7 @@ module.exports = function(runtime, scope){
var body = res.body();
r.body.string = body.string.bind(body);
r.body.bytes = body.bytes.bind(body);
r.body.json = function(){
r.body.json = function () {
return JSON.parse(r.body.string());
}
r.body.contentType = body.contentType();

View File

@@ -1,9 +1,10 @@
module.exports = function (runtime, scope) {
const ResultAdapter = require("result_adapter");
function images(){
}
if(android.os.Build.VERSION.SDK_INT >= 21){
util.__assignFunctions__(runtime.images, images, ['requestScreenCapture', 'captureScreen', 'read', 'copy', 'load', 'clip', 'pixel'])
util.__assignFunctions__(runtime.images, images, ['captureScreen', 'read', 'copy', 'load', 'clip', 'pixel'])
}
images.opencvImporter = JavaImporter(
org.opencv.core.Point,
@@ -55,6 +56,18 @@ module.exports = function (runtime, scope) {
var colorFinder = javaImages.colorFinder;
images.requestScreenCapture = function(landscape) {
let ScreenCapturer = com.stardust.autojs.core.image.capture.ScreenCapturer;
var orientation = ScreenCapturer.ORIENTATION_AUTO;
if(landscape === true){
orientation = ScreenCapturer.ORIENTATION_LANDSCAPE;
}
if(landscape === false){
orientation = ScreenCapturer.ORIENTATION_PORTRAIT;
}
return ResultAdapter.promise(javaImages.requestScreenCapture(orientation)).await();
}
images.save = function (img, path, format, quality) {
format = format || "png";
quality = quality == undefined ? 100 : quality;

View File

@@ -32,7 +32,10 @@ import java.util.concurrent.atomic.AtomicReference;
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public class ScreenCapturer {
public static final int ORIENTATION_AUTO = -1;
public static final int ORIENTATION_AUTO = Configuration.ORIENTATION_UNDEFINED;
public static final int ORIENTATION_LANDSCAPE = Configuration.ORIENTATION_LANDSCAPE ;
public static final int ORIENTATION_PORTRAIT = Configuration.ORIENTATION_PORTRAIT ;
private static final String LOG_TAG = "ScreenCapturer";
private final MediaProjectionManager mProjectionManager;

View File

@@ -5,12 +5,15 @@ import android.os.Looper;
import android.os.MessageQueue;
import android.util.Log;
import com.stardust.autojs.rhino.AutoJsContext;
import com.stardust.autojs.runtime.ScriptRuntime;
import com.stardust.autojs.runtime.api.Threads;
import com.stardust.autojs.runtime.api.Timers;
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
import com.stardust.lang.ThreadCompat;
import org.mozilla.javascript.Context;
import java.util.HashSet;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -81,6 +84,9 @@ public class Loopers implements MessageQueue.IdleHandler {
if (waitWhenIdle.get() || !waitIds.get().isEmpty()) {
return false;
}
if (((AutoJsContext) Context.getCurrentContext()).hasPendingContinuation()) {
return false;
}
CopyOnWriteArrayList<LooperQuitHandler> handlers = looperQuitHandlers.get();
if (handlers == null) {
return true;

View File

@@ -5,6 +5,7 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.os.Build;
import android.os.Looper;
import androidx.annotation.Nullable;
import android.view.WindowManager;
@@ -12,7 +13,9 @@ import android.view.WindowManager;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.Theme;
import com.stardust.autojs.rhino.continuation.Continuation;
import com.stardust.autojs.runtime.ScriptBridges;
import com.stardust.autojs.runtime.ScriptRuntime;
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
import com.stardust.concurrent.VolatileDispose;
import com.stardust.util.ArrayUtils;
@@ -60,17 +63,22 @@ public class BlockedMaterialDialog extends MaterialDialog {
private VolatileDispose<Object> mResultBox;
private UiHandler mUiHandler;
private Object mCallback;
private Continuation mContinuation;
private ScriptBridges mScriptBridges;
private boolean mNotified = false;
public Builder(Context context, UiHandler uiHandler, ScriptBridges scriptBridges, Object callback) {
public Builder(Context context, ScriptRuntime runtime, Object callback) {
super(context);
super.theme(Theme.LIGHT);
mUiHandler = uiHandler;
mScriptBridges = scriptBridges;
mUiHandler = runtime.uiHandler;
mScriptBridges = runtime.bridges;
mCallback = callback;
if (Looper.getMainLooper() != Looper.myLooper()) {
mResultBox = new VolatileDispose<>();
} else {
if (mCallback == null) {
mContinuation = runtime.createContinuation();
}
}
}
@@ -88,6 +96,9 @@ public class BlockedMaterialDialog extends MaterialDialog {
if (mCallback != null) {
mScriptBridges.callFunction(mCallback, null, new Object[]{r});
}
if (mContinuation != null) {
mContinuation.resumeWith(Continuation.Result.Companion.success(r));
}
if (mResultBox != null) {
mResultBox.setAndNotify(r);
}
@@ -168,6 +179,9 @@ public class BlockedMaterialDialog extends MaterialDialog {
} else {
mUiHandler.post(Builder.super::show);
}
if (mContinuation != null) {
mContinuation.suspend();
}
if (mResultBox != null) {
return mResultBox.blockedGetOrThrow(ScriptInterruptedException.class);
} else {

View File

@@ -0,0 +1,49 @@
package com.stardust.autojs.core.util
import com.stardust.autojs.core.internal.Functions
class ScriptPromiseAdapter {
interface Callback {
fun call(arg: Any?)
}
private var mResolveCallback: Callback? = null
private var mRejectCallback: Callback? = null
private var mResult: Any? = UNSET
private var mError: Any? = UNSET
fun onResolve(callback: Callback): ScriptPromiseAdapter {
mResolveCallback = callback
mResult.let {
if (it !== UNSET) {
callback.call(it)
}
}
return this
}
fun onReject(callback: Callback): ScriptPromiseAdapter {
mRejectCallback = callback
mError.let {
if (it !== UNSET) {
callback.call(it)
}
}
return this
}
fun resolve(result: Any?) {
mResult = result
mResolveCallback?.call(result)
}
fun reject(error: Any?) {
mError = error
mRejectCallback?.call(error)
}
companion object {
private val UNSET = Object()
}
}

View File

@@ -12,6 +12,8 @@ import com.stardust.autojs.script.JavaScriptSource;
import com.stardust.autojs.script.ScriptSource;
import com.stardust.util.Callback;
import org.mozilla.javascript.ContinuationPending;
/**
* Created by Stardust on 2017/7/28.
*/
@@ -44,8 +46,9 @@ public class LoopBasedJavaScriptEngine extends RhinoJavaScriptEngine {
Object o = LoopBasedJavaScriptEngine.super.execute((JavaScriptSource) source);
if (callback != null)
callback.onResult(o);
} catch (ContinuationPending pending) {
pending.printStackTrace();
} catch (Exception e) {
Log.d("DDDDD", "execute: " + e.getMessage());
if (callback == null) {
throw e;
} else {

View File

@@ -16,6 +16,7 @@ import com.stardust.pio.PFiles;
import com.stardust.pio.UncheckedIOException;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.commonjs.module.RequireBuilder;
@@ -23,6 +24,7 @@ import org.mozilla.javascript.commonjs.module.provider.SoftCachingModuleScriptPr
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collections;
import java.util.Locale;
@@ -39,7 +41,7 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
private static final String LOG_TAG = "RhinoJavaScriptEngine";
private static final String MODULES_PATH = "modules";
private static StringScriptSource sInitScript;
private static Script sInitScript;
private static final ConcurrentHashMap<Context, RhinoJavaScriptEngine> sContextEngineMap = new ConcurrentHashMap<>();
private Context mContext;
@@ -69,7 +71,8 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
Reader reader = source.getNonNullScriptReader();
try {
reader = preprocess(reader);
return mContext.evaluateReader(mScriptable, reader, source.toString(), 1, null);
Script script = mContext.compileReader(reader, source.toString(), 1, null);
return mContext.executeScriptWithContinuations(script, mScriptable);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
@@ -104,21 +107,19 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
mThread = Thread.currentThread();
ScriptableObject.putProperty(mScriptable, "__engine__", this);
initRequireBuilder(mContext, mScriptable);
mContext.evaluateString(mScriptable, getInitScript().getScript(), SOURCE_NAME_INIT, 1, null);
mContext.executeScriptWithContinuations(getInitScript(), mScriptable);
}
private JavaScriptSource getInitScript() {
if (sInitScript == null || BuildConfig.DEBUG)
sInitScript = new StringScriptSource(readInitScript());
return sInitScript;
}
private String readInitScript() {
try {
return PFiles.read(mAndroidContext.getAssets().open("init.js"));
} catch (IOException e) {
throw new RuntimeException(e);
private Script getInitScript() {
if (sInitScript == null || BuildConfig.DEBUG) {
try {
Reader reader = new InputStreamReader(mAndroidContext.getAssets().open("init.js"));
sInitScript = mContext.compileReader(reader, SOURCE_NAME_INIT, 1, null);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
return sInitScript;
}
void initRequireBuilder(Context context, Scriptable scope) {

View File

@@ -4,9 +4,11 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
@@ -23,6 +25,8 @@ import com.stardust.autojs.engine.ScriptEngineManager;
import com.stardust.autojs.runtime.ScriptRuntime;
import com.stardust.autojs.script.ScriptSource;
import org.mozilla.javascript.ContinuationPending;
/**
* Created by Stardust on 2017/2/5.
*/
@@ -84,6 +88,8 @@ public class ScriptExecuteActivity extends AppCompatActivity {
try {
prepare();
doExecution();
} catch (ContinuationPending pending) {
pending.printStackTrace();
} catch (Exception e) {
onException(e);
}

View File

@@ -0,0 +1,31 @@
package com.stardust.autojs.rhino
import org.mozilla.javascript.Context
import org.mozilla.javascript.ContextFactory
import org.mozilla.javascript.ContinuationPending
import org.mozilla.javascript.Scriptable
class AutoJsContext(factory: ContextFactory?) : Context(factory) {
private val mContinuations = HashSet<Any>()
override fun captureContinuation(): ContinuationPending {
val continuationPending = super.captureContinuation()
mContinuations.add(continuationPending.continuation)
return continuationPending
}
override fun resumeContinuation(continuation: Any, scope: Scriptable?, functionResult: Any?): Any {
mContinuations.remove(continuation)
return super.resumeContinuation(continuation, scope, functionResult)
}
fun hasPendingContinuation(): Boolean {
return mContinuations.isNotEmpty()
}
companion object {
private val EMPTY_RUNNABLE = Runnable { }
}
}

View File

@@ -33,7 +33,7 @@ public class InterruptibleAndroidContextFactory extends AndroidContextFactory {
@Override
protected Context makeContext() {
Context cx = super.makeContext();
Context cx = new AutoJsContext(this);
cx.setInstructionObserverThreshold(10000);
return cx;
}

View File

@@ -0,0 +1,59 @@
package com.stardust.autojs.rhino.continuation
import com.stardust.autojs.core.looper.Timer
import com.stardust.autojs.core.looper.TimerThread
import com.stardust.autojs.rhino.AutoJsContext
import com.stardust.autojs.runtime.ScriptRuntime
import org.mozilla.javascript.Context
import org.mozilla.javascript.ContinuationPending
import org.mozilla.javascript.Scriptable
class Continuation(val context: AutoJsContext, val scope: Scriptable, private val mTimer: Timer) {
var pending: ContinuationPending? = null
private set
private val mThread: Thread = Thread.currentThread()
class Result(val result: Any?, val error: Any?) {
companion object {
fun success(result: Any?): Result {
return Result(result, null)
}
fun failure(error: Any?): Result {
return Result(null, error)
}
}
}
fun suspend() {
if (pending != null) {
throw IllegalStateException("call suspend twice!")
}
context.captureContinuation().let {
pending = it
throw it
}
}
fun resumeWith(result: Result) {
val continuation = pending?.continuation
?: throw IllegalStateException("call resume() without suspend()!")
if (mThread == Thread.currentThread()) {
context.resumeContinuation(continuation, scope, result)
} else {
mTimer.postDelayed({
context.resumeContinuation(continuation, scope, result)
}, 0)
}
}
companion object {
fun create(runtime: ScriptRuntime, scope: Scriptable): Continuation {
val context = Context.getCurrentContext() as AutoJsContext
return Continuation(context, scope, runtime.timers.timerForCurrentThread)
}
}
}

View File

@@ -7,7 +7,6 @@ package com.stardust.autojs.runtime;
public class ScriptBridges {
public interface Bridges {
Object[] NO_ARGUMENTS = new Object[0];

View File

@@ -1,7 +1,5 @@
package com.stardust.autojs.runtime;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.os.Looper;
@@ -13,10 +11,10 @@ import com.stardust.autojs.ScriptEngineService;
import com.stardust.autojs.annotation.ScriptVariable;
import com.stardust.autojs.core.accessibility.AccessibilityBridge;
import com.stardust.autojs.core.image.Colors;
import com.stardust.autojs.core.permission.PermissionRequestProxyActivity;
import com.stardust.autojs.core.permission.Permissions;
import com.stardust.autojs.rhino.AndroidClassLoader;
import com.stardust.autojs.rhino.TopLevelScope;
import com.stardust.autojs.rhino.continuation.Continuation;
import com.stardust.autojs.runtime.api.AbstractShell;
import com.stardust.autojs.runtime.api.AppUtils;
import com.stardust.autojs.runtime.api.Console;
@@ -54,6 +52,7 @@ import com.stardust.view.accessibility.AccessibilityInfoProvider;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.ScriptStackElement;
import org.mozilla.javascript.Scriptable;
import java.io.BufferedReader;
import java.io.File;
@@ -62,13 +61,9 @@ import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
/**
* Created by Stardust on 2017/1/27.
@@ -407,8 +402,8 @@ public class ScriptRuntime {
ignoresException(floaty::closeAll);
try {
events.emit("exit");
} catch (Throwable ignored) {
console.error("exception on exit: ", ignored);
} catch (Throwable e) {
console.error("exception on exit: ", e);
}
ignoresException(threads::shutDownAll);
ignoresException(events::recycle);
@@ -451,6 +446,15 @@ public class ScriptRuntime {
return mProperties.remove(key);
}
public Continuation createContinuation() {
return Continuation.Companion.create(this, mTopLevelScope);
}
public Continuation createContinuation(Scriptable scope) {
return Continuation.Companion.create(this, scope);
}
public static String getStackTrace(Throwable e, boolean printJavaStackTrace) {
String message = e.getMessage();
StringBuilder scriptTrace = new StringBuilder(message == null ? "" : message + "\n");

View File

@@ -132,7 +132,7 @@ public class Dialogs {
if (context == null || ((Activity) context).isFinishing()) {
context = getContext();
}
return (BlockedMaterialDialog.Builder) new BlockedMaterialDialog.Builder(context, mRuntime.uiHandler, mRuntime.bridges, callback)
return (BlockedMaterialDialog.Builder) new BlockedMaterialDialog.Builder(context, mRuntime, callback)
.theme(Theme.LIGHT);
}

View File

@@ -2,7 +2,6 @@ package com.stardust.autojs.runtime.api;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
@@ -12,7 +11,9 @@ import android.media.Image;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.RequiresApi;
import android.util.Base64;
import android.view.Gravity;
@@ -25,8 +26,8 @@ import com.stardust.autojs.core.image.capture.ScreenCapturer;
import com.stardust.autojs.core.opencv.Mat;
import com.stardust.autojs.core.opencv.OpenCVHelper;
import com.stardust.autojs.core.ui.inflater.util.Drawables;
import com.stardust.autojs.core.util.ScriptPromiseAdapter;
import com.stardust.autojs.runtime.ScriptRuntime;
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
import com.stardust.concurrent.VolatileDispose;
import com.stardust.pio.UncheckedIOException;
import com.stardust.util.ScreenMetrics;
@@ -70,34 +71,26 @@ public class Images {
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public boolean requestScreenCapture(int orientation) {
public ScriptPromiseAdapter requestScreenCapture(int orientation) {
ScriptRuntime.requiresApi(21);
ScriptPromiseAdapter promiseAdapter = new ScriptPromiseAdapter();
if (mScreenCapturer != null) {
mScreenCapturer.setOrientation(orientation);
return true;
promiseAdapter.resolve(true);
return promiseAdapter;
}
final VolatileDispose<Boolean> requestResult = new VolatileDispose<>();
Looper servantLooper = mScriptRuntime.loopers.getServantLooper();
mScreenCaptureRequester.setOnActivityResultCallback((result, data) -> {
if (result == Activity.RESULT_OK) {
mScreenCapturer = new ScreenCapturer(mContext, data, orientation, ScreenMetrics.getDeviceScreenDensity(),
new Handler(mScriptRuntime.loopers.getServantLooper()));
requestResult.setAndNotify(true);
new Handler(servantLooper));
promiseAdapter.resolve(true);
} else {
requestResult.setAndNotify(false);
promiseAdapter.resolve(false);
}
});
mScreenCaptureRequester.request();
return requestResult.blockedGetOrThrow(ScriptInterruptedException.class);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public boolean requestScreenCapture(boolean landscape) {
return requestScreenCapture(landscape ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public boolean requestScreenCapture() {
return requestScreenCapture(ScreenCapturer.ORIENTATION_AUTO);
return promiseAdapter;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)

View File

@@ -81,14 +81,14 @@ public class Timers {
public boolean hasPendingCallbacks() {
//如果是脚本主线程则检查所有子线程中的定时回调。mFutureCallbackUptimeMillis用来记录所有子线程中定时最久的一个。
// if (mThreads.getMainThread() == Thread.currentThread()) {
// return mMaxCallbackUptimeMillisForAllThreads.get() > SystemClock.uptimeMillis();
//}
//否则检查当前线程的定时回调
if (mThreads.getMainThread() == Thread.currentThread()) {
return mMaxCallbackUptimeMillisForAllThreads.get() > SystemClock.uptimeMillis();
}
// 否则检查当前线程的定时回调
return getTimerForCurrentThread().hasPendingCallbacks();
}
public void recycle(){
public void recycle() {
mMainTimer.removeAllCallbacks();
}