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

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