just to save works

This commit is contained in:
hyb1996
2018-03-17 14:34:59 +08:00
parent 976e78e07d
commit c5e3050732
26 changed files with 500 additions and 209 deletions

View File

@@ -13,6 +13,7 @@
>
<activity
android:name=".execution.ScriptExecuteActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/AppTheme"/>

View File

@@ -96,12 +96,15 @@ runtime.init();
importClass(android.view.KeyEvent);
importClass(com.stardust.autojs.core.util.Shell);
importClass(android.graphics.Paint);
//重定向require以便支持相对路径
(function(){
var __require__ = require;
global.require = function(path){
path = files.path(path);
if(!path.startsWith("http://") && !path.startsWith("https://")){
path = files.path(path);
}
return __require__(path);
};
})();

View File

@@ -110,6 +110,10 @@ public class EventEmitter {
mBridges = bridges;
}
protected void setTimer(Timer timer) {
mTimer = timer;
}
public EventEmitter once(String eventName, Object listener) {
getListeners(eventName).add(listener, true);
return this;

View File

@@ -0,0 +1,148 @@
package com.stardust.autojs.core.graphics;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.stardust.autojs.core.eventloop.EventEmitter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by Stardust on 2018/3/16.
*/
@SuppressLint("ViewConstructor")
public class ScriptCanvasView extends SurfaceView implements SurfaceHolder.Callback {
private static final String LOG_TAG = "ScriptCanvasView";
private volatile boolean mDrawing = true;
private EventEmitter mEventEmitter;
private final SurfaceHolder mHolder;
private ExecutorService mDrawingThreadPool;
public ScriptCanvasView(Context context, EventEmitter eventEmitter) {
super(context);
mEventEmitter = eventEmitter;
mHolder = getHolder();
init();
}
private void init() {
mHolder.addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
performDraw();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
private void performDraw() {
if (mDrawingThreadPool == null)
mDrawingThreadPool = Executors.newCachedThreadPool();
mDrawingThreadPool.execute(() -> {
SurfaceHolder holder = getHolder();
while (mDrawing) {
Canvas canvas = holder.lockCanvas();
canvas.drawColor(Color.WHITE);
emit("draw", canvas, this);
holder.unlockCanvasAndPost(canvas);
}
Log.d(LOG_TAG, "drawing thread: mRunning = false");
});
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
if (visibility == VISIBLE) {
if (mDrawingThreadPool != null) {
mDrawing = true;
performDraw();
}
} else {
mDrawing = false;
}
super.onWindowVisibilityChanged(visibility);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(LOG_TAG, "surfaceDestroyed: mRunning = true");
mDrawing = false;
Log.d(LOG_TAG, "surfaceDestroyed: mRunning = false");
mDrawingThreadPool.shutdown();
mDrawingThreadPool = null;
}
public EventEmitter once(String eventName, Object listener) {
return mEventEmitter.once(eventName, listener);
}
public EventEmitter on(String eventName, Object listener) {
return mEventEmitter.on(eventName, listener);
}
public EventEmitter addListener(String eventName, Object listener) {
return mEventEmitter.addListener(eventName, listener);
}
public boolean emit(String eventName, Object... args) {
return mEventEmitter.emit(eventName, args);
}
public String[] eventNames() {
return mEventEmitter.eventNames();
}
public int listenerCount(String eventName) {
return mEventEmitter.listenerCount(eventName);
}
public Object[] listeners(String eventName) {
return mEventEmitter.listeners(eventName);
}
public EventEmitter prependListener(String eventName, Object listener) {
return mEventEmitter.prependListener(eventName, listener);
}
public EventEmitter prependOnceListener(String eventName, Object listener) {
return mEventEmitter.prependOnceListener(eventName, listener);
}
public EventEmitter removeAllListeners() {
return mEventEmitter.removeAllListeners();
}
public EventEmitter removeAllListeners(String eventName) {
return mEventEmitter.removeAllListeners(eventName);
}
public EventEmitter removeListener(String eventName, Object listener) {
return mEventEmitter.removeListener(eventName, listener);
}
public EventEmitter setMaxListeners(int n) {
return mEventEmitter.setMaxListeners(n);
}
public int getMaxListeners() {
return mEventEmitter.getMaxListeners();
}
public static int defaultMaxListeners() {
return EventEmitter.defaultMaxListeners();
}
}

View File

@@ -24,7 +24,7 @@ public class LooperHelper {
public static void quitForThread(Thread thread) {
Looper looper = sLoopers.remove(thread);
if (looper != null)
if (looper != null && looper != Looper.getMainLooper())
looper.quit();
}
}

View File

@@ -87,7 +87,7 @@ public class TimerThread extends ThreadCompat {
return getTimer().setTimeout(callback, delay, args);
}
private Timer getTimer() {
public Timer getTimer() {
if (mTimer == null) {
throw new IllegalStateException("thread is not alive");
}

View File

@@ -49,30 +49,7 @@ import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
/**
* Copyright Nicholas White 2015.
* Source: https://github.com/nickwah/DynamicLayoutInflator
* <p>
* Licensed under the MIT License:
* <p>
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* <p>
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* <p>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
public class DynamicLayoutInflater {
private static final String LOG_TAG = "DynamicLayoutInflater";

View File

@@ -0,0 +1,32 @@
package com.stardust.autojs.core.ui.inflater.attrsetter;
import android.content.Context;
import android.support.annotation.Nullable;
import com.stardust.autojs.core.eventloop.EventEmitter;
import com.stardust.autojs.core.graphics.ScriptCanvasView;
import com.stardust.autojs.core.ui.inflater.ValueParser;
import com.stardust.autojs.core.ui.inflater.ViewCreator;
import com.stardust.autojs.runtime.ScriptRuntime;
import java.util.Map;
/**
* Created by Stardust on 2018/3/16.
*/
public class CanvasViewAttrSetter extends BaseViewAttrSetter<ScriptCanvasView> {
private ScriptRuntime mScriptRuntime;
public CanvasViewAttrSetter(ValueParser valueParser, ScriptRuntime runtime) {
super(valueParser);
mScriptRuntime = runtime;
}
@Nullable
@Override
public ViewCreator<ScriptCanvasView> getCreator() {
return (context, attrs) -> new ScriptCanvasView(context, new EventEmitter(mScriptRuntime.bridges));
}
}

View File

@@ -12,6 +12,7 @@ import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TimePicker;
import com.stardust.autojs.core.graphics.ScriptCanvasView;
import com.stardust.autojs.core.ui.widget.JsButton;
import com.stardust.autojs.core.ui.widget.JsEditText;
import com.stardust.autojs.core.ui.widget.JsFrameLayout;
@@ -64,6 +65,7 @@ public class XmlConverter {
.map("checkbox", CheckBox.class.getName())
.map("scroll", ScrollView.class.getName())
.map("toolbar", Toolbar.class.getName())
.map("canvas", ScriptCanvasView.class.getName())
);
private static final AttributeHandler ATTRIBUTE_HANDLER = new AttributeHandler.AttrNameRouter()

View File

@@ -44,7 +44,6 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
private Scriptable mScriptable;
private Thread mThread;
private android.content.Context mAndroidContext;
private Thread.UncaughtExceptionHandler mUncaughtExceptionHandler;
public RhinoJavaScriptEngine(android.content.Context context) {
mAndroidContext = context;
@@ -157,14 +156,6 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
context.setWrapFactory(new WrapFactory());
}
public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler uiThreadExceptionHandler) {
mUncaughtExceptionHandler = uiThreadExceptionHandler;
}
public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() {
return mUncaughtExceptionHandler;
}
private class WrapFactory extends org.mozilla.javascript.WrapFactory {
@Override
@@ -208,7 +199,7 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
try {
return super.doTopCall(callable, cx, scope, thisObj, args);
} catch (Exception e) {
mUncaughtExceptionHandler.uncaughtException(Thread.currentThread(), e);
getRuntime().exit(e);
return null;
}
}

View File

@@ -40,6 +40,13 @@ public interface ScriptEngine<S extends ScriptSource> {
Object getTag(String key);
String cwd();
void uncaughtException(Exception throwable);
Exception getUncaughtException();
/**
* @hide
*/
@@ -60,11 +67,12 @@ public interface ScriptEngine<S extends ScriptSource> {
private Map<String, Object> mTags = new ConcurrentHashMap<>();
private OnDestroyListener mOnDestroyListener;
private boolean mDestroyed = false;
private Exception mUncaughtException;
@Override
public synchronized void setTag(String key, Object value) {
if(value == null)
if (value == null)
return;
mTags.put(key, value);
}
@@ -97,5 +105,16 @@ public interface ScriptEngine<S extends ScriptSource> {
throw new SecurityException("setOnDestroyListener can be called only once");
mOnDestroyListener = onDestroyListener;
}
@Override
public void uncaughtException(Exception throwable) {
mUncaughtException = throwable;
forceStop();
}
@Override
public Exception getUncaughtException() {
return mUncaughtException;
}
}
}

View File

@@ -0,0 +1,76 @@
package com.stardust.autojs.engine;
import com.stardust.autojs.script.ScriptSource;
/**
* Created by Stardust on 2018/3/17.
*/
public class ScriptEngineProxy<S extends ScriptSource> implements ScriptEngine<S> {
private final ScriptEngine<S> mScriptEngine;
public ScriptEngineProxy(ScriptEngine<S> scriptEngine) {
mScriptEngine = scriptEngine;
}
@Override
public void put(String name, Object value) {
mScriptEngine.put(name, value);
}
@Override
public Object execute(S scriptSource) {
return mScriptEngine.execute(scriptSource);
}
@Override
public void forceStop() {
mScriptEngine.forceStop();
}
@Override
public void destroy() {
mScriptEngine.destroy();
}
@Override
public boolean isDestroyed() {
return mScriptEngine.isDestroyed();
}
@Override
public void setTag(String key, Object value) {
mScriptEngine.setTag(key, value);
}
@Override
public Object getTag(String key) {
return mScriptEngine.getTag(key);
}
@Override
public String cwd() {
return mScriptEngine.cwd();
}
@Override
public void uncaughtException(Exception throwable) {
mScriptEngine.uncaughtException(throwable);
}
@Override
public Exception getUncaughtException() {
return mScriptEngine.getUncaughtException();
}
@Override
public void setOnDestroyListener(OnDestroyListener listener) {
mScriptEngine.setOnDestroyListener(listener);
}
@Override
public void init() {
mScriptEngine.init();
}
}

View File

@@ -23,10 +23,6 @@ public class LoopedBasedJavaScriptExecution extends RunnableScriptExecution {
long delay = getConfig().delay;
sleep(delay);
final LoopBasedJavaScriptEngine javaScriptEngine = (LoopBasedJavaScriptEngine) engine;
javaScriptEngine.setUncaughtExceptionHandler((t, e) -> {
javaScriptEngine.forceStop();
getListener().onException(this, (Exception) e);
});
final long interval = getConfig().interval;
javaScriptEngine.getRuntime().loopers.setMainLooperQuitHandler(new Loopers.LooperQuitHandler() {
long times = getConfig().loopTimes == 0 ? Integer.MAX_VALUE : getConfig().loopTimes;

View File

@@ -1,5 +1,6 @@
package com.stardust.autojs.execution;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -9,6 +10,7 @@ import com.stardust.autojs.engine.LoopBasedJavaScriptEngine;
import com.stardust.autojs.engine.RhinoJavaScriptEngine;
import com.stardust.autojs.engine.ScriptEngine;
import com.stardust.autojs.engine.ScriptEngineManager;
import com.stardust.autojs.engine.ScriptEngineProxy;
import com.stardust.autojs.script.ScriptSource;
import com.stardust.util.IntentExtras;
@@ -16,7 +18,7 @@ import com.stardust.util.IntentExtras;
* Created by Stardust on 2017/2/5.
*/
public class ScriptExecuteActivity extends AppCompatActivity implements Thread.UncaughtExceptionHandler {
public class ScriptExecuteActivity extends AppCompatActivity {
private static final String EXTRA_EXECUTION = ScriptExecuteActivity.class.getName() + ".execution";
@@ -24,7 +26,8 @@ public class ScriptExecuteActivity extends AppCompatActivity implements Thread.U
private ScriptEngine mScriptEngine;
private ScriptExecutionListener mExecutionListener;
private ScriptSource mScriptSource;
private ScriptExecution mScriptExecution;
private ActivityScriptExecution mScriptExecution;
private IntentExtras mIntentExtras;
public static ActivityScriptExecution execute(Context context, ScriptEngineManager manager, ScriptExecutionTask task) {
ActivityScriptExecution execution = new ActivityScriptExecution(manager, task);
@@ -37,22 +40,34 @@ public class ScriptExecuteActivity extends AppCompatActivity implements Thread.U
return execution;
}
// FIXME: 2018/3/16 如果Activity被回收则得不到改进
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentExtras extras = IntentExtras.fromIntent(getIntent());
if (extras.get(EXTRA_EXECUTION) == null) {
mIntentExtras = readIntentExtras(savedInstanceState);
if (mIntentExtras == null || mIntentExtras.get(EXTRA_EXECUTION) == null) {
finish();
return;
}
mScriptExecution = extras.get(EXTRA_EXECUTION);
mScriptExecution = mIntentExtras.get(EXTRA_EXECUTION);
mScriptSource = mScriptExecution.getSource();
mScriptEngine = mScriptExecution.getEngine();
mScriptEngine = mScriptExecution.createEngine(this);
mExecutionListener = mScriptExecution.getListener();
((RhinoJavaScriptEngine) mScriptEngine).setUncaughtExceptionHandler((t, e) -> onException((Exception) e));
runScript();
}
private IntentExtras readIntentExtras(Bundle savedInstanceState) {
IntentExtras extras = IntentExtras.fromIntent(getIntent());
if (extras == null && savedInstanceState != null) {
int id = savedInstanceState.getInt(IntentExtras.EXTRA_ID, -1);
if (id == -1) {
return null;
}
extras = IntentExtras.fromId(id);
}
return extras;
}
private void runScript() {
try {
prepare();
@@ -93,7 +108,12 @@ public class ScriptExecuteActivity extends AppCompatActivity implements Thread.U
@Override
public void finish() {
mExecutionListener.onSuccess(mScriptExecution, mResult);
Exception exception = mScriptEngine.getUncaughtException();
if (exception != null) {
onException(exception);
} else {
mExecutionListener.onSuccess(mScriptExecution, mResult);
}
super.finish();
}
@@ -106,8 +126,12 @@ public class ScriptExecuteActivity extends AppCompatActivity implements Thread.U
}
@Override
public void uncaughtException(Thread t, Throwable e) {
onException((Exception) e);
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mIntentExtras == null)
return;
IntentExtras extras = IntentExtras.newExtras().putAll(mIntentExtras);
outState.putInt(IntentExtras.EXTRA_ID, extras.getId());
}
private static class ActivityScriptExecution extends ScriptExecution.AbstractScriptExecution {
@@ -120,12 +144,23 @@ public class ScriptExecuteActivity extends AppCompatActivity implements Thread.U
mScriptEngineManager = manager;
}
@SuppressWarnings("unchecked")
public ScriptEngine createEngine(Activity activity) {
if (mScriptEngine != null) {
mScriptEngine.forceStop();
}
mScriptEngine = new ScriptEngineProxy(mScriptEngineManager.createEngineOfSourceOrThrow(getSource())) {
@Override
public void forceStop() {
super.forceStop();
activity.finish();
}
};
return mScriptEngine;
}
@Override
public ScriptEngine getEngine() {
if (mScriptEngine == null) {
mScriptEngine = mScriptEngineManager.createEngineOfSourceOrThrow(getSource());
}
return mScriptEngine;
}

View File

@@ -9,6 +9,7 @@ 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.engine.JavaScriptEngine;
import com.stardust.autojs.engine.RhinoJavaScriptEngine;
import com.stardust.autojs.engine.ScriptEngine;
import com.stardust.autojs.rhino.AndroidClassLoader;
@@ -321,22 +322,10 @@ public class ScriptRuntime {
}
}
public void exit(Object obj) throws Throwable {
mThread.interrupt();
if (!(obj instanceof Throwable)) {
console.error(obj);
return;
}
Throwable e = (Exception) obj;
public void exit(Exception e) {
engines.myEngine().uncaughtException(e);
if (Looper.myLooper() != Looper.getMainLooper()) {
throw e;
} else {
Thread.UncaughtExceptionHandler handler = ((RhinoJavaScriptEngine) engines.myEngine()).getUncaughtExceptionHandler();
if (handler != null) {
handler.uncaughtException(Thread.currentThread(), e);
} else {
console.error(e);
}
throw new ScriptException(e);
}
}

View File

@@ -39,7 +39,7 @@ public class Files {
}
public String cwd() {
return ((ScriptEngine.AbstractScriptEngine) mRuntime.engines.myEngine()).cwd();
return mRuntime.engines.myEngine().cwd();
}
public PFileInterface open(String path, String mode, String encoding, int bufferSize) {

View File

@@ -3,10 +3,12 @@ package com.stardust.autojs.runtime.api;
import android.content.Context;
import android.graphics.drawable.Drawable;
import com.stardust.autojs.core.graphics.ScriptCanvasView;
import com.stardust.autojs.core.ui.ConvertLayoutInflater;
import com.stardust.autojs.core.ui.JsLayoutInflater;
import com.stardust.autojs.core.ui.inflater.DynamicLayoutInflater;
import com.stardust.autojs.core.ui.inflater.ValueParser;
import com.stardust.autojs.core.ui.inflater.attrsetter.CanvasViewAttrSetter;
import com.stardust.autojs.core.ui.inflater.attrsetter.JsImageViewAttrSetter;
import com.stardust.autojs.core.ui.widget.JsImageView;
import com.stardust.autojs.rhino.ProxyObject;
@@ -38,6 +40,8 @@ public class UI extends ProxyObject {
DynamicLayoutInflater inflater = new DynamicLayoutInflater(mValueParser);
inflater.registerViewAttrSetter(JsImageView.class.getName(),
new JsImageViewAttrSetter(mValueParser));
inflater.registerViewAttrSetter(ScriptCanvasView.class.getName(),
new CanvasViewAttrSetter(mValueParser, runtime));
mJsLayoutInflater = new ConvertLayoutInflater(inflater);
mProperties.put("layoutInflater", mJsLayoutInflater);
}