Every Android release build runs lint, which AGP 8.7 cannot run on JDK 26, the JDK Homebrew's Gradle starts on; make android failed unless JAVA_HOME was set by hand. gradle/gradle-daemon-jvm.properties now runs the Gradle daemon on JDK 17 whatever launches it. The Helm values trusted every private address range as a proxy, which let any client on the LAN choose its own address for the sign-in rate limits. They now name only the nginx host and the k3s pod network. The port gains healthcheckPath, so the pod gets readiness and liveness probes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
72 lines
2.9 KiB
Markdown
72 lines
2.9 KiB
Markdown
# KeepOrg for Android
|
|
|
|
A KDBX synchronization adapter with no third-party dependencies: the server
|
|
is reached with `java.net.HttpURLConnection`, JSON is parsed with the
|
|
platform's `org.json`, and the two screens are built in code. The release APK
|
|
is about 650 KB.
|
|
|
|
## How a KeePass application sees it
|
|
|
|
`KeepOrgProvider` is a `DocumentsProvider`, so the mirrored databases appear
|
|
in every file picker under "KeepOrg". KeePassDX and Keepass2Android open them
|
|
like any other document. When the application closes a file it had opened for
|
|
writing, the provider triggers a synchronization pass, which is how a save
|
|
reaches the server.
|
|
|
|
`SyncService` is a foreground service that holds the server's event stream
|
|
open, so a change pushed by a colleague arrives without opening the app. The
|
|
user turns it on from the main screen; it is not started at boot.
|
|
|
|
Nothing the app holds may leave the device in a backup or a device transfer:
|
|
`res/xml/data_extraction_rules.xml` and `res/xml/backup_rules.xml` exclude
|
|
every domain. The session token authenticates as the user, and the mirrored
|
|
files are the organization's password databases.
|
|
|
|
The merge policy in `Mirror.decide` is the same table as the desktop client's
|
|
`internal/client/decide.go`. **Keep the two in step.**
|
|
|
|
## Building
|
|
|
|
Requirements: a JDK 17 installed (Gradle 8.9 or newer runs its daemon on it
|
|
whatever JDK launches Gradle, per `gradle/gradle-daemon-jvm.properties`:
|
|
AGP 8.7's lint, which every release build runs, fails on newer JDKs), and an
|
|
Android SDK with
|
|
`platforms;android-35` and `build-tools;35.0.0`.
|
|
|
|
```sh
|
|
export ANDROID_HOME=$HOME/android-sdk
|
|
make android # unsigned release APK, named for the F-Droid repository
|
|
make android-lint # lint; the project is kept at zero findings
|
|
make android-publish
|
|
```
|
|
|
|
The Gradle wrapper is not committed, so the Makefile calls whichever `gradle`
|
|
is on `PATH`. Generate a wrapper if you prefer one:
|
|
`gradle wrapper --gradle-version 8.10.2`.
|
|
|
|
The release build is deliberately unsigned: the private F-Droid repository
|
|
signs what it publishes.
|
|
|
|
### Building on an arm64 Linux host
|
|
|
|
Google ships `aapt2` for linux-x86_64 only, so an aarch64 build host needs to
|
|
be able to run it. Debian's own arm64 `aapt2` is too old to read the API 35
|
|
`android.jar`, so run the official one through QEMU instead:
|
|
|
|
```sh
|
|
sudo dpkg --add-architecture amd64
|
|
sudo apt update
|
|
sudo apt install qemu-user-static libc6:amd64 libstdc++6:amd64 zlib1g:amd64
|
|
```
|
|
|
|
After that `aapt2` runs transparently and the build needs no further flags.
|
|
Everything else in the Android toolchain (R8, the resource merger, zipflinger,
|
|
apksig) is Java and architecture-independent.
|
|
|
|
## What is verified and what is not
|
|
|
|
The APK builds clean and lint reports no issues, and the packaged DEX contains
|
|
every class the manifest names. The app has **not** been exercised on a device
|
|
or emulator: there was none available. The first real run is where to confirm
|
|
the picker integration, the save-triggered sync and the notification.
|