add: promise support; fix: dialogs and setClip ANR on ui thread;

This commit is contained in:
hyb1996
2017-12-01 20:52:51 +08:00
parent 45bbc33ab7
commit 1413bf21a5
12 changed files with 607 additions and 184 deletions

View File

@@ -8,8 +8,8 @@ android {
applicationId "com.stardust.scriptdroid" applicationId "com.stardust.scriptdroid"
minSdkVersion 17 minSdkVersion 17
targetSdkVersion 23 targetSdkVersion 23
versionCode 226 versionCode 227
versionName "3.0.0 Alpha26" versionName "3.0.0 Alpha27"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
ndk { ndk {
@@ -115,8 +115,6 @@ dependencies {
compile 'com.squareup.retrofit2:converter-gson:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0' compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
//JDeferred
compile 'org.jdeferred:jdeferred-android-aar:1.2.6'
//Glide //Glide
compile('com.github.bumptech.glide:glide:4.2.0', { compile('com.github.bumptech.glide:glide:4.2.0', {
exclude group: 'com.android.support' exclude group: 'com.android.support'

View File

@@ -0,0 +1,70 @@
"ui";
ui.layout(
<vertical>
<button id="callback" align="center">回调形式</button>
<button id="promise" align="center">Promise形式</button>
<button id="calc" align="center">简单计算器</button>
</vertical>
);
ui.callback.click(()=>{
dialogs.confirm("要弹出输入框吗?", "", function(b){
if(b){
dialogs.rawInput("输入", "", function(str){
alert("您输入的是:" + str);
});
}else{
ui.finish();
}
});
});
ui.promise.click(()=>{
dialogs.confirm("要弹出输入框吗")
.then(function(b){
if(b){
return dialogs.rawInput("输入");
}else{
ui.finish();
}
}).then(function(str){
alert("您输入的是:" + str);
});
});
ui.calc.click(()=>{
let num1, num2, op;
dialogs.input("请输入第一个数字")
.then(n => {
num1 = n;
return dialogs.singleChoice("请选择运算", ["加", "减", "乘", "除", "幂"]);
})
.then(o => {
op = o;
return dialogs.input("请输入第二个数字");
})
.then(n => {
num2 = n;
var result;
switch(op){
case 0:
result = num1 + num2;
break;
case 1:
result = num1 - num2;
break;
case 2:
result = num1 * num2;
break;
case 3:
result = num1 / num2;
break;
case 4:
result = Math.pow(num1, num2);
break;
}
alert("运算结果", result);
});
});

View File

@@ -51,6 +51,9 @@ dependencies {
compile 'com.github.hyb1996:EnhancedFloaty:0.17' compile 'com.github.hyb1996:EnhancedFloaty:0.17'
compile 'com.github.hyb1996:OpenCvLib:2.4.13.4-imgproc' compile 'com.github.hyb1996:OpenCvLib:2.4.13.4-imgproc'
compile 'com.makeramen:roundedimageview:2.3.0' compile 'com.makeramen:roundedimageview:2.3.0'
//JDeferred
compile 'org.jdeferred:jdeferred-android-aar:1.2.6'
// Gson // Gson
compile 'com.google.code.gson:gson:2.8.0' compile 'com.google.code.gson:gson:2.8.0'
// Terminal emulator // Terminal emulator

View File

@@ -46,8 +46,9 @@ __runtime__.bridges.setBridges({
}); });
var __that__ = this; var __that__ = this;
JSON = require('__json2__.js'); var Promise = require('promise.js');
util = require('__util__.js'); var JSON = require('__json2__.js');
var util = require('__util__.js');
var __asGlobal__ = function(obj, functions){ var __asGlobal__ = function(obj, functions){
var len = functions.length; var len = functions.length;

View File

@@ -2,43 +2,107 @@
module.exports = function(__runtime__, scope){ module.exports = function(__runtime__, scope){
var dialogs = {}; var dialogs = {};
dialogs.rawInput = function(title, prefill){ dialogs.rawInput = function(title, prefill, callback){
prefill = prefill || ""; prefill = prefill || "";
var s = __runtime__.dialogs.rawInput(title, prefill); if(isUiThread() && !callback){
return s ? String(s) : null; return new Promise(function(resolve, reject){
rtDialogs().rawInput(title, prefill, function(){
resolve.apply(null, Array.prototype.slice.call(arguments));
});
});
}
return rtDialogs().rawInput(title, prefill, callback ? callback : null);
}; };
dialogs.input = function(title, prefill){ dialogs.input = function(title, prefill, callback){
return eval(dialogs.rawInput(title, prefill)); prefill = prefill || "";
if(isUiThread() && !callback){
return new Promise(function(resolve, reject){
rtDialogs().rawInput(title, prefill, function(str){
resolve(eval(str));
});
});
}
if(callback){
dialogs.rawInput(title, prefill, function(str){
callback(eval(str));
});
return;
}
return eval(dialogs.rawInput(title, prefill), callback ? callback : null);
} }
dialogs.prompt = dialogs.rawInput; dialogs.prompt = dialogs.rawInput;
dialogs.alert = function(title, prefill){ dialogs.alert = function(title, prefill, callback){
prefill = prefill || ""; prefill = prefill || "";
return __runtime__.dialogs.alert(title, prefill); if(isUiThread() && !callback){
} return new Promise(function(resolve, reject){
rtDialogs().alert(title, prefill, function(){
dialogs.confirm = function(title, prefill){ resolve.apply(null, Array.prototype.slice.call(arguments));
prefill = prefill || ""; });
return __runtime__.dialogs.confirm(title, prefill); });
}
dialogs.select = function(title, items){
if(items instanceof Array){
return __runtime__.dialogs.select(title, items);
} }
return __runtime__.dialogs.select(title, [].slice.call(arguments, 1)); return rtDialogs().alert(title, prefill, callback ? callback : null);
} }
dialogs.singleChoice = function(title, items, index){ dialogs.confirm = function(title, prefill, callback){
prefill = prefill || "";
if(isUiThread() && !callback){
return new Promise(function(resolve, reject){
rtDialogs().confirm(title, prefill, function(){
resolve.apply(null, Array.prototype.slice.call(arguments));
});
});
}
return rtDialogs().confirm(title, prefill, callback ? callback : null);
}
dialogs.select = function(title, items, callback){
if(items instanceof Array){
if(isUiThread() && !callback){
return new Promise(function(resolve, reject){
rtDialogs().select(title, items, function(){
resolve.apply(null, Array.prototype.slice.call(arguments));
});
});
}
return rtDialogs().select(title, items, callback ? callback : null);
}
return rtDialogs().select(title, [].slice.call(arguments, 1));
}
dialogs.singleChoice = function(title, items, index, callback){
index = index || 0; index = index || 0;
return __runtime__.dialogs.singleChoice(title, index, items); if(isUiThread() && !callback){
return new Promise(function(resolve, reject){
rtDialogs().singleChoice(title, index, items, function(){
resolve.apply(null, Array.prototype.slice.call(arguments));
});
});
}
return rtDialogs().singleChoice(title, index, items, callback ? callback : null);
} }
dialogs.multiChoice = function(title, items, index){ dialogs.multiChoice = function(title, items, index, callback){
index = index || []; index = index || [];
var javaArray = __runtime__.dialogs.multiChoice(title, index, items); if(isUiThread() && !callback){
return new Promise(function(resolve, reject){
rtDialogs().singleChoice(title, index, items, function(r){
resolve.apply(null, toJsArray(r));
});
});
}
if(callback){
return rtDialogs().multiChoice(title, index, items, function(r){
callback(toJsArray(r));
});
}
return toJsArray(rtDialogs().multiChoice(title, index, items, null));
}
function toJsArray(javaArray){
var jsArray = []; var jsArray = [];
var len = javaArray.length; var len = javaArray.length;
for (var i = 0;i < len;i++){ for (var i = 0;i < len;i++){
@@ -47,21 +111,26 @@ module.exports = function(__runtime__, scope){
return jsArray; return jsArray;
} }
scope.rawInput = function(title, prefill){ function rtDialogs(){
return dialogs.rawInput(title, prefill); var d = __runtime__.dialogs;
if(!isUiThread()){
return d.nonUiDialogs;
}else{
return d;
}
} }
scope.alert = function(title, prefill){ function isUiThread(){
dialogs.alert(title, prefill); return android.os.Looper.myLooper() == android.os.Looper.getMainLooper();
} }
scope.confirm = function(title, prefill){ scope.rawInput = dialogs.rawInput.bind(dialogs);
return dialogs.confirm(title, prefill);
}
scope.prompt = function(title, prefill){ scope.alert = dialogs.alert.bind(dialogs);
return dialogs.prompt(title, prefill);
} scope.confirm = dialogs.confirm.bind(dialogs);
scope.prompt = dialogs.prompt.bind(dialogs);
return dialogs; return dialogs;
} }

View File

@@ -0,0 +1,215 @@
'use strict';
function asap(action){
action();
}
function noop() {}
// States:
//
// 0 - pending
// 1 - fulfilled with _value
// 2 - rejected with _value
// 3 - adopted the state of another promise, _value
//
// once the state is no longer pending (0) it is immutable
// All `_` prefixed properties will be reduced to `_{random number}`
// at build time to obfuscate them and discourage their use.
// We don't use symbols or Object.defineProperty to fully hide them
// because the performance isn't good enough.
// to avoid using try/catch inside critical functions, we
// extract them to here.
var LAST_ERROR = null;
var IS_ERROR = {};
function getThen(obj) {
try {
return obj.then;
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
function tryCallOne(fn, a) {
try {
return fn(a);
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
function tryCallTwo(fn, a, b) {
try {
fn(a, b);
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
module.exports = Promise;
function Promise(fn) {
if (typeof this !== 'object') {
throw new TypeError('Promises must be constructed via new');
}
if (typeof fn !== 'function') {
throw new TypeError('Promise constructor\'s argument is not a function');
}
this._deferredState = 0;
this._state = 0;
this._value = null;
this._deferreds = null;
if (fn === noop) return;
doResolve(fn, this);
}
Promise._onHandle = null;
Promise._onReject = null;
Promise._noop = noop;
Promise.prototype.then = function(onFulfilled, onRejected) {
if (this.constructor !== Promise) {
return safeThen(this, onFulfilled, onRejected);
}
var res = new Promise(noop);
handle(this, new Handler(onFulfilled, onRejected, res));
return res;
};
function safeThen(self, onFulfilled, onRejected) {
return new self.constructor(function (resolve, reject) {
var res = new Promise(noop);
res.then(resolve, reject);
handle(self, new Handler(onFulfilled, onRejected, res));
});
}
function handle(self, deferred) {
while (self._state === 3) {
self = self._value;
}
if (Promise._onHandle) {
Promise._onHandle(self);
}
if (self._state === 0) {
if (self._deferredState === 0) {
self._deferredState = 1;
self._deferreds = deferred;
return;
}
if (self._deferredState === 1) {
self._deferredState = 2;
self._deferreds = [self._deferreds, deferred];
return;
}
self._deferreds.push(deferred);
return;
}
handleResolved(self, deferred);
}
function handleResolved(self, deferred) {
asap(function() {
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
if (self._state === 1) {
resolve(deferred.promise, self._value);
} else {
reject(deferred.promise, self._value);
}
return;
}
var ret = tryCallOne(cb, self._value);
if (ret === IS_ERROR) {
reject(deferred.promise, LAST_ERROR);
} else {
resolve(deferred.promise, ret);
}
});
}
function resolve(self, newValue) {
// Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
if (newValue === self) {
return reject(
self,
new TypeError('A promise cannot be resolved with itself.')
);
}
if (
newValue &&
(typeof newValue === 'object' || typeof newValue === 'function')
) {
var then = getThen(newValue);
if (then === IS_ERROR) {
return reject(self, LAST_ERROR);
}
if (
then === self.then &&
newValue instanceof Promise
) {
self._state = 3;
self._value = newValue;
finale(self);
return;
} else if (typeof then === 'function') {
doResolve(then.bind(newValue), self);
return;
}
}
self._state = 1;
self._value = newValue;
finale(self);
}
function reject(self, newValue) {
self._state = 2;
self._value = newValue;
if (Promise._onReject) {
Promise._onReject(self, newValue);
}
finale(self);
}
function finale(self) {
if (self._deferredState === 1) {
handle(self, self._deferreds);
self._deferreds = null;
}
if (self._deferredState === 2) {
for (var i = 0; i < self._deferreds.length; i++) {
handle(self, self._deferreds[i]);
}
self._deferreds = null;
}
}
function Handler(onFulfilled, onRejected, promise){
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
this.promise = promise;
}
/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/
function doResolve(fn, promise) {
var done = false;
var res = tryCallTwo(fn, function (value) {
if (done) return;
done = true;
resolve(promise, value);
}, function (reason) {
if (done) return;
done = true;
reject(promise, reason);
});
if (!done && res === IS_ERROR) {
done = true;
reject(promise, LAST_ERROR);
}
}

View File

@@ -164,8 +164,8 @@ public class RootAutomator {
public void touchUp(int id) throws IOException { public void touchUp(int id) throws IOException {
sendEvent(EV_ABS, ABS_MT_TRACKING_ID, id); sendEvent(EV_ABS, ABS_MT_TRACKING_ID, id);
// sendEvent(EV_KEY, BTN_TOUCH, 0x00000000); sendEvent(EV_KEY, BTN_TOUCH, 0x00000000);
// sendEvent(EV_KEY, BTN_TOOL_FINGER, 0x00000000); sendEvent(EV_KEY, BTN_TOOL_FINGER, 0x00000000);
sendEvent(EV_SYN, SYN_REPORT, 0x00000000); sendEvent(EV_SYN, SYN_REPORT, 0x00000000);
} }

View File

@@ -3,8 +3,7 @@ package com.stardust.autojs.core.ui;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.ContextWrapper; import android.content.ContextWrapper;
import android.content.DialogInterface; import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
@@ -12,9 +11,14 @@ import android.view.WindowManager;
import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.Theme; import com.afollestad.materialdialogs.Theme;
import com.stardust.concurrent.VolatileBox; import com.stardust.autojs.runtime.ScriptBridges;
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
import com.stardust.concurrent.VolatileDispose;
import com.stardust.util.ArrayUtils;
import com.stardust.util.UiHandler; import com.stardust.util.UiHandler;
import org.jdeferred.impl.DeferredObject;
/** /**
* Created by Stardust on 2017/5/8. * Created by Stardust on 2017/5/8.
*/ */
@@ -48,98 +52,121 @@ public class BlockedMaterialDialog extends MaterialDialog {
public static class Builder extends MaterialDialog.Builder { public static class Builder extends MaterialDialog.Builder {
private VolatileDispose<Object> mResultBox;
private UiHandler mUiHandler; private UiHandler mUiHandler;
private Object mCallback;
private ScriptBridges mScriptBridges;
private boolean mNotified = false;
public Builder(Context context, UiHandler uiHandler) { public Builder(Context context, UiHandler uiHandler, ScriptBridges scriptBridges, Object callback) {
super(context); super(context);
super.theme(Theme.LIGHT); super.theme(Theme.LIGHT);
mUiHandler = uiHandler; mUiHandler = uiHandler;
mScriptBridges = scriptBridges;
mCallback = callback;
if (Looper.getMainLooper() != Looper.myLooper()) {
mResultBox = new VolatileDispose<>();
}
} }
public MaterialDialog.Builder input(@Nullable CharSequence hint, @Nullable CharSequence prefill, boolean allowEmptyInput, final VolatileBox<String> result) { public MaterialDialog.Builder input(@Nullable CharSequence hint, @Nullable CharSequence prefill, boolean allowEmptyInput) {
dismissListener(result); super.input(hint, prefill, allowEmptyInput, (dialog, input) -> setAndNotify(input.toString()));
super.input(hint, prefill, allowEmptyInput, new MaterialDialog.InputCallback() { return this;
@Override }
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
result.set(input.toString()); private void setAndNotify(Object r) {
synchronized (result) { if (mNotified) {
result.notify(); return;
} }
mNotified = true;
if (mCallback != null) {
mScriptBridges.callFunction(mCallback, null, new Object[]{r});
}
if (mResultBox != null) {
mResultBox.setAndNotify(r);
}
}
private void setAndNotify(int r) {
if (mNotified) {
return;
}
mNotified = true;
if (mCallback != null) {
mScriptBridges.callFunction(mCallback, null, new int[]{r});
}
if (mResultBox != null) {
mResultBox.setAndNotify(r);
}
}
private void setAndNotify(boolean r) {
if (mNotified) {
return;
}
mNotified = true;
if (mCallback != null) {
mScriptBridges.callFunction(mCallback, null, new boolean[]{r});
}
if (mResultBox != null) {
mResultBox.setAndNotify(r);
}
}
public Builder alert() {
dismissListener(dialog -> setAndNotify(null));
onAny((dialog, which) -> setAndNotify(null));
return this;
}
public Builder confirm() {
dismissListener(dialog -> setAndNotify(false));
onAny((dialog, which) -> {
if (which == DialogAction.POSITIVE) {
setAndNotify(true);
} else {
setAndNotify(false);
} }
}); });
return this; return this;
} }
public Builder confirm(final VolatileBox<Boolean> result) { public MaterialDialog.Builder itemsCallback() {
onAny(new SingleButtonCallback() { dismissListener(dialog -> setAndNotify(-1));
@Override super.itemsCallback((dialog, itemView, position, text) -> setAndNotify(position));
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { return this;
if (which == DialogAction.POSITIVE) { }
result.setAndNotify(true);
} else { public MaterialDialog.Builder itemsCallbackMultiChoice(@Nullable Integer[] selectedIndices) {
result.setAndNotify(false); dismissListener(dialog -> setAndNotify(new Integer[0]));
} super.itemsCallbackMultiChoice(selectedIndices, (dialog, which, text) -> {
} setAndNotify(ArrayUtils.unbox(which));
return true;
}); });
return this; return this;
} }
public MaterialDialog.Builder itemsCallback(final VolatileBox<Integer> result) { public MaterialDialog.Builder itemsCallbackSingleChoice(int selectedIndex) {
dismissListener(result); dismissListener(dialog -> setAndNotify(-1));
super.itemsCallback(new ListCallback() { super.itemsCallbackSingleChoice(selectedIndex, (dialog, itemView, which, text) -> {
@Override setAndNotify(which);
public void onSelection(MaterialDialog dialog, View itemView, int position, CharSequence text) { return true;
result.setAndNotify(position);
}
}); });
return this; return this;
} }
public MaterialDialog.Builder itemsCallbackMultiChoice(@Nullable Integer[] selectedIndices, final VolatileBox<Integer[]> result) {
dismissListener(result);
super.itemsCallbackMultiChoice(selectedIndices, new ListCallbackMultiChoice() {
@Override
public boolean onSelection(MaterialDialog dialog, Integer[] which, CharSequence[] text) {
result.setAndNotify(which);
return true;
}
});
return this;
}
public MaterialDialog.Builder itemsCallbackSingleChoice(int selectedIndex, final VolatileBox<Integer> result) { public Object showAndGet() {
dismissListener(result); if (Looper.myLooper() == Looper.getMainLooper()) {
super.itemsCallbackSingleChoice(selectedIndex, new ListCallbackSingleChoice() { super.show();
@Override } else {
public boolean onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) { mUiHandler.post(Builder.super::show);
result.setAndNotify(which); }
return true; if (mResultBox != null) {
} return mResultBox.blockedGetOrThrow(ScriptInterruptedException.class);
}); } else {
return this; return null;
} }
public Builder dismissListener(final VolatileBox<?> result) {
super.dismissListener(new OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
synchronized (result) {
result.notify();
}
}
});
return this;
}
@Override
public MaterialDialog show() {
mUiHandler.post(new Runnable() {
@Override
public void run() {
Builder.super.show();
}
});
return null;
} }
@Override @Override

View File

@@ -49,7 +49,7 @@ public interface AttributeHandler {
if (!attr.getNodeName().equals("style")) { if (!attr.getNodeName().equals("style")) {
layoutXml.append("android:"); layoutXml.append("android:");
} }
layoutXml.append(mapAttrName(nodeName, attr.getLocalName())) layoutXml.append(mapAttrName(nodeName, attr.getNodeName()))
.append("=\"").append(mapAttrValue(nodeName, attr.getNodeName(), attr.getNodeValue())).append("\"\n"); .append("=\"").append(mapAttrValue(nodeName, attr.getNodeName(), attr.getNodeValue())).append("\"\n");
return true; return true;
} }

View File

@@ -13,7 +13,7 @@ public class ScriptBridges {
Object[] NO_ARGUMENTS = new Object[0]; Object[] NO_ARGUMENTS = new Object[0];
Object call(Object func, Object target, Object[] arg); Object call(Object func, Object target, Object arg);
Object toArray(Object o); Object toArray(Object o);
@@ -26,7 +26,7 @@ public class ScriptBridges {
mBridges = bridges; mBridges = bridges;
} }
public Object callFunction(Object func, Object target, Object[] args) { public Object callFunction(Object func, Object target, Object args) {
checkBridges(); checkBridges();
return mBridges.call(func, target, args); return mBridges.call(func, target, args);
} }

View File

@@ -25,7 +25,6 @@ import com.stardust.autojs.runtime.exception.ScriptEnvironmentException;
import com.stardust.autojs.runtime.exception.ScriptException; import com.stardust.autojs.runtime.exception.ScriptException;
import com.stardust.autojs.runtime.exception.ScriptInterruptedException; import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
import com.stardust.autojs.core.accessibility.SimpleActionAutomator; import com.stardust.autojs.core.accessibility.SimpleActionAutomator;
import com.stardust.concurrent.VolatileBox;
import com.stardust.autojs.runtime.api.UI; import com.stardust.autojs.runtime.api.UI;
import com.stardust.concurrent.VolatileDispose; import com.stardust.concurrent.VolatileDispose;
import com.stardust.pio.UncheckedIOException; import com.stardust.pio.UncheckedIOException;
@@ -38,8 +37,6 @@ import com.stardust.util.UiHandler;
import com.stardust.view.accessibility.AccessibilityInfoProvider; import com.stardust.view.accessibility.AccessibilityInfoProvider;
import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -174,7 +171,7 @@ public class ScriptRuntime {
images = new Images(context, this, builder.mScreenCaptureRequester); images = new Images(context, this, builder.mScreenCaptureRequester);
} }
engines = new Engines(builder.mEngineService); engines = new Engines(builder.mEngineService);
dialogs = new Dialogs(app, mUiHandler); dialogs = new Dialogs(app, mUiHandler, bridges);
} }
public void init() { public void init() {
@@ -217,23 +214,16 @@ public class ScriptRuntime {
} }
public void setClip(final String text) { public void setClip(final String text) {
final Object lock = new Object(); if (Looper.myLooper() == Looper.getMainLooper()) {
mUiHandler.post(new Runnable() { ClipboardUtil.setClip(mUiHandler.getContext(), text);
@Override return;
public void run() {
ClipboardUtil.setClip(mUiHandler.getContext(), text);
synchronized (lock) {
lock.notify();
}
}
});
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
throw new ScriptInterruptedException();
}
} }
VolatileDispose<Object> dispose = new VolatileDispose<>();
mUiHandler.post(() -> {
ClipboardUtil.setClip(mUiHandler.getContext(), text);
dispose.setAndNotify(text);
});
dispose.blockedGet();
} }
public String getClip() { public String getClip() {

View File

@@ -9,10 +9,9 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.Theme; import com.afollestad.materialdialogs.Theme;
import com.stardust.autojs.R; import com.stardust.autojs.R;
import com.stardust.autojs.annotation.ScriptInterface; import com.stardust.autojs.annotation.ScriptInterface;
import com.stardust.autojs.annotation.ScriptVariable;
import com.stardust.autojs.core.ui.BlockedMaterialDialog; import com.stardust.autojs.core.ui.BlockedMaterialDialog;
import com.stardust.autojs.runtime.exception.ScriptInterruptedException; import com.stardust.autojs.runtime.ScriptBridges;
import com.stardust.autojs.runtime.api.AppUtils;
import com.stardust.concurrent.VolatileBox;
import com.stardust.util.ArrayUtils; import com.stardust.util.ArrayUtils;
import com.stardust.util.UiHandler; import com.stardust.util.UiHandler;
@@ -25,50 +24,49 @@ public class Dialogs {
private AppUtils mAppUtils; private AppUtils mAppUtils;
private UiHandler mUiHandler; private UiHandler mUiHandler;
private ContextThemeWrapper mThemeWrapper; private ContextThemeWrapper mThemeWrapper;
private ScriptBridges mScriptBridges;
public Dialogs(AppUtils appUtils, UiHandler uiHandler) { @ScriptVariable
public final NonUiDialogs nonUiDialogs = new NonUiDialogs();
public Dialogs(AppUtils appUtils, UiHandler uiHandler, ScriptBridges scriptBridges) {
mAppUtils = appUtils; mAppUtils = appUtils;
mUiHandler = uiHandler; mUiHandler = uiHandler;
mScriptBridges = scriptBridges;
} }
@ScriptInterface @ScriptInterface
public String rawInput(String title, String prefill) { public Object rawInput(String title, String prefill, Object callback) {
VolatileBox<String> result = new VolatileBox<>(null); return ((BlockedMaterialDialog.Builder) dialogBuilder(callback)
dialogBuilder() .input(null, prefill, true)
.input(null, prefill, true, result) .title(title))
.title(title) .showAndGet();
.show();
return result.blockedGetOrThrow(ScriptInterruptedException.class);
} }
@ScriptInterface @ScriptInterface
public void alert(String title, String content) { public Object alert(String title, String content, Object callback) {
VolatileBox<Void> lock = new VolatileBox<>(); MaterialDialog.Builder builder = dialogBuilder(callback)
MaterialDialog.Builder builder = dialogBuilder() .alert()
.dismissListener(lock)
.title(title) .title(title)
.positiveText(R.string.ok); .positiveText(R.string.ok);
if (!TextUtils.isEmpty(content)) { if (!TextUtils.isEmpty(content)) {
builder.content(content); builder.content(content);
} }
builder.show(); return ((BlockedMaterialDialog.Builder) builder).showAndGet();
lock.blockedGetOrThrow(ScriptInterruptedException.class);
} }
@ScriptInterface @ScriptInterface
public boolean confirm(String title, String content) { public Object confirm(String title, String content, Object callback) {
VolatileBox<Boolean> result = new VolatileBox<>(false); MaterialDialog.Builder builder = dialogBuilder(callback)
MaterialDialog.Builder builder = dialogBuilder() .confirm()
.dismissListener(result)
.confirm(result)
.title(title) .title(title)
.positiveText(R.string.ok) .positiveText(R.string.ok)
.negativeText(R.string.cancel); .negativeText(R.string.cancel);
if (!TextUtils.isEmpty(content)) { if (!TextUtils.isEmpty(content)) {
builder.content(content); builder.content(content);
} }
builder.show(); return ((BlockedMaterialDialog.Builder) builder).showAndGet();
return result.blockedGetOrThrow(ScriptInterruptedException.class);
} }
private Context getContext() { private Context getContext() {
@@ -79,47 +77,99 @@ public class Dialogs {
} }
@ScriptInterface @ScriptInterface
public int select(String title, String... items) { public Object select(String title, Object... args) {
VolatileBox<Integer> result = new VolatileBox<>(-1); Object callback = getCallback(args);
dialogBuilder() String[] items = getItems(args);
.itemsCallback(result) return ((BlockedMaterialDialog.Builder) dialogBuilder(callback)
.itemsCallback()
.title(title) .title(title)
.items((CharSequence[]) items) .items((CharSequence[]) items))
.show(); .showAndGet();
return result.blockedGetOrThrow(ScriptInterruptedException.class); }
private String[] getItems(Object[] args) {
int len = 0;
if (args.length > 1) {
if (args[args.length - 1] instanceof CharSequence) {
len = args.length;
} else {
len = args.length - 1;
}
}
String[] items = new String[len];
for (int i = 0; i < len; i++) {
items[i] = args[i] == null ? null : args[i].toString();
}
return items;
}
private Object getCallback(Object[] args) {
if (args.length > 1 && !(args[args.length - 1] instanceof CharSequence)) {
return args[args.length - 1];
}
return null;
} }
@ScriptInterface @ScriptInterface
public int singleChoice(String title, int selectedIndex, String... items) { public Object singleChoice(String title, int selectedIndex, String[] items, Object callback) {
VolatileBox<Integer> result = new VolatileBox<>(-1); return ((BlockedMaterialDialog.Builder) dialogBuilder(callback)
dialogBuilder() .itemsCallbackSingleChoice(selectedIndex)
.itemsCallbackSingleChoice(selectedIndex, result)
.title(title) .title(title)
.positiveText(R.string.ok) .positiveText(R.string.ok)
.items((CharSequence[]) items) .items((CharSequence[]) items))
.show(); .showAndGet();
return result.blockedGetOrThrow(ScriptInterruptedException.class);
} }
@ScriptInterface @ScriptInterface
public int[] multiChoice(String title, int[] indices, String... items) { public Object multiChoice(String title, int[] indices, String[] items, Object callback) {
VolatileBox<Integer[]> result = new VolatileBox<>(new Integer[0]); return ((BlockedMaterialDialog.Builder) dialogBuilder(callback)
dialogBuilder() .itemsCallbackMultiChoice(ArrayUtils.box(indices))
.itemsCallbackMultiChoice(ArrayUtils.box(indices), result)
.title(title) .title(title)
.positiveText(R.string.ok) .positiveText(R.string.ok)
.items((CharSequence[]) items) .items((CharSequence[]) items))
.show(); .showAndGet();
return ArrayUtils.unbox(result.blockedGetOrThrow(ScriptInterruptedException.class));
} }
private BlockedMaterialDialog.Builder dialogBuilder() { private BlockedMaterialDialog.Builder dialogBuilder(Object callback) {
Context context = mAppUtils.getCurrentActivity(); Context context = mAppUtils.getCurrentActivity();
if (context == null || ((Activity) context).isFinishing()) { if (context == null || ((Activity) context).isFinishing()) {
context = getContext(); context = getContext();
} }
return (BlockedMaterialDialog.Builder) new BlockedMaterialDialog.Builder(context, mUiHandler) return (BlockedMaterialDialog.Builder) new BlockedMaterialDialog.Builder(context, mUiHandler, mScriptBridges, callback)
.theme(Theme.LIGHT); .theme(Theme.LIGHT);
} }
public class NonUiDialogs {
public String rawInput(String title, String prefill, Object callback) {
return (String) Dialogs.this.rawInput(title, prefill, callback);
}
@ScriptInterface
public boolean confirm(String title, String content, Object callback) {
return (boolean) Dialogs.this.confirm(title, content, callback);
}
@ScriptInterface
public int select(String title, Object... args) {
return (Integer) Dialogs.this.select(title, args);
}
@ScriptInterface
public int singleChoice(String title, int selectedIndex, String[] items, Object callback) {
return (int) Dialogs.this.singleChoice(title, selectedIndex, items, callback);
}
@ScriptInterface
public int[] multiChoice(String title, int[] indices, String[] items, Object callback) {
return (int[]) Dialogs.this.multiChoice(title, indices, items, callback);
}
@ScriptInterface
public Object alert(String title, String content, Object callback) {
return Dialogs.this.alert(title, content, callback);
}
}
} }