新增 连接电脑支持运行和保存项目
This commit is contained in:
@@ -115,6 +115,10 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.CREATE_SHORTCUT" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.autojs.autojs.model.script;
|
package org.autojs.autojs.model.script;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|||||||
@@ -1,23 +1,36 @@
|
|||||||
package org.autojs.autojs.pluginclient;
|
package org.autojs.autojs.pluginclient;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonNull;
|
import com.google.gson.JsonNull;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.stardust.app.GlobalAppContext;
|
import com.stardust.app.GlobalAppContext;
|
||||||
import com.stardust.autojs.execution.ScriptExecution;
|
import com.stardust.autojs.execution.ScriptExecution;
|
||||||
|
import com.stardust.autojs.project.ProjectLauncher;
|
||||||
import com.stardust.autojs.script.StringScriptSource;
|
import com.stardust.autojs.script.StringScriptSource;
|
||||||
|
import com.stardust.io.Zip;
|
||||||
import com.stardust.pio.PFiles;
|
import com.stardust.pio.PFiles;
|
||||||
|
import com.stardust.util.MD5;
|
||||||
|
|
||||||
import org.autojs.autojs.Pref;
|
import org.autojs.autojs.Pref;
|
||||||
import org.autojs.autojs.R;
|
import org.autojs.autojs.R;
|
||||||
import org.autojs.autojs.autojs.AutoJs;
|
import org.autojs.autojs.autojs.AutoJs;
|
||||||
import org.autojs.autojs.model.script.Scripts;
|
import org.autojs.autojs.model.script.Scripts;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Stardust on 2017/5/11.
|
* Created by Stardust on 2017/5/11.
|
||||||
*/
|
*/
|
||||||
@@ -32,7 +45,7 @@ public class DevPluginResponseHandler implements Handler {
|
|||||||
String name = getName(data);
|
String name = getName(data);
|
||||||
String id = data.get("id").getAsString();
|
String id = data.get("id").getAsString();
|
||||||
runScript(id, name, script);
|
runScript(id, name, script);
|
||||||
return false;
|
return true;
|
||||||
})
|
})
|
||||||
.handler("stop", data -> {
|
.handler("stop", data -> {
|
||||||
String id = data.get("id").getAsString();
|
String id = data.get("id").getAsString();
|
||||||
@@ -43,7 +56,7 @@ public class DevPluginResponseHandler implements Handler {
|
|||||||
String script = data.get("script").getAsString();
|
String script = data.get("script").getAsString();
|
||||||
String name = getName(data);
|
String name = getName(data);
|
||||||
saveScript(name, script);
|
saveScript(name, script);
|
||||||
return false;
|
return true;
|
||||||
})
|
})
|
||||||
.handler("rerun", data -> {
|
.handler("rerun", data -> {
|
||||||
String id = data.get("id").getAsString();
|
String id = data.get("id").getAsString();
|
||||||
@@ -51,21 +64,54 @@ public class DevPluginResponseHandler implements Handler {
|
|||||||
String name = getName(data);
|
String name = getName(data);
|
||||||
stopScript(id);
|
stopScript(id);
|
||||||
runScript(id, name, script);
|
runScript(id, name, script);
|
||||||
return false;
|
return true;
|
||||||
})
|
})
|
||||||
.handler("stopAll", data -> {
|
.handler("stopAll", data -> {
|
||||||
AutoJs.getInstance().getScriptEngineService().stopAllAndToast();
|
AutoJs.getInstance().getScriptEngineService().stopAllAndToast();
|
||||||
return false;
|
return true;
|
||||||
|
}))
|
||||||
|
.handler("bytes_command", new Router("command")
|
||||||
|
.handler("run_project", data -> {
|
||||||
|
launchProject(data.get("dir").getAsString());
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.handler("save_project", data -> {
|
||||||
|
saveProject(data.get("name").getAsString(), data.get("dir").getAsString());
|
||||||
|
return true;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
private HashMap<String, ScriptExecution> mScriptExecutions = new HashMap<>();
|
private HashMap<String, ScriptExecution> mScriptExecutions = new HashMap<>();
|
||||||
|
private final File mCacheDir;
|
||||||
|
|
||||||
|
public DevPluginResponseHandler(File cacheDir) {
|
||||||
|
mCacheDir = cacheDir;
|
||||||
|
if (cacheDir.exists()) {
|
||||||
|
if (cacheDir.isDirectory()) {
|
||||||
|
PFiles.deleteFilesOfDir(cacheDir);
|
||||||
|
} else {
|
||||||
|
cacheDir.delete();
|
||||||
|
cacheDir.mkdirs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(JsonObject data) {
|
public boolean handle(JsonObject data) {
|
||||||
return mRouter.handle(data);
|
return mRouter.handle(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Observable<File> handleBytes(JsonObject data, JsonWebSocket.Bytes bytes) {
|
||||||
|
String id = data.get("data").getAsJsonObject().get("id").getAsString();
|
||||||
|
String idMd5 = MD5.md5(id);
|
||||||
|
return Observable.fromCallable(() -> {
|
||||||
|
File dir = new File(mCacheDir, idMd5);
|
||||||
|
Zip.unzip(new ByteArrayInputStream(bytes.byteString.toByteArray()), dir);
|
||||||
|
return dir;
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
private void runScript(String viewId, String name, String script) {
|
private void runScript(String viewId, String name, String script) {
|
||||||
if (TextUtils.isEmpty(name)) {
|
if (TextUtils.isEmpty(name)) {
|
||||||
name = "[" + viewId + "]";
|
name = "[" + viewId + "]";
|
||||||
@@ -75,6 +121,18 @@ public class DevPluginResponseHandler implements Handler {
|
|||||||
mScriptExecutions.put(viewId, Scripts.run(new StringScriptSource("[remote]" + name, script)));
|
mScriptExecutions.put(viewId, Scripts.run(new StringScriptSource("[remote]" + name, script)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void launchProject(String dir) {
|
||||||
|
try {
|
||||||
|
new ProjectLauncher(dir)
|
||||||
|
.launch(AutoJs.getInstance().getScriptEngineService());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
GlobalAppContext.toast(R.string.text_invalid_project);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void stopScript(String viewId) {
|
private void stopScript(String viewId) {
|
||||||
ScriptExecution execution = mScriptExecutions.get(viewId);
|
ScriptExecution execution = mScriptExecutions.get(viewId);
|
||||||
if (execution != null) {
|
if (execution != null) {
|
||||||
@@ -104,4 +162,42 @@ public class DevPluginResponseHandler implements Handler {
|
|||||||
PFiles.write(file, script);
|
PFiles.write(file, script);
|
||||||
GlobalAppContext.toast(R.string.text_script_save_successfully);
|
GlobalAppContext.toast(R.string.text_script_save_successfully);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
|
private void saveProject(String name, String dir) {
|
||||||
|
if (TextUtils.isEmpty(name)) {
|
||||||
|
name = "untitled";
|
||||||
|
}
|
||||||
|
name = PFiles.getNameWithoutExtension(name);
|
||||||
|
File toDir = new File(Pref.getScriptDirPath(), name);
|
||||||
|
Observable.fromCallable(() -> {
|
||||||
|
copyDir(new File(dir), toDir);
|
||||||
|
return toDir.getPath();
|
||||||
|
}).subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(dest ->
|
||||||
|
GlobalAppContext.toast(R.string.text_project_save_success, dest),
|
||||||
|
err ->
|
||||||
|
GlobalAppContext.toast(R.string.text_project_save_error, err.getMessage())
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyDir(File fromDir, File toDir) throws FileNotFoundException {
|
||||||
|
toDir.mkdirs();
|
||||||
|
File[] files = fromDir.listFiles();
|
||||||
|
if (files == null || files.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (File file : files) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
copyDir(file, new File(toDir, file.getName()));
|
||||||
|
} else {
|
||||||
|
FileOutputStream fos = new FileOutputStream(new File(toDir, file.getName()));
|
||||||
|
PFiles.write(new FileInputStream(file), fos, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,15 @@ import android.util.Pair;
|
|||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.stardust.app.GlobalAppContext;
|
||||||
import com.stardust.util.MapBuilder;
|
import com.stardust.util.MapBuilder;
|
||||||
|
|
||||||
import org.autojs.autojs.BuildConfig;
|
import org.autojs.autojs.BuildConfig;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.File;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@@ -36,6 +39,7 @@ public class DevPluginService {
|
|||||||
private static final int CLIENT_VERSION = 2;
|
private static final int CLIENT_VERSION = 2;
|
||||||
private static final String LOG_TAG = "DevPluginService";
|
private static final String LOG_TAG = "DevPluginService";
|
||||||
private static final String TYPE_HELLO = "hello";
|
private static final String TYPE_HELLO = "hello";
|
||||||
|
private static final String TYPE_BYTES_COMMAND = "bytes_command";
|
||||||
private static final long HANDSHAKE_TIMEOUT = 10 * 1000;
|
private static final long HANDSHAKE_TIMEOUT = 10 * 1000;
|
||||||
|
|
||||||
public static class State {
|
public static class State {
|
||||||
@@ -68,15 +72,21 @@ public class DevPluginService {
|
|||||||
private static final int PORT = 9317;
|
private static final int PORT = 9317;
|
||||||
private static DevPluginService sInstance = new DevPluginService();
|
private static DevPluginService sInstance = new DevPluginService();
|
||||||
private final PublishSubject<State> mConnectionState = PublishSubject.create();
|
private final PublishSubject<State> mConnectionState = PublishSubject.create();
|
||||||
private final DevPluginResponseHandler mResponseHandler = new DevPluginResponseHandler();
|
private final DevPluginResponseHandler mResponseHandler;
|
||||||
private final Handler mHandshakeTimeoutHandler = new Handler(Looper.getMainLooper());
|
private final HashMap<String, JsonWebSocket.Bytes> mBytes = new HashMap<>();
|
||||||
|
private final HashMap<String, JsonObject> mRequiredBytesCommands = new HashMap<>();
|
||||||
|
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||||
private volatile JsonWebSocket mSocket;
|
private volatile JsonWebSocket mSocket;
|
||||||
|
|
||||||
public static DevPluginService getInstance() {
|
public static DevPluginService getInstance() {
|
||||||
return sInstance;
|
return sInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DevPluginService() {
|
||||||
|
File cache = new File(GlobalAppContext.get().getCacheDir(), "remote_project");
|
||||||
|
mResponseHandler = new DevPluginResponseHandler(cache);
|
||||||
|
}
|
||||||
|
|
||||||
@AnyThread
|
@AnyThread
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
return mSocket != null && !mSocket.isClosed();
|
return mSocket != null && !mSocket.isClosed();
|
||||||
@@ -134,14 +144,22 @@ public class DevPluginService {
|
|||||||
.build()))
|
.build()))
|
||||||
.doOnNext(socket -> {
|
.doOnNext(socket -> {
|
||||||
mSocket = socket;
|
mSocket = socket;
|
||||||
socket.data()
|
subscribeMessage(socket);
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.doOnComplete(() -> mConnectionState.onNext(new State(State.DISCONNECTED)))
|
|
||||||
.subscribe(data -> onSocketData(socket, data), this::onSocketError);
|
|
||||||
sayHelloToServer(socket);
|
sayHelloToServer(socket);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
|
private void subscribeMessage(JsonWebSocket socket) {
|
||||||
|
socket.data()
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.doOnComplete(() -> mConnectionState.onNext(new State(State.DISCONNECTED)))
|
||||||
|
.subscribe(data -> onSocketData(socket, data), this::onSocketError);
|
||||||
|
socket.bytes()
|
||||||
|
.doOnComplete(() -> mConnectionState.onNext(new State(State.DISCONNECTED)))
|
||||||
|
.subscribe(data -> onSocketData(socket, data), this::onSocketError);
|
||||||
|
}
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
private void onSocketError(Throwable e) {
|
private void onSocketError(Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -158,24 +176,64 @@ public class DevPluginService {
|
|||||||
Log.w(LOG_TAG, "onSocketData: not json object: " + element);
|
Log.w(LOG_TAG, "onSocketData: not json object: " + element);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
JsonObject obj = element.getAsJsonObject();
|
try {
|
||||||
JsonElement type = obj.get("type");
|
JsonObject obj = element.getAsJsonObject();
|
||||||
if (type != null && type.isJsonPrimitive() && type.getAsString().equals(TYPE_HELLO)) {
|
JsonElement typeElement = obj.get("type");
|
||||||
onServerHello(jsonWebSocket, obj);
|
if (typeElement == null || !typeElement.isJsonPrimitive()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
String type = typeElement.getAsString();
|
||||||
|
if (type.equals(TYPE_HELLO)) {
|
||||||
|
onServerHello(jsonWebSocket, obj);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (TYPE_BYTES_COMMAND.equals(type)) {
|
||||||
|
String md5 = obj.get("md5").getAsString();
|
||||||
|
JsonWebSocket.Bytes bytes = mBytes.remove(md5);
|
||||||
|
if (bytes != null) {
|
||||||
|
handleBytes(obj, bytes);
|
||||||
|
} else {
|
||||||
|
mRequiredBytesCommands.put(md5, obj);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mResponseHandler.handle(obj);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
mResponseHandler.handle(obj);
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
|
private void handleBytes(JsonObject obj, JsonWebSocket.Bytes bytes) {
|
||||||
|
mResponseHandler.handleBytes(obj, bytes)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(dir -> {
|
||||||
|
obj.get("data").getAsJsonObject().add("dir", new JsonPrimitive(dir.getPath()));
|
||||||
|
mResponseHandler.handle(obj);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private void sayHelloToServer(JsonWebSocket socket) throws IOException {
|
private void onSocketData(JsonWebSocket jsonWebSocket, JsonWebSocket.Bytes bytes) {
|
||||||
|
JsonObject command = mRequiredBytesCommands.remove(bytes.md5);
|
||||||
|
if (command != null) {
|
||||||
|
handleBytes(command, bytes);
|
||||||
|
} else {
|
||||||
|
mBytes.put(bytes.md5, bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
private void sayHelloToServer(JsonWebSocket socket) {
|
||||||
writeMap(socket, TYPE_HELLO, new MapBuilder<String, Object>()
|
writeMap(socket, TYPE_HELLO, new MapBuilder<String, Object>()
|
||||||
.put("device_name", Build.BRAND + " " + Build.MODEL)
|
.put("device_name", Build.BRAND + " " + Build.MODEL)
|
||||||
.put("client_version", CLIENT_VERSION)
|
.put("client_version", CLIENT_VERSION)
|
||||||
.put("app_version", BuildConfig.VERSION_NAME)
|
.put("app_version", BuildConfig.VERSION_NAME)
|
||||||
.put("app_version_code", BuildConfig.VERSION_CODE)
|
.put("app_version_code", BuildConfig.VERSION_CODE)
|
||||||
.build());
|
.build());
|
||||||
mHandshakeTimeoutHandler.postDelayed(() -> {
|
mHandler.postDelayed(() -> {
|
||||||
if (mSocket != socket && !socket.isClosed()) {
|
if (mSocket != socket && !socket.isClosed()) {
|
||||||
onHandshakeTimeout(socket);
|
onHandshakeTimeout(socket);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,14 +19,28 @@ import okhttp3.Request;
|
|||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import okhttp3.WebSocket;
|
import okhttp3.WebSocket;
|
||||||
import okhttp3.WebSocketListener;
|
import okhttp3.WebSocketListener;
|
||||||
|
import okio.ByteString;
|
||||||
|
|
||||||
public class JsonWebSocket extends WebSocketListener {
|
public class JsonWebSocket extends WebSocketListener {
|
||||||
|
|
||||||
|
public static class Bytes {
|
||||||
|
public final String md5;
|
||||||
|
public final ByteString byteString;
|
||||||
|
public final long timestamp;
|
||||||
|
|
||||||
|
public Bytes(String md5, ByteString byteString) {
|
||||||
|
this.md5 = md5;
|
||||||
|
this.byteString = byteString;
|
||||||
|
this.timestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final String LOG_TAG = "JsonWebSocket";
|
private static final String LOG_TAG = "JsonWebSocket";
|
||||||
|
|
||||||
private final WebSocket mWebSocket;
|
private final WebSocket mWebSocket;
|
||||||
private final JsonParser mJsonParser = new JsonParser();
|
private final JsonParser mJsonParser = new JsonParser();
|
||||||
private final PublishSubject<JsonElement> mJsonElementPublishSubject = PublishSubject.create();
|
private final PublishSubject<JsonElement> mJsonElementPublishSubject = PublishSubject.create();
|
||||||
|
private final PublishSubject<Bytes> mBytesPublishSubject = PublishSubject.create();
|
||||||
private volatile boolean mClosed = false;
|
private volatile boolean mClosed = false;
|
||||||
|
|
||||||
public JsonWebSocket(OkHttpClient client, Request request) {
|
public JsonWebSocket(OkHttpClient client, Request request) {
|
||||||
@@ -35,14 +49,24 @@ public class JsonWebSocket extends WebSocketListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(WebSocket webSocket, String text) {
|
public void onMessage(WebSocket webSocket, String text) {
|
||||||
Log.d(LOG_TAG, "onMessage: " + text);
|
Log.d(LOG_TAG, "onMessage: text = " + text);
|
||||||
dispatchJson(text);
|
dispatchJson(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(WebSocket webSocket, ByteString bytes) {
|
||||||
|
Log.d(LOG_TAG, "onMessage: ByteString = " + bytes.toString());
|
||||||
|
mBytesPublishSubject.onNext(new Bytes(bytes.md5().hex(), bytes));
|
||||||
|
}
|
||||||
|
|
||||||
public Observable<JsonElement> data() {
|
public Observable<JsonElement> data() {
|
||||||
return mJsonElementPublishSubject;
|
return mJsonElementPublishSubject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Observable<Bytes> bytes(){
|
||||||
|
return mBytesPublishSubject;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean write(JsonElement element) {
|
public boolean write(JsonElement element) {
|
||||||
String json = element.toString();
|
String json = element.toString();
|
||||||
Log.d(LOG_TAG, "write: length = " + json.length() + ", json = " + element);
|
Log.d(LOG_TAG, "write: length = " + json.length() + ", json = " + element);
|
||||||
|
|||||||
136
app/src/main/java/org/autojs/autojs/tool/SafeJsonElement.java
Normal file
136
app/src/main/java/org/autojs/autojs/tool/SafeJsonElement.java
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
package org.autojs.autojs.tool;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonNull;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
public class SafeJsonElement extends JsonElement {
|
||||||
|
private final JsonElement mJsonElement;
|
||||||
|
|
||||||
|
public SafeJsonElement(JsonElement jsonElement) {
|
||||||
|
mJsonElement = jsonElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement deepCopy() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isJsonArray() {
|
||||||
|
return mJsonElement.isJsonArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isJsonObject() {
|
||||||
|
return mJsonElement.isJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isJsonPrimitive() {
|
||||||
|
return mJsonElement.isJsonPrimitive();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isJsonNull() {
|
||||||
|
return mJsonElement.isJsonNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObject getAsJsonObject() {
|
||||||
|
return mJsonElement.getAsJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SafeJsonObject getAsSafeJsonObject() {
|
||||||
|
try {
|
||||||
|
return new SafeJsonObject(mJsonElement.getAsJsonObject());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonArray getAsJsonArray() {
|
||||||
|
return mJsonElement.getAsJsonArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonPrimitive getAsJsonPrimitive() {
|
||||||
|
return mJsonElement.getAsJsonPrimitive();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonNull getAsJsonNull() {
|
||||||
|
return mJsonElement.getAsJsonNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getAsBoolean() {
|
||||||
|
return mJsonElement.getAsBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Number getAsNumber() {
|
||||||
|
return mJsonElement.getAsNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAsString() {
|
||||||
|
return mJsonElement.getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getAsDouble() {
|
||||||
|
return mJsonElement.getAsDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getAsFloat() {
|
||||||
|
return mJsonElement.getAsFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getAsLong() {
|
||||||
|
return mJsonElement.getAsLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAsInt() {
|
||||||
|
return mJsonElement.getAsInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte getAsByte() {
|
||||||
|
return mJsonElement.getAsByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char getAsCharacter() {
|
||||||
|
return mJsonElement.getAsCharacter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigDecimal getAsBigDecimal() {
|
||||||
|
return mJsonElement.getAsBigDecimal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger getAsBigInteger() {
|
||||||
|
return mJsonElement.getAsBigInteger();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short getAsShort() {
|
||||||
|
return mJsonElement.getAsShort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return mJsonElement.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
161
app/src/main/java/org/autojs/autojs/tool/SafeJsonObject.java
Normal file
161
app/src/main/java/org/autojs/autojs/tool/SafeJsonObject.java
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
package org.autojs.autojs.tool;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonNull;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class SafeJsonObject extends JsonElement {
|
||||||
|
|
||||||
|
private final JsonObject mJsonObject;
|
||||||
|
|
||||||
|
public SafeJsonObject(JsonObject jsonObject) {
|
||||||
|
mJsonObject = jsonObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObject deepCopy() {
|
||||||
|
return mJsonObject.deepCopy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(String property, JsonElement value) {
|
||||||
|
mJsonObject.add(property, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonElement remove(String property) {
|
||||||
|
return mJsonObject.remove(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addProperty(String property, String value) {
|
||||||
|
mJsonObject.addProperty(property, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addProperty(String property, Number value) {
|
||||||
|
mJsonObject.addProperty(property, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addProperty(String property, Boolean value) {
|
||||||
|
mJsonObject.addProperty(property, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addProperty(String property, Character value) {
|
||||||
|
mJsonObject.addProperty(property, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Map.Entry<String, JsonElement>> entrySet() {
|
||||||
|
return mJsonObject.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> keySet() {
|
||||||
|
return mJsonObject.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return mJsonObject.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean has(String memberName) {
|
||||||
|
return mJsonObject.has(memberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonElement get(String memberName) {
|
||||||
|
return mJsonObject.get(memberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonPrimitive getAsJsonPrimitive(String memberName) {
|
||||||
|
return mJsonObject.getAsJsonPrimitive(memberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonArray getAsJsonArray(String memberName) {
|
||||||
|
return mJsonObject.getAsJsonArray(memberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObject getAsJsonObject(String memberName) {
|
||||||
|
return mJsonObject.getAsJsonObject(memberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isJsonArray() {
|
||||||
|
return mJsonObject.isJsonArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isJsonObject() {
|
||||||
|
return mJsonObject.isJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isJsonPrimitive() {
|
||||||
|
return mJsonObject.isJsonPrimitive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isJsonNull() {
|
||||||
|
return mJsonObject.isJsonNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObject getAsJsonObject() {
|
||||||
|
return mJsonObject.getAsJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonArray getAsJsonArray() {
|
||||||
|
return mJsonObject.getAsJsonArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonPrimitive getAsJsonPrimitive() {
|
||||||
|
return mJsonObject.getAsJsonPrimitive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonNull getAsJsonNull() {
|
||||||
|
return mJsonObject.getAsJsonNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getAsBoolean() {
|
||||||
|
return mJsonObject.getAsBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Number getAsNumber() {
|
||||||
|
return mJsonObject.getAsNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAsString() {
|
||||||
|
return mJsonObject.getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAsDouble() {
|
||||||
|
return mJsonObject.getAsDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getAsFloat() {
|
||||||
|
return mJsonObject.getAsFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAsLong() {
|
||||||
|
return mJsonObject.getAsLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAsInt() {
|
||||||
|
return mJsonObject.getAsInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getAsByte() {
|
||||||
|
return mJsonObject.getAsByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
public char getAsCharacter() {
|
||||||
|
return mJsonObject.getAsCharacter();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getAsBigDecimal() {
|
||||||
|
return mJsonObject.getAsBigDecimal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigInteger getAsBigInteger() {
|
||||||
|
return mJsonObject.getAsBigInteger();
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getAsShort() {
|
||||||
|
return mJsonObject.getAsShort();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -145,6 +145,7 @@ public class CircularMenu implements Recorder.OnStateChangedListener, LayoutInsp
|
|||||||
explorerView.setOnItemOperatedListener(file -> dialog.dismiss());
|
explorerView.setOnItemOperatedListener(file -> dialog.dismiss());
|
||||||
explorerView.setOnItemClickListener((view, item) -> Scripts.run(item.toScriptFile()));
|
explorerView.setOnItemClickListener((view, item) -> Scripts.run(item.toScriptFile()));
|
||||||
DialogUtils.showDialog(dialog);
|
DialogUtils.showDialog(dialog);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Optional
|
@Optional
|
||||||
|
|||||||
@@ -424,4 +424,6 @@
|
|||||||
<string name="error_pattern_syntax">正则表达式错误</string>
|
<string name="error_pattern_syntax">正则表达式错误</string>
|
||||||
<string name="text_invalid_package_name">非法包名</string>
|
<string name="text_invalid_package_name">非法包名</string>
|
||||||
<string name="error_cannot_rename">重命名失败</string>
|
<string name="error_cannot_rename">重命名失败</string>
|
||||||
|
<string name="text_project_save_success">项目已经保存到%s</string>
|
||||||
|
<string name="text_project_save_error">项目保存失败 %s</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -119,11 +119,7 @@ public class AndroidClassLoader extends ClassLoader implements GeneratedClassLoa
|
|||||||
|
|
||||||
private String generateDexFileName(File jar) {
|
private String generateDexFileName(File jar) {
|
||||||
String message = jar.getPath() + "_" + jar.lastModified();
|
String message = jar.getPath() + "_" + jar.lastModified();
|
||||||
try {
|
return MD5.md5(message);
|
||||||
return MD5.md5(message);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DexClassLoader loadDex(File file) throws FileNotFoundException {
|
public DexClassLoader loadDex(File file) throws FileNotFoundException {
|
||||||
|
|||||||
@@ -69,6 +69,19 @@ public class GlobalAppContext {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void toast(final int resId, final Object... args) {
|
||||||
|
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||||
|
Toast.makeText(get(), getString(resId, args), Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(get(), getString(resId, args), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static void post(Runnable r) {
|
public static void post(Runnable r) {
|
||||||
sHandler.post(r);
|
sHandler.post(r);
|
||||||
}
|
}
|
||||||
|
|||||||
49
common/src/main/java/com/stardust/io/Zip.java
Normal file
49
common/src/main/java/com/stardust/io/Zip.java
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package com.stardust.io;
|
||||||
|
|
||||||
|
import com.stardust.pio.PFile;
|
||||||
|
import com.stardust.pio.PFiles;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
|
import static com.stardust.pio.PFiles.closeSilently;
|
||||||
|
|
||||||
|
public class Zip {
|
||||||
|
|
||||||
|
public static void unzip(InputStream stream, File dir) throws IOException {
|
||||||
|
FileOutputStream fos = null;
|
||||||
|
ZipInputStream zis = null;
|
||||||
|
try {
|
||||||
|
zis = new ZipInputStream(stream);
|
||||||
|
ZipEntry entry;
|
||||||
|
while ((entry = zis.getNextEntry()) != null) {
|
||||||
|
File file = new File(dir, entry.getName());
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
file.mkdirs();
|
||||||
|
} else {
|
||||||
|
PFiles.ensureDir(file.getPath());
|
||||||
|
fos = new FileOutputStream(file);
|
||||||
|
PFiles.write(zis, fos, false);
|
||||||
|
fos.close();
|
||||||
|
fos = null;
|
||||||
|
zis.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
closeSilently(fos);
|
||||||
|
closeSilently(stream);
|
||||||
|
closeSilently(zis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unzip(File zipFile, File dir) throws IOException {
|
||||||
|
unzip(new FileInputStream(zipFile), dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import android.util.Log;
|
|||||||
|
|
||||||
import com.stardust.util.Func1;
|
import com.stardust.util.Func1;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
@@ -174,20 +175,28 @@ public class PFiles {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void write(InputStream is, OutputStream os) {
|
public static void write(InputStream is, OutputStream os, boolean close) {
|
||||||
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
||||||
try {
|
try {
|
||||||
while (is.available() > 0) {
|
while (is.available() > 0) {
|
||||||
int n = is.read(buffer);
|
int n = is.read(buffer);
|
||||||
os.write(buffer, 0, n);
|
if (n > 0) {
|
||||||
|
os.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (close) {
|
||||||
|
is.close();
|
||||||
|
os.close();
|
||||||
}
|
}
|
||||||
is.close();
|
|
||||||
os.close();
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void write(InputStream is, OutputStream os) {
|
||||||
|
write(is, os, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void write(String path, String text) {
|
public static void write(String path, String text) {
|
||||||
write(new File(path), text);
|
write(new File(path), text);
|
||||||
@@ -296,7 +305,7 @@ public class PFiles {
|
|||||||
if (list == null)
|
if (list == null)
|
||||||
throw new IOException("not a directory: " + assetsDir);
|
throw new IOException("not a directory: " + assetsDir);
|
||||||
for (String file : list) {
|
for (String file : list) {
|
||||||
if(TextUtils.isEmpty(file)){
|
if (TextUtils.isEmpty(file)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String fullAssetsPath = join(assetsDir, file);
|
String fullAssetsPath = join(assetsDir, file);
|
||||||
@@ -500,4 +509,15 @@ public class PFiles {
|
|||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void closeSilently(Closeable closeable) {
|
||||||
|
if (closeable == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
closeable.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,17 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
|
|
||||||
public class MD5 {
|
public class MD5 {
|
||||||
|
|
||||||
public static byte[] md5Bytes(String message) throws NoSuchAlgorithmException {
|
public static byte[] md5Bytes(String message) {
|
||||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
try {
|
||||||
md5.update(message.getBytes());
|
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||||
return md5.digest();
|
md5.update(message.getBytes());
|
||||||
|
return md5.digest();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String md5(String message) throws NoSuchAlgorithmException {
|
public static String md5(String message) {
|
||||||
byte[] bytes = md5Bytes(message);
|
byte[] bytes = md5Bytes(message);
|
||||||
StringBuilder hexString = new StringBuilder(32);
|
StringBuilder hexString = new StringBuilder(32);
|
||||||
for (byte b : bytes) {
|
for (byte b : bytes) {
|
||||||
@@ -22,5 +26,7 @@ public class MD5 {
|
|||||||
hexString.append(hex);
|
hexString.append(hex);
|
||||||
}
|
}
|
||||||
return hexString.toString();
|
return hexString.toString();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"appVersionCode": 433,
|
"appVersionCode": 434,
|
||||||
"appVersionName": "4.0.4 Alpha4",
|
"appVersionName": "4.0.4 Alpha5",
|
||||||
"target": 28,
|
"target": 28,
|
||||||
"mini": 17,
|
"mini": 17,
|
||||||
"compile": 28,
|
"compile": 28,
|
||||||
|
|||||||
Reference in New Issue
Block a user