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,