* Decompile and recreate Soul Browser v1.4.85 with APK CI builds Co-authored-by: KaKi87 <KaKi87@pm.me> * Fix CI build: include bundled google.jks and remove PR trigger The apktool build failed in CI because app/unknown/.../google.jks was excluded by the *.jks gitignore rule. Whitelist app-bundled JKS files and commit the missing resource. Also remove the redundant pull_request workflow trigger. Co-authored-by: KaKi87 <KaKi87@pm.me> * Change package ID to com.github.kaki87.soulbrowser Rename the application ID so the rebuilt APK can be installed alongside the original Soul Browser from Google Play. - Update AndroidManifest package, permissions, providers, and actions - Update R class references in smali (com/github/kaki87/soulbrowser/R) - Update shortcuts.xml targetPackage - Rename app label to "Soul Rebuild" for easy identification Co-authored-by: KaKi87 <KaKi87@pm.me> * Fix install compatibility: remove split APK metadata and bundle native libs The rebuilt APK still declared requiredSplitTypes and Play Store split metadata, causing Android to reject standalone installs as incompatible. - Remove requiredSplitTypes and split-related manifest meta-data - Merge native libs from all ABI splits with uncompressed storage - Page-align before signing for extractNativeLibs=false - Add .so to apktool doNotCompress list Co-authored-by: KaKi87 <KaKi87@pm.me> * Fix install failure: use apksigner v2/v3 and bundle arm64 native libs Target SDK 36 requires APK Signature Scheme v2+, but jarsigner only produces v1 signatures, causing Android to reject the install. - Sign with apksigner (v1+v2+v3) instead of jarsigner - Add arm64-v8a native libraries from universal Soul Browser 1.4.79 - Set extractNativeLibs=true for reliable sideload installs - Commit stable debug keystore for consistent signatures across builds Co-authored-by: KaKi87 <KaKi87@pm.me> * Add committed debug keystore for consistent APK signatures Co-authored-by: KaKi87 <KaKi87@pm.me> * Fix startup crash: use original classes2.dex desugar libraries Apktool recompiles smali_classes2 into a broken classes2.dex, causing ClassNotFoundException for j$.com.android.tools.r8.a at ML Kit init. Inject the original classes2.dex (Java 8+ desugar libs) after apktool build instead of using the recompiled version. Co-authored-by: KaKi87 <KaKi87@pm.me> * Fix missing drawable resources from density split APKs The base APK is an app bundle module; density-specific drawables like seek_thumb_nor_b live in config.xhdpi.apk and were missing after rebuild, causing Resources$NotFoundException at runtime. - Merge non-9-patch resources from split APKs before apktool build - Sync public.xml IDs from R smali only when backing files exist - Add 713 density-specific resource IDs to public.xml Co-authored-by: KaKi87 <KaKi87@pm.me> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
89 lines
3 KiB
Python
Executable file
89 lines
3 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Add missing resource IDs from R$*.smali into public.xml."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
R_DIR = ROOT / "app" / "smali_classes3" / "com" / "mycompany" / "app" / "soulbrowser"
|
|
PUBLIC_XML = ROOT / "app" / "res" / "values" / "public.xml"
|
|
|
|
FIELD_RE = re.compile(r"\.field public static (\w+):I = (0x[0-9a-f]+)")
|
|
ENTRY_RE = re.compile(r'<public type="([^"]+)" name="([^"]+)" id="(0x[0-9a-f]+)"')
|
|
|
|
|
|
def resource_file_exists(rtype: str, name: str) -> bool:
|
|
res_dir = ROOT / "app" / "res"
|
|
if rtype == "drawable":
|
|
patterns = [f"**/{name}.png", f"**/{name}.xml", f"**/{name}.webp", f"**/{name}.9.png"]
|
|
elif rtype == "mipmap":
|
|
patterns = [f"**/{name}.png", f"**/{name}.webp"]
|
|
elif rtype == "layout":
|
|
patterns = [f"layout*/{name}.xml", f"**/{name}.xml"]
|
|
elif rtype == "xml":
|
|
patterns = [f"xml/{name}.xml"]
|
|
elif rtype == "raw":
|
|
patterns = [f"raw/{name}.*"]
|
|
else:
|
|
patterns = [f"**/{name}.xml"]
|
|
|
|
return any(any(res_dir.glob(pattern)) for pattern in patterns)
|
|
|
|
|
|
def load_r_entries() -> list[tuple[str, str, str]]:
|
|
entries: list[tuple[str, str, str]] = []
|
|
for smali in sorted(R_DIR.glob("R$*.smali")):
|
|
rtype = smali.stem.removeprefix("R$")
|
|
text = smali.read_text(encoding="utf-8")
|
|
for name, rid in FIELD_RE.findall(text):
|
|
entries.append((rtype, name, rid))
|
|
return entries
|
|
|
|
|
|
def main() -> int:
|
|
if not PUBLIC_XML.exists():
|
|
print(f"error: missing {PUBLIC_XML}", file=sys.stderr)
|
|
return 1
|
|
|
|
content = PUBLIC_XML.read_text(encoding="utf-8")
|
|
existing = {(m.group(1), m.group(2), m.group(3)) for m in ENTRY_RE.finditer(content)}
|
|
existing_ids = {m.group(3) for m in ENTRY_RE.finditer(content)}
|
|
existing_names = {(m.group(1), m.group(2)) for m in ENTRY_RE.finditer(content)}
|
|
|
|
missing: list[tuple[str, str, str]] = []
|
|
for rtype, name, rid in load_r_entries():
|
|
key = (rtype, name, rid)
|
|
if key in existing:
|
|
continue
|
|
if rid in existing_ids or (rtype, name) in existing_names:
|
|
continue
|
|
if not resource_file_exists(rtype, name):
|
|
continue
|
|
missing.append(key)
|
|
|
|
if not missing:
|
|
print("public.xml is already complete")
|
|
return 0
|
|
|
|
lines = [f' <public type="{rtype}" name="{name}" id="{rid}" />' for rtype, name, rid in missing]
|
|
insertion = "\n".join(lines) + "\n"
|
|
if content.rstrip().endswith("</resources>"):
|
|
updated = re.sub(r"</resources>\s*$", insertion + "</resources>\n", content, count=1)
|
|
else:
|
|
print("error: public.xml missing </resources> footer", file=sys.stderr)
|
|
return 1
|
|
|
|
if updated == content:
|
|
print("error: failed to update public.xml", file=sys.stderr)
|
|
return 1
|
|
|
|
PUBLIC_XML.write_text(updated, encoding="utf-8")
|
|
print(f"Added {len(missing)} missing public.xml entries")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|