A new thread for the same task means the previous try failed, so old branches, PRs, and conclusions should not be trusted by default. Co-authored-by: Cursor <cursoragent@cursor.com>
91 lines
8.3 KiB
Markdown
91 lines
8.3 KiB
Markdown
# AGENTS.md
|
|
|
|
This repo rebuilds a signed Android APK (Soul2 / Soul2⁺ Browser) from Apktool smali sources under `app/`. There is no Gradle app module, no npm/pip app stack, and no long-running service. The practical check for most changes is `./scripts/build.sh` plus `apksigner verify` on the APK in `dist/`.
|
|
|
|
`sources/java/` and `sources/resources/` are JADX output for reading and searching. The buildable truth is `app/` (smali + res). Some injected helpers live as Java under `sources/warp-runtime/`, `sources/ocr-runtime/`, etc., and are compiled to dex by `scripts/compile-*.sh` then merged by the build.
|
|
|
|
## Package ID and CI
|
|
|
|
- Tracked `app/` sources use the **testing** application ID (`net.kaki87.soul2.testing`, Soul2⁺ Browser). Feature-branch work should leave that alone.
|
|
- Stable releases (`net.kaki87.soul2`, Soul2 Browser) are produced only when CI builds a tag whose commit is on `main` and sets `SOUL_PACKAGE_ID=net.kaki87.soul2`. Plain pushes to `main` skip the APK job.
|
|
- Do not hand-edit package names across the tree. Use `scripts/set-package-id.py` when a rewrite is required, and run `python3 scripts/set-package-id.py --self-test` after changing that script.
|
|
- Smali must reference `Lnet/kaki87/soul2/testing/R$string` (or other `R$*`) in the tracked tree. A leftover `.../soul2/testing/testing/R$string` or a `set-package-id` double rewrite has crashed Copy URL and similar paths before.
|
|
|
|
## Worktrees and PRs
|
|
|
|
Feature work normally lands in a **git worktree** on its own branch, not on the primary `main` checkout. Parallel agent sessions each get their own worktree so builds, dirty trees, and emulator runs do not collide.
|
|
|
|
- If this thread is a retry of a mission that was tried before, treat earlier attempts as failed. Discard their branches, PRs, patches, and conclusions unless the user points at something specific to keep. A new thread exists because the previous one did not succeed.
|
|
- Always create and use a feature branch unless the user explicitly says not to. Prefer names like `fix/...` or `feature/...` (short and descriptive). Hosts may also create `t3code/...` or `cursor/...` branches; keep using that branch if the session already checked it out.
|
|
- Do not commit or push to `main` unless the user explicitly asks. Open a PR into `main` instead.
|
|
- Always push the branch after commits. Always open a PR into `main` if one does not exist yet for that branch; if a PR already exists, push updates to it.
|
|
- One concern per PR. After a PR merges, start the next change from an updated `main` (new branch/worktree), rather than piling unrelated commits onto the old branch.
|
|
- Before opening or updating a PR, rebase or merge `main` if the branch has drifted. Resource-ID and smali conflicts are common after adjacent features land; resolve them in the worktree, then push.
|
|
- Non-`main` branch pushes get a CI APK build. Use that artifact (or a local `./scripts/build.sh`) for device checks. Keep the PR focused: no generated `build_date.xml` / `soul2_info.xml`, no accidental `bundled-warp/lib/` noise, no unrelated reformatting.
|
|
- PR summaries should say what changed and how to verify (build and/or a short UI path). Do not merge unless asked.
|
|
|
|
## Build
|
|
|
|
- Run `./scripts/build.sh` (details in the README Development notes). Needs Java 21+, `curl`, `zip`/`unzip`, `keytool`, and Android SDK **build-tools** with `apksigner` on `PATH` (or via `ANDROID_HOME` / `ANDROID_SDK_ROOT`).
|
|
- Output: signed APK under `dist/`. Signature schemes v1+v2+v3 are required (targetSdk 36).
|
|
- `apktool` is downloaded into `tools/` by the build script (gitignored).
|
|
- `app/res/values/build_date.xml` and `app/res/values/soul2_info.xml` are generated at build time and gitignored. INFO screen text comes from the README `<!-- BEGIN INFO -->` block via `scripts/prepare-info-string.py`. Do not commit those XML files.
|
|
- `bundled-warp/lib/` is gitignored for normal `git add` but kept in the repo history; CI refreshes it with `git add -f` when `bundled-warp/soulamz.inputs` changes. Local soulamz rebuilds should not dirty commits.
|
|
- OCR/WARP dex prebuilts live under `prebuilts/` (tracked; CI updates when `prebuilts/tooling.inputs` changes).
|
|
- There is no separate unit-test suite. Prefer a green local (or CI) APK build over guessing.
|
|
|
|
## Editing smali and resources
|
|
|
|
### New strings and resource IDs
|
|
|
|
When adding a string (or other resource) used from smali:
|
|
|
|
1. Add the entry in `app/res/values/strings.xml` (English only here).
|
|
2. Allocate a **new** ID in `app/res/values/public.xml` and the matching field in `app/smali_classes3/com/mycompany/app/soulbrowser/R$string.smali`.
|
|
3. Keep `sources/java/.../R.java` in sync if you touch it for readability, but apktool does not build from that tree.
|
|
|
|
ID collisions break `main` after merge even when a PR build was green on an older base. Never reuse an ID that already exists on `main`. After Eruda, `reload_saved_tabs` reused `dev_tools`'s `0x7f12059d` and apktool failed only once both landed together. Scan the highest existing custom IDs before picking the next one. `scripts/sync-public-xml.py` can add missing `public.xml` rows from `R$*.smali`; it does not invent new IDs for you.
|
|
|
|
### Copy an existing pattern
|
|
|
|
Before inventing control flow, find the closest shipped feature and mirror it:
|
|
|
|
| Kind of change | Good anchors |
|
|
|----------------|--------------|
|
|
| Main-menu item | `MainConst.smali`, `MenuListAdapter*.smali`, Eruda/DevTools (`DevToolsHelper.smali`) |
|
|
| Settings row | The target `Setting*.smali` + `SettingListAdapter*.smali`; match group corner modes (top of group ≠ middle row) |
|
|
| Link/image long-press | `DialogUrlLink.smali`, preview hooks in `DialogWebView*.smali` / `WebViewActivity*.smali` |
|
|
| Prefs | Existing `Pref*.smali` getters/setters; do not invent fields on foreign classes |
|
|
| Tab list / multi-select | `WebTabAdapter.smali`, `DialogTabMain.smali` |
|
|
|
|
Settings list crashes in the WARP work came from writing final adapter fields or assuming ViewHolder members that are not in this decompile. Read the class you call into. If JADX and smali disagree on a field, trust the smali.
|
|
|
|
### Smali mechanics that have burned builds
|
|
|
|
- Keep `.locals` / register widths honest after inserts. Wide values (`long`/`double`) take two registers.
|
|
- Do not blindly rewrite or "refresh" unrelated `sparse-switch` / `packed-switch` tables. A package-id helper once remapped colliding `:sswitch_N` labels file-wide and corrupted Jsoup/SVGParser, which made Image viewer show "No images" (`VerifyError`). Scope switch edits to the method you mean.
|
|
- Anonymous classes are separate `Foo$N.smali` files; wire listeners there the same way neighboring features do.
|
|
- After process-death / intent bugs, clear consumed launch extras once handled (`WebViewActivity` external-link reopen).
|
|
|
|
### i18n
|
|
|
|
English strings are owned in this repo. Other locales come from [soul-browser-i18n](https://github.com/KaKi87/soul-browser-i18n) at build time (`scripts/sync-i18n.py`). Do not commit downloaded locale trees. CI can push English to the i18n repo from `main` via `scripts/push-i18n-english.py`.
|
|
|
|
## Emulator and device smoke
|
|
|
|
Native libs in the APK are **ARM-only**. On an x86_64 emulator you need an image that advertises ARM ABIs via NDK translation, e.g. `system-images;android-30;google_apis;x86_64`. API 33 `google_apis` x86_64 has failed install with `INSTALL_FAILED_NO_MATCHING_ABIS`.
|
|
|
|
`./scripts/run-emulator.sh` creates the `soul_test` AVD if needed and starts a headless emulator. It forces `-accel off` (TCG) for nested-VM hosts where KVM misbehaves; on a normal desktop with working KVM you can boot the same AVD with acceleration enabled instead. Start `adb` first. Avoid `pkill -f emulator` (it can kill the calling shell); prefer `killall qemu-system-x86_64-headless` when that is the process in use.
|
|
|
|
Smoke after boot (local/feature APKs use the testing id):
|
|
|
|
```bash
|
|
adb wait-for-device
|
|
adb shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 2; done'
|
|
adb install -r -g dist/soul-browser-*.apk
|
|
adb shell am start -a android.intent.action.VIEW -d 'https://example.com/' \
|
|
-n net.kaki87.soul2.testing/com.mycompany.app.web.WebLauncher
|
|
# First launch shows onboarding; tap Start (id/splash_apply_view)
|
|
```
|
|
|
|
Stable-tag APKs use `net.kaki87.soul2` in the component name. When UI is not part of the task, skip the emulator and rely on build + `apksigner verify`. Physical devices via ADB are fine when the user already has one connected; prefer CI or local `dist/` APKs the user asked you to use.
|