70 lines
3.3 KiB
Markdown
70 lines
3.3 KiB
Markdown
# Tally for Android
|
|
|
|
Two screens, reached from a hamburger drawer: the standard calculator (design 9.2), an
|
|
NCalc-style pad with a live answer as you type, and the converter (design 9.6), which
|
|
reopens where it was left and shows a value in every unit at once. Settings has the
|
|
theme: follow the device, light or dark. The engine behind it is the finished one, so
|
|
`2^100 + 1`, `x = 5` then `x * 2` and `cagr(10000, 25000, 5) * 100` all work. The
|
|
programmer and financial screens (design 9) are not built.
|
|
|
|
## Building
|
|
|
|
Nothing is installed system-wide; the toolchain comes from mise. **Setup, emulator and
|
|
deployment, including wireless adb, are in [SETUP.md](SETUP.md)** - this file is about what
|
|
the app is.
|
|
|
|
```
|
|
mise install && mise run android-sdk # toolchain, then the SDK packages
|
|
zig build android # the native library, one .so per ABI
|
|
cd android && gradle assembleDebug
|
|
```
|
|
|
|
`zig build android` writes `zig-out/android/{arm64-v8a,x86_64,armeabi-v7a}/libtally.so`,
|
|
which is the `jniLibs` layout Gradle expects, so Gradle only packages it. Gradle never
|
|
invokes Zig, and there is no CMake or `ndk-build` in this project: the library is built
|
|
without the NDK entirely (design 6.2), and the JNI glue is hand-written Zig
|
|
(`engine/src/jni.zig`) rather than generated from `jni.h`.
|
|
|
|
Because Gradle never invokes Zig, `zig build android` has to be re-run by hand after any
|
|
engine or JNI change, or the APK gets a stale library.
|
|
|
|
The Gradle wrapper is not committed; `gradle wrapper` writes one, or Android Studio offers
|
|
to on first open.
|
|
|
|
## The first thing to run, and it needs no device
|
|
|
|
```
|
|
zig build jvm-test
|
|
```
|
|
|
|
`engine/src/jni.zig` reaches the JNI function table by index, because there is no `jni.h`
|
|
here to name the entries. Four of those indices have to be right. JNI's table is fixed by
|
|
the specification rather than by the platform, so a desktop JVM checks them just as well as
|
|
a phone: this step builds the library for the host, loads it from Java (which runs
|
|
`JNI_OnLoad`, which round-trips a string through two of the four), and drives every entry
|
|
point. Moving one index by a slot makes it fail.
|
|
|
|
Then, with a device or emulator attached:
|
|
|
|
```
|
|
cd android && gradle connectedAndroidTest
|
|
```
|
|
|
|
`TallyEngineTest` covers what only the real app can: that a session remembers `x` and
|
|
`Ans`, that `2^100 + 1` arrives with its last digit intact, that `12 in to ft` is exactly
|
|
`1`, that an error carries the engine's own wording rather than a second copy written in
|
|
Kotlin, and that a closed session refuses to evaluate instead of crashing. It passes on an
|
|
Android 15 x86_64 image with 16KB pages. Note that it uninstalls the app when it finishes.
|
|
|
|
Nothing has been verified on a physical device yet, and arm64-v8a is the interesting one:
|
|
it is the ABI phones actually use, and the only one where the page-size bug in design 6.3
|
|
showed up.
|
|
|
|
## What is deliberately not here
|
|
|
|
- **No arithmetic.** Kotlin parses no expressions, formats no numbers and words no errors.
|
|
All of it is the shared engine, so a wrong answer is wrong in the CLI and TUI too, which
|
|
is the point of having one engine.
|
|
- **No error table.** Messages come from `engine.phrase` through the JSON.
|
|
- **No unit list.** `TallySession.unitCatalog()` returns the engine's own tables, aliases
|
|
included, for whenever the convert screen is built.
|