diff --git a/.idea/dictionaries/Stardust.xml b/.idea/dictionaries/Stardust.xml index 49fb13b70..56bd14789 100644 --- a/.idea/dictionaries/Stardust.xml +++ b/.idea/dictionaries/Stardust.xml @@ -11,6 +11,7 @@ loopers opencv prefill + recents scriptable storages tasker diff --git a/app/release/output.json b/app/release/output.json index 2bcc50bc3..605644dc0 100644 --- a/app/release/output.json +++ b/app/release/output.json @@ -1 +1 @@ -[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":203},"path":"inrt-release.apk","properties":{"packageId":"com.stardust.auojs.inrt","split":"","minSdkVersion":"17"}}] \ No newline at end of file +[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":253,"versionName":"4.1.0 Alpha3","enabled":true,"outputFile":"inrt-release.apk","fullName":"release","baseName":"release"},"path":"inrt-release.apk","properties":{}}] \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 447695b04..76c542cac 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,7 @@ - + @@ -15,7 +15,7 @@ - + @@ -85,6 +85,7 @@ android:name=".ui.edit.EditActivity_" android:configChanges="orientation|screenSize" android:multiprocess="true" + android:taskAffinity="org.autojs.autojs.edit" android:theme="@style/EditorTheme"> @@ -275,6 +276,7 @@ + = Build.VERSION_CODES.LOLLIPOP) { + finishAndRemoveTask(); + } else { + finish(); + } } private void showExitConfirmDialog() { @@ -207,9 +218,9 @@ private void showExitConfirmDialog() { .neutralText(R.string.text_exit_directly) .onNegative((dialog, which) -> { mEditorView.saveFile(); - EditActivity.super.finish(); + finishAndRemoveFromRecents(); }) - .onNeutral((dialog, which) -> EditActivity.super.finish()) + .onNeutral((dialog, which) -> finishAndRemoveFromRecents()) .show(); } @@ -294,4 +305,5 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis super.onRequestPermissionsResult(requestCode, permissions, grantResults); mRequestPermissionCallbacks.onRequestPermissionsResult(requestCode, permissions, grantResults); } + } diff --git a/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerView.java b/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerView.java index 9ef8ae67e..e4155f852 100644 --- a/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerView.java +++ b/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerView.java @@ -557,7 +557,7 @@ void run() { @OnClick(R.id.edit) void edit() { - Scripts.INSTANCE.edit(new ScriptFile(mExplorerItem.getPath())); + Scripts.INSTANCE.edit(getContext(), new ScriptFile(mExplorerItem.getPath())); notifyOperated(); } diff --git a/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityWebView.java b/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityWebView.java index 032e40c4e..a2a96a501 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityWebView.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityWebView.java @@ -75,7 +75,7 @@ void save() { .subscribe(file -> Snackbar.make(CommunityWebView.this, getResources().getString(R.string.format_file_downloaded, file.getPath()) , Snackbar.LENGTH_LONG) - .setAction(R.string.text_open, v -> Scripts.INSTANCE.edit(file)) + .setAction(R.string.text_open, v -> Scripts.INSTANCE.edit(getContext(), file)) .show(), error -> { error.printStackTrace(); diff --git a/app/src/main/java/org/autojs/autojs/ui/main/scripts/MyScriptListFragment.java b/app/src/main/java/org/autojs/autojs/ui/main/scripts/MyScriptListFragment.java index bc6cb7855..3ec2a9dc2 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/scripts/MyScriptListFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/scripts/MyScriptListFragment.java @@ -62,7 +62,7 @@ void setUpViews() { mExplorerView.setExplorer(Explorers.workspace(), ExplorerDirPage.createRoot(Pref.getScriptDirPath())); mExplorerView.setOnItemClickListener((view, item) -> { if (item.isEditable()) { - Scripts.INSTANCE.edit(item.toScriptFile()); + Scripts.INSTANCE.edit(getActivity(), item.toScriptFile()); } else { IntentUtil.viewFile(GlobalAppContext.get(), item.getPath(), AppFileProvider.AUTHORITY); } diff --git a/app/src/main/java/org/autojs/autojs/ui/shortcut/ShortcutIconSelectActivity.java b/app/src/main/java/org/autojs/autojs/ui/shortcut/ShortcutIconSelectActivity.java index 78dbaccd3..3df3e7019 100644 --- a/app/src/main/java/org/autojs/autojs/ui/shortcut/ShortcutIconSelectActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/shortcut/ShortcutIconSelectActivity.java @@ -9,8 +9,6 @@ import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; import android.net.Uri; -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -18,18 +16,18 @@ import android.view.ViewGroup; import android.widget.ImageView; -import org.autojs.autojs.R; -import org.autojs.autojs.tool.BitmapTool; -import org.autojs.autojs.ui.BaseActivity; - import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.ViewById; +import org.autojs.autojs.R; +import org.autojs.autojs.tool.BitmapTool; +import org.autojs.autojs.ui.BaseActivity; import org.autojs.autojs.workground.WrapContentGridLayoutManger; import java.util.ArrayList; import java.util.List; +import androidx.recyclerview.widget.RecyclerView; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; @@ -93,7 +91,7 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { startActivityForResult(new Intent(Intent.ACTION_GET_CONTENT) - .setType("image/*"), 11209); + .setType("image/*"), 11234); return true; } diff --git a/autojs/src/main/assets/modules/__dialogs__.js b/autojs/src/main/assets/modules/__dialogs__.js index 26e782540..8217d0c0f 100644 --- a/autojs/src/main/assets/modules/__dialogs__.js +++ b/autojs/src/main/assets/modules/__dialogs__.js @@ -155,7 +155,7 @@ module.exports = function(__runtime__, scope){ builder.input(wrapNonNullString(properties.inputHint), wrapNonNullString(properties.inputPrefill), function(dialog, input){ input = input.toString(); - builder.emit("input_change", dialog, input); + builder.dialog.emit("input_change", builder.dialog, input); }) .alwaysCallInputCallback(); } @@ -163,23 +163,23 @@ module.exports = function(__runtime__, scope){ var itemsSelectMode = properties.itemsSelectMode; if(itemsSelectMode == undefined || itemsSelectMode == 'select'){ builder.itemsCallback(function(dialog, view, position, text){ - dialog.emit("item_select", position, text.toString(), dialog); + builder.dialog.emit("item_select", position, text.toString(), builder.dialog); }); }else if(itemsSelectMode == 'single'){ builder.itemsCallbackSingleChoice(properties.itemsSelectedIndex == undefined ? -1 : properties.itemsSelectedIndex, function(dialog, view, which, text){ - dialog.emit("single_choice", which, text.toString(), dialog); + builder.dialog.emit("single_choice", which, text.toString(), builder.dialog); return true; }); }else if(itemsSelectMode == 'multi'){ builder.itemsCallbackMultiChoice(properties.itemsSelectedIndex == undefined ? null : properties.itemsSelectedIndex, function(dialog, view, indices, texts){ - dialog.emit("multi_choice", toJsArray(indices, (l, i)=> parseInt(l.get(i)), - toJsArray(texts, (l, i)=> l.get(i).toString())), dialog); + builder.dialog.emit("multi_choice", toJsArray(indices, (l, i)=> parseInt(l.get(i)), + toJsArray(texts, (l, i)=> l.get(i).toString())), builder.dialog); return true; }); }else{ - throw new Error("unknown itemsSelecteMode " + itemsSelectMode); + throw new Error("unknown itemsSelectMode " + itemsSelectMode); } } if(properties.progress != undefined){ @@ -191,9 +191,17 @@ module.exports = function(__runtime__, scope){ if(properties.checkBoxPrompt != undefined || properties.checkBoxChecked != undefined){ builder.checkBoxPrompt(wrapNonNullString(properties.checkBoxPrompt), !!properties.checkBoxChecked, function(view, checked){ - builder.emit("check", checked, builder.getDialog()); + builder.getDialog().emit("check", checked, builder.getDialog()); }); } + if(properties.customView != undefined) { + let customView = properties.customView; + if(typeof(customView) == 'xml' || typeof(customView) == 'string') { + customView = ui.run(() => ui.inflate(customView)); + } + let wrapInScrollView = (properties.wrapInScrollView === undefined) ? true : properties.wrapInScrollView; + builder.customView(customView, wrapInScrollView); + } } function wrapNonNullString(str){ diff --git a/autojs/src/main/assets/modules/__engines__.js b/autojs/src/main/assets/modules/__engines__.js index 9c428c785..a8104e42c 100644 --- a/autojs/src/main/assets/modules/__engines__.js +++ b/autojs/src/main/assets/modules/__engines__.js @@ -37,7 +37,7 @@ module.exports = function(__runtime__, scope){ } config.delay = c.delay || 0; config.interval = c.interval || 0; - config.loopTimes = c.loopTimes || 1; + config.loopTimes = (c.loopTimes === undefined)? 1 : c.loopTimes; if(c.arguments){ var arguments = c.arguments; for(var key in arguments){ diff --git a/autojs/src/main/assets/modules/__ui__.js b/autojs/src/main/assets/modules/__ui__.js index f9fcb9a07..c0dec7735 100644 --- a/autojs/src/main/assets/modules/__ui__.js +++ b/autojs/src/main/assets/modules/__ui__.js @@ -11,7 +11,7 @@ module.exports = function (runtime, global) { ui.__defineGetter__("emitter", ()=> activity ? activity.getEventEmitter() : null); ui.layout = function (xml) { - if(!activity){ + if(typeof(activity) == 'undefined'){ throw new Error("需要在ui模式下运行才能使用该函数"); } runtime.ui.layoutInflater.setContext(activity); @@ -24,15 +24,18 @@ module.exports = function (runtime, global) { } ui.inflate = function(xml, parent, attachToParent){ - if(!activity){ - throw new Error("需要在ui模式下运行才能使用该函数"); - } if(typeof(xml) == 'xml'){ xml = xml.toXMLString(); } parent = parent || null; attachToParent = !!attachToParent; - runtime.ui.layoutInflater.setContext(activity); + let ctx; + if(typeof(activity) == 'undefined') { + ctx = new android.view.ContextThemeWrapper(context, com.stardust.autojs.R.style.ScriptTheme); + } else { + ctx = activity; + } + runtime.ui.layoutInflater.setContext(ctx); return runtime.ui.layoutInflater.inflate(xml.toString(), parent, attachToParent); } diff --git a/autojs/src/main/java/com/stardust/autojs/core/graphics/ScriptCanvasView.java b/autojs/src/main/java/com/stardust/autojs/core/graphics/ScriptCanvasView.java deleted file mode 100644 index e442ab9d5..000000000 --- a/autojs/src/main/java/com/stardust/autojs/core/graphics/ScriptCanvasView.java +++ /dev/null @@ -1,188 +0,0 @@ -package com.stardust.autojs.core.graphics; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.os.SystemClock; -import android.util.Log; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -import com.stardust.autojs.core.eventloop.EventEmitter; -import com.stardust.autojs.runtime.ScriptRuntime; -import com.stardust.autojs.runtime.exception.ScriptInterruptedException; - -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; - private ScriptRuntime mScriptRuntime; - private volatile long mTimePerDraw = 1000 / 30; - - public ScriptCanvasView(Context context, ScriptRuntime scriptRuntime) { - super(context); - mScriptRuntime = scriptRuntime; - mEventEmitter = new EventEmitter(mScriptRuntime.bridges); - mHolder = getHolder(); - init(); - } - - - public void setMaxFps(int maxFps) { - if (maxFps <= 0) { - mTimePerDraw = 0; - } else { - mTimePerDraw = 100 / maxFps; - } - } - - private void init() { - mHolder.addCallback(this); - setZOrderOnTop(false); - } - - @Override - public void surfaceCreated(SurfaceHolder holder) { - performDraw(); - Log.d(LOG_TAG, "surfaceCreated: " + this); - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - - } - - private synchronized void performDraw() { - if (mDrawingThreadPool != null) - return; - mDrawingThreadPool = Executors.newCachedThreadPool(); - mDrawingThreadPool.execute(() -> { - Canvas canvas = null; - SurfaceHolder holder = getHolder(); - long time = SystemClock.uptimeMillis(); - ScriptCanvas scriptCanvas = new ScriptCanvas(); - try { - while (mDrawing) { - canvas = holder.lockCanvas(); - scriptCanvas.setCanvas(canvas); - scriptCanvas.drawColor(Color.WHITE); - emit("draw", scriptCanvas, ScriptCanvasView.this); - holder.unlockCanvasAndPost(canvas); - canvas = null; - long dt = mTimePerDraw - (SystemClock.uptimeMillis() - time); - if (dt > 0) { - sleep(dt); - } - time = SystemClock.uptimeMillis(); - } - } catch (Exception e) { - mScriptRuntime.exit(e); - mDrawing = false; - } finally { - if (canvas != null) { - holder.unlockCanvasAndPost(canvas); - } - } - }); - } - - private void sleep(long dt) { - try { - Thread.sleep(dt); - } catch (InterruptedException e) { - throw new ScriptInterruptedException(e); - } - } - - @Override - protected void onWindowVisibilityChanged(int visibility) { - Log.d(LOG_TAG, "onWindowVisibilityChanged: " + this + ": visibility=" + visibility + ", mDrawingThreadPool=" + mDrawingThreadPool); - if (visibility == VISIBLE) { - mDrawing = true; - } else { - mDrawing = false; - } - super.onWindowVisibilityChanged(visibility); - } - - @Override - public synchronized void surfaceDestroyed(SurfaceHolder holder) { - mDrawing = false; - mDrawingThreadPool.shutdown(); - mDrawingThreadPool = null; - Log.d(LOG_TAG, "surfaceDestroyed: " + this); - } - - 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(); - } - -} diff --git a/autojs/src/main/java/com/stardust/autojs/core/graphics/ScriptCanvasView.kt b/autojs/src/main/java/com/stardust/autojs/core/graphics/ScriptCanvasView.kt new file mode 100644 index 000000000..dcf3e5a11 --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/core/graphics/ScriptCanvasView.kt @@ -0,0 +1,178 @@ +package com.stardust.autojs.core.graphics + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Canvas +import android.graphics.SurfaceTexture +import android.os.SystemClock +import android.util.Log +import android.view.TextureView +import android.view.View +import com.stardust.autojs.core.eventloop.EventEmitter +import com.stardust.autojs.runtime.ScriptRuntime +import com.stardust.autojs.runtime.exception.ScriptInterruptedException +import com.stardust.ext.ifNull +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +/** + * Created by Stardust on 2018/3/16. + */ + +@SuppressLint("ViewConstructor") +class ScriptCanvasView(context: Context, private val mScriptRuntime: ScriptRuntime) : TextureView(context), TextureView.SurfaceTextureListener { + @Volatile + private var mDrawing = true + private val mEventEmitter: EventEmitter = EventEmitter(mScriptRuntime.bridges) + private var mDrawingThreadPool: ExecutorService? = null + @Volatile + private var mTimePerDraw = (1000 / 30).toLong() + + val maxListeners: Int + get() = mEventEmitter.maxListeners + + init { + surfaceTextureListener = this + } + + fun setMaxFps(maxFps: Int) { + mTimePerDraw = if (maxFps <= 0) { + 0 + } else { + (100 / maxFps).toLong() + } + } + + @Synchronized + private fun performDraw() { + ::mDrawingThreadPool.ifNull { + Executors.newCachedThreadPool() + }.run { + execute { + var canvas: Canvas? = null + var time = SystemClock.uptimeMillis() + val scriptCanvas = ScriptCanvas() + try { + while (mDrawing) { + canvas = lockCanvas() + scriptCanvas.setCanvas(canvas) + // scriptCanvas.drawColor(Color.WHITE); + emit("draw", scriptCanvas, this@ScriptCanvasView) + unlockCanvasAndPost(canvas) + canvas = null + val dt = mTimePerDraw - (SystemClock.uptimeMillis() - time) + if (dt > 0) { + sleep(dt) + } + time = SystemClock.uptimeMillis() + } + } catch (e: Exception) { + mScriptRuntime.exit(e) + mDrawing = false + } finally { + if (canvas != null) { + unlockCanvasAndPost(canvas) + } + } + } + } + } + + private fun sleep(dt: Long) { + try { + Thread.sleep(dt) + } catch (e: InterruptedException) { + throw ScriptInterruptedException(e) + } + + } + + override fun onWindowVisibilityChanged(visibility: Int) { + Log.d(LOG_TAG, "onWindowVisibilityChanged: " + this + ": visibility=" + visibility + ", mDrawingThreadPool=" + mDrawingThreadPool) + val oldDrawing = mDrawing + mDrawing = visibility == View.VISIBLE + if (!oldDrawing && mDrawing) { + performDraw() + } + super.onWindowVisibilityChanged(visibility) + } + + fun once(eventName: String, listener: Any): EventEmitter { + return mEventEmitter.once(eventName, listener) + } + + fun on(eventName: String, listener: Any): EventEmitter { + return mEventEmitter.on(eventName, listener) + } + + fun addListener(eventName: String, listener: Any): EventEmitter { + return mEventEmitter.addListener(eventName, listener) + } + + fun emit(eventName: String, vararg args: Any): Boolean { + return mEventEmitter.emit(eventName, *args) + } + + fun eventNames(): Array { + return mEventEmitter.eventNames() + } + + fun listenerCount(eventName: String): Int { + return mEventEmitter.listenerCount(eventName) + } + + fun listeners(eventName: String): Array { + return mEventEmitter.listeners(eventName) + } + + fun prependListener(eventName: String, listener: Any): EventEmitter { + return mEventEmitter.prependListener(eventName, listener) + } + + fun prependOnceListener(eventName: String, listener: Any): EventEmitter { + return mEventEmitter.prependOnceListener(eventName, listener) + } + + fun removeAllListeners(): EventEmitter { + return mEventEmitter.removeAllListeners() + } + + fun removeAllListeners(eventName: String): EventEmitter { + return mEventEmitter.removeAllListeners(eventName) + } + + fun removeListener(eventName: String, listener: Any): EventEmitter { + return mEventEmitter.removeListener(eventName, listener) + } + + fun setMaxListeners(n: Int): EventEmitter { + return mEventEmitter.setMaxListeners(n) + } + + override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) { + performDraw() + Log.d(LOG_TAG, "onSurfaceTextureAvailable: ${this}, width = $width, height = $height") + } + + override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {} + + override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean { + mDrawing = false + mDrawingThreadPool?.shutdown() + Log.d(LOG_TAG, "onSurfaceTextureDestroyed: ${this}") + return true + } + + override fun onSurfaceTextureUpdated(surface: SurfaceTexture) { + + } + + companion object { + + private const val LOG_TAG = "ScriptCanvasView" + + fun defaultMaxListeners(): Int { + return EventEmitter.defaultMaxListeners() + } + } +} diff --git a/autojs/src/main/java/com/stardust/autojs/core/http/MutableOkHttp.java b/autojs/src/main/java/com/stardust/autojs/core/http/MutableOkHttp.java index f80c5ed5f..1d4f607e7 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/http/MutableOkHttp.java +++ b/autojs/src/main/java/com/stardust/autojs/core/http/MutableOkHttp.java @@ -1,5 +1,6 @@ package com.stardust.autojs.core.http; +import java.net.SocketTimeoutException; import java.util.Collections; import java.util.concurrent.TimeUnit; @@ -19,13 +20,24 @@ public class MutableOkHttp extends OkHttpClient { private long mTimeout = 30 * 1000; private Interceptor mRetryInterceptor = chain -> { Request request = chain.request(); - Response response = chain.proceed(request); + Response response = null; int tryCount = 0; - while (!response.isSuccessful() && tryCount < getMaxRetries()) { + do { + boolean succeed; + try { + response = chain.proceed(request); + succeed = response.isSuccessful(); + } catch (SocketTimeoutException e) { + succeed = false; + if (tryCount >= getMaxRetries()) { + throw e; + } + } + if (succeed || tryCount >= getMaxRetries()) { + return response; + } tryCount++; - response = chain.proceed(request); - } - return response; + } while (true); }; public MutableOkHttp() { @@ -64,6 +76,7 @@ public long getTimeout() { public void setTimeout(long timeout) { + mTimeout = timeout; muteClient(); } diff --git a/autojs/src/main/java/com/stardust/autojs/core/looper/Loopers.java b/autojs/src/main/java/com/stardust/autojs/core/looper/Loopers.java index 31b402f0d..c7cd424fc 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/looper/Loopers.java +++ b/autojs/src/main/java/com/stardust/autojs/core/looper/Loopers.java @@ -17,10 +17,13 @@ import java.util.HashSet; import java.util.concurrent.CopyOnWriteArrayList; +import androidx.annotation.Nullable; + /** * Created by Stardust on 2017/7/29. */ +@SuppressWarnings("ConstantConditions") public class Loopers implements MessageQueue.IdleHandler { private static final String LOG_TAG = "Loopers"; @@ -32,9 +35,27 @@ public interface LooperQuitHandler { private static final Runnable EMPTY_RUNNABLE = () -> { }; - private volatile ThreadLocal waitWhenIdle = new ThreadLocal<>(); - private volatile ThreadLocal> waitIds = new ThreadLocal<>(); - private volatile ThreadLocal maxWaitId = new ThreadLocal<>(); + private volatile ThreadLocal waitWhenIdle = new ThreadLocal() { + @Nullable + @Override + protected Boolean initialValue() { + return Looper.myLooper() == Looper.getMainLooper(); + } + }; + private volatile ThreadLocal> waitIds = new ThreadLocal>() { + @Nullable + @Override + protected HashSet initialValue() { + return new HashSet<>(); + } + }; + private volatile ThreadLocal maxWaitId = new ThreadLocal() { + @Nullable + @Override + protected Integer initialValue() { + return 0; + } + }; private volatile ThreadLocal> looperQuitHandlers = new ThreadLocal<>(); private volatile Looper mServantLooper; private Timers mTimers; @@ -126,7 +147,7 @@ public Looper getServantLooper() { return mServantLooper; } - public void quitServantLooper() { + private void quitServantLooper() { if (mServantLooper == null) return; mServantLooper.quit(); @@ -134,12 +155,14 @@ public void quitServantLooper() { public int waitWhenIdle() { int id = maxWaitId.get(); + Log.d(LOG_TAG, "waitWhenIdle: " + id); maxWaitId.set(id + 1); waitIds.get().add(id); return id; } public void doNotWaitWhenIdle(int waitId) { + Log.d(LOG_TAG, "doNotWaitWhenIdle: " + waitId); waitIds.get().remove(waitId); } @@ -181,9 +204,6 @@ public void prepare() { if (Looper.myLooper() == null) LooperHelper.prepare(); Looper.myQueue().addIdleHandler(this); - waitWhenIdle.set(Looper.myLooper() == Looper.getMainLooper()); - waitIds.set(new HashSet<>()); - maxWaitId.set(0); } public void notifyThreadExit(TimerThread thread) { diff --git a/autojs/src/main/java/com/stardust/autojs/core/looper/Timer.java b/autojs/src/main/java/com/stardust/autojs/core/looper/Timer.java index 324c16b6e..032181863 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/looper/Timer.java +++ b/autojs/src/main/java/com/stardust/autojs/core/looper/Timer.java @@ -89,6 +89,10 @@ public void postDelayed(Runnable r, long interval) { } } + public void post(Runnable r) { + + } + public boolean clearInterval(int id) { return clearCallback(id); } diff --git a/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/JsDialogBuilder.java b/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/JsDialogBuilder.java index e089536fa..5dc2ff131 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/JsDialogBuilder.java +++ b/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/JsDialogBuilder.java @@ -34,7 +34,7 @@ public JsDialogBuilder(Context context, ScriptRuntime runtime) { super(context); mTimer = runtime.timers.getTimerForCurrentThread(); mLoopers = runtime.loopers; - mEmitter = new EventEmitter(runtime.bridges, mTimer); + mEmitter = new EventEmitter(runtime.bridges); mUiHandler = runtime.uiHandler; setUpEvents(); } @@ -70,7 +70,6 @@ private void setUpEvents() { public void onShowCalled() { mTimer.postDelayed(() -> mWaitId = mLoopers.waitWhenIdle(), 0); - } public JsDialog getDialog() { diff --git a/autojs/src/main/java/com/stardust/autojs/core/util/Shell.java b/autojs/src/main/java/com/stardust/autojs/core/util/Shell.java index 8e49ec784..c1a44a2b1 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/util/Shell.java +++ b/autojs/src/main/java/com/stardust/autojs/core/util/Shell.java @@ -5,18 +5,13 @@ import android.preference.PreferenceManager; import android.util.Log; +import com.stardust.autojs.runtime.ScriptRuntime; import com.stardust.autojs.runtime.api.AbstractShell; import com.stardust.autojs.runtime.exception.ScriptInterruptedException; -import com.stardust.autojs.runtime.ScriptRuntime; -import com.stardust.lang.ThreadCompat; import com.stardust.pio.UncheckedIOException; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; +import java.util.ArrayList; import jackpal.androidterm.ShellTermSession; import jackpal.androidterm.emulatorview.TermSession; @@ -68,9 +63,11 @@ public void onInterrupted(InterruptedException e) { private volatile TermSession mTermSession; private final Object mInitLock = new Object(); private final Object mExitLock = new Object(); + private final Object mCommandOutputLock = new Object(); private volatile RuntimeException mInitException; private volatile boolean mInitialized = false; private volatile boolean mWaitingExit = false; + private volatile String mCommandOutput = null; private final boolean mShouldReadOutput; private Callback mCallback; @@ -114,6 +111,18 @@ public void exec(String command) { mTermSession.write(command + "\n"); } + public String execAndWaitFor(String command) { + exec(command); + synchronized (mCommandOutputLock) { + try { + mCommandOutputLock.wait(); + return mCommandOutput; + } catch (InterruptedException e) { + throw new ScriptInterruptedException(); + } + } + } + public void setCallback(Callback callback) { mCallback = callback; } @@ -131,13 +140,13 @@ private void ensureInitialized() { checkInitException(); throw new IllegalStateException(); } - }else { + } else { logDebug("ensureInitialized: init"); } } - private void logDebug(String log){ - if(DEBUG){ + private void logDebug(String log) { + if (DEBUG) { Log.d(TAG, log); } } @@ -150,7 +159,7 @@ private void checkInitException() { private void waitInitialization() { synchronized (mInitLock) { - if(mInitialized){ + if (mInitialized) { return; } logDebug("waitInitialization: enter"); @@ -204,33 +213,11 @@ public TermSession getTermSession() { private class MyShellTermSession extends ShellTermSession { - private BufferedReader mBufferedReader; - private OutputStream mOutputStream; - private Thread mReadingThread; + private StringBuilder mStringBuffer = new StringBuilder(); + private ArrayList mCommandOutputs = new ArrayList<>(); public MyShellTermSession(TermSettings settings, String initialCommand) throws IOException { super(settings, initialCommand); - PipedInputStream pipedInputStream = new PipedInputStream(8192); - mBufferedReader = new BufferedReader(new InputStreamReader(pipedInputStream)); - mOutputStream = new PipedOutputStream(pipedInputStream); - if (mShouldReadOutput) { - startReadingThread(); - } - } - - private void startReadingThread() { - mReadingThread = new ThreadCompat(() -> { - String line; - try { - while (!Thread.currentThread().isInterrupted() - && (line = mBufferedReader.readLine()) != null) { - onNewLine(line); - } - } catch (IOException e) { - e.printStackTrace(); - } - }); - mReadingThread.start(); } private void onNewLine(String line) { @@ -239,6 +226,8 @@ private void onNewLine(String line) { if (!isRoot() && line.endsWith(" $ sh")) { notifyInitialized(); } + } else { + mCommandOutputs.add(line); } if (mCallback != null) { mCallback.onNewLine(line); @@ -248,28 +237,56 @@ private void onNewLine(String line) { } } - private void onOutput(String str){ + private void onCommandOutput(ArrayList output) { + StringBuilder result = new StringBuilder(); + for (int i = 1; i < output.size(); i++) { + result.append(output.get(i)); + if (i < output.size() - 1) { + result.append("\n"); + } + } + logDebug("onCommandOutput: lines = " + output + ", output = " + result); + synchronized (mCommandOutputLock) { + mCommandOutput = result.toString(); + mCommandOutputLock.notifyAll(); + } + } + + private void onOutput(String str) { logDebug("onOutput: " + str); if (!mInitialized) { if (isRoot() && str.endsWith(":/ # ")) { notifyInitialized(); } } + int start = 0; + int i; + while (true) { + i = str.indexOf("\n", start); + if (i > 0) { + onNewLine((mStringBuffer.toString() + str.substring(0, i - 1)).trim()); + mStringBuffer.delete(0, mStringBuffer.length()); + } else { + if (start <= str.length() - 1) { + mStringBuffer.append(str.substring(start)); + } + break; + } + start = i + 1; + } + if (str.endsWith(" # ") || str.endsWith(" $ ")) { + onCommandOutput(mCommandOutputs); + mCommandOutputs.clear(); + } if (mCallback != null) { - mCallback.onOutput(str); + mCallback.onOutput(str.replace("\r", "")); } } @Override protected void processInput(byte[] data, int offset, int count) { - try { - onOutput(new String(data, offset, count)); - mOutputStream.write(data, offset, count); - } catch (IOException e) { - e.printStackTrace(); - finish(); - } + onOutput(new String(data, offset, count)); } private void notifyExit() { @@ -299,21 +316,6 @@ protected void onProcessExit() { } } - @Override - public void finish() { - super.finish(); - if (!mShouldReadOutput) - return; - if(mReadingThread != null){ - mReadingThread.interrupt(); - } - try { - mBufferedReader.close(); - mOutputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } } } diff --git a/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.kt b/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.kt index d2d5f25c4..d8bdde59f 100644 --- a/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.kt +++ b/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.kt @@ -68,7 +68,7 @@ open class RhinoJavaScriptEngine(private val mAndroidContext: android.content.Co runtime.topLevelScope = mScriptable } - public override fun doExecution(source: JavaScriptSource): Any { + public override fun doExecution(source: JavaScriptSource): Any? { var reader = source.nonNullScriptReader try { reader = preprocess(reader) diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/AbstractShell.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/AbstractShell.java index 24911ec74..f0d17a5bf 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/AbstractShell.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/AbstractShell.java @@ -12,7 +12,6 @@ public abstract class AbstractShell { - public static class Result { public int code = -1; public String error; diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Dialogs.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Dialogs.java index 14425ba1e..75d0ce72a 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Dialogs.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Dialogs.java @@ -144,7 +144,6 @@ public MaterialDialog.Builder newBuilder() { } return new JsDialogBuilder(context, mRuntime) .theme(Theme.LIGHT); - } public class NonUiDialogs { diff --git a/automator/src/main/java/com/stardust/automator/search/DFS.kt b/automator/src/main/java/com/stardust/automator/search/DFS.kt index 6f7efdd8a..005bd3fb7 100644 --- a/automator/src/main/java/com/stardust/automator/search/DFS.kt +++ b/automator/src/main/java/com/stardust/automator/search/DFS.kt @@ -2,6 +2,7 @@ package com.stardust.automator.search import com.stardust.automator.UiObject import com.stardust.automator.filter.Filter +import java.util.* import kotlin.collections.ArrayList @@ -12,32 +13,26 @@ import kotlin.collections.ArrayList object DFS : SearchAlgorithm { override fun search(root: UiObject, filter: Filter, limit: Int): ArrayList { - val list = ArrayList() - if (filter.filter(root)) { - list.add(root) - if (list.size >= limit) { - return list + val result = ArrayList() + val stack = LinkedList() + stack.push(root) + while (stack.isNotEmpty()) { + val parent = stack.pop() + for (i in parent.childCount - 1 downTo 0) { + val child = parent.child(i) ?: continue + stack.push(child) } - } - searchChildren(root, list, filter, limit) - return list - } - - private fun searchChildren(parent: UiObject, list: MutableList, filter: Filter, limit: Int) { - for (i in 0 until parent.childCount) { - val child = parent.child(i) ?: continue - val isTarget = filter.filter(child) - if (isTarget) { - list.add(child) - if (list.size >= limit) { + if (filter.filter(parent)) { + result.add(parent) + if (result.size >= limit) { break } - } - searchChildren(child, list, filter, limit) - if (!isTarget) { - child.recycle() + } else { + if (parent !== root) { + parent.recycle() + } } } + return result } - } diff --git a/common/src/main/java/com/stardust/ext/Common.kt b/common/src/main/java/com/stardust/ext/Common.kt new file mode 100644 index 000000000..b725e67c7 --- /dev/null +++ b/common/src/main/java/com/stardust/ext/Common.kt @@ -0,0 +1,13 @@ +package com.stardust.ext + +import kotlin.reflect.KMutableProperty0 + +fun > T.ifNull(provider: () -> R): R { + val value = this.get() + if (value != null) { + return value + } + val newValue = provider() + set(newValue) + return newValue +} \ No newline at end of file diff --git a/common/src/main/java/com/stardust/io/ByteBufferBackedInputStream.kt b/common/src/main/java/com/stardust/io/ByteBufferBackedInputStream.kt new file mode 100644 index 000000000..34b5fb74d --- /dev/null +++ b/common/src/main/java/com/stardust/io/ByteBufferBackedInputStream.kt @@ -0,0 +1,30 @@ +package com.stardust.io + +import java.io.IOException +import java.io.InputStream +import java.nio.ByteBuffer + +class ByteBufferBackedInputStream(private var buf: ByteBuffer) : InputStream() { + + @Throws(IOException::class) + override fun read(): Int { + return if (!buf.hasRemaining()) { + -1 + } else buf.get().toInt() and 0xFF + } + + @Throws(IOException::class) + override fun read(bytes: ByteArray, off: Int, len: Int): Int { + if (!buf.hasRemaining()) { + return -1 + } + val read = Math.min(len, available()) + buf.get(bytes, off, read) + buf.position(buf.position() - read) + return read + } + + override fun available(): Int { + return buf.position() + } +} \ No newline at end of file diff --git a/common/src/main/java/com/stardust/io/ByteBufferBackedOutputStream.kt b/common/src/main/java/com/stardust/io/ByteBufferBackedOutputStream.kt new file mode 100644 index 000000000..7c3ee425d --- /dev/null +++ b/common/src/main/java/com/stardust/io/ByteBufferBackedOutputStream.kt @@ -0,0 +1,19 @@ +package com.stardust.io + +import java.io.IOException +import java.io.OutputStream +import java.nio.ByteBuffer + +class ByteBufferBackedOutputStream(private var buf: ByteBuffer) : OutputStream() { + + @Throws(IOException::class) + override fun write(b: Int) { + buf.put(b.toByte()) + } + + @Throws(IOException::class) + override fun write(bytes: ByteArray, off: Int, len: Int) { + buf.put(bytes, off, len) + } + +} \ No newline at end of file diff --git a/inrt/src/main/assets/project/main.js b/inrt/src/main/assets/project/main.js index bbb7dc698..b89ececfb 100644 --- a/inrt/src/main/assets/project/main.js +++ b/inrt/src/main/assets/project/main.js @@ -1 +1 @@ -Y?Mz \ No newline at end of file +toast("Hello, Auto.js"); \ No newline at end of file diff --git a/inrt/src/main/java/com/stardust/auojs/inrt/SettingsActivity.kt b/inrt/src/main/java/com/stardust/auojs/inrt/SettingsActivity.kt index b2fffe43a..02efc172e 100644 --- a/inrt/src/main/java/com/stardust/auojs/inrt/SettingsActivity.kt +++ b/inrt/src/main/java/com/stardust/auojs/inrt/SettingsActivity.kt @@ -24,7 +24,7 @@ class SettingsActivity : AppCompatActivity() { val toolbar = findViewById(R.id.toolbar) toolbar.setTitle(R.string.text_settings) setSupportActionBar(toolbar) - toolbar.setNavigationOnClickListener { v -> finish() } + toolbar.setNavigationOnClickListener { finish() } supportActionBar?.setDisplayHomeAsUpEnabled(true) } diff --git a/inrt/src/main/java/com/stardust/auojs/inrt/autojs/XJavaScriptEngine.kt b/inrt/src/main/java/com/stardust/auojs/inrt/autojs/XJavaScriptEngine.kt index ea55a42c8..75e954813 100644 --- a/inrt/src/main/java/com/stardust/auojs/inrt/autojs/XJavaScriptEngine.kt +++ b/inrt/src/main/java/com/stardust/auojs/inrt/autojs/XJavaScriptEngine.kt @@ -17,22 +17,28 @@ class XJavaScriptEngine(context: Context) : LoopBasedJavaScriptEngine(context) { override fun execute(source: ScriptSource, callback: ExecuteCallback?) { if (source is JavaScriptFileSource) { try { - execute(source.file) + if (execute(source.file)) { + return + } } catch (e: Throwable) { e.printStackTrace() + return } - } else { - super.execute(source, callback) } + super.execute(source, callback) } - private fun execute(file: File) { + private fun execute(file: File): Boolean { val bytes = PFiles.readBytes(file.path) + if (!EncryptedScriptFileHeader.isValidFile(bytes)) { + return false + } try { super.execute(StringScriptSource(file.name, String(ScriptEncryption.decrypt(bytes, EncryptedScriptFileHeader.BLOCK_SIZE)))) } catch (e: GeneralSecurityException) { e.printStackTrace() } + return true } } \ No newline at end of file diff --git a/inrt/src/main/java/com/stardust/auojs/inrt/launch/AssetsProjectLauncher.kt b/inrt/src/main/java/com/stardust/auojs/inrt/launch/AssetsProjectLauncher.kt index 96c9e991e..5adbbe31b 100644 --- a/inrt/src/main/java/com/stardust/auojs/inrt/launch/AssetsProjectLauncher.kt +++ b/inrt/src/main/java/com/stardust/auojs/inrt/launch/AssetsProjectLauncher.kt @@ -70,7 +70,7 @@ open class AssetsProjectLauncher(private val mAssetsProjectDir: String, private val source = JavaScriptFileSource("main", mMainScriptFile) val config = ExecutionConfig(workingDirectory = mProjectDir) if (source.executionMode and JavaScriptSource.EXECUTION_MODE_UI != 0) { - config.intentFlags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NO_HISTORY + config.intentFlags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_TASK_ON_HOME } else { activity?.finish() }