4040public class ModManagerUpdater {
4141
4242 private static final String TAG = "ModManagerUpdater" ;
43- private static final String PROXY_URL = "https://gh-proxy.org/" ;
43+ private static final String DEFAULT_PROXY_URL = "https://gh-proxy.org/" ;
4444
4545 private final Activity activity ;
4646 private String versionJsonUrl ;
4747 private Path basePath ;
4848 private boolean useProxy = true ;
49+ /** 自定义代理前缀;null 时用 {@link #DEFAULT_PROXY_URL}(仅在 useProxy 开启时生效) */
50+ private String customProxyUrl ;
4951
5052 public static ModManagerUpdater create (Activity activity ) {
5153 return new ModManagerUpdater (activity );
@@ -188,7 +190,7 @@ private void showDownloadDialog(VersionInfo remote, Path mgr, Path rt, Path mods
188190 Log .w (TAG , "Failed to delete local version.json: " + e .getMessage ());
189191 }
190192 }
191- Log .i (TAG , "Starting download: url=" + remote .managerUrl + " target=" + mgr );
193+ Log .i (TAG , "Starting download: url=" + remote .downloadUrl () + " target=" + mgr );
192194 var dialog = new AlertDialog .Builder (activity )
193195 .setTitle (hasLocal ? "更新中" : "下载中" )
194196 .setMessage ("正在下载..." )
@@ -244,18 +246,19 @@ private Path downloadAndExtractSync(VersionInfo version, Path targetDir, AlertDi
244246 var zipFile = targetDir .getParent ().resolve ("manager-download.zip" );
245247
246248 updateDialog (dialog , "正在下载..." , -1 );
247- downloadFile (version .managerUrl , zipFile ,
249+ downloadFile (version .downloadUrl () , zipFile ,
248250 pct -> updateDialog (dialog , "正在下载 " + pct + "%" , pct ));
249251
250252 long zipSize = Files .size (zipFile );
251253 Log .i (TAG , "Downloaded " + zipSize + " bytes" );
252254
253- if (version .sha256 != null && !version .sha256 .isEmpty ()) {
255+ var expected = version .downloadSha256 ();
256+ if (expected != null && !expected .isEmpty ()) {
254257 updateDialog (dialog , "校验中..." , -1 );
255258 Log .i (TAG , "Verifying SHA-256..." );
256259 var actual = sha256 (zipFile );
257- if (!actual .equalsIgnoreCase (version . sha256 )) {
258- Log .e (TAG , "SHA-256 mismatch! expected=" + version . sha256 + " actual=" + actual );
260+ if (!actual .equalsIgnoreCase (expected )) {
261+ Log .e (TAG , "SHA-256 mismatch! expected=" + expected + " actual=" + actual );
259262 Files .delete (zipFile );
260263 throw new IOException ("SHA-256 mismatch" );
261264 }
@@ -288,7 +291,7 @@ private void launch(Path mgr, Path rt, Path mods, boolean useNewThread) {
288291 Log .i (TAG , "Created mods directory: " + mods );
289292 }
290293
291- // 从 version.json 读取入口配置,不存在则用默认值
294+ // 从 version.json 读取入口配置(platforms.android),无则用编译期默认值
292295 var verFile = mgr .resolve ("version.json" );
293296 String dll = "StArray.ModManager.dll" ;
294297 String type = "StArray.ModManager.Managed" ;
@@ -297,17 +300,20 @@ private void launch(Path mgr, Path rt, Path mods, boolean useNewThread) {
297300 try {
298301 var localVer = JSON .parseObject (
299302 new String (Files .readAllBytes (verFile )), VersionInfo .class );
300- if (localVer .entryAssembly != null && !localVer .entryAssembly .isEmpty ())
301- dll = localVer .entryAssembly ;
302- if (localVer .entryMethod != null && !localVer .entryMethod .isEmpty ()) {
303- // 格式:"TypeName::MethodName" → type, method
304- var parts = localVer .entryMethod .split ("::" , 2 );
305- if (parts .length == 2 ) {
306- type = parts [0 ].trim ();
307- method = parts [1 ].trim ();
308- } else {
309- // 单值当作方法名,类型取默认
310- method = localVer .entryMethod .trim ();
303+ var android = localVer .android ();
304+ if (android != null ) {
305+ if (android .entryAssembly != null && !android .entryAssembly .isEmpty ())
306+ dll = android .entryAssembly ;
307+ if (android .entryMethod != null && !android .entryMethod .isEmpty ()) {
308+ // 格式:"TypeName::MethodName" → type, method
309+ var parts = android .entryMethod .split ("::" , 2 );
310+ if (parts .length == 2 ) {
311+ type = parts [0 ].trim ();
312+ method = parts [1 ].trim ();
313+ } else {
314+ // 单值当作方法名,类型取默认
315+ method = android .entryMethod .trim ();
316+ }
311317 }
312318 }
313319 Log .i (TAG , "Entry from version.json: " + dll + " → " + type + "::" + method );
@@ -348,22 +354,52 @@ public static class VersionInfo {
348354 public String version ;
349355 @ JSONField (name = "versionCode" )
350356 public int versionCode ;
357+ @ JSONField (name = "platforms" )
358+ public PlatformInfo [] platforms ;
359+
360+ /** android 平台配置(platforms[].name == "android"),无则 null。 */
361+ public PlatformInfo android () {
362+ if (platforms == null ) return null ;
363+ for (var p : platforms ) {
364+ if ("android" .equalsIgnoreCase (p .name )) return p ;
365+ }
366+ return null ;
367+ }
368+
369+ /** 下载地址(android 平台的 manager 字段)。 */
370+ public String downloadUrl () {
371+ var p = android ();
372+ return p != null ? p .managerUrl : null ;
373+ }
374+
375+ /** SHA-256(android 平台)。 */
376+ public String downloadSha256 () {
377+ var p = android ();
378+ return p != null ? p .sha256 : null ;
379+ }
380+ }
381+
382+ /** platforms 数组元素(name/manager/sha256/entryAssembly/entryMethod)。 */
383+ public static class PlatformInfo {
384+ @ JSONField (name = "name" )
385+ public String name ;
351386 @ JSONField (name = "manager" )
352387 public String managerUrl ;
353388 @ JSONField (name = "sha256" )
354389 public String sha256 ;
355- /** 入口程序集文件名(如 "StArray.ModManager.dll") */
356390 @ JSONField (name = "entryAssembly" )
357391 public String entryAssembly ;
358- /** 入口类型与方法(如 "StArray.ModManager.Managed::Entry") */
359392 @ JSONField (name = "entryMethod" )
360393 public String entryMethod ;
361394 }
362395
363396 // ──── HTTP ────
364397
365398 private String proxyUrl (String url ) {
366- return useProxy ? PROXY_URL + url : url ;
399+ if (!useProxy ) return url ;
400+ var prefix = customProxyUrl != null && !customProxyUrl .isEmpty ()
401+ ? customProxyUrl : DEFAULT_PROXY_URL ;
402+ return prefix + url ;
367403 }
368404
369405 private String httpGetString (String urlStr ) throws IOException {
0 commit comments