Build from source
The Android app lives at the repository root. The website lives in site/ and is a separate
Node project.
Toolchain
Section titled “Toolchain”- JDK 21.
- Android SDK with platform 36 and build-tools 35.
- Gradle is provided by the wrapper.
Linux example, everything under ~/Android:
curl -fsSL https://dl.google.com/android/cli/latest/linux_x86_64/install.sh | bashmkdir -p ~/Android/toolscurl -sSL "https://api.adoptium.net/v3/binary/latest/21/ga/linux/x64/jdk/hotspot/normal/eclipse" \ | tar xz -C ~/Android/tools
export JAVA_HOME=$(echo ~/Android/tools/jdk-21*)export ANDROID_HOME=$HOME/Android/Sdkexport PATH="$JAVA_HOME/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
android sdk install platform-tools platforms/android-36 build-tools/35.0.0echo "sdk.dir=$ANDROID_HOME" > local.propertiesOn macOS install openjdk@21 and use the same SDK layout. The repository’s AGENTS.md has the
exact Homebrew path.
Lint and checks
Section titled “Lint and checks”Pull requests run the same gate as .github/workflows/ci.yml:
./gradlew detekt detektDebug detektDebugUnitTest # every detekt rule + ktlint + Compose rules./gradlew lintDebug # every Android Lint check, warnings are errors./gradlew testDebugUnitTest # unit tests + ArchitectureTestArchitectureTest keeps the language modules separate (generic code talks to the current
language through LanguageModule, never to lang/ja or lang/en directly). Findings older than
the linter are baselined; new code must be clean. Details in
docs/building.md.
Build and test
Section titled “Build and test”./gradlew testDebugUnitTest # JVM unit tests./gradlew assembleDebug # app/build/outputs/apk/debug/app-debug.apkFor on-device tests, point Gradle at one device. connectedAndroidTest uninstalls the app when it
finishes, wiping dictionaries and settings, and Gradle otherwise targets every connected device:
ANDROID_SERIAL=emulator-5554 ./gradlew connectedDebugAndroidTestSingle test class:
ANDROID_SERIAL=emulator-5554 ./gradlew connectedDebugAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.class=io.github.wilsonnet.droidsentenceminer.audio.AudioPipelineInstrumentedTestInstall
Section titled “Install”Debug builds are suffixed .debug and signed with the committed app/debug.keystore, so they
upgrade in place across machines.
adb install -r app/build/outputs/apk/debug/app-debug.apkadb shell appops set io.github.wilsonnet.droidsentenceminer.debug SYSTEM_ALERT_WINDOW allowadb shell pm grant io.github.wilsonnet.droidsentenceminer.debug android.permission.RECORD_AUDIOadb shell pm grant io.github.wilsonnet.droidsentenceminer.debug android.permission.POST_NOTIFICATIONSadb shell pm grant io.github.wilsonnet.droidsentenceminer.debug com.ichi2.anki.permission.READ_WRITE_DATABASEEmulator
Section titled “Emulator”emulator -avd dsm_test -gpu host -no-snapshot-save -no-boot-anim &adb -s emulator-5554 wait-for-device shell \ 'while [ "$(getprop sys.boot_completed)" != 1 ]; do sleep 1; done'The test AVD is API 35, google_apis_playstore, x86_64. Capture is unreliable on emulators;
validate OCR and audio on a real device.
Debug API
Section titled “Debug API”Debug builds start a loopback HTTP API on port 9099 for text control over adb:
adb forward tcp:9099 tcp:9099curl -s localhost:9099/statecurl -s "localhost:9099/scan?l=0.08&t=0.32&r=0.92&b=0.42" | python3 -m json.toolcurl -s localhost:9099/frame.png -o frame.pngcurl -s "localhost:9099/lookup?word=%E3%81%AA%E3%81%84"The full endpoint list is in .opencode/skills/dsm-debug-api/SKILL.md in the repository.
Versions
Section titled “Versions”AGP 8.13.2, Kotlin 2.3.21, KSP 2.3.12, compileSdk 36, minSdk 26, targetSdk 35, Compose BOM
2024.12.01, Room 2.8.5, ML Kit Japanese 16.0.1, ONNX Runtime Android 1.31.0, AnkiDroid API
v2.24.1. Version bumps live in gradle/libs.versions.toml; update this page and AGENTS.md with
them.