Make the Android app build, and fix what compiling it exposed

Why: the module was written but never compiled, so nothing about it was
proved. Building it found a name clash and a theme that would have crashed
every device below Android 10.

What:
- KeepOrgProvider.requireContext -> ctx(): ContentProvider gained
  requireContext() in API 30, and Kotlin refused the silent hiding.
- The application theme was @android:style/Theme.DeviceDefault.DayNight,
  which is API 29 while minSdk is 26. Replaced with @style/AppTheme, plain
  DeviceDefault in values/ and DayNight in values-v29/.
- Backup exclusions: data_extraction_rules.xml and backup_rules.xml exclude
  every domain for both cloud backup and device transfer. The session token
  authenticates as the user and the mirrored files are the organization's
  password databases; neither belongs in a backup.
- User-facing strings moved from code into strings.xml, so the app is
  translatable. Layouts stay in code, as before.
- mipmap-anydpi-v26 -> mipmap-anydpi (the qualifier is redundant at minSdk
  26), and the redundant activity label is gone.
- Lint is configured to write a text report, and `make android-lint` runs it;
  `make android` now fails with a clear message when ANDROID_HOME has no SDK.

Proved: the unsigned release APK builds (656 KB), lint reports "No issues
found", and the packaged DEX contains all 16 classes including every manifest
component. Toolchain used: JDK 21, Gradle 8.10.2, SDK platform 35 and
build-tools 35.0.0; on this arm64 host Google's x86-64 aapt2 runs under
qemu-user-static with amd64 multiarch libraries, which android/README.md now
documents. The app has still not run on a device: there is none here.
This commit is contained in:
2026-10-29 16:55:26 +01:00
parent 0e83acb9b1
commit a58fa6158d
15 changed files with 192 additions and 58 deletions
+42 -8
View File
@@ -2,7 +2,8 @@
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.
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
@@ -16,19 +17,52 @@ reaches the server.
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.
`internal/client/decide.go`. **Keep the two in step.**
## Building
The Gradle wrapper is not committed. Generate it once with a local Gradle
8.9 or newer, or let the Makefile call Gradle directly:
Requirements: JDK 17 or newer, Gradle 8.9 or newer, 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
```
gradle wrapper --gradle-version 8.9 # once, optional
make android # unsigned release APK
make android-publish # upload to the private F-Droid repo
```
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.