From 6b3849d55aa4d60334506235b592b93a1266214e Mon Sep 17 00:00:00 2001 From: Gust Date: Mon, 9 Sep 2024 23:46:56 +0800 Subject: [PATCH 01/18] fix miniaudio jni, fix gui clear --- desktop/glfw_gui/c/jni_minial.c | 6 +++--- extlib/xgui/src/main/java/org/mini/gui/GContainer.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/desktop/glfw_gui/c/jni_minial.c b/desktop/glfw_gui/c/jni_minial.c index 7a006671..e1edfc40 100755 --- a/desktop/glfw_gui/c/jni_minial.c +++ b/desktop/glfw_gui/c/jni_minial.c @@ -325,7 +325,7 @@ void setupCallback(Runtime *runtime, JClass *clazz) { c8 *type_s; c8 *clsname_s; if (!refers._callback_minial_on_recv_frames) { - clsname_s = "org/mini/media/MADevice"; + clsname_s = "org/mini/media/MaDevice"; name_s = "onReceiveFrames"; type_s = "(JIJ)V"; Utf8String *clsname = env->utf8_create_part_c(clsname_s, 0, strlen(clsname_s)); @@ -337,7 +337,7 @@ void setupCallback(Runtime *runtime, JClass *clazz) { env->utf8_destory(type); } if (!refers._callback_minial_on_send_frames) { - clsname_s = "org/mini/media/MADevice"; + clsname_s = "org/mini/media/MaDevice"; name_s = "onSendFrames"; type_s = "(JIJ)I"; Utf8String *clsname = env->utf8_create_part_c(clsname_s, 0, strlen(clsname_s)); @@ -349,7 +349,7 @@ void setupCallback(Runtime *runtime, JClass *clazz) { env->utf8_destory(type); } if (!refers._callback_minial_on_stop) { - clsname_s = "org/mini/media/MADevice"; + clsname_s = "org/mini/media/MaDevice"; name_s = "onStop"; type_s = "(J)V"; Utf8String *clsname = env->utf8_create_part_c(clsname_s, 0, strlen(clsname_s)); diff --git a/extlib/xgui/src/main/java/org/mini/gui/GContainer.java b/extlib/xgui/src/main/java/org/mini/gui/GContainer.java index 58dcdfc3..d61fdcc5 100755 --- a/extlib/xgui/src/main/java/org/mini/gui/GContainer.java +++ b/extlib/xgui/src/main/java/org/mini/gui/GContainer.java @@ -238,7 +238,7 @@ void clearImpl() { synchronized (elements) { int size = elements.size(); for (int i = 0; i < size; i++) { - removeImpl(elements.size() - 1); + removeImpl(0); } } } From 54a9e15098640636fa79e85f2d2bb2ad1d2d7e7f Mon Sep 17 00:00:00 2001 From: Gust Date: Thu, 19 Sep 2024 23:24:33 +0800 Subject: [PATCH 02/18] add ios iap api in glfm --- desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java | 4 ++++ mobile/java/glfm_gui/src/main/java/org/mini/glfm/Glfm.java | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java b/desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java index 493a2b1f..dd80c273 100644 --- a/desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java +++ b/desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java @@ -305,4 +305,8 @@ public static String glfmRemoteMethodCall(String inJsonStr) { return null; } + + public static void glfmBuyAppleProductById(long display, String productId) { + + } } diff --git a/mobile/java/glfm_gui/src/main/java/org/mini/glfm/Glfm.java b/mobile/java/glfm_gui/src/main/java/org/mini/glfm/Glfm.java index 6ede0e93..f6df87d8 100644 --- a/mobile/java/glfm_gui/src/main/java/org/mini/glfm/Glfm.java +++ b/mobile/java/glfm_gui/src/main/java/org/mini/glfm/Glfm.java @@ -266,5 +266,10 @@ public static String glfmRemoteMethodCall(String inJsonStr) { return glfmRemoteMethodCall(inJsonStrBytes); } + static native String glfmBuyAppleProductById(long display, byte[] cproductId); + public static void glfmBuyAppleProductById(long display, String productId) { + byte[] productIdBytes = toCstyleBytes(productId); + glfmBuyAppleProductById(display, productIdBytes); + } } From 2013bf500e5e8178c9976a9eda4f2d908800535a Mon Sep 17 00:00:00 2001 From: Gust Date: Thu, 19 Sep 2024 23:33:12 +0800 Subject: [PATCH 03/18] Update GNotifyListener.java --- .../xgui/src/main/java/org/mini/gui/event/GNotifyListener.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extlib/xgui/src/main/java/org/mini/gui/event/GNotifyListener.java b/extlib/xgui/src/main/java/org/mini/gui/event/GNotifyListener.java index ba511925..c270c544 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/event/GNotifyListener.java +++ b/extlib/xgui/src/main/java/org/mini/gui/event/GNotifyListener.java @@ -6,10 +6,11 @@ package org.mini.gui.event; /** - * * @author Gust */ public interface GNotifyListener { + public static final String NOTIFY_KEY_DEVICE_TOKEN = "glfm.device.token"; + public static final String NOTIFY_KEY_IOS_PURCHASE = "glfm.ios.purchase"; void onNotify(String key, String val); } From 312054f67f7cda0d714ec6d02c9060c15939f89b Mon Sep 17 00:00:00 2001 From: Gust Date: Sat, 21 Sep 2024 08:27:04 +0800 Subject: [PATCH 04/18] update notifylistener --- .../main/java/org/mini/gui/event/GNotifyListener.java | 10 ++++++++++ .../glfm_gui/src/main/java/org/mini/glfm/Glfm.java | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/extlib/xgui/src/main/java/org/mini/gui/event/GNotifyListener.java b/extlib/xgui/src/main/java/org/mini/gui/event/GNotifyListener.java index c270c544..c6f6dd51 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/event/GNotifyListener.java +++ b/extlib/xgui/src/main/java/org/mini/gui/event/GNotifyListener.java @@ -10,7 +10,17 @@ */ public interface GNotifyListener { public static final String NOTIFY_KEY_DEVICE_TOKEN = "glfm.device.token"; + /** + * ios IAP + * val FORMAT: code:receipt 例子: 0:9E32... + */ public static final String NOTIFY_KEY_IOS_PURCHASE = "glfm.ios.purchase"; + public static final int IAPPurchSuccess = 0, // 购买成功 + IAPPurchFailed = 1, // 购买失败 + IAPPurchCancel = 2, // 取消购买 + IAPPurchVerFailed = 3, // 订单校验失败 + IAPPurchVerSuccess = 4, // 订单校验成功 + IAPPurchNotArrow = 5; // 不允许内购 void onNotify(String key, String val); } diff --git a/mobile/java/glfm_gui/src/main/java/org/mini/glfm/Glfm.java b/mobile/java/glfm_gui/src/main/java/org/mini/glfm/Glfm.java index f6df87d8..6b6b99d3 100644 --- a/mobile/java/glfm_gui/src/main/java/org/mini/glfm/Glfm.java +++ b/mobile/java/glfm_gui/src/main/java/org/mini/glfm/Glfm.java @@ -266,7 +266,7 @@ public static String glfmRemoteMethodCall(String inJsonStr) { return glfmRemoteMethodCall(inJsonStrBytes); } - static native String glfmBuyAppleProductById(long display, byte[] cproductId); + static native void glfmBuyAppleProductById(long display, byte[] cproductId); public static void glfmBuyAppleProductById(long display, String productId) { byte[] productIdBytes = toCstyleBytes(productId); From 4e940c31dc516f590f9c38d501624d48461d4ab7 Mon Sep 17 00:00:00 2001 From: Gust Date: Mon, 23 Sep 2024 02:15:36 +0800 Subject: [PATCH 05/18] add apple iap charge --- desktop/glfw_gui/java/pom.xml | 4 +- extlib/xgui/pom.xml | 11 +- .../java/org/mini/apploader/AppManager.java | 67 ++++-- .../java/org/mini/glfm/GlfmCallBackImpl.java | 7 +- .../org/mini/gui/gscript/Interpreter.java | 6 +- .../java/org/mini/gui/gscript/Stdlib.java | 13 ++ .../main/java/org/mini/gui/gscript/Str.java | 4 +- .../org/mini/gui/guilib/GuiScriptLib.java | 49 ++++- extlib/xgui/src/main/resource/res/lang.json | 36 +-- .../src/main/resource/res/ui/AppManager.xml | 11 +- minijvm/java/pom.xml | 2 +- .../java/org/mini/http/MiniHttpClient.java | 36 +++ mobile/c/glfm/IAPManager.h | 36 +++ mobile/c/glfm/IAPManager.m | 207 ++++++++++++++++++ mobile/c/glfm/glfm.h | 2 + mobile/c/glfm/glfm_apple.m | 56 +++++ mobile/c/gui/jni_glfm.c | 18 ++ .../iosapp/iosapp.xcodeproj/project.pbxproj | 10 +- .../UserInterfaceState.xcuserstate | Bin 14344 -> 54676 bytes .../xcschemes/iosapp.xcscheme | 3 +- mobile/java/glfm_gui/pom.xml | 4 +- .../src/main/java/org/mini/glfm/Glfm.java | 23 +- 22 files changed, 538 insertions(+), 67 deletions(-) create mode 100644 mobile/c/glfm/IAPManager.h create mode 100644 mobile/c/glfm/IAPManager.m diff --git a/desktop/glfw_gui/java/pom.xml b/desktop/glfw_gui/java/pom.xml index 1ba50747..dc136d2d 100755 --- a/desktop/glfw_gui/java/pom.xml +++ b/desktop/glfw_gui/java/pom.xml @@ -8,7 +8,7 @@ io.github.digitalgust glfw_gui ${project.groupId}:${project.artifactId} - 1.1.7 + 1.1.9 miniJVM desktop platform gui library https://github.com/digitalgust/miniJVM @@ -160,7 +160,7 @@ io.github.digitalgust minijvm_rt - 1.1.7 + 1.1.9 diff --git a/extlib/xgui/pom.xml b/extlib/xgui/pom.xml index 74eec5d7..1f56f17e 100755 --- a/extlib/xgui/pom.xml +++ b/extlib/xgui/pom.xml @@ -9,7 +9,7 @@ io.github.digitalgust xgui ${project.groupId}:${project.artifactId} - 1.1.14 + 1.1.15 miniJVM mobile platform gui library https://github.com/digitalgust/miniJVM @@ -118,7 +118,6 @@ true central - true 7200 https://central.sonatype.com @@ -167,18 +166,20 @@ - + + + io.github.digitalgust glfw_gui - 1.1.7 + 1.1.9 io.github.digitalgust glfm_gui - 1.1.7 + 1.1.9 diff --git a/extlib/xgui/src/main/java/org/mini/apploader/AppManager.java b/extlib/xgui/src/main/java/org/mini/apploader/AppManager.java index 0272a5b4..f3a1176a 100644 --- a/extlib/xgui/src/main/java/org/mini/apploader/AppManager.java +++ b/extlib/xgui/src/main/java/org/mini/apploader/AppManager.java @@ -6,7 +6,10 @@ package org.mini.apploader; import com.ebsee.rmc.RMCUtil; +import org.mini.gui.event.GNotifyListener; import org.mini.gui.gscript.Interpreter; +import org.mini.gui.gscript.Str; +import org.mini.gui.guilib.GuiScriptLib; import org.mini.http.MiniHttpClient; import org.mini.http.MiniHttpServer; import org.mini.layout.xwebview.*; @@ -114,6 +117,10 @@ public class AppManager extends GApplication { static float devW, devH; + /** + * @return + */ + static public AppManager getInstance() { return instance; } @@ -202,22 +209,6 @@ void initExplorer() { } - void test() { - try { - System.out.println("Hello World!"); - String urlStr = "jar:http://localhost:30005/static/apps/ExApp.jar!/res/appmgr.png"; - URL url = new URL(urlStr); - URLConnection con = null; - - con = url.openConnection(); - - con.connect(); - System.out.println(con.getContentLength()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - void reloadAppList() { if (appList == null) { return; @@ -390,6 +381,11 @@ public static String getDateString(long millis) { } + /** + * ============================================================================== + * GForm that is used to show plugin manager + * ============================================================================== + */ class PluginMgrForm extends GForm implements XExplorerHolder { @@ -402,7 +398,7 @@ public void initForm() { final GCallBack ccb = GCallBack.getInstance(); devW = ccb.getDeviceWidth(); devH = ccb.getDeviceHeight(); - System.out.println("devW :" + devW + ", devH :" + devH); + //System.out.println("devW :" + devW + ", devH :" + devH); GForm.hideKeyboard(this); regStrings(); @@ -414,6 +410,39 @@ public void initForm() { GToolkit.setStyle(new GStyleDark()); } + setNotifyListener(new GNotifyListener() { + @Override + public void onNotify(String key, String val) { + try { + switch (key) { + case NOTIFY_KEY_DEVICE_TOKEN: + System.setProperty("device.token", val); + break; + case NOTIFY_KEY_IOS_PURCHASE: + if (val.indexOf(':') > 0) { + String[] ss = val.split(":"); + if (ss.length > 2) { + int code = Integer.parseInt(ss[0]); + String receipt = ss[1]; + byte[] scriptBytes = javax.microedition.io.Base64.decode(ss[2]); + String script = new String(scriptBytes, "utf-8"); + //System.out.println("script:" + script); + Interpreter inp = new Interpreter(); + inp.reglib(new GuiScriptLib(PluginMgrForm.this)); + inp.loadFromString(script); + inp.putGlobalVar("iap_code", Interpreter.getCachedInt(code)); + inp.putGlobalVar("iap_receipt", Interpreter.getCachedStr(receipt)); + inp.start(); + } + } + break; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + setPickListener((uid, url, data) -> { if (data == null && url != null) { File f = new File(url); @@ -474,7 +503,7 @@ GContainer getMainPanel(GForm form) { double[] inset = new double[4];//top,right,bottom,left Glfm.glfmGetDisplayChromeInsets(GCallBack.getInstance().getDisplay(), inset); int h = (int) (inset[0] / GCallBack.getInstance().getDeviceRatio()); - System.out.println("STATEBAR_HEIGHT " + inset[0] + " , " + inset[1] + " , " + inset[2] + " , " + inset[3]); + //System.out.println("STATEBAR_HEIGHT " + inset[0] + " , " + inset[1] + " , " + inset[2] + " , " + inset[3]); if (h <= 20) { h = 20; } @@ -502,7 +531,7 @@ GContainer getMainPanel(GForm form) { styleList.setSelectedIndex(1); } String url = AppLoader.getDownloadUrl(); - System.out.println("downloadurl=" + url); + //System.out.println("downloadurl=" + url); if (url != null) GToolkit.setCompText(mainSlot, "INPUT_URL", url); this.setSizeChangeListener((width, height) -> { diff --git a/extlib/xgui/src/main/java/org/mini/glfm/GlfmCallBackImpl.java b/extlib/xgui/src/main/java/org/mini/glfm/GlfmCallBackImpl.java index 8608c5f0..69612ac9 100644 --- a/extlib/xgui/src/main/java/org/mini/glfm/GlfmCallBackImpl.java +++ b/extlib/xgui/src/main/java/org/mini/glfm/GlfmCallBackImpl.java @@ -6,6 +6,7 @@ package org.mini.glfm; import org.mini.apploader.AppLoader; +import org.mini.apploader.AppManager; import org.mini.apploader.Sync; import org.mini.glfw.Glfw; import org.mini.gui.GCallBack; @@ -399,7 +400,11 @@ public void onNotify(long display, String key, String val) { if (gform == null) { return; } - gform.onDeviceNotify(key, val); + GForm mgrForm = AppManager.getInstance().getForm(); + mgrForm.onDeviceNotify(key, val);// notify to manager form first + if (mgrForm != gform) { + gform.onDeviceNotify(key, val); + } } @Override diff --git a/extlib/xgui/src/main/java/org/mini/gui/gscript/Interpreter.java b/extlib/xgui/src/main/java/org/mini/gui/gscript/Interpreter.java index 89479fe5..69a611ba 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/gscript/Interpreter.java +++ b/extlib/xgui/src/main/java/org/mini/gui/gscript/Interpreter.java @@ -75,6 +75,8 @@ public class Interpreter { * 构造方法 */ public Interpreter() { + //初始化 + init(); } /** @@ -147,8 +149,6 @@ public static void saveProp(String name, Properties prop) { */ public void loadFromFile(String path) { - //初始化 - init(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { @@ -228,8 +228,6 @@ public void loadFromFile(String path) { */ public void loadFromString(String code) { - init(); - int dquodation = 0; StringBuilder line = new StringBuilder(); ArrayList v = new ArrayList(); diff --git a/extlib/xgui/src/main/java/org/mini/gui/gscript/Stdlib.java b/extlib/xgui/src/main/java/org/mini/gui/gscript/Stdlib.java index de42d1bf..859eaae7 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/gscript/Stdlib.java +++ b/extlib/xgui/src/main/java/org/mini/gui/gscript/Stdlib.java @@ -2,6 +2,7 @@ import org.mini.crypt.XorCrypt; import org.mini.glfm.Glfm; +import org.mini.gui.GCallBack; import org.mini.reflect.ReflectMethod; import javax.microedition.io.Base64; @@ -66,6 +67,7 @@ public Stdlib(Interpreter inp) { methodNames.put("encrypt".toLowerCase(), this::encrypt);//加密 str= encrypt(str,key) methodNames.put("decrypt".toLowerCase(), this::decrypt);//解密 str= decrypt(str,key) methodNames.put("remoteMethodCall".toLowerCase(), this::remoteMethodCall);//远程调用 + methodNames.put("buyAppleProductById".toLowerCase(), this::buyAppleProductById);//远程调用 } @@ -684,4 +686,15 @@ private DataType remoteMethodCall(ArrayList para) { return null; } + private DataType buyAppleProductById(ArrayList para) { + try { + String str = Interpreter.popBackStr(para); + String scriptStr = Interpreter.popBackStr(para); + Glfm.glfmBuyAppleProductById(GCallBack.getInstance().getDisplay(), str, scriptStr); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + } diff --git a/extlib/xgui/src/main/java/org/mini/gui/gscript/Str.java b/extlib/xgui/src/main/java/org/mini/gui/gscript/Str.java index 6bde9ab7..efc0dce0 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/gscript/Str.java +++ b/extlib/xgui/src/main/java/org/mini/gui/gscript/Str.java @@ -11,13 +11,13 @@ public class Str extends DataType { Str(String s, boolean mutable) { type = DTYPE_STR; - value = s; + setVal(s); setRecyclable(mutable); } public void setVal(String s) { if (isRecyclable()) { - value = s; + value = s == null ? "" : s.intern(); } else { throw new RuntimeException("var is immutable"); } diff --git a/extlib/xgui/src/main/java/org/mini/gui/guilib/GuiScriptLib.java b/extlib/xgui/src/main/java/org/mini/gui/guilib/GuiScriptLib.java index 02860d73..73789a06 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/guilib/GuiScriptLib.java +++ b/extlib/xgui/src/main/java/org/mini/gui/guilib/GuiScriptLib.java @@ -84,6 +84,8 @@ public GuiScriptLib(GForm form) { methodNames.put("getVisible".toLowerCase(), this::getVisible);// methodNames.put("httpGet".toLowerCase(), this::httpGet);// methodNames.put("httpGetSync".toLowerCase(), this::httpGetSync);// + methodNames.put("httpPost".toLowerCase(), this::httpPost);// + methodNames.put("httpPostSync".toLowerCase(), this::httpPostSync);// } } @@ -736,9 +738,8 @@ private DataType getVisible(ArrayList para) { return Interpreter.getCachedBool(false); } - private DataType httpRequestImpl(ArrayList para, boolean async) { - String href = Interpreter.popBackStr(para); - String callback = Interpreter.popBackStr(para); + private DataType httpRequestImpl(String href, String postData, String callback, boolean async) { + if (href != null) { try { URL url = new URL(href); @@ -770,6 +771,10 @@ public void onCompleted(MiniHttpClient client, String url, byte[] data) { } } }); + if (postData != null) { + hc.setPostData(postData); + hc.setHeader("Content-Type", "application/x-www-form-urlencoded"); + } if (async) { hc.setProgressListener(new MiniHttpClient.ProgressListener() { @Override @@ -787,11 +792,45 @@ public void onProgress(MiniHttpClient client, int progress) { public DataType httpGetSync(ArrayList para) { - return httpRequestImpl(para, false); + if (para.size() < 1) { + System.out.println("call sub error: httpGetSync(url,callback)"); + return null; + } + String href = Interpreter.popBackStr(para); + String callback = para.isEmpty() ? null : Interpreter.popBackStr(para); + return httpRequestImpl(href, null, callback, false); } public DataType httpGet(ArrayList para) { - return httpRequestImpl(para, true); + if (para.size() < 1) { + System.out.println("call sub error: httpGet(url,callback)"); + return null; + } + String href = Interpreter.popBackStr(para); + String callback = para.isEmpty() ? null : Interpreter.popBackStr(para); + return httpRequestImpl(href, null, callback, true); + } + + public DataType httpPostSync(ArrayList para) { + if (para.size() < 2) { + System.out.println("call sub error: httpPostSync(url,postdata,callback)"); + return null; + } + String href = Interpreter.popBackStr(para); + String postData = Interpreter.popBackStr(para); + String callback = para.isEmpty() ? null : Interpreter.popBackStr(para); + return httpRequestImpl(href, postData, callback, false); + } + + public DataType httpPost(ArrayList para) { + if (para.size() < 2) { + System.out.println("call sub error: httpPost(url,postdata,callback)"); + return null; + } + String href = Interpreter.popBackStr(para); + String postData = Interpreter.popBackStr(para); + String callback = para.isEmpty() ? null : Interpreter.popBackStr(para); + return httpRequestImpl(href, postData, callback, true); } } diff --git a/extlib/xgui/src/main/resource/res/lang.json b/extlib/xgui/src/main/resource/res/lang.json index 8502a17e..b793e62d 100644 --- a/extlib/xgui/src/main/resource/res/lang.json +++ b/extlib/xgui/src/main/resource/res/lang.json @@ -33,7 +33,7 @@ "SETTING": [ "SETTING", "设置", - "设置" + "設定" ], "STR_BACK": [ "Back", @@ -143,7 +143,7 @@ "STR_FAIL": [ "Fail", "失败", - "失败" + "失敗" ], "PLUGIN": [ "PLUGIN", @@ -153,7 +153,7 @@ "BROWSE_FILE": [ "", "浏览", - "浏览" + "瀏覽" ], "STR_APP_NOT_RUNNING": [ "Plugin is not running ", @@ -238,17 +238,17 @@ "RETRIEVE_PWD": [ "Retrieve password", "找回密码", - "找回密码" + "找回密碼" ], "ACCOUNT_PWD_NO_EMPTY": [ "Account or password can not be empty", "账号或密码不能为空", - "账号或密码不能为空" + "帳號或密碼不能為空" ], "ACCOUNT_NO_EMPTY": [ "Account can not be empty", "账号不能为空", - "账号不能为空" + "帳號不能為空" ], "NOTICE_INPUT_EMAIL_MOBILE": [ "Input email or mobile", @@ -258,57 +258,57 @@ "SEND_CODE": [ "Send code", "发送验证码", - "发送验证码" + "發送驗證碼" ], "NOTICE_RCV_VERIFY_CODE": [ "After submitting your account, you will receive an email or SMS verification code.", "输入账号后,您将收到一封电子邮件或短信验证码。", - "输入账号后,您将收到一封电子邮件或短信验证码。" + "輸入帳號後,您將收到一封電子郵件或SMS驗證碼。" ], "CHANGE_PASSWORD": [ "Change password", "改变密码", - "改变密码" + "改變密碼" ], "EMAIL_OR_MOBILE": [ "email or mobile", "只可为邮箱或手机", - "只可为邮箱或手机" + "只可为電子郵件或手機" ], "OLD_PASSWORD": [ "Old password", "旧密码", - "旧密码" + "舊密碼" ], "NEW_PASSWORD": [ "New password", "新密码", - "新密码" + "新密碼" ], "PASSWORD_6_32": [ "password length 6-32", "密码长6-32字符", - "密码长6-32字符" + "密碼長6-32字符" ], "I_WANT_CHG_PWD": [ "I want to change my password", "我要改变我的密码", - "我要改变我的密码" + "我要改變我的密碼" ], "STR_SIGNIN_ALREADY": [ "Signed in", "已登录", - "已登录" + "已登入" ], "STR_NEED_SIGNIN": [ "Sign in", "去登录", - "去登录" + "去登入" ], "STR_MESSAGE": [ "Message", "消息", - "消息" + "訊息" ], "STR_SUCCESS": [ "Success", @@ -318,7 +318,7 @@ "STR_FAIL": [ "Fail", "失败", - "失败" + "失敗" ] } } \ No newline at end of file diff --git a/extlib/xgui/src/main/resource/res/ui/AppManager.xml b/extlib/xgui/src/main/resource/res/ui/AppManager.xml index 71623f11..35120310 100644 --- a/extlib/xgui/src/main/resource/res/ui/AppManager.xml +++ b/extlib/xgui/src/main/resource/res/ui/AppManager.xml @@ -19,8 +19,12 @@ ret sub getPolicy() '获取策略 + if(strlen(getEnv("DISCOVERY_URL"))<>0) + ret + eif + policyUrl=getEnv("POLICY_URL") - println("getPolicy"+policyUrl) + println("getPolicy:"+policyUrl) if(strlen(policyUrl)>0) url=policyUrl +"?"+ getQueryPara() httpGetSync(url, "ROOT_PAN.onPolicyRequestBack") @@ -41,6 +45,7 @@ sub signInAuto() '登录 + getPolicy() logintype=getEnv("LOGINTYPE") loginUid=getEnv("USERNAME") ep=getEnv("USERPASS") @@ -69,6 +74,7 @@ ret sub showDiscovery() '显示发现 + getPolicy() showSlot("SLOT_MGR", 3) if(!uiExist("DIS_HOME")) 'if there is not a discovery panel then show it showHome() @@ -76,11 +82,13 @@ ret sub showHome() + getPolicy() url=getEnv("DISCOVERY_URL") +"?"+ getQueryPara() openPage(url,"") ret sub showMy() '显示我的 + getPolicy() showSlot("SLOT_MGR", 4) token=getEnv("TOKEN") @@ -93,6 +101,7 @@ ret sub showProfile() '显示我的 + getPolicy() s1=getEnv("DISCOVERY_URL") s2=substr(s1,0,lastIdxOf(s1,"/")) s3=s2+"/profile.xml" +"?"+ getQueryPara() diff --git a/minijvm/java/pom.xml b/minijvm/java/pom.xml index c6d66f18..77c6b41b 100755 --- a/minijvm/java/pom.xml +++ b/minijvm/java/pom.xml @@ -8,7 +8,7 @@ io.github.digitalgust minijvm_rt ${project.groupId}:${project.artifactId} - 1.1.8 + 1.1.9 miniJVM runtime library https://github.com/digitalgust/miniJVM diff --git a/minijvm/java/src/main/java/org/mini/http/MiniHttpClient.java b/minijvm/java/src/main/java/org/mini/http/MiniHttpClient.java index 0f0a03ad..50e1c8ce 100644 --- a/minijvm/java/src/main/java/org/mini/http/MiniHttpClient.java +++ b/minijvm/java/src/main/java/org/mini/http/MiniHttpClient.java @@ -9,7 +9,10 @@ import javax.microedition.io.HttpConnection; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; /** * @author Gust @@ -19,6 +22,7 @@ public class MiniHttpClient extends Thread { String url; ByteArrayOutputStream baos; DownloadCompletedHandle handle; + Map outHeaders = new HashMap(); boolean exit; HttpConnection c = null; public static final CltLogger DEFAULT_LOGGER = new CltLogger() { @@ -37,6 +41,35 @@ public MiniHttpClient(final String url, CltLogger logger, final DownloadComplete if (logger != null) this.logger = logger; } + public void setPostData(ByteArrayOutputStream baos) { + this.baos = baos; + } + + public void setPostData(String str) { + if (str == null) return; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(str.getBytes("utf-8")); + this.baos = baos; + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void setHeader(String key, String value) { + outHeaders.put(key, value); + } + + void outputHeaders() { + try { + for (String key : outHeaders.keySet()) { + c.setRequestProperty(key, outHeaders.get(key)); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + abstract static public class CltLogger { public abstract void log(String s); } @@ -65,6 +98,7 @@ public void run() { logger.log("http url:" + url); updateProgress(5); c = (HttpConnection) Connector.open(url); + outputHeaders(); if (baos != null) { c.setRequestMethod(HttpConnection.POST); byte[] d = baos.toByteArray(); @@ -110,6 +144,8 @@ public void run() { String redirect = c.getHeaderField("Location"); logger.log("redirect:" + redirect); MiniHttpClient hc = new MiniHttpClient(redirect, logger, handle); + hc.setPostData(baos); + hc.outHeaders = outHeaders; hc.setProgressListener(getProgressListener()); hc.start(); } else { diff --git a/mobile/c/glfm/IAPManager.h b/mobile/c/glfm/IAPManager.h new file mode 100644 index 00000000..7d729e97 --- /dev/null +++ b/mobile/c/glfm/IAPManager.h @@ -0,0 +1,36 @@ +// +// IAPManager.h +// iosapp +// +// Created by Gust on 2024/9/8. +// Copyright © 2024 Gust. All rights reserved. +// + +#ifndef IAPManager_h +#define IAPManager_h + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef enum { + IAPPurchSuccess = 0, // 购买成功 + IAPPurchFailed = 1, // 购买失败 + IAPPurchCancel = 2, // 取消购买 + IAPPurchVerFailed = 3, // 订单校验失败 + IAPPurchVerSuccess = 4, // 订单校验成功 + IAPPurchNotArrow = 5, // 不允许内购 +}IAPPurchType; + +typedef void (^IAPCompletionHandle)(IAPPurchType type,NSData *data); + + + +@interface IAPManager : NSObject ++ (instancetype)shareIAPManager; +- (void)startPurchaseWithID:(NSString *)purchID completeHandle:(IAPCompletionHandle)handle; +@end + +NS_ASSUME_NONNULL_END + +#endif /* IAPManager_h */ diff --git a/mobile/c/glfm/IAPManager.m b/mobile/c/glfm/IAPManager.m new file mode 100644 index 00000000..59532bdd --- /dev/null +++ b/mobile/c/glfm/IAPManager.m @@ -0,0 +1,207 @@ +// +// IAPManager.m +// iosapp +// +// Created by Gust on 2024/9/8. +// Copyright © 2024 Gust. All rights reserved. +// + +#import + + +#import "IAPManager.h" +#import +#import + +@interface IAPManager(){ + NSString *_currentPurchasedID; + IAPCompletionHandle _iAPCompletionHandle; +} +@end + +@implementation IAPManager + ++ (instancetype)shareIAPManager{ + + static IAPManager *iAPManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken,^{ + iAPManager = [[IAPManager alloc] init]; + }); + return iAPManager; +} +- (instancetype)init{ + self = [super init]; + if (self) { + [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; + } + return self; +} + +- (void)dealloc{ + [[SKPaymentQueue defaultQueue] removeTransactionObserver:self]; +} + + +- (void)startPurchaseWithID:(NSString *)purchID completeHandle:(IAPCompletionHandle)handle{ + if (purchID) { + if ([SKPaymentQueue canMakePayments]) { + _currentPurchasedID = purchID; + _iAPCompletionHandle = handle; + + //从App Store中检索关于指定产品列表的本地化信息 + NSSet *nsset = [NSSet setWithArray:@[purchID]]; + SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset]; + request.delegate = self; + [request start]; + }else{ + [self handleActionWithType:IAPPurchNotArrow data:nil]; + } + } +} + +- (void)handleActionWithType:(IAPPurchType)type data:(NSData *)data{ +#if DEBUG + switch (type) { + case IAPPurchSuccess: + NSLog(@"购买成功"); + break; + case IAPPurchFailed: + NSLog(@"购买失败"); + break; + case IAPPurchCancel: + NSLog(@"用户取消购买"); + break; + case IAPPurchVerFailed: + NSLog(@"订单校验失败"); + break; + case IAPPurchVerSuccess: + NSLog(@"订单校验成功"); + break; + case IAPPurchNotArrow: + NSLog(@"不允许程序内付费"); + break; + default: + break; + } +#endif + if(_iAPCompletionHandle){ + _iAPCompletionHandle(type,data); + } +} + +- (void)verifyPurchaseWithPaymentTransaction:(SKPaymentTransaction *)transaction{ + //交易验证 + NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL]; + NSData *receipt = [NSData dataWithContentsOfURL:recepitURL]; + + if(!receipt){ + // 交易凭证为空验证失败 + [self handleActionWithType:IAPPurchVerFailed data:nil]; + return; + } + // 购买成功将交易凭证发送给服务端进行再次校验 + [self handleActionWithType:IAPPurchSuccess data:receipt]; + + // 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号 + [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; +} + +#pragma mark - SKProductsRequestDelegate +- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{ + NSArray *product = response.products; + if([product count] <= 0){ +#if DEBUG + NSLog(@"--------------没有商品------------------"); +#endif + return; + } + + SKProduct *p = nil; + for(SKProduct *pro in product){ + if([pro.productIdentifier isEqualToString:_currentPurchasedID]){ + p = pro; + break; + } + } + +#if DEBUG + NSLog(@"productID:%@", response.invalidProductIdentifiers); + NSLog(@"产品付费数量:%lu",(unsigned long)[product count]); + NSLog(@"产品描述:%@",[p description]); + NSLog(@"产品标题%@",[p localizedTitle]); + NSLog(@"产品本地化描述%@",[p localizedDescription]); + NSLog(@"产品价格:%@",[p price]); + NSLog(@"产品productIdentifier:%@",[p productIdentifier]); +#endif + + SKPayment *payment = [SKPayment paymentWithProduct:p]; + [[SKPaymentQueue defaultQueue] addPayment:payment]; +} + +//请求失败 +- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{ +#if DEBUG + NSLog(@"------------------从App Store中检索关于指定产品列表的本地化信息错误-----------------:%@", error); +#endif +} + +- (void)requestDidFinish:(SKRequest *)request{ +#if DEBUG + NSLog(@"------------requestDidFinish-----------------"); +#endif +} + +#pragma mark - SKPaymentTransactionObserver +- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{ + for (SKPaymentTransaction *tran in transactions) { + switch (tran.transactionState) { + case SKPaymentTransactionStatePurchased: + [self verifyPurchaseWithPaymentTransaction:tran]; + break; + case SKPaymentTransactionStatePurchasing: +#if DEBUG + NSLog(@"商品添加进列表"); +#endif + break; + case SKPaymentTransactionStateRestored: +#if DEBUG + NSLog(@"已经购买过商品"); +#endif + // 消耗型不支持恢复购买 + [[SKPaymentQueue defaultQueue] finishTransaction:tran]; + break; + case SKPaymentTransactionStateFailed: + [self failedTransaction:tran]; + break; + default: + break; + } + } +} + +// 交易失败 +- (void)failedTransaction:(SKPaymentTransaction *)transaction{ + if (transaction.error.code != SKErrorPaymentCancelled) { + [self handleActionWithType:IAPPurchFailed data:nil]; + }else{ + [self handleActionWithType:IAPPurchCancel data:nil]; + } + + [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; +} +@end + + +// 声明全局函数 +/* + -(void)purchaseWithProductID:(NSString *)productID{ + + [[IAPManager shareIAPManager] startPurchaseWithID:productID completeHandle:^(IAPPurchType type,NSData *data) { + + }]; + } +*/ + + + diff --git a/mobile/c/glfm/glfm.h b/mobile/c/glfm/glfm.h index ad79dfa7..fe231a70 100755 --- a/mobile/c/glfm/glfm.h +++ b/mobile/c/glfm/glfm.h @@ -581,6 +581,8 @@ int openOtherApp(const char *url, const char *more, int detectAppInstalled); void remoteMethodCall(const char *inJsonStr, Utf8String *outJsonStr); +void buyAppleProductById(GLFMDisplay * display, const char *cproductID, const char *base64HandleScript); + void pickPhotoAlbum(GLFMDisplay *display, int uid, int type); void pickPhotoCamera(GLFMDisplay *display, int uid, int type); diff --git a/mobile/c/glfm/glfm_apple.m b/mobile/c/glfm/glfm_apple.m index d04179fa..ef67f7d5 100644 --- a/mobile/c/glfm/glfm_apple.m +++ b/mobile/c/glfm/glfm_apple.m @@ -19,6 +19,7 @@ */ #include "glfm.h" +#include "IAPManager.h" #if !defined(GLFM_INCLUDE_METAL) #define GLFM_INCLUDE_METAL 1 @@ -2356,4 +2357,59 @@ void remoteMethodCall(const char *inJsonStr, Utf8String *outJsonStr){ } + +#pragma mark - IAP interface + +void buyAppleProductById(GLFMDisplay * display, const char *cproductID, const char *base64HandleScript){ + NSString *productID = [[NSString alloc] initWithCString:cproductID encoding:NSUTF8StringEncoding]; + //GLFMViewController *vc = (__bridge GLFMViewController *)display->platformData; + + + [[IAPManager shareIAPManager] startPurchaseWithID:productID completeHandle:^(IAPPurchType type,NSData *data) { + switch (type) { + case IAPPurchSuccess: + NSLog(@"IAPPurchSuccess"); + break; + case IAPPurchFailed: + NSLog(@"IAPPurchFailed"); + break; + case IAPPurchCancel: + NSLog(@"IAPPurchCancel"); + break; + case IAPPurchVerFailed: + NSLog(@"IAPPurchVerFailed"); + break; + case IAPPurchVerSuccess: + NSLog(@"IAPPurchVerSuccess"); + break; + case IAPPurchNotArrow: + NSLog(@"IAPPurchNotArrow"); + break; + default: + break; + } + const char *replyMsg = NULL; + NSString *nssd; + if (data == nil) { + nssd = @""; + } else { + nssd = [data base64EncodedStringWithOptions:0]; + } + + NSString *str = [NSString stringWithFormat:@"%d:%@:%s", (int)type, nssd, base64HandleScript]; + + replyMsg = [str UTF8String]; + + + static char *key = "glfm.ios.purchase"; + + + + if(display->notifyFunc){ + display->notifyFunc(display, key, replyMsg); + } + }]; + +} + #endif diff --git a/mobile/c/gui/jni_glfm.c b/mobile/c/gui/jni_glfm.c index 84f82961..d998da37 100755 --- a/mobile/c/gui/jni_glfm.c +++ b/mobile/c/gui/jni_glfm.c @@ -859,6 +859,23 @@ int org_mini_glfm_Glfm_glfmRemoteMethodCall(Runtime *runtime, JClass *clazz) { return 0; } + +int org_mini_glfm_Glfm_glfmBuyAppleProductById(Runtime *runtime, JClass *clazz) { + JniEnv *env = runtime->jnienv; + s32 pos = 0; + GLFMDisplay *window = (__refer) (intptr_t) env->localvar_getLong_2slot(runtime->localvar, pos); + pos += 2; + Instance *inPIDStr = env->localvar_getRefer(runtime->localvar, pos); + const c8 *cproductID = inPIDStr->arr_body; + pos += 1; + Instance *inBase64HandleScriptStr = env->localvar_getRefer(runtime->localvar, pos); + const c8 *cscript = inBase64HandleScriptStr->arr_body; + if(window && cproductID){ + buyAppleProductById(window, cproductID, cscript); + } + return 0; +} + /* ============================== jni utils =================================*/ int org_mini_glfm_utils_Gutil_f2b(Runtime *runtime, JClass *clazz) { @@ -1660,6 +1677,7 @@ static java_native_method method_glfm_table[] = { {"org/mini/glfm/Glfm", "glfmStartVideo", "(JJ)V", org_mini_glfm_Glfm_glfmStartVideo}, {"org/mini/glfm/Glfm", "glfmOpenOtherApp", "([B[BI)I", org_mini_glfm_Glfm_glfmOpenOtherApp}, {"org/mini/glfm/Glfm", "glfmRemoteMethodCall", "([B)Ljava/lang/String;", org_mini_glfm_Glfm_glfmRemoteMethodCall}, + {"org/mini/glfm/Glfm", "glfmBuyAppleProductById", "(J[B[B)V", org_mini_glfm_Glfm_glfmBuyAppleProductById}, }; diff --git a/mobile/iosapp/iosapp.xcodeproj/project.pbxproj b/mobile/iosapp/iosapp.xcodeproj/project.pbxproj index 75e9c159..41be739c 100644 --- a/mobile/iosapp/iosapp.xcodeproj/project.pbxproj +++ b/mobile/iosapp/iosapp.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ 451F272D23A24C3A00F99984 /* nanovg.c in Sources */ = {isa = PBXBuildFile; fileRef = 45D78F62209D4330003B9317 /* nanovg.c */; }; 451F272F23A24C7B00F99984 /* jni_minial.m in Sources */ = {isa = PBXBuildFile; fileRef = 4535FA2721944F7300B888BA /* jni_minial.m */; }; 4581C5CC226DA7BE00E88538 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 45D78F2B209D3E62003B9317 /* Assets.xcassets */; }; + 4589A5FD2C8DFB920079DDFC /* IAPManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4589A5FC2C8DFB920079DDFC /* IAPManager.m */; }; 45D7904B20A002C4003B9317 /* resfiles in Resources */ = {isa = PBXBuildFile; fileRef = 45D7904A20A002C4003B9317 /* resfiles */; }; 45F996B4253B69E40053E7A0 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 45F9966D253B69DE0053E7A0 /* md5.c */; }; 45F996B5253B69E40053E7A0 /* cmac.c in Sources */ = {isa = PBXBuildFile; fileRef = 45F9966E253B69DE0053E7A0 /* cmac.c */; }; @@ -166,6 +167,8 @@ 4581C389226D637C00E88538 /* tinycthread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tinycthread.h; sourceTree = ""; }; 4581C38A226D637C00E88538 /* d_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = d_type.h; sourceTree = ""; }; 4581C38B226D637C00E88538 /* linkedlist.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = linkedlist.c; sourceTree = ""; }; + 4589A5FB2C8DFB6C0079DDFC /* IAPManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IAPManager.h; sourceTree = ""; }; + 4589A5FC2C8DFB920079DDFC /* IAPManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IAPManager.m; sourceTree = ""; }; 45D78F1F209D3E61003B9317 /* iosapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosapp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 45D78F2B209D3E62003B9317 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 45D78F30209D3E62003B9317 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -491,6 +494,8 @@ 592538032BF5D27A00E5D1DC /* glfm_emscripten.c */, 592538052BF5D27A00E5D1DC /* glfm_internal.h */, 45D78F47209D4330003B9317 /* glfm.h */, + 4589A5FB2C8DFB6C0079DDFC /* IAPManager.h */, + 4589A5FC2C8DFB920079DDFC /* IAPManager.m */, ); name = glfm; path = ../../c/glfm; @@ -881,6 +886,7 @@ 45F996DE253B69E40053E7A0 /* pk_wrap.c in Sources */, 451F272123A24C3A00F99984 /* hashtable.c in Sources */, 45F996E4253B69E40053E7A0 /* oid.c in Sources */, + 4589A5FD2C8DFB920079DDFC /* IAPManager.m in Sources */, 451F272223A24C3A00F99984 /* miniz_wrapper.c in Sources */, 451F272323A24C3A00F99984 /* bytebuf.c in Sources */, 451F272423A24C3A00F99984 /* linkedlist.c in Sources */, @@ -1099,7 +1105,7 @@ ); OTHER_CFLAGS = ""; OTHER_LDFLAGS = "-ObjC"; - PRODUCT_BUNDLE_IDENTIFIER = com.ebsee.minipack; + PRODUCT_BUNDLE_IDENTIFIER = com.ysbit.wushuang; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1130,7 +1136,7 @@ ); OTHER_CFLAGS = ""; OTHER_LDFLAGS = "-ObjC"; - PRODUCT_BUNDLE_IDENTIFIER = com.ebsee.minipack; + PRODUCT_BUNDLE_IDENTIFIER = com.ysbit.wushuang; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/mobile/iosapp/iosapp.xcodeproj/project.xcworkspace/xcuserdata/Gust.xcuserdatad/UserInterfaceState.xcuserstate b/mobile/iosapp/iosapp.xcodeproj/project.xcworkspace/xcuserdata/Gust.xcuserdatad/UserInterfaceState.xcuserstate index 5991e158d254e2c1377ef530078db40f9534214c..73b0705a8f1afc400ca6a3912addb01725a9d57b 100644 GIT binary patch literal 54676 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

hBKzz?>ylU9E!S_8_iATME{IT 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 delta 8273 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 io.github.digitalgust glfm_gui ${project.groupId}:${project.artifactId} - 1.1.7 + 1.1.9 miniJVM mobile platform gui library https://github.com/digitalgust/miniJVM @@ -159,7 +159,7 @@ io.github.digitalgust minijvm_rt - 1.1.7 + 1.1.9 diff --git a/mobile/java/glfm_gui/src/main/java/org/mini/glfm/Glfm.java b/mobile/java/glfm_gui/src/main/java/org/mini/glfm/Glfm.java index 6b6b99d3..be1d951c 100644 --- a/mobile/java/glfm_gui/src/main/java/org/mini/glfm/Glfm.java +++ b/mobile/java/glfm_gui/src/main/java/org/mini/glfm/Glfm.java @@ -261,15 +261,32 @@ public static int glfmGetUserInterfaceOrientation(long display) { static native String glfmRemoteMethodCall(byte[] inJsonStr); + /** + * call android method from json string + * + * @param inJsonStr + * @return + */ public static String glfmRemoteMethodCall(String inJsonStr) { byte[] inJsonStrBytes = toCstyleBytes(inJsonStr.toString()); return glfmRemoteMethodCall(inJsonStrBytes); } - static native void glfmBuyAppleProductById(long display, byte[] cproductId); + static native void glfmBuyAppleProductById(long display, byte[] cproductId, byte[] cbase64HandleScript); - public static void glfmBuyAppleProductById(long display, String productId) { + /** + * Call this method to buy an in-app-purchase on iOS. + *

+ * on result, GLFM onNotify will be called with key="glfm.ios.purchase", value="resultcode:receipt:base64HandleScript" + * AppManager call script process the result. + * + * @param display GCallback().getInstance().getDisplay() + * @param productId iap product id + * @param base64HandleScript base64 encoded script to handle the result. + */ + public static void glfmBuyAppleProductById(long display, String productId, String base64HandleScript) { byte[] productIdBytes = toCstyleBytes(productId); - glfmBuyAppleProductById(display, productIdBytes); + byte[] base64HandleScriptBytes = toCstyleBytes(base64HandleScript); + glfmBuyAppleProductById(display, productIdBytes, base64HandleScriptBytes); } } From c42edb3d91ed58cc7cbb877cf60bd23b252fbe06 Mon Sep 17 00:00:00 2001 From: Gust Date: Mon, 23 Sep 2024 22:51:15 +0800 Subject: [PATCH 06/18] glfw fix iap --- desktop/glfw_gui/java/pom.xml | 2 +- desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/desktop/glfw_gui/java/pom.xml b/desktop/glfw_gui/java/pom.xml index dc136d2d..716867ea 100755 --- a/desktop/glfw_gui/java/pom.xml +++ b/desktop/glfw_gui/java/pom.xml @@ -8,7 +8,7 @@ io.github.digitalgust glfw_gui ${project.groupId}:${project.artifactId} - 1.1.9 + 1.1.10 miniJVM desktop platform gui library https://github.com/digitalgust/miniJVM diff --git a/desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java b/desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java index dd80c273..14e9d71f 100644 --- a/desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java +++ b/desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java @@ -306,7 +306,7 @@ public static String glfmRemoteMethodCall(String inJsonStr) { } - public static void glfmBuyAppleProductById(long display, String productId) { + public static void glfmBuyAppleProductById(long display, String productId, String base64HandleScript) { } } From ebd35e29c4a3a781abb0c3a0221871caceaf4c55 Mon Sep 17 00:00:00 2001 From: Gust Date: Mon, 23 Sep 2024 23:06:53 +0800 Subject: [PATCH 07/18] update gui --- extlib/xgui/pom.xml | 4 +- .../java/org/mini/apploader/AppManager.java | 102 +++++++++++------- .../main/java/org/mini/gui/GCmdHandler.java | 98 +++++++++-------- .../src/main/java/org/mini/gui/GForm.java | 1 - .../src/main/java/org/mini/gui/GObject.java | 18 ---- .../src/main/java/org/mini/gui/GTextBox.java | 23 ++-- .../src/main/java/org/mini/gui/GViewPort.java | 29 +++-- .../src/main/java/org/mini/gui/GViewSlot.java | 14 ++- .../org/mini/gui/gscript/EnvVarProvider.java | 8 ++ .../org/mini/gui/gscript/Interpreter.java | 50 ++------- .../java/org/mini/gui/gscript/Stdlib.java | 4 +- .../main/java/org/mini/layout/XObject.java | 5 +- .../java/org/mini/layout/XmlExtAssist.java | 20 ++++ ...esourceLoader.java => ResourceLoader.java} | 26 ++--- .../{XUrlHelper.java => UrlHelper.java} | 20 ++-- .../mini/layout/xwebview/XExplorerHolder.java | 5 - .../{XExplorer.java => XuiBrowser.java} | 18 ++-- .../layout/xwebview/XuiBrowserHolder.java | 9 ++ .../xwebview/{XPage.java => XuiPage.java} | 20 ++-- .../{XResource.java => XuiResource.java} | 2 +- ...plorerScriptLib.java => XuiScriptLib.java} | 45 ++++---- ...XFileUrlHelper.java => FileUrlHelper.java} | 4 +- ...XHttpUrlHelper.java => HttpUrlHelper.java} | 4 +- .../{XJarUrlHelper.java => JarUrlHelper.java} | 4 +- minijvm/c/jvm/jdwp.c | 2 +- .../UserInterfaceState.xcuserstate | Bin 54676 -> 57669 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 2 +- mobile/java/glfm_gui/pom.xml | 2 +- 28 files changed, 280 insertions(+), 259 deletions(-) create mode 100644 extlib/xgui/src/main/java/org/mini/gui/gscript/EnvVarProvider.java rename extlib/xgui/src/main/java/org/mini/layout/xwebview/{XResourceLoader.java => ResourceLoader.java} (79%) rename extlib/xgui/src/main/java/org/mini/layout/xwebview/{XUrlHelper.java => UrlHelper.java} (62%) delete mode 100644 extlib/xgui/src/main/java/org/mini/layout/xwebview/XExplorerHolder.java rename extlib/xgui/src/main/java/org/mini/layout/xwebview/{XExplorer.java => XuiBrowser.java} (89%) create mode 100644 extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiBrowserHolder.java rename extlib/xgui/src/main/java/org/mini/layout/xwebview/{XPage.java => XuiPage.java} (85%) rename extlib/xgui/src/main/java/org/mini/layout/xwebview/{XResource.java => XuiResource.java} (97%) rename extlib/xgui/src/main/java/org/mini/layout/xwebview/{ExplorerScriptLib.java => XuiScriptLib.java} (80%) rename extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/{XFileUrlHelper.java => FileUrlHelper.java} (84%) rename extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/{XHttpUrlHelper.java => HttpUrlHelper.java} (93%) rename extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/{XJarUrlHelper.java => JarUrlHelper.java} (90%) diff --git a/extlib/xgui/pom.xml b/extlib/xgui/pom.xml index 1f56f17e..897c7d41 100755 --- a/extlib/xgui/pom.xml +++ b/extlib/xgui/pom.xml @@ -174,12 +174,12 @@ io.github.digitalgust glfw_gui - 1.1.9 + 1.1.10 io.github.digitalgust glfm_gui - 1.1.9 + 1.1.10 diff --git a/extlib/xgui/src/main/java/org/mini/apploader/AppManager.java b/extlib/xgui/src/main/java/org/mini/apploader/AppManager.java index f3a1176a..90a26c5a 100644 --- a/extlib/xgui/src/main/java/org/mini/apploader/AppManager.java +++ b/extlib/xgui/src/main/java/org/mini/apploader/AppManager.java @@ -5,30 +5,31 @@ */ package org.mini.apploader; -import com.ebsee.rmc.RMCUtil; +import org.mini.apploader.bean.LangBean; +import org.mini.glfm.Glfm; +import org.mini.gui.*; import org.mini.gui.event.GNotifyListener; +import org.mini.gui.gscript.EnvVarProvider; import org.mini.gui.gscript.Interpreter; -import org.mini.gui.gscript.Str; import org.mini.gui.guilib.GuiScriptLib; import org.mini.http.MiniHttpClient; import org.mini.http.MiniHttpServer; -import org.mini.layout.xwebview.*; -import org.mini.apploader.bean.LangBean; -import org.mini.gui.guilib.HttpRequestReply; -import org.mini.glfm.Glfm; -import org.mini.gui.*; import org.mini.json.JsonParser; -import org.mini.layout.*; +import org.mini.layout.UITemplate; +import org.mini.layout.XContainer; +import org.mini.layout.XEventHandler; +import org.mini.layout.XmlExtAssist; +import org.mini.layout.xwebview.XuiScriptLib; +import org.mini.layout.xwebview.XuiBrowser; +import org.mini.layout.xwebview.XuiBrowserHolder; import java.io.*; -import java.net.URL; -import java.net.URLConnection; import java.util.*; /** * @author Gust */ -public class AppManager extends GApplication { +public class AppManager extends GApplication implements XuiBrowserHolder { public static final String POLICY_URL = "POLICY_URL"; public static final String DISCOVERY_URL = "DISCOVERY_URL"; public static final String ACCOUNT_BASE_URL = "ACCOUNT_BASE_URL"; @@ -85,7 +86,7 @@ public class AppManager extends GApplication { // GApplication preApp; PluginMgrForm mgrForm; - XExplorer explorer; + XuiBrowser browser; GViewSlot mainSlot; @@ -116,6 +117,7 @@ public class AppManager extends GApplication { static final int PICK_PHOTO = 101, PICK_CAMERA = 102, PICK_QR = 103, PICK_HEAD = 104; static float devW, devH; + XmlExtAssist assist; /** * @return @@ -163,6 +165,12 @@ public GForm getForm() { if (mgrForm != null) return mgrForm; mgrForm = new PluginMgrForm(null); + // init script environment + assist = new XmlExtAssist(mgrForm); + assist.addExtScriptLib(new XuiScriptLib(AppManager.getInstance())); + updateScriptEnvironment(); + assist.setEnvVarProvider(envVarProvider); + //init form mgrForm.initForm(); floatButton = new GHomeButton(mgrForm); mgrForm.add(floatButton); @@ -200,12 +208,8 @@ void setStyleButton(int i) { } void initExplorer() { -// queryServerPolicy(); - XmlExtAssist assist = new XmlExtAssist(mgrForm); - assist.addExtScriptLib(new ExplorerScriptLib(mgrForm)); - GContainer wv = GToolkit.getComponent(mgrForm, "TD_DISCOVERY"); - explorer = new XExplorer(wv, eventHandler, assist); + browser = new XuiBrowser(wv, eventHandler, assist); } @@ -381,13 +385,18 @@ public static String getDateString(long millis) { } + @Override + public XuiBrowser getBrowser() { + return browser; + } + /** * ============================================================================== * GForm that is used to show plugin manager * ============================================================================== */ - class PluginMgrForm extends GForm implements XExplorerHolder { + class PluginMgrForm extends GForm { public PluginMgrForm(GForm form) { super(form); @@ -493,8 +502,6 @@ GContainer getMainPanel(GForm form) { } catch (Exception e) { } - // init script environment - updateScriptEnvironment(); UITemplate uit = new UITemplate(xmlStr); for (String s : uit.getVariable()) { @@ -512,9 +519,7 @@ GContainer getMainPanel(GForm form) { uit.setVar("NAV_HEIGHT", Integer.toString(h)); - eventHandler = new PluginEventHandler(); - XmlExtAssist assist = new XmlExtAssist(form); - assist.addExtScriptLib(new ExplorerScriptLib(this));//使支持openpage, downloadinstall, downloadsave + eventHandler = new PluginEventHandler();//使支持openpage, downloadinstall, downloadsave XContainer xcon = (XContainer) XContainer.parseXml(uit.parse(), assist); xcon.build((int) devW, (int) (devH), eventHandler); GContainer pan = xcon.getGui(); @@ -536,7 +541,7 @@ GContainer getMainPanel(GForm form) { this.setSizeChangeListener((width, height) -> { xcon.reSize(width, height); - initExplorer(); +// browser.getWebView().getLayout().reSize(width, height); }); reloadAppList(); return pan; @@ -570,10 +575,7 @@ public boolean paint(long vg) { return true; } - @Override - public XExplorer getExplorer() { - return explorer; - } + } class PluginEventHandler extends XEventHandler { @@ -793,15 +795,43 @@ public void onStateChange(GObject gobj) { } } + + EnvVarProvider envVarProvider = new EnvVarProvider() { + + @Override + public void setEnvVar(String envName, String envValue) { + if (envName == null) { + System.out.println("[WARN]envvar key is null"); + } + if (envValue == null) { + envValue = ""; + } + String enLow = envName.toLowerCase(); + AppLoader.setProperty(enLow, envValue); + } + + @Override + public String getEnvVar(String envName) { + String val = AppLoader.getProperty(envName.toLowerCase()); + if (val == null) { + val = ""; + } + //System.out.println("getEnvVar:" + envName + "=" + val); + return val; + } + }; + void updateScriptEnvironment() { - Interpreter.setEnvVar("LANG", AppLoader.getLangName()); - Interpreter.setEnvVar("APPID", AppLoader.getBaseInfo("appid")); - Interpreter.setEnvVar("APPZONE", AppLoader.getBaseInfo("appzone")); - Interpreter.setEnvVar("SVER", AppLoader.getBaseInfo("sver")); - Interpreter.setEnvVar("JAR", System.getProperty("os.name").toLowerCase()); - Interpreter.setEnvVar("FROM", AppLoader.getBaseInfo("from")); - Interpreter.setEnvVar("CVER", AppLoader.getBaseInfo("cver")); - Interpreter.setEnvVar("POLICY_URL", AppLoader.getBaseInfo("policyUrl")); + envVarProvider.setEnvVar("lang", AppLoader.getLangName()); + envVarProvider.setEnvVar("appid", AppLoader.getBaseInfo("appid")); + envVarProvider.setEnvVar("appzone", AppLoader.getBaseInfo("appzone")); + envVarProvider.setEnvVar("sver", AppLoader.getBaseInfo("sver")); + envVarProvider.setEnvVar("jar", System.getProperty("os.name").toLowerCase()); + envVarProvider.setEnvVar("from", AppLoader.getBaseInfo("from")); + envVarProvider.setEnvVar("cver", AppLoader.getBaseInfo("cver")); + envVarProvider.setEnvVar("policy_url", AppLoader.getBaseInfo("policyUrl")); + envVarProvider.setEnvVar("discovery_url", null); + envVarProvider.setEnvVar("ACCOUNT_BASE_URL", null); } diff --git a/extlib/xgui/src/main/java/org/mini/gui/GCmdHandler.java b/extlib/xgui/src/main/java/org/mini/gui/GCmdHandler.java index 6692f9cd..c304da08 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/GCmdHandler.java +++ b/extlib/xgui/src/main/java/org/mini/gui/GCmdHandler.java @@ -27,13 +27,18 @@ public class GCmdHandler { final static List cmds = Collections.synchronizedList(new ArrayList()); + static List cmdQueue = new ArrayList(); final static List message = new ArrayList(); static byte[] curShowMessage; float[] insets = new float[4]; float[] bond = new float[4]; + GCmd msgCmd; public void addCmd(GCmd cmd) { + if (cmd == null) { + return; + } cmds.add(cmd); } @@ -47,50 +52,52 @@ public void addCmd(int cmdId, Object attachment) { public void process(GForm form) { synchronized (cmds) { - for (int i = 0, imax = cmds.size(); i < imax; i++) { - GCmd cmd = cmds.get(i); - try { - switch (cmd.cmdId) { - case GCmd.GCMD_DESTORY_TEXTURE: { - Integer tex = (Integer) cmd.attachment; - if (tex != null) { - Nanovg.nvgDeleteImage(form.getNvContext(), tex); - //System.out.println("delete image " + tex); - } - break; - } - case GCmd.GCMD_SHOW_MESSAGE: { - message.add((String) cmd.attachment); - break; - } - case GCmd.GCMD_CLEAR_MESSAGE: { - message.clear(); - break; - } - case GCmd.GCMD_SHOW_KEYBOARD: { - Glfm.glfmSetKeyboardVisible(form.getWinContext(), true); - break; - } - case GCmd.GCMD_HIDE_KEYBOARD: { - Glfm.glfmSetKeyboardVisible(form.getWinContext(), false); - break; + cmdQueue.clear(); + cmdQueue.addAll(cmds); + cmds.clear(); + } + for (int i = 0, imax = cmdQueue.size(); i < imax; i++) { + GCmd cmd = cmdQueue.get(i); + try { + switch (cmd.cmdId) { + case GCmd.GCMD_DESTORY_TEXTURE: { + Integer tex = (Integer) cmd.attachment; + if (tex != null) { + Nanovg.nvgDeleteImage(form.getNvContext(), tex); + //System.out.println("delete image " + tex); } - case GCmd.GCMD_RUN_CODE: { - if (cmd.attachment instanceof Runnable) { - Runnable runnable = (Runnable) cmd.attachment; - runnable.run(); - } - break; + break; + } + case GCmd.GCMD_SHOW_MESSAGE: { + message.add((String) cmd.attachment); + break; + } + case GCmd.GCMD_CLEAR_MESSAGE: { + message.clear(); + break; + } + case GCmd.GCMD_SHOW_KEYBOARD: { + Glfm.glfmSetKeyboardVisible(form.getWinContext(), true); + break; + } + case GCmd.GCMD_HIDE_KEYBOARD: { + Glfm.glfmSetKeyboardVisible(form.getWinContext(), false); + break; + } + case GCmd.GCMD_RUN_CODE: { + if (cmd.attachment instanceof Runnable) { + Runnable runnable = (Runnable) cmd.attachment; + runnable.run(); } - default: { + break; + } + default: { - } } - } catch (Exception e) { - e.printStackTrace(); } + } catch (Exception e) { + e.printStackTrace(); } - cmds.clear(); } } @@ -98,16 +105,21 @@ void paint(GForm form) { if (curShowMessage == null) { if (message.size() > 0) { curShowMessage = GLUtil.toCstyleBytes(message.remove(0)); - GForm.timer.schedule(new TimerTask() { + msgCmd = new GCmd(new Runnable() { + int tick = 0; + @Override public void run() { - try { + GForm.flush(); + if (tick++ < 50) { + GForm.addCmd(msgCmd); + } else { curShowMessage = null; - GForm.flush(); - } catch (Exception e) { + msgCmd = null; } } - }, 1500); + }); + GForm.addCmd(msgCmd); } } else { long vg = form.getNvContext(); diff --git a/extlib/xgui/src/main/java/org/mini/gui/GForm.java b/extlib/xgui/src/main/java/org/mini/gui/GForm.java index 2544c8f1..abb0288f 100755 --- a/extlib/xgui/src/main/java/org/mini/gui/GForm.java +++ b/extlib/xgui/src/main/java/org/mini/gui/GForm.java @@ -22,7 +22,6 @@ */ public class GForm extends GContainer { - final protected static Timer timer = new Timer(true);//用于更新画面,UI系统采取按需刷新的原则 static GCmdHandler cmdHandler = new GCmdHandler(); private boolean inited = false; diff --git a/extlib/xgui/src/main/java/org/mini/gui/GObject.java b/extlib/xgui/src/main/java/org/mini/gui/GObject.java index 5a2052a0..1712d101 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/GObject.java +++ b/extlib/xgui/src/main/java/org/mini/gui/GObject.java @@ -145,24 +145,6 @@ public void focus() { } - public void schedule(TimerTask task, long delay, long period) { - if (GForm.timer != null) { -// getForm().setActiveListener(active -> { -// -// }); - GForm.timer.schedule(new TimerTask() { - @Override - public void run() { - try { - task.run(); - } catch (Exception e) { - cancel();//cancel this ,not task - } - } - }, delay, period); - } - } - public T findParentByName(String name) { if (name == null) return null; if (parent != null) { diff --git a/extlib/xgui/src/main/java/org/mini/gui/GTextBox.java b/extlib/xgui/src/main/java/org/mini/gui/GTextBox.java index 72e4bbc4..6be8b87f 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/GTextBox.java +++ b/extlib/xgui/src/main/java/org/mini/gui/GTextBox.java @@ -544,9 +544,8 @@ public void touchEvent(int touchid, int phase, int x, int y) { } else if (caret >= 0) { setCaretIndex(caret); } // - if (task != null) { - task.cancel(); - task = null; + if (inertiaCmd != null) { + inertiaCmd = null; } break; } @@ -670,7 +669,8 @@ public void keyEventGlfm(int key, int action, int mods) { //总共做多少次操作 long maxMoveCount = 120; //惯性任务 - TimerTask task; + + GCmd inertiaCmd; @Override public boolean inertiaEvent(float x1, float y1, float x2, float y2, final long moveTime) { @@ -680,8 +680,9 @@ public boolean inertiaEvent(float x1, float y1, float x2, float y2, final long m double dx = x2 - x1; final double dy = y2 - y1; float scrollDelta = 0; + Runnable task; //System.out.println("inertia time: " + moveTime + " , count: " + maxMoveCount + " pos: x1,y1,x2,y2 = " + x1 + "," + y1 + "," + x2 + "," + y2); - task = new TimerTask() { + task = new Runnable() { //惯性速度 double speed = dy / (moveTime / inertiaPeriod); //阴力 @@ -701,20 +702,16 @@ public void run() { } GForm.flush(); if (count++ > maxMoveCount) { - try { - this.cancel(); - } catch (Exception e) { - } + inertiaCmd = null; } + GForm.addCmd(inertiaCmd); } catch (Exception e) { e.printStackTrace(); } } }; - Timer timer = GForm.timer; - if (timer != null) { - timer.schedule(task, 0, inertiaPeriod); - } + inertiaCmd = new GCmd(task); + GForm.addCmd(inertiaCmd); return true; } diff --git a/extlib/xgui/src/main/java/org/mini/gui/GViewPort.java b/extlib/xgui/src/main/java/org/mini/gui/GViewPort.java index a184b999..dab20567 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/GViewPort.java +++ b/extlib/xgui/src/main/java/org/mini/gui/GViewPort.java @@ -195,9 +195,8 @@ public void reAlign() { public void touchEvent(int touchid, int phase, int x, int y) { switch (phase) { case Glfm.GLFMTouchPhaseBegan: { - if (task != null) { - task.cancel(); - task = null; + if (inertiaCmd != null) { + inertiaCmd = null; } touched = true; break; @@ -218,7 +217,8 @@ public void touchEvent(int touchid, int phase, int x, int y) { //初速度加成 float addOn = 1.5f; //惯性任务 - TimerTask task; + + GCmd inertiaCmd; @Override public boolean inertiaEvent(float x1, float y1, float x2, float y2, final long moveTime) { @@ -231,12 +231,13 @@ public boolean inertiaEvent(float x1, float y1, float x2, float y2, final long m // final double dx = x2 - x1; final double dy = y2 - y1; + Runnable task; //System.out.println("inertia time: " + moveTime + " , count: " + maxMoveCount + " pos: x1,y1,x2,y2 = " + x1 + "," + y1 + "," + x2 + "," + y2); if (Math.abs(dy) > Math.abs(dx)) { if (getInnerH() <= getH()) { return false; } - task = new TimerTask() { + task = new Runnable() { //惯性速度 double speedY = dy * addOn / (moveTime / inertiaPeriod); //阻力 @@ -262,11 +263,9 @@ public void run() { } GForm.flush(); if (count++ > maxMoveCount || tmpScrollY < 0 || tmpScrollY > 1) { - try { - this.cancel(); - } catch (Exception e) { - } + inertiaCmd = null; } + GForm.addCmd(inertiaCmd); } catch (Exception e) { e.printStackTrace(); } @@ -302,20 +301,18 @@ public void run() { } GForm.flush(); if (count++ > maxMoveCount || tmpScrollX < 0 || tmpScrollX > 1) { - try { - this.cancel(); - } catch (Exception e) { - } + inertiaCmd = null; } + GForm.addCmd(inertiaCmd); } catch (Exception e) { e.printStackTrace(); } } }; } - Timer timer = GForm.timer; - if (timer != null) { - timer.schedule(task, 0, (long) inertiaPeriod); + if (task != null) { + inertiaCmd = new GCmd(task); + GForm.addCmd(inertiaCmd); } return true; } diff --git a/extlib/xgui/src/main/java/org/mini/gui/GViewSlot.java b/extlib/xgui/src/main/java/org/mini/gui/GViewSlot.java index 131e0d93..6d614ec9 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/GViewSlot.java +++ b/extlib/xgui/src/main/java/org/mini/gui/GViewSlot.java @@ -44,6 +44,7 @@ boolean canMoveToDown() { protected float dragBeginX, dragBeginY; static final int SWAP_PERIOD = 16; + GCmd swapTask; public GViewSlot(GForm form, float w, float h, int scrollMod) { this(form, 0, 0, w, h, scrollMod); @@ -170,7 +171,9 @@ public void moveTo(GObject go, long timeInMils) { GObject curGo = getElementsImpl().get(active); if (curGo != null && go != null) { SlotSwaper swaper = new SlotSwaper(this, curGo, go, timeInMils); - GForm.timer.schedule(swaper, 0, (long) SWAP_PERIOD); + swapTask = new GCmd(swaper); + GForm.addCmd(swapTask); + GForm.flush(); this.active = getElementsImpl().indexOf(go); //notify doStateChanged(this); @@ -178,7 +181,7 @@ public void moveTo(GObject go, long timeInMils) { } } - class SlotSwaper extends TimerTask { + class SlotSwaper implements Runnable { int counter = 0, maxCounter; long timeInMils; @@ -212,7 +215,7 @@ public void run() { if ((distX == 0 && scrollMode == SCROLL_MODE_HORIZONTAL) || (distY == 0 && scrollMode == SCROLL_MODE_VERTICAL)) { - cancel(); + swapTask = null; return; } counter++; @@ -223,10 +226,11 @@ public void run() { } else { curX = slotOrignalX - distX; curY = slotOrignalY - distY; - cancel(); + swapTask = null; } slots.setInnerLocation(curX, curY); - //System.out.println("==slot(" + slots.getInnerX() + "," + slots.getInnerY() + "), from:" + from + "to:" + to + ")"); + GForm.addCmd(swapTask); + //System.out.println(swapTask + " " + slots.getInnerX() + "," + slots.getInnerY() + "), from:" + from + "to:" + to + ")"); GForm.flush(); } catch (Exception e) { e.printStackTrace(); diff --git a/extlib/xgui/src/main/java/org/mini/gui/gscript/EnvVarProvider.java b/extlib/xgui/src/main/java/org/mini/gui/gscript/EnvVarProvider.java new file mode 100644 index 00000000..fccc4bba --- /dev/null +++ b/extlib/xgui/src/main/java/org/mini/gui/gscript/EnvVarProvider.java @@ -0,0 +1,8 @@ +package org.mini.gui.gscript; + +public interface EnvVarProvider { + + public String getEnvVar(String envName); + + public void setEnvVar(String envName, String envValue); +} diff --git a/extlib/xgui/src/main/java/org/mini/gui/gscript/Interpreter.java b/extlib/xgui/src/main/java/org/mini/gui/gscript/Interpreter.java index 69a611ba..e29636e4 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/gscript/Interpreter.java +++ b/extlib/xgui/src/main/java/org/mini/gui/gscript/Interpreter.java @@ -48,9 +48,7 @@ public class Interpreter { static final int NOT_KEYWORD = -1, KEYWORD_IF = 0, KEYWORD_ELSE = 1, KEYWORD_ENDIF = 2, KEYWORD_WHILE = 3, KEYWORD_LOOP = 4, KEYWORD_SUB = 5, KEYWORD_RET = 6, KEYWORD_CALL = 7, KEYWORD_SET_VAR = 8, KEYWORD_SET_ARR = 9; public static final int ERR_ILLEGAL = 0, ERR_VAR = 1, ERR_TYPE_INVALID = 2, ERR_NOSUB = 3, ERR_NO_VAR = 4, ERR_PARA_CALC = 5, ERR_PAESEPARA = 6, ERR_NO_SRC = 7, ERR_OPSYMB = 8, ERR_ARR_OUT = 9; public static final String[] STRS_ERR = {" Illegal statment ,", " Invalid variable name ", " Data type error ", " No such method ", " No such variable ", " Method parameter error ", " Parameter count error ", " Code not load yet ", " Operation symbol error ", " Array out of bounds "}; - //环境变量访问器 - static private Properties envVar; - static private final String ENV_VAR_FILENAME = "/webenv.properties"; + //源码字符串数组 //private ArrayList srcCode; private Statement[] srcCompiled; @@ -61,6 +59,8 @@ public class Interpreter { private HashMap subAddr = new HashMap(); //系统过程及扩充过程列表 ,extend method lib private ArrayList extSubList = new ArrayList(); + //环境变量访问器 + EnvVarProvider envVarProvider; static final int MAX_CACHE_SIZE = 512; @@ -97,50 +97,22 @@ private void init() {//init localvar table cache reglib(stdlib); } - static public String getEnvVar(String envName) { - loadEnvVar(); - String s = (String) envVar.get(envName); - return s == null ? "" : s; + public void setEnvVarProvider(EnvVarProvider envVarProvider) { + this.envVarProvider = envVarProvider; } - static public void setEnvVar(String envName, String envValue) { - loadEnvVar(); - envVar.put(envName, envValue); - saveProp(ENV_VAR_FILENAME, envVar); + public String getEnvVar(String envName) { + String s = envVarProvider != null ? envVarProvider.getEnvVar(envName) : ""; + return s; } - static private synchronized void loadEnvVar() { - if (envVar == null) { - envVar = new Properties(); - loadProp(ENV_VAR_FILENAME, envVar); - } - } - public static void loadProp(String fname, Properties prop) { - try { - File f = new File(GCallBack.getInstance().getAppSaveRoot() + fname); - if (f.exists()) { - FileInputStream fis = new FileInputStream(f); - prop.load(fis); - //System.out.println(fname + " size: " + prop.size()); - fis.close(); - } - } catch (IOException ex) { - ex.printStackTrace(); + public void setEnvVar(String envName, String envValue) { + if (envVarProvider != null) { + envVarProvider.setEnvVar(envName, envValue); } } - public static void saveProp(String name, Properties prop) { - try { - File f = new File(GCallBack.getInstance().getAppSaveRoot() + name); - - FileOutputStream fos = new FileOutputStream(f); - prop.store(fos, ""); - fos.close(); - } catch (IOException ex) { - ex.printStackTrace(); - } - } /** * 装载脚本 diff --git a/extlib/xgui/src/main/java/org/mini/gui/gscript/Stdlib.java b/extlib/xgui/src/main/java/org/mini/gui/gscript/Stdlib.java index 859eaae7..59798ae0 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/gscript/Stdlib.java +++ b/extlib/xgui/src/main/java/org/mini/gui/gscript/Stdlib.java @@ -73,14 +73,14 @@ public Stdlib(Interpreter inp) { public DataType getEnv(ArrayList para) { String key = Interpreter.popBackStr(para); - return Interpreter.getCachedStr(Interpreter.getEnvVar(key)); + return Interpreter.getCachedStr(inp.getEnvVar(key)); } public DataType setEnv(ArrayList para) { String key = Interpreter.popBackStr(para); String val = Interpreter.popBackStr(para); - Interpreter.setEnvVar(key, val); + inp.setEnvVar(key, val); return null; } diff --git a/extlib/xgui/src/main/java/org/mini/layout/XObject.java b/extlib/xgui/src/main/java/org/mini/layout/XObject.java index 0310c74f..e45135a7 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/XObject.java +++ b/extlib/xgui/src/main/java/org/mini/layout/XObject.java @@ -2,7 +2,6 @@ import org.mini.gui.*; import org.mini.gui.gscript.Interpreter; -import org.mini.gui.gscript.Lib; import org.xmlpull.v1.KXmlParser; public abstract class XObject implements GLayout { @@ -269,9 +268,7 @@ public final void initGuiMore() { Interpreter inp = container.getInterpreter(); if (inp != null) { if (assist != null) { - for (Lib lib : assist.getExtScriptLibs()) { - inp.reglib(lib); - } + assist.interpreterSetup(inp); } } } diff --git a/extlib/xgui/src/main/java/org/mini/layout/XmlExtAssist.java b/extlib/xgui/src/main/java/org/mini/layout/XmlExtAssist.java index b7bb1ad6..c3488d5e 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/XmlExtAssist.java +++ b/extlib/xgui/src/main/java/org/mini/layout/XmlExtAssist.java @@ -3,6 +3,8 @@ import org.mini.gui.GForm; import org.mini.gui.GImage; import org.mini.gui.GToolkit; +import org.mini.gui.gscript.EnvVarProvider; +import org.mini.gui.gscript.Interpreter; import org.mini.gui.guilib.GuiScriptLib; import org.mini.gui.gscript.Lib; @@ -26,6 +28,8 @@ public interface XLoader { protected XLoader loader; XEventHandler eventHandler; + EnvVarProvider envVarProvider; + public XmlExtAssist(GForm form) { //if (form == null) throw new RuntimeException("Form can not be null"); this.form = form; @@ -98,6 +102,7 @@ public void copyFrom(XmlExtAssist assist) { extScriptLibs.addAll(assist.extScriptLibs); loader = assist.loader; form = assist.form; + envVarProvider = assist.envVarProvider; } XEventHandler getEventHandler() { @@ -107,4 +112,19 @@ XEventHandler getEventHandler() { void setEventHandler(XEventHandler eventHandler) { this.eventHandler = eventHandler; } + + public EnvVarProvider getEnvVarProvider() { + return envVarProvider; + } + + public void setEnvVarProvider(EnvVarProvider envVarProvider) { + this.envVarProvider = envVarProvider; + } + + public void interpreterSetup(Interpreter inp) { + inp.setEnvVarProvider(envVarProvider); + for (Lib lib : getExtScriptLibs()) { + inp.reglib(lib); + } + } } diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XResourceLoader.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/ResourceLoader.java similarity index 79% rename from extlib/xgui/src/main/java/org/mini/layout/xwebview/XResourceLoader.java rename to extlib/xgui/src/main/java/org/mini/layout/xwebview/ResourceLoader.java index 4e996996..67e29d46 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XResourceLoader.java +++ b/extlib/xgui/src/main/java/org/mini/layout/xwebview/ResourceLoader.java @@ -9,15 +9,15 @@ import java.util.HashMap; import java.util.Map; -public class XResourceLoader implements XmlExtAssist.XLoader { +public class ResourceLoader implements XmlExtAssist.XLoader { URL url; static GImage notfoundImage; static String notfoundText = "
"; - Map resources = new HashMap<>(); + Map resources = new HashMap<>(); - public XResourceLoader() { + public ResourceLoader() { notfoundImage = GImage.createImageFromJar("/res/ui/notfound.jpg"); } @@ -30,8 +30,8 @@ public void clearCache() { resources.clear(); } - public XResource loadResource(String pUrl) { - String resUrl = XUrlHelper.normalizeUrl(url, pUrl); + public XuiResource loadResource(String pUrl) { + String resUrl = UrlHelper.normalizeUrl(url, pUrl); try { URL u = new URL(resUrl); URLConnection conn = u.openConnection(); @@ -44,9 +44,9 @@ public XResource loadResource(String pUrl) { while (read < data.length) { read += o.read(data, read, data.length - read); } - XResource resource = new XResource(); + XuiResource resource = new XuiResource(); resource.url = resUrl; - resource.type = XResource.getTypeByString(type); + resource.type = XuiResource.getTypeByString(type); resource.data = data; return resource; } @@ -58,16 +58,16 @@ public XResource loadResource(String pUrl) { public GImage loadImage(String pUrl) { - String resUrl = XUrlHelper.normalizeUrl(url, pUrl); + String resUrl = UrlHelper.normalizeUrl(url, pUrl); try { - XResource res = resources.get(resUrl); + XuiResource res = resources.get(resUrl); if (res == null) { res = loadResource(resUrl); } if (res != null) { if (res.image == null) { res.image = GImage.createImage(res.data); - res.type = XResource.TYPE_IMAGE; + res.type = XuiResource.TYPE_IMAGE; resources.put(resUrl, res); } if (res.image == null) { @@ -82,16 +82,16 @@ public GImage loadImage(String pUrl) { } public String loadXml(String pUrl) { - String resUrl = XUrlHelper.normalizeUrl(url, pUrl); + String resUrl = UrlHelper.normalizeUrl(url, pUrl); try { - XResource res = resources.get(resUrl); + XuiResource res = resources.get(resUrl); if (res == null) { res = loadResource(resUrl); } if (res != null) { if (res.xml == null) { res.xml = new String(res.data, "UTF-8"); - res.type = XResource.TYPE_XML; + res.type = XuiResource.TYPE_XML; resources.put(resUrl, res); } return res.xml; diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XUrlHelper.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/UrlHelper.java similarity index 62% rename from extlib/xgui/src/main/java/org/mini/layout/xwebview/XUrlHelper.java rename to extlib/xgui/src/main/java/org/mini/layout/xwebview/UrlHelper.java index b17a16ef..9337a624 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XUrlHelper.java +++ b/extlib/xgui/src/main/java/org/mini/layout/xwebview/UrlHelper.java @@ -1,32 +1,32 @@ package org.mini.layout.xwebview; -import org.mini.layout.xwebview.urlhelper.XFileUrlHelper; -import org.mini.layout.xwebview.urlhelper.XHttpUrlHelper; -import org.mini.layout.xwebview.urlhelper.XJarUrlHelper; +import org.mini.layout.xwebview.urlhelper.FileUrlHelper; +import org.mini.layout.xwebview.urlhelper.HttpUrlHelper; +import org.mini.layout.xwebview.urlhelper.JarUrlHelper; import java.net.URL; -public abstract class XUrlHelper { +public abstract class UrlHelper { - public static XUrlHelper getHelper(String urlStr) { + public static UrlHelper getHelper(String urlStr) { try { return getHelper(new URL(urlStr)); } catch (Exception e) { e.printStackTrace(); } - return new XJarUrlHelper(); + return new JarUrlHelper(); } - public static XUrlHelper getHelper(URL homeUrl) { + public static UrlHelper getHelper(URL homeUrl) { switch (homeUrl.getProtocol()) { case "http": case "https": - return new XHttpUrlHelper(); + return new HttpUrlHelper(); case "file": - return new XFileUrlHelper(); + return new FileUrlHelper(); case "jar": default: - return new XJarUrlHelper(); + return new JarUrlHelper(); } } diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XExplorerHolder.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XExplorerHolder.java deleted file mode 100644 index 7e4697fd..00000000 --- a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XExplorerHolder.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.mini.layout.xwebview; - -public interface XExplorerHolder { - XExplorer getExplorer(); -} diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XExplorer.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiBrowser.java similarity index 89% rename from extlib/xgui/src/main/java/org/mini/layout/xwebview/XExplorer.java rename to extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiBrowser.java index 2b654c95..771337bb 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XExplorer.java +++ b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiBrowser.java @@ -33,13 +33,13 @@ * A Jar directory * jar:http://www.foo.com/bar/baz.jar!/COM/foo/ */ -public class XExplorer { +public class XuiBrowser { public static final int MAX_PAGE_SIZE = 10; - List pages = new java.util.ArrayList<>(); + List pages = new java.util.ArrayList<>(); GContainer webView; private XEventHandler eventHandler; XmlExtAssist assist; - XPage currentPage; + XuiPage currentPage; /** * web explorer @@ -49,7 +49,7 @@ public class XExplorer { * @param eventHandler native event handler * @param assist parse xml ui assists, like load image, load xml, register script library */ - public XExplorer(GContainer webView, XEventHandler eventHandler, XmlExtAssist assist) { + public XuiBrowser(GContainer webView, XEventHandler eventHandler, XmlExtAssist assist) { this.webView = webView; this.eventHandler = eventHandler; this.assist = assist; @@ -57,7 +57,7 @@ public XExplorer(GContainer webView, XEventHandler eventHandler, XmlExtAssist as public void gotoPage(String homeUrl) { removeAfterAtCurrentPage(); - XPage page = new XPage(homeUrl, this); + XuiPage page = new XuiPage(homeUrl, this); showPage(page); } @@ -69,7 +69,7 @@ public XmlExtAssist getAssist() { return assist; } - public XPage getCurrentPage() { + public XuiPage getCurrentPage() { return currentPage; } @@ -83,7 +83,7 @@ private void removeAfterAtCurrentPage() { } } - private void showPage(XPage page) { + private void showPage(XuiPage page) { if (webView != null && page != null) { Thread thread = new Thread(() -> { GuiScriptLib.showProgressBar(assist.getForm(), 50); @@ -112,7 +112,7 @@ public void back() { } int index = pages.indexOf(currentPage); if (pages.size() > 0 && index >= 1) { - XPage page = pages.get(index - 1); + XuiPage page = pages.get(index - 1); showPage(page); } } @@ -123,7 +123,7 @@ public void forward() { } int index = pages.indexOf(currentPage); if (pages.size() > 0 && index < pages.size() - 1) { - XPage page = pages.get(index + 1); + XuiPage page = pages.get(index + 1); showPage(page); } } diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiBrowserHolder.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiBrowserHolder.java new file mode 100644 index 00000000..6e98832c --- /dev/null +++ b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiBrowserHolder.java @@ -0,0 +1,9 @@ +package org.mini.layout.xwebview; + +import org.mini.gui.GForm; + +public interface XuiBrowserHolder { + XuiBrowser getBrowser(); + + public GForm getForm(); +} diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XPage.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiPage.java similarity index 85% rename from extlib/xgui/src/main/java/org/mini/layout/xwebview/XPage.java rename to extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiPage.java index 595cc398..8a428354 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XPage.java +++ b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiPage.java @@ -14,15 +14,15 @@ /** * a xmlui page */ -public class XPage { +public class XuiPage { - XExplorer explorer; + XuiBrowser explorer; XEventHandler eventDelegate; XmlExtAssist assistDelegate; URL url; GContainer pan; - public XPage(String homeUrl, XExplorer explorer) { + public XuiPage(String homeUrl, XuiBrowser browser) { try { //urlStr="jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class"; url = new URL(homeUrl); @@ -33,11 +33,11 @@ public XPage(String homeUrl, XExplorer explorer) { } catch (MalformedURLException e) { throw new RuntimeException(e); } - XEventHandler eventDelegate = new XPageEventDelegate(explorer, url); + XEventHandler eventDelegate = new XPageEventDelegate(browser, url); this.eventDelegate = eventDelegate; - this.assistDelegate = new XmlExtAssist(explorer.getAssist().getForm()); - this.assistDelegate.copyFrom(explorer.getAssist()); + this.assistDelegate = new XmlExtAssist(browser.getAssist().getForm()); + this.assistDelegate.copyFrom(browser.getAssist()); } public GContainer getGui(float width, float height) { @@ -45,7 +45,7 @@ public GContainer getGui(float width, float height) { return pan; } try { - XResourceLoader loader = new XResourceLoader(); + ResourceLoader loader = new ResourceLoader(); loader.setURL(url); assistDelegate.setLoader(loader); @@ -83,11 +83,11 @@ public URL getUrl() { public static class XPageEventDelegate extends XEventHandler { - XExplorer explorer; + XuiBrowser explorer; XEventHandler eventHandler; URL url; - XPageEventDelegate(XExplorer explorer, URL purl) { + XPageEventDelegate(XuiBrowser explorer, URL purl) { this.explorer = explorer; this.eventHandler = explorer.getEventHandler(); this.url = purl; @@ -116,7 +116,7 @@ public void flying(GObject gObject, float x, float y) { @Override public void gotoHref(GObject gobj, String href) { if (href != null) { - String resurl = XUrlHelper.normalizeUrl(url, href); + String resurl = UrlHelper.normalizeUrl(url, href); explorer.gotoPage(resurl); } eventHandler.gotoHref(gobj, href); diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XResource.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiResource.java similarity index 97% rename from extlib/xgui/src/main/java/org/mini/layout/xwebview/XResource.java rename to extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiResource.java index d97af1c4..f39be990 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XResource.java +++ b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiResource.java @@ -2,7 +2,7 @@ import org.mini.gui.GImage; -public class XResource { +public class XuiResource { public static final int TYPE_UNKNOWN = 0; public static final int TYPE_IMAGE = 1; diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/ExplorerScriptLib.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiScriptLib.java similarity index 80% rename from extlib/xgui/src/main/java/org/mini/layout/xwebview/ExplorerScriptLib.java rename to extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiScriptLib.java index 7bada23b..c1662ae5 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/xwebview/ExplorerScriptLib.java +++ b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiScriptLib.java @@ -11,7 +11,6 @@ import org.mini.gui.gscript.Lib; import org.mini.gui.guilib.GuiScriptLib; import org.mini.http.MiniHttpClient; -import org.mini.nanovg.Nanovg; import java.io.FileOutputStream; import java.io.IOException; @@ -24,13 +23,13 @@ * * @author Gust */ -public class ExplorerScriptLib extends Lib { - XExplorerHolder holder; +public class XuiScriptLib extends Lib { + XuiBrowserHolder holder; /** * */ - public ExplorerScriptLib(XExplorerHolder holder) { + public XuiScriptLib(XuiBrowserHolder holder) { this.holder = holder; // script method register @@ -62,12 +61,12 @@ public DataType openPage(ArrayList para) { String href = Interpreter.popBackStr(para); String callback = Interpreter.popBackStr(para); if (href != null) { - XPage page = holder.getExplorer().getCurrentPage(); + XuiPage page = holder.getBrowser().getCurrentPage(); if (page != null) {// may be href="/abc/c.xml" - href = XUrlHelper.normalizeUrl(page.getUrl(), href); //fix as : http://www.abc.com/abc/c.xml + href = UrlHelper.normalizeUrl(page.getUrl(), href); //fix as : http://www.abc.com/abc/c.xml } - holder.getExplorer().gotoPage(href); - GuiScriptLib.doHttpCallback(holder.getExplorer().getWebView().getForm(), callback, href, 0, ""); + holder.getBrowser().gotoPage(href); + GuiScriptLib.doHttpCallback(holder.getForm(), callback, href, 0, ""); } return null; } @@ -77,9 +76,9 @@ public DataType downloadInstall(ArrayList para) { String href = Interpreter.popBackStr(para); String callback = Interpreter.popBackStr(para); if (href != null) { - XPage page = holder.getExplorer().getCurrentPage(); + XuiPage page = holder.getBrowser().getCurrentPage(); if (page != null) {// may be href="/abc/c.xml" - href = XUrlHelper.normalizeUrl(page.getUrl(), href); //fix as : http://www.abc.com/abc/c.zip + href = UrlHelper.normalizeUrl(page.getUrl(), href); //fix as : http://www.abc.com/abc/c.zip } MiniHttpClient hc = new MiniHttpClient(href, null, new MiniHttpClient.DownloadCompletedHandle() { @@ -90,7 +89,7 @@ public void onCompleted(MiniHttpClient client, String url, byte[] data) { GCmd cmd = new GCmd( () -> { AppManager.getInstance().getDownloadCallback().onCompleted(client, url, data); - GuiScriptLib.doHttpCallback(holder.getExplorer().getWebView().getForm(), callback, url, 0, ""); + GuiScriptLib.doHttpCallback(holder.getForm(), callback, url, 0, ""); }); GForm.addCmd(cmd); GForm.flush(); @@ -99,7 +98,7 @@ public void onCompleted(MiniHttpClient client, String url, byte[] data) { } }); hc.setProgressListener((MiniHttpClient client, int progress) -> { - GuiScriptLib.showProgressBar(holder.getExplorer().getWebView().getForm(), progress); + GuiScriptLib.showProgressBar(holder.getForm(), progress); }); hc.start(); } @@ -110,9 +109,9 @@ public DataType downloadSave(ArrayList para) { String href = Interpreter.popBackStr(para); String callback = Interpreter.popBackStr(para); if (href != null) { - XPage page = holder.getExplorer().getCurrentPage(); + XuiPage page = holder.getBrowser().getCurrentPage(); if (page != null) {// may be href="/abc/c.xml" - href = XUrlHelper.normalizeUrl(page.getUrl(), href); //fix as : http://www.abc.com/abc/c.zip + href = UrlHelper.normalizeUrl(page.getUrl(), href); //fix as : http://www.abc.com/abc/c.zip } MiniHttpClient hc = new MiniHttpClient(href, null, new MiniHttpClient.DownloadCompletedHandle() { @Override @@ -138,7 +137,7 @@ public void onCompleted(MiniHttpClient client, String url, byte[] data) { fos.write(data); fos.close(); - GuiScriptLib.doHttpCallback(holder.getExplorer().getWebView().getForm(), callback, url, 0, path); + GuiScriptLib.doHttpCallback(holder.getForm(), callback, url, 0, path); } catch (IOException e) { throw new RuntimeException(e); } @@ -149,7 +148,7 @@ public void onCompleted(MiniHttpClient client, String url, byte[] data) { } }); hc.setProgressListener((MiniHttpClient client, int progress) -> { - GuiScriptLib.showProgressBar(holder.getExplorer().getWebView().getForm(), progress); + GuiScriptLib.showProgressBar(holder.getForm(), progress); }); hc.start(); } @@ -159,16 +158,16 @@ public void onCompleted(MiniHttpClient client, String url, byte[] data) { public DataType getPageBaseUrl(ArrayList para) { String href = ""; - XPage page = holder.getExplorer().getCurrentPage(); + XuiPage page = holder.getBrowser().getCurrentPage(); if (page != null) {// may be href="/abc/c.xml" - href = XUrlHelper.normalizeUrl(page.getUrl(), "/"); //fix as : http://www.abc.com/abc/c.xml + href = UrlHelper.normalizeUrl(page.getUrl(), "/"); //fix as : http://www.abc.com/abc/c.xml } return Interpreter.getCachedStr(href); } public DataType getPageUrl(ArrayList para) { String href = ""; - XPage page = holder.getExplorer().getCurrentPage(); + XuiPage page = holder.getBrowser().getCurrentPage(); if (page != null) {// may be href="/abc/c.xml" href = page.getUrl().toString(); } @@ -176,7 +175,7 @@ public DataType getPageUrl(ArrayList para) { } public DataType prevPage(ArrayList para) { - XExplorer explorer = holder.getExplorer(); + XuiBrowser explorer = holder.getBrowser(); if (explorer != null) { explorer.back(); } @@ -184,7 +183,7 @@ public DataType prevPage(ArrayList para) { } public DataType nextPage(ArrayList para) { - XExplorer explorer = holder.getExplorer(); + XuiBrowser explorer = holder.getBrowser(); if (explorer != null) { explorer.forward(); } @@ -192,9 +191,9 @@ public DataType nextPage(ArrayList para) { } public DataType refreshPage(ArrayList para) { - XExplorer explorer = holder.getExplorer(); + XuiBrowser explorer = holder.getBrowser(); if (explorer != null) { - XPage page = explorer.getCurrentPage(); + XuiPage page = explorer.getCurrentPage(); if (page != null) { page.reset(); explorer.gotoPage(page.getUrl().toString()); diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/XFileUrlHelper.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/FileUrlHelper.java similarity index 84% rename from extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/XFileUrlHelper.java rename to extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/FileUrlHelper.java index d15573d3..b61fcf8a 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/XFileUrlHelper.java +++ b/extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/FileUrlHelper.java @@ -1,10 +1,10 @@ package org.mini.layout.xwebview.urlhelper; -import org.mini.layout.xwebview.XUrlHelper; +import org.mini.layout.xwebview.UrlHelper; import java.net.URL; -public class XFileUrlHelper extends XUrlHelper { +public class FileUrlHelper extends UrlHelper { public String getProtocol() { return "file"; } diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/XHttpUrlHelper.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/HttpUrlHelper.java similarity index 93% rename from extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/XHttpUrlHelper.java rename to extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/HttpUrlHelper.java index c1e49fc5..b3629e82 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/XHttpUrlHelper.java +++ b/extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/HttpUrlHelper.java @@ -1,10 +1,10 @@ package org.mini.layout.xwebview.urlhelper; -import org.mini.layout.xwebview.XUrlHelper; +import org.mini.layout.xwebview.UrlHelper; import java.net.URL; -public class XHttpUrlHelper extends XUrlHelper { +public class HttpUrlHelper extends UrlHelper { public String getProtocol() { return "file"; } diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/XJarUrlHelper.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/JarUrlHelper.java similarity index 90% rename from extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/XJarUrlHelper.java rename to extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/JarUrlHelper.java index 53d4a06c..66cc995d 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/XJarUrlHelper.java +++ b/extlib/xgui/src/main/java/org/mini/layout/xwebview/urlhelper/JarUrlHelper.java @@ -1,10 +1,10 @@ package org.mini.layout.xwebview.urlhelper; -import org.mini.layout.xwebview.XUrlHelper; +import org.mini.layout.xwebview.UrlHelper; import java.net.URL; -public class XJarUrlHelper extends XUrlHelper { +public class JarUrlHelper extends UrlHelper { public String getProtocol() { return "file"; } diff --git a/minijvm/c/jvm/jdwp.c b/minijvm/c/jvm/jdwp.c index fd1b9d82..a5b4754b 100644 --- a/minijvm/c/jvm/jdwp.c +++ b/minijvm/c/jvm/jdwp.c @@ -2465,7 +2465,7 @@ s32 jdwp_client_process(JdwpServer *jdwpserver, JdwpClient *client) { break; } case JDWP_CMD_ThreadReference_SuspendCount: {//11.12 - jvm_printf("[JDWP]%x not support\n", jdwppacket_get_cmd_err(req)); + //jvm_printf("[JDWP]%x not support\n", jdwppacket_get_cmd_err(req)); Instance *jthread = jdwppacket_read_refer(req); Runtime *r = jthread_get_stackframe_value(jdwpserver->jvm, jthread); if (r) { diff --git a/mobile/iosapp/iosapp.xcodeproj/project.xcworkspace/xcuserdata/Gust.xcuserdatad/UserInterfaceState.xcuserstate b/mobile/iosapp/iosapp.xcodeproj/project.xcworkspace/xcuserdata/Gust.xcuserdatad/UserInterfaceState.xcuserstate index 73b0705a8f1afc400ca6a3912addb01725a9d57b..5a6f59c686e0b4bb064e5b5a29c22c80bba3d8d7 100644 GIT binary patch delta 26868 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�q%-~RPG97g-A*fVx-1&t-U!fDz&8+MXA))e>>{6 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 W@22nnbyH?!{AIUcfBFB`cKj1QXouhc diff --git a/mobile/iosapp/iosapp.xcodeproj/xcuserdata/Gust.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/mobile/iosapp/iosapp.xcodeproj/xcuserdata/Gust.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 5c8a871e..e8a0e063 100644 --- a/mobile/iosapp/iosapp.xcodeproj/xcuserdata/Gust.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/mobile/iosapp/iosapp.xcodeproj/xcuserdata/Gust.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -118,7 +118,7 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "1387" endingLineNumber = "1387" - landmarkName = "jdwp_client_process(client)" + landmarkName = "jdwp_eventset_clear(jdwpserver, id)" landmarkType = "9"> diff --git a/mobile/java/glfm_gui/pom.xml b/mobile/java/glfm_gui/pom.xml index 9d462e05..30d23a32 100755 --- a/mobile/java/glfm_gui/pom.xml +++ b/mobile/java/glfm_gui/pom.xml @@ -8,7 +8,7 @@ io.github.digitalgust glfm_gui ${project.groupId}:${project.artifactId} - 1.1.9 + 1.1.10 miniJVM mobile platform gui library https://github.com/digitalgust/miniJVM From 18e9ecd7412d7ff9371d37ebdf0a63b4e5926c9b Mon Sep 17 00:00:00 2001 From: Gust Date: Wed, 25 Sep 2024 00:15:36 +0800 Subject: [PATCH 08/18] update gui --- extlib/xgui/pom.xml | 4 +- .../java/org/mini/apploader/AppManager.java | 38 ++++++++++++++++--- .../org/mini/gui/guilib/GuiScriptLib.java | 11 ++++-- .../org/mini/layout/xwebview/XuiPage.java | 2 +- .../org/mini/layout/xwebview/XuiResource.java | 37 ++++++++++++++++++ ...urceLoader.java => XuiResourceLoader.java} | 4 +- .../src/main/resource/res/ui/AppManager.xml | 27 ++++++++----- 7 files changed, 100 insertions(+), 23 deletions(-) rename extlib/xgui/src/main/java/org/mini/layout/xwebview/{ResourceLoader.java => XuiResourceLoader.java} (97%) diff --git a/extlib/xgui/pom.xml b/extlib/xgui/pom.xml index 897c7d41..b8e73cc2 100755 --- a/extlib/xgui/pom.xml +++ b/extlib/xgui/pom.xml @@ -196,7 +196,7 @@ run - + @@ -211,7 +211,7 @@ - + diff --git a/extlib/xgui/src/main/java/org/mini/apploader/AppManager.java b/extlib/xgui/src/main/java/org/mini/apploader/AppManager.java index 90a26c5a..1f067cd8 100644 --- a/extlib/xgui/src/main/java/org/mini/apploader/AppManager.java +++ b/extlib/xgui/src/main/java/org/mini/apploader/AppManager.java @@ -12,6 +12,7 @@ import org.mini.gui.gscript.EnvVarProvider; import org.mini.gui.gscript.Interpreter; import org.mini.gui.guilib.GuiScriptLib; +import org.mini.gui.guilib.HttpRequestReply; import org.mini.http.MiniHttpClient; import org.mini.http.MiniHttpServer; import org.mini.json.JsonParser; @@ -19,9 +20,7 @@ import org.mini.layout.XContainer; import org.mini.layout.XEventHandler; import org.mini.layout.XmlExtAssist; -import org.mini.layout.xwebview.XuiScriptLib; -import org.mini.layout.xwebview.XuiBrowser; -import org.mini.layout.xwebview.XuiBrowserHolder; +import org.mini.layout.xwebview.*; import java.io.*; import java.util.*; @@ -830,9 +829,38 @@ void updateScriptEnvironment() { envVarProvider.setEnvVar("from", AppLoader.getBaseInfo("from")); envVarProvider.setEnvVar("cver", AppLoader.getBaseInfo("cver")); envVarProvider.setEnvVar("policy_url", AppLoader.getBaseInfo("policyUrl")); - envVarProvider.setEnvVar("discovery_url", null); - envVarProvider.setEnvVar("ACCOUNT_BASE_URL", null); + envVarProvider.setEnvVar("discovery_url", ""); + envVarProvider.setEnvVar("account_base_url", ""); + envVarProvider.setEnvVar("profile_url", ""); + envVarProvider.setEnvVar("shop_url", ""); + envVarProvider.setEnvVar("pay_url", ""); + envVarProvider.setEnvVar("plugin_url", ""); } + public String[] getPolicy(String url) { + try { + if (url == null) { + return null; + } + XuiResourceLoader loader = new XuiResourceLoader(); + XuiResource res = loader.loadResource(url); + if (res != null) { + String json = res.getString(); + if (json != null) { + JsonParser parser = new JsonParser<>(); + HttpRequestReply reply = parser.deserial(json, HttpRequestReply.class); + if (reply != null && reply.getCode() == 0) { + String s = reply.getReply(); + String[] ss = s.split("\n"); + return ss; + } + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } } diff --git a/extlib/xgui/src/main/java/org/mini/gui/guilib/GuiScriptLib.java b/extlib/xgui/src/main/java/org/mini/gui/guilib/GuiScriptLib.java index 73789a06..7b3f2d74 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/guilib/GuiScriptLib.java +++ b/extlib/xgui/src/main/java/org/mini/gui/guilib/GuiScriptLib.java @@ -111,11 +111,14 @@ public static void doHttpCallback(GForm form, String callback, String url, int c if (callback.contains(".")) { String[] ss = callback.split("\\."); GContainer gobj = GToolkit.getComponent(form, ss[0]); - Interpreter inp = gobj.getInterpreter(); - inp.callSub(ss[1] + "(\"" + url + "\"," + code + ",\"" + reply + "\")"); - } else { - System.out.println("httpRequest callback no GContainer specified: " + callback); + if (gobj != null) { + Interpreter inp = gobj.getInterpreter(); + inp.callSub(ss[1] + "(\"" + url + "\"," + code + ",\"" + reply + "\")"); + return; + } } + System.out.println("httpRequest callback no GContainer specified: " + callback); + } } diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiPage.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiPage.java index 8a428354..bdc5b74b 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiPage.java +++ b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiPage.java @@ -45,7 +45,7 @@ public GContainer getGui(float width, float height) { return pan; } try { - ResourceLoader loader = new ResourceLoader(); + XuiResourceLoader loader = new XuiResourceLoader(); loader.setURL(url); assistDelegate.setLoader(loader); diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiResource.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiResource.java index f39be990..5d0bc849 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiResource.java +++ b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiResource.java @@ -2,12 +2,15 @@ import org.mini.gui.GImage; +import java.io.UnsupportedEncodingException; + public class XuiResource { public static final int TYPE_UNKNOWN = 0; public static final int TYPE_IMAGE = 1; public static final int TYPE_XML = 2; public static final int TYPE_JSON = 3; + public static final int TYPE_TEXT = 4; String url; @@ -16,6 +19,8 @@ public class XuiResource { GImage image; String xml; + String json; + /** * 根据content-type获取类型 @@ -38,7 +43,39 @@ public static int getTypeByString(String type) { return TYPE_XML; } else if (type.startsWith("image/")) { return TYPE_IMAGE; + } else if (type.startsWith("text/")) { + return TYPE_TEXT; } return TYPE_UNKNOWN; } + + public int getType() { + return type; + } + + public String getUrl() { + return url; + } + + public byte[] getData() { + return data; + } + + public GImage getImage() { + return image; + } + + public String getXml() { + return xml; + } + + public String getString() { + try { + if (type == TYPE_JSON || type == TYPE_TEXT || type == TYPE_XML) { + return new String(data, "utf-8"); + } + } catch (UnsupportedEncodingException e) { + } + return null; + } } diff --git a/extlib/xgui/src/main/java/org/mini/layout/xwebview/ResourceLoader.java b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiResourceLoader.java similarity index 97% rename from extlib/xgui/src/main/java/org/mini/layout/xwebview/ResourceLoader.java rename to extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiResourceLoader.java index 67e29d46..f5478316 100644 --- a/extlib/xgui/src/main/java/org/mini/layout/xwebview/ResourceLoader.java +++ b/extlib/xgui/src/main/java/org/mini/layout/xwebview/XuiResourceLoader.java @@ -9,7 +9,7 @@ import java.util.HashMap; import java.util.Map; -public class ResourceLoader implements XmlExtAssist.XLoader { +public class XuiResourceLoader implements XmlExtAssist.XLoader { URL url; static GImage notfoundImage; @@ -17,7 +17,7 @@ public class ResourceLoader implements XmlExtAssist.XLoader { Map resources = new HashMap<>(); - public ResourceLoader() { + public XuiResourceLoader() { notfoundImage = GImage.createImageFromJar("/res/ui/notfound.jpg"); } diff --git a/extlib/xgui/src/main/resource/res/ui/AppManager.xml b/extlib/xgui/src/main/resource/res/ui/AppManager.xml index 35120310..e08092ad 100644 --- a/extlib/xgui/src/main/resource/res/ui/AppManager.xml +++ b/extlib/xgui/src/main/resource/res/ui/AppManager.xml @@ -11,7 +11,7 @@ sub getQueryPara() s="appid="+ getEnv("APPID") + "&appzone=" + getEnv("APPZONE") + "&ver=" + getEnv("SVER") +"&cver="+ getEnv("CVER") +"&from="+ getEnv("FROM") +"&jar="+ getEnv("JAR") +"&lang="+ getEnv("LANG") +"&token="+ getEnv("TOKEN") - println(s) + 'println(s) ret s sub onCallBack(url,code,msg) '通用回调 @@ -19,7 +19,7 @@ ret sub getPolicy() '获取策略 - if(strlen(getEnv("DISCOVERY_URL"))<>0) + if(strlen(getEnv("DISCOVERY_URL"))>0) ret eif @@ -36,8 +36,11 @@ urls=split(msg,"\n") setEnv("DISCOVERY_URL",urls[0]) setEnv("ACCOUNT_BASE_URL",urls[1]) - println("DISCOVERY_URL="+getEnv("DISCOVERY_URL")) - println("ACCOUNT_BASE_URL="+getEnv("ACCOUNT_BASE_URL")) + setEnv("PROFILE_URL",urls[2]) + setEnv("SHOP_URL",urls[3]) + setEnv("PAY_URL",urls[4]) + setEnv("PLUGIN_URL",urls[5]) + println("urls:\n"+urls) else showMsg("onPolicyRequestBack:"+code+","+msg) eif @@ -222,10 +225,14 @@ - - - - + + + + @@ -244,7 +251,9 @@ - + From a5e52572a43284960b37ecc61050e6745e25296e Mon Sep 17 00:00:00 2001 From: Gust Date: Sun, 29 Sep 2024 21:03:07 +0800 Subject: [PATCH 09/18] update gui and set java.version --- extlib/xgui/src/main/java/org/mini/gui/GViewSlot.java | 2 +- .../xgui/src/main/java/org/mini/gui/guilib/GuiScriptLib.java | 3 +++ minijvm/java/src/main/resource/sys.properties | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/extlib/xgui/src/main/java/org/mini/gui/GViewSlot.java b/extlib/xgui/src/main/java/org/mini/gui/GViewSlot.java index 6d614ec9..4902df08 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/GViewSlot.java +++ b/extlib/xgui/src/main/java/org/mini/gui/GViewSlot.java @@ -169,7 +169,7 @@ public void moveTo(int slot, long timeInMils) { public void moveTo(GObject go, long timeInMils) { if (containsImpl(go)) { GObject curGo = getElementsImpl().get(active); - if (curGo != null && go != null) { + if (curGo != null && go != null && swapTask == null) { SlotSwaper swaper = new SlotSwaper(this, curGo, go, timeInMils); swapTask = new GCmd(swaper); GForm.addCmd(swapTask); diff --git a/extlib/xgui/src/main/java/org/mini/gui/guilib/GuiScriptLib.java b/extlib/xgui/src/main/java/org/mini/gui/guilib/GuiScriptLib.java index 7b3f2d74..768267b0 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/guilib/GuiScriptLib.java +++ b/extlib/xgui/src/main/java/org/mini/gui/guilib/GuiScriptLib.java @@ -111,6 +111,9 @@ public static void doHttpCallback(GForm form, String callback, String url, int c if (callback.contains(".")) { String[] ss = callback.split("\\."); GContainer gobj = GToolkit.getComponent(form, ss[0]); + if (gobj == null) { + gobj = GToolkit.getComponent(GCallBack.getInstance().getApplication().getForm(), ss[0]); + } if (gobj != null) { Interpreter inp = gobj.getInterpreter(); inp.callSub(ss[1] + "(\"" + url + "\"," + code + ",\"" + reply + "\")"); diff --git a/minijvm/java/src/main/resource/sys.properties b/minijvm/java/src/main/resource/sys.properties index 3af707c0..e41a8167 100644 --- a/minijvm/java/src/main/resource/sys.properties +++ b/minijvm/java/src/main/resource/sys.properties @@ -6,7 +6,7 @@ java.class.path=jvm_set_value : classpath jvm startup with -cp java.home= java.vendor=org.minijvm java.vendor.url=https://github.com/digitalgust/miniJVM -java.version=1.0 +java.version=1.8.0 line.separator=jvm_set_value: posix="\n" windows="\r\n" os.arch= os.name=jvm_set_value: "Mac" "Linux" "Windows" From 732178037f97044dc4b4a8fe21fe6084d73df35e Mon Sep 17 00:00:00 2001 From: Gust Date: Tue, 8 Oct 2024 21:33:33 +0800 Subject: [PATCH 10/18] add md5 and sha1 --- .../java/java/security/DigestException.java | 22 ++ .../security/GeneralSecurityException.java | 23 ++ .../java/java/security/MessageDigest.java | 167 +++++++++++ .../security/NoSuchAlgorithmException.java | 22 ++ .../security/NoSuchProviderException.java | 14 + .../src/main/java/java/security/Provider.java | 9 + .../src/main/java/org/mini/crypt/MD5.java | 248 ++++++++++++++++ .../src/main/java/org/mini/crypt/SHA1.java | 269 ++++++++++++++++++ 8 files changed, 774 insertions(+) create mode 100644 minijvm/java/src/main/java/java/security/DigestException.java create mode 100644 minijvm/java/src/main/java/java/security/GeneralSecurityException.java create mode 100644 minijvm/java/src/main/java/java/security/MessageDigest.java create mode 100644 minijvm/java/src/main/java/java/security/NoSuchAlgorithmException.java create mode 100644 minijvm/java/src/main/java/java/security/NoSuchProviderException.java create mode 100644 minijvm/java/src/main/java/java/security/Provider.java create mode 100644 minijvm/java/src/main/java/org/mini/crypt/MD5.java create mode 100644 minijvm/java/src/main/java/org/mini/crypt/SHA1.java diff --git a/minijvm/java/src/main/java/java/security/DigestException.java b/minijvm/java/src/main/java/java/security/DigestException.java new file mode 100644 index 00000000..bef76d4c --- /dev/null +++ b/minijvm/java/src/main/java/java/security/DigestException.java @@ -0,0 +1,22 @@ +package java.security; + +public class DigestException extends GeneralSecurityException { + + + public DigestException() { + super(); + } + + public DigestException(String msg) { + super(msg); + } + + public DigestException(String message, Throwable cause) { + super(message, cause); + } + + public DigestException(Throwable cause) { + super(cause); + } +} + diff --git a/minijvm/java/src/main/java/java/security/GeneralSecurityException.java b/minijvm/java/src/main/java/java/security/GeneralSecurityException.java new file mode 100644 index 00000000..c99e34ab --- /dev/null +++ b/minijvm/java/src/main/java/java/security/GeneralSecurityException.java @@ -0,0 +1,23 @@ +package java.security; + + +public class GeneralSecurityException extends Exception { + + + public GeneralSecurityException() { + super(); + } + + public GeneralSecurityException(String msg) { + super(msg); + } + + public GeneralSecurityException(String message, Throwable cause) { + super(message, cause); + } + + public GeneralSecurityException(Throwable cause) { + super(cause); + } +} + diff --git a/minijvm/java/src/main/java/java/security/MessageDigest.java b/minijvm/java/src/main/java/java/security/MessageDigest.java new file mode 100644 index 00000000..bac3a833 --- /dev/null +++ b/minijvm/java/src/main/java/java/security/MessageDigest.java @@ -0,0 +1,167 @@ +package java.security; + + +import org.mini.crypt.MD5; +import org.mini.crypt.SHA1; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.ByteBuffer; + +public abstract class MessageDigest { + + + protected String algorithm; + + // The state of this digest + protected static final int INITIAL = 0; + protected static final int IN_PROGRESS = 1; + protected int state = INITIAL; + + protected Provider provider; + + + protected MessageDigest(String algorithm) { + this.algorithm = algorithm; + } + + public static MessageDigest getInstance(String algorithm) + throws NoSuchAlgorithmException { + try { + MessageDigest md = null; + switch (algorithm) { + case "MD5": + md = new MD5(); + break; + case "SHA-1": + md = new SHA1(); + break; + default: + throw new NoSuchAlgorithmException(algorithm + " not found"); + } + + return md; + + } catch (Exception e) { + throw new NoSuchAlgorithmException(algorithm + " not found"); + } + } + + public static MessageDigest getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { + return getInstance(algorithm); + } + + public static MessageDigest getInstance(String algorithm, + Provider provider) + throws NoSuchAlgorithmException { + return null; + } + + public final Provider getProvider() { + return this.provider; + } + + abstract public void update(byte input); + + abstract public void update(byte[] input, int offset, int len); + + abstract public void update(byte[] input); + + public final void update(ByteBuffer input) { + if (input == null) { + throw new NullPointerException(); + } + state = IN_PROGRESS; + byte[] b = new byte[input.remaining()]; + input.get(b); + update(b); + } + + abstract public byte[] digest(); + + abstract public int digest(byte[] buf, int offset, int len) throws DigestException; + + abstract public byte[] digest(byte[] input); + + private String getProviderName() { + return (provider == null) ? "(no provider)" : provider.getName(); + } + + public String toString() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream p = new PrintStream(baos); + p.print(algorithm + " Message Digest from " + getProviderName() + ", "); + switch (state) { + case INITIAL: + p.print(""); + break; + case IN_PROGRESS: + p.print(""); + break; + } + p.println(); + return (baos.toString()); + } + + /** + * Compares two digests for equality. Does a simple byte compare. + * + * @param digesta one of the digests to compare. + * @param digestb the other digest to compare. + * @return true if the digests are equal, false otherwise. + */ + public static boolean isEqual(byte[] digesta, byte[] digestb) { + /* All bytes in digesta are examined to determine equality. + * The calculation time depends only on the length of digesta + * It does not depend on the length of digestb or the contents + * of digesta and digestb. + */ + if (digesta == digestb) return true; + if (digesta == null || digestb == null) { + return false; + } + + int lenA = digesta.length; + int lenB = digestb.length; + + if (lenB == 0) { + return lenA == 0; + } + + int result = 0; + result |= lenA - lenB; + + // time-constant comparison + for (int i = 0; i < lenA; i++) { + // If i >= lenB, indexB is 0; otherwise, i. + int indexB = ((i - lenB) >>> 31) * i; + result |= digesta[i] ^ digestb[indexB]; + } + return result == 0; + } + + /** + * Resets the digest for further use. + */ + abstract public void reset(); + + public final String getAlgorithm() { + return this.algorithm; + } + + public final int getDigestLength() { + return -1; + } + + public Object clone() throws CloneNotSupportedException { + if (this instanceof Cloneable) { + return super.clone(); + } else { + throw new CloneNotSupportedException(); + } + } + + +} + diff --git a/minijvm/java/src/main/java/java/security/NoSuchAlgorithmException.java b/minijvm/java/src/main/java/java/security/NoSuchAlgorithmException.java new file mode 100644 index 00000000..ee836c3f --- /dev/null +++ b/minijvm/java/src/main/java/java/security/NoSuchAlgorithmException.java @@ -0,0 +1,22 @@ +package java.security; + + +public class NoSuchAlgorithmException extends GeneralSecurityException { + + + public NoSuchAlgorithmException() { + super(); + } + + public NoSuchAlgorithmException(String msg) { + super(msg); + } + + public NoSuchAlgorithmException(String message, Throwable cause) { + super(message, cause); + } + + public NoSuchAlgorithmException(Throwable cause) { + super(cause); + } +} diff --git a/minijvm/java/src/main/java/java/security/NoSuchProviderException.java b/minijvm/java/src/main/java/java/security/NoSuchProviderException.java new file mode 100644 index 00000000..ec95060f --- /dev/null +++ b/minijvm/java/src/main/java/java/security/NoSuchProviderException.java @@ -0,0 +1,14 @@ +package java.security; + + +public class NoSuchProviderException extends GeneralSecurityException { + + + public NoSuchProviderException() { + super(); + } + + public NoSuchProviderException(String msg) { + super(msg); + } +} diff --git a/minijvm/java/src/main/java/java/security/Provider.java b/minijvm/java/src/main/java/java/security/Provider.java new file mode 100644 index 00000000..3b353a20 --- /dev/null +++ b/minijvm/java/src/main/java/java/security/Provider.java @@ -0,0 +1,9 @@ +package java.security; + +import java.util.Properties; + +public abstract class Provider extends Properties { + public String getName() { + return (String) get("provider.name"); + } +} diff --git a/minijvm/java/src/main/java/org/mini/crypt/MD5.java b/minijvm/java/src/main/java/org/mini/crypt/MD5.java new file mode 100644 index 00000000..e13e4a9e --- /dev/null +++ b/minijvm/java/src/main/java/org/mini/crypt/MD5.java @@ -0,0 +1,248 @@ +package org.mini.crypt; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.DigestException; +import java.security.MessageDigest; +import java.util.Arrays; + +public class MD5 extends MessageDigest { + int[] init; + + byte[] buf; + int bufOffset; + long messageBits; + + byte[] oneByte = new byte[]{0}; + + public MD5() { + super("MD5"); + reset(); + } + + @Override + public void reset() { + state = INITIAL; + + init = new int[]{0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476}; + buf = new byte[64]; + bufOffset = 0; + messageBits = 0; + } + + @Override + public void update(byte[] src, int off, int len) { + if (src == null) { + return; + } + if (src.length == 0) { + return; + } + if (off < 0 || off >= src.length || len < 0 || len > src.length) { + throw new IllegalArgumentException("src offset or length error"); + } + if (off == 0 && len == buf.length) { + update(src); + } else { + byte[] b = new byte[len]; + System.arraycopy(src, off, b, 0, len); + update(b); + } + } + + @Override + public void update(byte input) { + oneByte[0] = input; + update(oneByte); + } + + @Override + public void update(byte[] src) { + if (src == null) { + return; + } + state = IN_PROGRESS; + + int srcOffset = 0; + int srcRemaining = 0; + int copyLength = 0; + try { + while (true) { + copyLength = buf.length - bufOffset; + srcRemaining = src.length - srcOffset; + if (srcRemaining < copyLength) { + copyLength = srcRemaining; + System.arraycopy(src, srcOffset, buf, bufOffset, copyLength); + bufOffset += copyLength; + messageBits += copyLength * 8; + return; + } else { + System.arraycopy(src, srcOffset, buf, bufOffset, copyLength); + init = md5_2(buf, init); + bufOffset = 0; + srcOffset += copyLength; + messageBits += copyLength * 8; + } + } + + + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public byte[] digest() { + for (int i = bufOffset; i < buf.length; i++) { + buf[i] = 0; + } + if (bufOffset < 56) { + buf[bufOffset] = -128; + } else { + buf[bufOffset] = -128; + init = md5_2(buf, init); + bufOffset = 0; + Arrays.fill(buf, (byte) 0); + } + for (int i = 0; i < 8; i++) { + buf[56 + i] = new Long(messageBits >>> i * 8).byteValue(); + } + + init = md5_2(buf, init); + + ByteBuffer bb = ByteBuffer.allocate(16); + bb.put((byte) (init[0] >>> 0)); + bb.put((byte) (init[0] >>> 8)); + bb.put((byte) (init[0] >>> 16)); + bb.put((byte) (init[0] >>> 24)); + bb.put((byte) (init[1] >>> 0)); + bb.put((byte) (init[1] >>> 8)); + bb.put((byte) (init[1] >>> 16)); + bb.put((byte) (init[1] >>> 24)); + bb.put((byte) (init[2] >>> 0)); + bb.put((byte) (init[2] >>> 8)); + bb.put((byte) (init[2] >>> 16)); + bb.put((byte) (init[2] >>> 24)); + bb.put((byte) (init[3] >>> 0)); + bb.put((byte) (init[3] >>> 8)); + bb.put((byte) (init[3] >>> 16)); + bb.put((byte) (init[3] >>> 24)); + //clear buf + reset(); + + return bb.array(); + } + + @Override + public int digest(byte[] buf, int offset, int len) throws DigestException { + byte[] md5 = digest(); + if (len >= md5.length) { + System.arraycopy(md5, 0, buf, offset, md5.length); + return md5.length; + } else { + System.arraycopy(md5, 0, buf, offset, len); + return len; + } + } + + @Override + public byte[] digest(byte[] input) { + update(input); + return digest(); + } + + int s[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, + 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, + 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; + int[] k = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, + 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, + 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, + 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, + 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, + 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, + 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, + 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, + 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, + 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}; + + private int[] md5_2(byte[] bytes, int[] input) { + int A = input[0], B = input[1], C = input[2], D = input[3]; + int a = A, b = B, c = C, d = D; + + + for (int i = 0; i < 64; i++) { + int f, g; + if (i < 16) { + f = (b & c) | (~b & d); + g = i; + } else if (i < 32) { + f = (b & d) | (~d & c); + g = (5 * i + 1) % 16; + } else if (i < 48) { + f = b ^ c ^ d; + g = (3 * i + 5) % 16; + } else { + f = c ^ (~d | b); + g = 7 * i % 16; + } + + int m = ((bytes[4 * g + 3] & 0xff) << 24) + | ((bytes[4 * g + 2] & 0xff) << 16) + | ((bytes[4 * g + 1] & 0xff) << 8) + | ((bytes[4 * g + 0] & 0xff) << 0);// + int b_temp = b; + b = b + Integer.rotateLeft(a + f + m + k[i], s[i]); + a = d; + d = c; + c = b_temp; + } + A += a; + B += b; + C += c; + D += d; + + input[0] = A; + input[1] = B; + input[2] = C; + input[3] = D; + + return input; + } + + +// public static void main(String[] args) { +// MD5 md5 = new MD5(); +// md5.update("hello".getBytes());//5d41402abc4b2a76b9719d911017c592 +// System.out.println(byteArrayToHex(md5.digest())); +// +// MD5 md52 = new MD5();//3f863ccb5fcb828e4eaaed663b28763a +// File f = new File("D:\\GitHub\\miniJVM\\binary\\win_x64\\apps\\shl.jar"); +// byte[] b = new byte[1024]; +// try { +// FileInputStream fis = new FileInputStream(f); +// while (fis.available() > 0) { +// int len = fis.read(b); +// md52.update(b, 0, len); +// } +// fis.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// System.out.println(byteArrayToHex(md52.digest())); +// System.out.println(byteArrayToHex(md52.digest())); +// System.out.println(byteArrayToHex(md52.digest())); +// } +// +// public static String byteArrayToHex(byte[] a) { +// StringBuilder sb = new StringBuilder(a.length * 2); +// for (byte b : a) +// sb.append(String.format("%02x", b & 0xff)); +// return sb.toString(); +// } +} diff --git a/minijvm/java/src/main/java/org/mini/crypt/SHA1.java b/minijvm/java/src/main/java/org/mini/crypt/SHA1.java new file mode 100644 index 00000000..aceb1197 --- /dev/null +++ b/minijvm/java/src/main/java/org/mini/crypt/SHA1.java @@ -0,0 +1,269 @@ +package org.mini.crypt; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.DigestException; +import java.security.MessageDigest; +import java.util.Arrays; + +/** + * This is my custom implementation of the cryptographic hash function SHA-1 on a String. + *

+ * The ideas in this program are modified from a lecture on youtube given by Christof Paar, + * as well as the ideas found in the FIPS publication. + * + * @author Joe Peacock + * @Version 1.2 + * @Date 11/22/14 + */ +public class SHA1 extends MessageDigest { + //the message to be hashed + private static String message = "Default Message Value"; + //the iniial input into the algorithm + private static int A = 0x67452301; + private static int B = 0xEFCDAB89; + private static int C = 0x98BADCFE; + private static int D = 0x10325476; + private static int E = 0xC3D2E1F0; + + int[] init = new int[5]; //initial inputs. Stored in an array for convenience + + + byte[] buf; + int bufOffset; + long messageBits; + + byte[] oneByte = new byte[]{0}; + + + public SHA1() { + super("SHA-1"); + reset(); + } + + @Override + public void reset() { + state = INITIAL; + + init = new int[5]; //iniial inputs. Stored in an array for convenience + buf = new byte[64]; + bufOffset = 0; + messageBits = 0; + + init[0] = A; + init[1] = B; + init[2] = C; + init[3] = D; + init[4] = E; + } + + @Override + public void update(byte[] src, int off, int len) { + if (src == null) { + return; + } + if (src.length == 0) { + return; + } + if (off < 0 || off >= src.length || len < 0 || len > src.length) { + throw new IllegalArgumentException("src offset or length error"); + } + if (off == 0 && len == buf.length) { + update(src); + } else { + byte[] b = new byte[len]; + System.arraycopy(src, off, b, 0, len); + update(b); + } + } + + @Override + public void update(byte input) { + oneByte[0] = input; + update(oneByte); + } + + @Override + public void update(byte[] src) { + if (src == null) { + return; + } + state = IN_PROGRESS; + + int srcOffset = 0; + int srcRemaining = 0; + int copyLength = 0; + try { + while (true) { + copyLength = buf.length - bufOffset; + srcRemaining = src.length - srcOffset; + if (srcRemaining < copyLength) { + copyLength = srcRemaining; + System.arraycopy(src, srcOffset, buf, bufOffset, copyLength); + bufOffset += copyLength; + messageBits += copyLength * 8; + return; + } else { + System.arraycopy(src, srcOffset, buf, bufOffset, copyLength); + init = sha_2(buf, init); + bufOffset = 0; + srcOffset += copyLength; + messageBits += copyLength * 8; + } + } + + + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public byte[] digest() { + for (int i = bufOffset; i < buf.length; i++) { + buf[i] = 0; + } + if (bufOffset < 56) { + buf[bufOffset] = -128; + } else { + buf[bufOffset] = -128; + init = sha_2(buf, init); + bufOffset = 0; + Arrays.fill(buf, (byte) 0); + } + for (int i = 0; i < 8; i++) { + buf[64 - 1 - i] = (byte) ((messageBits >>> (8 * i)) & 0xFF); + } + + init = sha_2(buf, init); + + ByteBuffer bb = ByteBuffer.allocate(20); + bb.put((byte) (init[0] >>> 24)); + bb.put((byte) (init[0] >>> 16)); + bb.put((byte) (init[0] >>> 8)); + bb.put((byte) (init[0] >>> 0)); + bb.put((byte) (init[1] >>> 24)); + bb.put((byte) (init[1] >>> 16)); + bb.put((byte) (init[1] >>> 8)); + bb.put((byte) (init[1] >>> 0)); + bb.put((byte) (init[2] >>> 24)); + bb.put((byte) (init[2] >>> 16)); + bb.put((byte) (init[2] >>> 8)); + bb.put((byte) (init[2] >>> 0)); + bb.put((byte) (init[3] >>> 24)); + bb.put((byte) (init[3] >>> 16)); + bb.put((byte) (init[3] >>> 8)); + bb.put((byte) (init[3] >>> 0)); + bb.put((byte) (init[4] >>> 24)); + bb.put((byte) (init[4] >>> 16)); + bb.put((byte) (init[4] >>> 8)); + bb.put((byte) (init[4] >>> 0)); + //clear buf + reset(); + + return bb.array(); + } + + @Override + public int digest(byte[] buf, int offset, int len) throws DigestException { + return 0; + } + + @Override + public byte[] digest(byte[] input) { + return new byte[0]; + } + + + private static int[] sha_2(byte[] block, int[] input) { + + int[] W = new int[80]; + + for (int j = 0; j < 16; j++) { + W[j] = ((block[(j << 2)] & 0xFF) << 24) + | ((block[(j << 2) + 1] & 0xFF) << 16) + | ((block[(j << 2) + 2] & 0xFF) << 8) + | (block[(j << 2) + 3] & 0xFF); + } + + for (int j = 16; j < 80; j++) { + W[j] = leftRotate(W[j - 3] ^ W[j - 8] ^ W[j - 14] ^ W[j - 16], 1); + } + + int a = input[0]; + int b = input[1]; + int c = input[2]; + int d = input[3]; + int e = input[4]; + + for (int j = 0; j < 80; j++) { + int f, k; + if (j < 20) { + f = (b & c) | ((~b) & d); + k = 0x5A827999; + } else if (j < 40) { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } else if (j < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } else { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + + int temp = leftRotate(a, 5) + f + e + k + W[j]; + e = d; + d = c; + c = leftRotate(b, 30); + b = a; + a = temp; + } + + input[0] += a; + input[1] += b; + input[2] += c; + input[3] += d; + input[4] += e; + + return input; + } + + private static int leftRotate(int value, int bits) { + return (value << bits) | (value >>> (32 - bits)); + } + + +// public static void main(String[] args) { +// SHA1 md5 = new SHA1();//aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d +// md5.update("01234567890123456789012345678901234567890123456789012345".getBytes()); +//// md5.update("hello".getBytes()); +// System.out.println(byteArrayToHex(md5.digest())); +// +// SHA1 md52 = new SHA1();//6fccfe04eeabef6526f5317f9480ddfe6a1a871b +// File f = new File("D:\\GitHub\\miniJVM\\binary\\win_x64\\apps\\shl.jar"); +// byte[] b = new byte[123]; +// try { +// FileInputStream fis = new FileInputStream(f); +// while (fis.available() > 0) { +// int len = fis.read(b); +// md52.update(b, 0, len); +// } +// fis.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// System.out.println(byteArrayToHex(md52.digest())); +// System.out.println(byteArrayToHex(md52.digest())); +// System.out.println(byteArrayToHex(md52.digest())); +// } +// +// public static String byteArrayToHex(byte[] a) { +// StringBuilder sb = new StringBuilder(a.length * 2); +// for (byte b : a) +// sb.append(String.format("%02x", b & 0xff)); +// return sb.toString(); +// } +} From c85dc90700d6c080cb04dd5c3adfa87bc83fecad Mon Sep 17 00:00:00 2001 From: Gust Date: Tue, 8 Oct 2024 21:51:29 +0800 Subject: [PATCH 11/18] fix messagedigest --- .../src/main/java/java/security/MessageDigest.java | 14 +++----------- minijvm/java/src/main/java/org/mini/crypt/MD5.java | 6 +++++- .../java/src/main/java/org/mini/crypt/SHA1.java | 6 +++++- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/minijvm/java/src/main/java/java/security/MessageDigest.java b/minijvm/java/src/main/java/java/security/MessageDigest.java index bac3a833..57e5432a 100644 --- a/minijvm/java/src/main/java/java/security/MessageDigest.java +++ b/minijvm/java/src/main/java/java/security/MessageDigest.java @@ -104,13 +104,6 @@ public String toString() { return (baos.toString()); } - /** - * Compares two digests for equality. Does a simple byte compare. - * - * @param digesta one of the digests to compare. - * @param digestb the other digest to compare. - * @return true if the digests are equal, false otherwise. - */ public static boolean isEqual(byte[] digesta, byte[] digestb) { /* All bytes in digesta are examined to determine equality. * The calculation time depends only on the length of digesta @@ -141,9 +134,6 @@ public static boolean isEqual(byte[] digesta, byte[] digestb) { return result == 0; } - /** - * Resets the digest for further use. - */ abstract public void reset(); public final String getAlgorithm() { @@ -151,9 +141,11 @@ public final String getAlgorithm() { } public final int getDigestLength() { - return -1; + return getDigestLengthImpl(); } + protected abstract int getDigestLengthImpl(); + public Object clone() throws CloneNotSupportedException { if (this instanceof Cloneable) { return super.clone(); diff --git a/minijvm/java/src/main/java/org/mini/crypt/MD5.java b/minijvm/java/src/main/java/org/mini/crypt/MD5.java index e13e4a9e..3fa3f32e 100644 --- a/minijvm/java/src/main/java/org/mini/crypt/MD5.java +++ b/minijvm/java/src/main/java/org/mini/crypt/MD5.java @@ -93,6 +93,10 @@ public void update(byte[] src) { } } + protected int getDigestLengthImpl() { + return init.length * Integer.BYTES; + } + @Override public byte[] digest() { for (int i = bufOffset; i < buf.length; i++) { @@ -112,7 +116,7 @@ public byte[] digest() { init = md5_2(buf, init); - ByteBuffer bb = ByteBuffer.allocate(16); + ByteBuffer bb = ByteBuffer.allocate(getDigestLengthImpl()); bb.put((byte) (init[0] >>> 0)); bb.put((byte) (init[0] >>> 8)); bb.put((byte) (init[0] >>> 16)); diff --git a/minijvm/java/src/main/java/org/mini/crypt/SHA1.java b/minijvm/java/src/main/java/org/mini/crypt/SHA1.java index aceb1197..34e525b5 100644 --- a/minijvm/java/src/main/java/org/mini/crypt/SHA1.java +++ b/minijvm/java/src/main/java/org/mini/crypt/SHA1.java @@ -120,6 +120,10 @@ public void update(byte[] src) { } } + protected int getDigestLengthImpl() { + return init.length * Integer.BYTES; + } + @Override public byte[] digest() { for (int i = bufOffset; i < buf.length; i++) { @@ -139,7 +143,7 @@ public byte[] digest() { init = sha_2(buf, init); - ByteBuffer bb = ByteBuffer.allocate(20); + ByteBuffer bb = ByteBuffer.allocate(getDigestLengthImpl()); bb.put((byte) (init[0] >>> 24)); bb.put((byte) (init[0] >>> 16)); bb.put((byte) (init[0] >>> 8)); From 1a6e105edf44c438cb0eafac5130fdb485ffa825 Mon Sep 17 00:00:00 2001 From: Gust Date: Wed, 9 Oct 2024 00:05:22 +0800 Subject: [PATCH 12/18] add open other app in script lib --- .../src/main/java/org/mini/glfm/Glfm.java | 19 +++++++ .../java/org/mini/gui/gscript/Stdlib.java | 50 +++++++++++++++++++ extlib/xgui/src/main/resource/res/lang.json | 5 ++ .../src/main/resource/res/ui/AppManager.xml | 29 ++++++----- minijvm/c/jvm/jvm_util.c | 2 +- 5 files changed, 92 insertions(+), 13 deletions(-) diff --git a/desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java b/desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java index 14e9d71f..218cc7b6 100644 --- a/desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java +++ b/desktop/glfw_gui/java/src/main/java/org/mini/glfm/Glfm.java @@ -297,6 +297,25 @@ public static boolean glfmExtensionSupported(String extension) { } public static int glfmOpenOtherApp(byte[] cStyleURL, byte[] cStyleMore, int detectAppInstalled) { + try { + String url = new String(cStyleURL, "utf-8"); + String more = new String(cStyleMore, "utf-8"); + String osName = System.getProperty("os.name", "");// 获取操作系统的名字 + if (osName.startsWith("Mac OS")) { + // Mac OS + if (more.length() > 0) { + Runtime.getRuntime().exec("open " + url + " " + more); + } else { + Runtime.getRuntime().exec("open " + url); + } + } else if (osName.startsWith("Windows")) { + // Windows + Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url); + } + + } catch (Exception e) { + e.printStackTrace(); + } return 1; } diff --git a/extlib/xgui/src/main/java/org/mini/gui/gscript/Stdlib.java b/extlib/xgui/src/main/java/org/mini/gui/gscript/Stdlib.java index 59798ae0..e4ef758f 100644 --- a/extlib/xgui/src/main/java/org/mini/gui/gscript/Stdlib.java +++ b/extlib/xgui/src/main/java/org/mini/gui/gscript/Stdlib.java @@ -2,6 +2,7 @@ import org.mini.crypt.XorCrypt; import org.mini.glfm.Glfm; +import org.mini.glwrap.GLUtil; import org.mini.gui.GCallBack; import org.mini.reflect.ReflectMethod; @@ -10,6 +11,7 @@ import java.lang.reflect.Method; import java.net.URLDecoder; import java.net.URLEncoder; +import java.security.MessageDigest; import java.util.Random; import java.util.ArrayList; @@ -66,8 +68,11 @@ public Stdlib(Interpreter inp) { methodNames.put("setbit".toLowerCase(), this::setbit);//设整数第n位 methodNames.put("encrypt".toLowerCase(), this::encrypt);//加密 str= encrypt(str,key) methodNames.put("decrypt".toLowerCase(), this::decrypt);//解密 str= decrypt(str,key) + methodNames.put("md5".toLowerCase(), this::md5);//md5 str= md5(str) 返回32位字符串(16字节串) + methodNames.put("sha1".toLowerCase(), this::sha1);//sha1 str= sha1(str) 返回40位字符串(20字节串) methodNames.put("remoteMethodCall".toLowerCase(), this::remoteMethodCall);//远程调用 methodNames.put("buyAppleProductById".toLowerCase(), this::buyAppleProductById);//远程调用 + methodNames.put("openOtherApp".toLowerCase(), this::openOtherApp);//打开其他应用 } @@ -675,6 +680,39 @@ private DataType decrypt(ArrayList para) { return Interpreter.getCachedStr(""); } + public static String byteArrayToHex(byte[] a) { + StringBuilder sb = new StringBuilder(a.length * 2); + for (byte b : a) + sb.append(String.format("%02x", b & 0xff)); + return sb.toString(); + } + + private DataType md5(ArrayList para) { + try { + String str = Interpreter.popBackStr(para); + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(str.getBytes("utf-8")); + String str1 = (byteArrayToHex(md.digest())); + return Interpreter.getCachedStr(str1); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + private DataType sha1(ArrayList para) { + try { + String str = Interpreter.popBackStr(para); + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(str.getBytes("utf-8")); + String str1 = (byteArrayToHex(md.digest())); + return Interpreter.getCachedStr(str1); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + private DataType remoteMethodCall(ArrayList para) { try { String str = Interpreter.popBackStr(para); @@ -697,4 +735,16 @@ private DataType buyAppleProductById(ArrayList para) { return null; } + private DataType openOtherApp(ArrayList para) { + try { + String urlstr = Interpreter.popBackStr(para); + String moreStr = Interpreter.popBackStr(para); + int detectAppInstalled = Interpreter.popBackInt(para); + Glfm.glfmOpenOtherApp(GLUtil.toCstyleBytes(urlstr), GLUtil.toCstyleBytes(moreStr), detectAppInstalled); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + } diff --git a/extlib/xgui/src/main/resource/res/lang.json b/extlib/xgui/src/main/resource/res/lang.json index b793e62d..9f8b4b2b 100644 --- a/extlib/xgui/src/main/resource/res/lang.json +++ b/extlib/xgui/src/main/resource/res/lang.json @@ -319,6 +319,11 @@ "Fail", "失败", "失敗" + ], + "STR_UPDATE": [ + "Update", + "检测更新", + "检测更新" ] } } \ No newline at end of file diff --git a/extlib/xgui/src/main/resource/res/ui/AppManager.xml b/extlib/xgui/src/main/resource/res/ui/AppManager.xml index e08092ad..c5d38b91 100644 --- a/extlib/xgui/src/main/resource/res/ui/AppManager.xml +++ b/extlib/xgui/src/main/resource/res/ui/AppManager.xml @@ -40,6 +40,7 @@ setEnv("SHOP_URL",urls[3]) setEnv("PAY_URL",urls[4]) setEnv("PLUGIN_URL",urls[5]) + setEnv("UPDATE_URL",urls[6]) println("urls:\n"+urls) else showMsg("onPolicyRequestBack:"+code+","+msg) @@ -112,6 +113,14 @@ openPage(s3,"") ret + sub checkUpdate() + url=getEnv("UPDATE_URL")+"?"+ getQueryPara() + if(strlen(url)>0) + openOtherApp(url,"",0) ' 第一个为 url,第二个为参数,第三个为检测是否App 存在,0为不检测,1为检测 + eif + ret + + ]]> @@ -197,8 +206,7 @@ - +
@@ -225,14 +233,10 @@ - - - - + + + + @@ -251,8 +255,7 @@ -