capacitor-dex-editor 0.0.32 → 0.0.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -67,6 +67,9 @@ dependencies {
67
67
  // APK Signer - V1/V2/V3/V4 签名支持 (Android 7.0+)
68
68
  api 'com.android.tools.build:apksig:8.7.2'
69
69
 
70
+ // ARSCLib - 二进制 XML 解析和修改(无限制)
71
+ api 'io.github.nicholsontech:arsc:2.6.3'
72
+
70
73
  testImplementation "junit:junit:$junitVersion"
71
74
  androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
72
75
  androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
@@ -2407,18 +2407,47 @@ public class DexManager {
2407
2407
  // ==================== XML/资源操作方法 ====================
2408
2408
 
2409
2409
  /**
2410
- * 获取 APK 的 AndroidManifest.xml(解码为可读 XML)
2410
+ * 获取 APK 的 AndroidManifest.xml(使用 ARSCLib 解码为可读 XML)
2411
2411
  */
2412
2412
  public JSObject getManifestFromApk(String apkPath) throws Exception {
2413
2413
  JSObject result = new JSObject();
2414
2414
 
2415
+ try {
2416
+ // 使用 ARSCLib 读取 Manifest
2417
+ com.reandroid.apk.ApkModule apkModule = com.reandroid.apk.ApkModule.loadApkFile(new java.io.File(apkPath));
2418
+ com.reandroid.arsc.chunk.xml.AndroidManifestBlock manifest = apkModule.getAndroidManifest();
2419
+
2420
+ if (manifest == null) {
2421
+ apkModule.close();
2422
+ throw new Exception("AndroidManifest.xml not found in APK");
2423
+ }
2424
+
2425
+ // 序列化为可读 XML
2426
+ String xmlContent = manifest.serializeToXml();
2427
+ apkModule.close();
2428
+
2429
+ result.put("manifest", xmlContent);
2430
+
2431
+ } catch (Exception e) {
2432
+ Log.e(TAG, "Get manifest error: " + e.getMessage(), e);
2433
+ // 回退到旧实现
2434
+ result.put("manifest", getManifestFallback(apkPath));
2435
+ }
2436
+
2437
+ return result;
2438
+ }
2439
+
2440
+ /**
2441
+ * 获取 Manifest 的回退实现(使用简单 AXML 解析器)
2442
+ */
2443
+ private String getManifestFallback(String apkPath) {
2415
2444
  java.util.zip.ZipFile zipFile = null;
2416
2445
  try {
2417
2446
  zipFile = new java.util.zip.ZipFile(apkPath);
2418
2447
  java.util.zip.ZipEntry manifestEntry = zipFile.getEntry("AndroidManifest.xml");
2419
2448
 
2420
2449
  if (manifestEntry == null) {
2421
- throw new Exception("AndroidManifest.xml not found in APK");
2450
+ return "# AndroidManifest.xml not found";
2422
2451
  }
2423
2452
 
2424
2453
  java.io.InputStream is = zipFile.getInputStream(manifestEntry);
@@ -2430,19 +2459,15 @@ public class DexManager {
2430
2459
  }
2431
2460
  is.close();
2432
2461
 
2433
- // 解析二进制 AXML
2434
- byte[] axmlData = baos.toByteArray();
2435
- String xmlContent = decodeAxml(axmlData);
2436
-
2437
- result.put("manifest", xmlContent);
2462
+ return decodeAxml(baos.toByteArray());
2438
2463
 
2464
+ } catch (Exception e) {
2465
+ return "# Error reading manifest: " + e.getMessage();
2439
2466
  } finally {
2440
2467
  if (zipFile != null) {
2441
2468
  try { zipFile.close(); } catch (Exception ignored) {}
2442
2469
  }
2443
2470
  }
2444
-
2445
- return result;
2446
2471
  }
2447
2472
 
2448
2473
  /**
@@ -2636,7 +2661,7 @@ public class DexManager {
2636
2661
  }
2637
2662
 
2638
2663
  /**
2639
- * 精准替换 AndroidManifest.xml 中的字符串(直接修改二进制 AXML)
2664
+ * 精准替换 AndroidManifest.xml 中的字符串(使用 ARSCLib,无长度限制)
2640
2665
  */
2641
2666
  public JSObject replaceInManifest(String apkPath, org.json.JSONArray replacements) throws Exception {
2642
2667
  JSObject result = new JSObject();
@@ -2644,26 +2669,17 @@ public class DexManager {
2644
2669
  int replacedCount = 0;
2645
2670
 
2646
2671
  try {
2647
- // 读取 APK 中的 AndroidManifest.xml
2648
- java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(apkPath);
2649
- java.util.zip.ZipEntry manifestEntry = zipFile.getEntry("AndroidManifest.xml");
2672
+ // 使用 ARSCLib 读取和修改 Manifest
2673
+ com.reandroid.apk.ApkModule apkModule = com.reandroid.apk.ApkModule.loadApkFile(new java.io.File(apkPath));
2674
+ com.reandroid.arsc.chunk.xml.AndroidManifestBlock manifest = apkModule.getAndroidManifest();
2650
2675
 
2651
- if (manifestEntry == null) {
2676
+ if (manifest == null) {
2652
2677
  throw new Exception("AndroidManifest.xml not found in APK");
2653
2678
  }
2654
2679
 
2655
- // 读取 AXML 数据
2656
- java.io.InputStream is = zipFile.getInputStream(manifestEntry);
2657
- java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
2658
- byte[] buffer = new byte[8192];
2659
- int len;
2660
- while ((len = is.read(buffer)) != -1) {
2661
- baos.write(buffer, 0, len);
2662
- }
2663
- is.close();
2664
- zipFile.close();
2665
-
2666
- byte[] axmlData = baos.toByteArray();
2680
+ // 获取 XML 字符串进行替换
2681
+ String xmlContent = manifest.serializeToXml();
2682
+ String originalContent = xmlContent;
2667
2683
 
2668
2684
  // 执行替换
2669
2685
  for (int i = 0; i < replacements.length(); i++) {
@@ -2671,20 +2687,28 @@ public class DexManager {
2671
2687
  String oldValue = replacement.getString("oldValue");
2672
2688
  String newValue = replacement.getString("newValue");
2673
2689
 
2674
- // AXML 中替换字符串
2675
- ReplaceResult replaceResult = replaceStringInAxml(axmlData, oldValue, newValue);
2676
- axmlData = replaceResult.data;
2690
+ int count = 0;
2691
+ String before = xmlContent;
2692
+ xmlContent = xmlContent.replace(oldValue, newValue);
2693
+
2694
+ // 计算替换次数
2695
+ int idx = 0;
2696
+ while ((idx = before.indexOf(oldValue, idx)) != -1) {
2697
+ count++;
2698
+ idx += oldValue.length();
2699
+ }
2677
2700
 
2678
2701
  JSObject detail = new JSObject();
2679
2702
  detail.put("oldValue", oldValue);
2680
2703
  detail.put("newValue", newValue);
2681
- detail.put("count", replaceResult.count);
2704
+ detail.put("count", count);
2682
2705
  details.put(detail);
2683
2706
 
2684
- replacedCount += replaceResult.count;
2707
+ replacedCount += count;
2685
2708
  }
2686
2709
 
2687
2710
  if (replacedCount == 0) {
2711
+ apkModule.close();
2688
2712
  result.put("success", true);
2689
2713
  result.put("replacedCount", 0);
2690
2714
  result.put("details", details);
@@ -2692,52 +2716,17 @@ public class DexManager {
2692
2716
  return result;
2693
2717
  }
2694
2718
 
2695
- // 替换 APK 中的 AndroidManifest.xml
2696
- java.io.File apkFile = new java.io.File(apkPath);
2697
- java.io.File tempApk = new java.io.File(apkPath + ".tmp");
2698
-
2699
- java.util.zip.ZipInputStream zis = new java.util.zip.ZipInputStream(
2700
- new java.io.BufferedInputStream(new java.io.FileInputStream(apkFile)));
2701
- java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(
2702
- new java.io.BufferedOutputStream(new java.io.FileOutputStream(tempApk)));
2703
-
2704
- java.util.zip.ZipEntry entry;
2705
- while ((entry = zis.getNextEntry()) != null) {
2706
- if (entry.getName().equals("AndroidManifest.xml")) {
2707
- // 写入修改后的 Manifest
2708
- java.util.zip.ZipEntry newEntry = new java.util.zip.ZipEntry("AndroidManifest.xml");
2709
- newEntry.setMethod(java.util.zip.ZipEntry.DEFLATED);
2710
- zos.putNextEntry(newEntry);
2711
- zos.write(axmlData);
2712
- zos.closeEntry();
2713
- } else {
2714
- // 复制其他文件
2715
- java.util.zip.ZipEntry newEntry = new java.util.zip.ZipEntry(entry.getName());
2716
- newEntry.setTime(entry.getTime());
2717
- if (entry.getMethod() == java.util.zip.ZipEntry.STORED) {
2718
- newEntry.setMethod(java.util.zip.ZipEntry.STORED);
2719
- newEntry.setSize(entry.getSize());
2720
- newEntry.setCrc(entry.getCrc());
2721
- } else {
2722
- newEntry.setMethod(java.util.zip.ZipEntry.DEFLATED);
2723
- }
2724
- zos.putNextEntry(newEntry);
2725
- if (!entry.isDirectory()) {
2726
- byte[] buf = new byte[8192];
2727
- int n;
2728
- while ((n = zis.read(buf)) != -1) {
2729
- zos.write(buf, 0, n);
2730
- }
2731
- }
2732
- zos.closeEntry();
2733
- }
2734
- zis.closeEntry();
2735
- }
2719
+ // 解析修改后的 XML 并更新 Manifest
2720
+ manifest.parseXmlString(xmlContent);
2721
+ manifest.refresh();
2736
2722
 
2737
- zis.close();
2738
- zos.close();
2723
+ // 保存 APK
2724
+ java.io.File tempApk = new java.io.File(apkPath + ".tmp");
2725
+ apkModule.writeApk(tempApk);
2726
+ apkModule.close();
2739
2727
 
2740
2728
  // 替换原文件
2729
+ java.io.File apkFile = new java.io.File(apkPath);
2741
2730
  if (!apkFile.delete()) {
2742
2731
  Log.e(TAG, "Failed to delete original APK");
2743
2732
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-dex-editor",
3
- "version": "0.0.32",
3
+ "version": "0.0.33",
4
4
  "description": "Capacitor-plugin-for-editing-DEX-files-in-APK",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",