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.
69 lines
2.8 KiB
Markdown
69 lines
2.8 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: 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
|
|
```
|
|
|
|
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.
|