From 78e0d6b08876e30f48bd32f0653afb5b1229cc14 Mon Sep 17 00:00:00 2001
From: hyb1996 <946994919@qq.com>
Date: Fri, 19 Oct 2018 15:27:09 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E8=BF=9E=E6=8E=A5?=
=?UTF-8?q?=E7=94=B5=E8=84=91=E6=94=AF=E6=8C=81=E8=BF=90=E8=A1=8C=E5=92=8C?=
=?UTF-8?q?=E4=BF=9D=E5=AD=98=E9=A1=B9=E7=9B=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/src/main/AndroidManifest.xml | 4 +
.../autojs/autojs/model/script/Scripts.java | 1 +
.../DevPluginResponseHandler.java | 104 ++++++++++-
.../autojs/pluginclient/DevPluginService.java | 90 ++++++++--
.../autojs/pluginclient/JsonWebSocket.java | 26 ++-
.../autojs/autojs/tool/SafeJsonElement.java | 136 +++++++++++++++
.../autojs/autojs/tool/SafeJsonObject.java | 161 ++++++++++++++++++
.../autojs/ui/floating/CircularMenu.java | 1 +
app/src/main/res/values/strings.xml | 2 +
.../autojs/rhino/AndroidClassLoader.java | 6 +-
.../com/stardust/app/GlobalAppContext.java | 13 ++
common/src/main/java/com/stardust/io/Zip.java | 49 ++++++
.../main/java/com/stardust/pio/PFiles.java | 30 +++-
.../src/main/java/com/stardust/util/MD5.java | 16 +-
project-versions.json | 4 +-
15 files changed, 605 insertions(+), 38 deletions(-)
create mode 100644 app/src/main/java/org/autojs/autojs/tool/SafeJsonElement.java
create mode 100644 app/src/main/java/org/autojs/autojs/tool/SafeJsonObject.java
create mode 100644 common/src/main/java/com/stardust/io/Zip.java
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8b24cc0c..d861c3fe 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -115,6 +115,10 @@
+
+
+
+
{
String id = data.get("id").getAsString();
@@ -43,7 +56,7 @@ public class DevPluginResponseHandler implements Handler {
String script = data.get("script").getAsString();
String name = getName(data);
saveScript(name, script);
- return false;
+ return true;
})
.handler("rerun", data -> {
String id = data.get("id").getAsString();
@@ -51,21 +64,54 @@ public class DevPluginResponseHandler implements Handler {
String name = getName(data);
stopScript(id);
runScript(id, name, script);
- return false;
+ return true;
})
.handler("stopAll", data -> {
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 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
public boolean handle(JsonObject data) {
return mRouter.handle(data);
}
+ public Observable 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) {
if (TextUtils.isEmpty(name)) {
name = "[" + viewId + "]";
@@ -75,6 +121,18 @@ public class DevPluginResponseHandler implements Handler {
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) {
ScriptExecution execution = mScriptExecutions.get(viewId);
if (execution != null) {
@@ -104,4 +162,42 @@ public class DevPluginResponseHandler implements Handler {
PFiles.write(file, script);
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);
+ }
+ }
+ }
+
}
diff --git a/app/src/main/java/org/autojs/autojs/pluginclient/DevPluginService.java b/app/src/main/java/org/autojs/autojs/pluginclient/DevPluginService.java
index 51de8c25..381654f2 100644
--- a/app/src/main/java/org/autojs/autojs/pluginclient/DevPluginService.java
+++ b/app/src/main/java/org/autojs/autojs/pluginclient/DevPluginService.java
@@ -12,12 +12,15 @@ import android.util.Pair;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import com.stardust.app.GlobalAppContext;
import com.stardust.util.MapBuilder;
import org.autojs.autojs.BuildConfig;
-import java.io.IOException;
+import java.io.File;
import java.net.SocketTimeoutException;
+import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -36,6 +39,7 @@ public class DevPluginService {
private static final int CLIENT_VERSION = 2;
private static final String LOG_TAG = "DevPluginService";
private static final String TYPE_HELLO = "hello";
+ private static final String TYPE_BYTES_COMMAND = "bytes_command";
private static final long HANDSHAKE_TIMEOUT = 10 * 1000;
public static class State {
@@ -68,15 +72,21 @@ public class DevPluginService {
private static final int PORT = 9317;
private static DevPluginService sInstance = new DevPluginService();
private final PublishSubject mConnectionState = PublishSubject.create();
- private final DevPluginResponseHandler mResponseHandler = new DevPluginResponseHandler();
- private final Handler mHandshakeTimeoutHandler = new Handler(Looper.getMainLooper());
-
+ private final DevPluginResponseHandler mResponseHandler;
+ private final HashMap mBytes = new HashMap<>();
+ private final HashMap mRequiredBytesCommands = new HashMap<>();
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
private volatile JsonWebSocket mSocket;
public static DevPluginService getInstance() {
return sInstance;
}
+ public DevPluginService() {
+ File cache = new File(GlobalAppContext.get().getCacheDir(), "remote_project");
+ mResponseHandler = new DevPluginResponseHandler(cache);
+ }
+
@AnyThread
public boolean isConnected() {
return mSocket != null && !mSocket.isClosed();
@@ -134,14 +144,22 @@ public class DevPluginService {
.build()))
.doOnNext(socket -> {
mSocket = socket;
- socket.data()
- .observeOn(AndroidSchedulers.mainThread())
- .doOnComplete(() -> mConnectionState.onNext(new State(State.DISCONNECTED)))
- .subscribe(data -> onSocketData(socket, data), this::onSocketError);
+ subscribeMessage(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
private void onSocketError(Throwable e) {
e.printStackTrace();
@@ -158,24 +176,64 @@ public class DevPluginService {
Log.w(LOG_TAG, "onSocketData: not json object: " + element);
return;
}
- JsonObject obj = element.getAsJsonObject();
- JsonElement type = obj.get("type");
- if (type != null && type.isJsonPrimitive() && type.getAsString().equals(TYPE_HELLO)) {
- onServerHello(jsonWebSocket, obj);
- return;
+ try {
+ JsonObject obj = element.getAsJsonObject();
+ JsonElement typeElement = obj.get("type");
+ if (typeElement == null || !typeElement.isJsonPrimitive()) {
+ 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
- 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()
.put("device_name", Build.BRAND + " " + Build.MODEL)
.put("client_version", CLIENT_VERSION)
.put("app_version", BuildConfig.VERSION_NAME)
.put("app_version_code", BuildConfig.VERSION_CODE)
.build());
- mHandshakeTimeoutHandler.postDelayed(() -> {
+ mHandler.postDelayed(() -> {
if (mSocket != socket && !socket.isClosed()) {
onHandshakeTimeout(socket);
}
diff --git a/app/src/main/java/org/autojs/autojs/pluginclient/JsonWebSocket.java b/app/src/main/java/org/autojs/autojs/pluginclient/JsonWebSocket.java
index 641adf46..f81301da 100644
--- a/app/src/main/java/org/autojs/autojs/pluginclient/JsonWebSocket.java
+++ b/app/src/main/java/org/autojs/autojs/pluginclient/JsonWebSocket.java
@@ -19,14 +19,28 @@ import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
+import okio.ByteString;
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 final WebSocket mWebSocket;
private final JsonParser mJsonParser = new JsonParser();
private final PublishSubject mJsonElementPublishSubject = PublishSubject.create();
+ private final PublishSubject mBytesPublishSubject = PublishSubject.create();
private volatile boolean mClosed = false;
public JsonWebSocket(OkHttpClient client, Request request) {
@@ -35,14 +49,24 @@ public class JsonWebSocket extends WebSocketListener {
@Override
public void onMessage(WebSocket webSocket, String text) {
- Log.d(LOG_TAG, "onMessage: " + text);
+ Log.d(LOG_TAG, "onMessage: text = " + 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 data() {
return mJsonElementPublishSubject;
}
+ public Observable bytes(){
+ return mBytesPublishSubject;
+ }
+
public boolean write(JsonElement element) {
String json = element.toString();
Log.d(LOG_TAG, "write: length = " + json.length() + ", json = " + element);
diff --git a/app/src/main/java/org/autojs/autojs/tool/SafeJsonElement.java b/app/src/main/java/org/autojs/autojs/tool/SafeJsonElement.java
new file mode 100644
index 00000000..2206aabf
--- /dev/null
+++ b/app/src/main/java/org/autojs/autojs/tool/SafeJsonElement.java
@@ -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();
+ }
+}
diff --git a/app/src/main/java/org/autojs/autojs/tool/SafeJsonObject.java b/app/src/main/java/org/autojs/autojs/tool/SafeJsonObject.java
new file mode 100644
index 00000000..90b55f5c
--- /dev/null
+++ b/app/src/main/java/org/autojs/autojs/tool/SafeJsonObject.java
@@ -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> entrySet() {
+ return mJsonObject.entrySet();
+ }
+
+ public Set 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();
+ }
+}
diff --git a/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenu.java b/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenu.java
index 4fadb0a2..c9109f79 100644
--- a/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenu.java
+++ b/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenu.java
@@ -145,6 +145,7 @@ public class CircularMenu implements Recorder.OnStateChangedListener, LayoutInsp
explorerView.setOnItemOperatedListener(file -> dialog.dismiss());
explorerView.setOnItemClickListener((view, item) -> Scripts.run(item.toScriptFile()));
DialogUtils.showDialog(dialog);
+
}
@Optional
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 504dead0..569bef37 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -424,4 +424,6 @@
正则表达式错误
非法包名
重命名失败
+ 项目已经保存到%s
+ 项目保存失败 %s
diff --git a/autojs/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java b/autojs/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java
index 0548a4bb..f1124e63 100644
--- a/autojs/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java
+++ b/autojs/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java
@@ -119,11 +119,7 @@ public class AndroidClassLoader extends ClassLoader implements GeneratedClassLoa
private String generateDexFileName(File jar) {
String message = jar.getPath() + "_" + jar.lastModified();
- try {
- return MD5.md5(message);
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
+ return MD5.md5(message);
}
public DexClassLoader loadDex(File file) throws FileNotFoundException {
diff --git a/common/src/main/java/com/stardust/app/GlobalAppContext.java b/common/src/main/java/com/stardust/app/GlobalAppContext.java
index 6915d4a2..3e99d8e3 100644
--- a/common/src/main/java/com/stardust/app/GlobalAppContext.java
+++ b/common/src/main/java/com/stardust/app/GlobalAppContext.java
@@ -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) {
sHandler.post(r);
}
diff --git a/common/src/main/java/com/stardust/io/Zip.java b/common/src/main/java/com/stardust/io/Zip.java
new file mode 100644
index 00000000..9e006709
--- /dev/null
+++ b/common/src/main/java/com/stardust/io/Zip.java
@@ -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);
+ }
+
+
+}
diff --git a/common/src/main/java/com/stardust/pio/PFiles.java b/common/src/main/java/com/stardust/pio/PFiles.java
index d61825c6..7ec89600 100644
--- a/common/src/main/java/com/stardust/pio/PFiles.java
+++ b/common/src/main/java/com/stardust/pio/PFiles.java
@@ -9,6 +9,7 @@ import android.util.Log;
import com.stardust.util.Func1;
+import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
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];
try {
while (is.available() > 0) {
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) {
throw new UncheckedIOException(e);
}
}
+ public static void write(InputStream is, OutputStream os) {
+ write(is, os, true);
+ }
+
public static void write(String path, String text) {
write(new File(path), text);
@@ -296,7 +305,7 @@ public class PFiles {
if (list == null)
throw new IOException("not a directory: " + assetsDir);
for (String file : list) {
- if(TextUtils.isEmpty(file)){
+ if (TextUtils.isEmpty(file)) {
continue;
}
String fullAssetsPath = join(assetsDir, file);
@@ -500,4 +509,15 @@ public class PFiles {
throw new UncheckedIOException(e);
}
}
+
+ public static void closeSilently(Closeable closeable) {
+ if (closeable == null) {
+ return;
+ }
+ try {
+ closeable.close();
+ } catch (IOException ignored) {
+
+ }
+ }
}
diff --git a/common/src/main/java/com/stardust/util/MD5.java b/common/src/main/java/com/stardust/util/MD5.java
index 824a37b7..aaa6f69c 100644
--- a/common/src/main/java/com/stardust/util/MD5.java
+++ b/common/src/main/java/com/stardust/util/MD5.java
@@ -5,13 +5,17 @@ import java.security.NoSuchAlgorithmException;
public class MD5 {
- public static byte[] md5Bytes(String message) throws NoSuchAlgorithmException {
- MessageDigest md5 = MessageDigest.getInstance("MD5");
- md5.update(message.getBytes());
- return md5.digest();
+ public static byte[] md5Bytes(String message) {
+ try {
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+ 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);
StringBuilder hexString = new StringBuilder(32);
for (byte b : bytes) {
@@ -22,5 +26,7 @@ public class MD5 {
hexString.append(hex);
}
return hexString.toString();
+
+
}
}
diff --git a/project-versions.json b/project-versions.json
index 05d5fa82..05034643 100644
--- a/project-versions.json
+++ b/project-versions.json
@@ -1,6 +1,6 @@
{
- "appVersionCode": 433,
- "appVersionName": "4.0.4 Alpha4",
+ "appVersionCode": 434,
+ "appVersionName": "4.0.4 Alpha5",
"target": 28,
"mini": 17,
"compile": 28,