NeverTooManyBooks is a standard Android Gradle project. A clean debug build requires no special credentials or keys — just a working Android development environment. Release builds require an external signing configuration loaded from a properties file outside the repository root, so signing secrets are never committed to version control.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/tfonteyn/NeverTooManyBooks/llms.txt
Use this file to discover all available pages before exploring further.
BUILDING file notes to use whichever latest or non-broken version works; there is no minimum pinned version in the repo.libs.versions.javaToolchain). Gradle will automatically provision the correct JDK via toolchain resolution if it is not already installed, but having JDK 17+ available locally avoids a download on first build.minSdk: API 26 (Android 8.0 Oreo)compileSdk/targetSdk: API 36 (latest stable at time of writing)- Install both the platform package and the Build Tools for each SDK level you need.
The commit hash of This means every APK carries a 12-character git hash for traceability.
Make sure
HEAD is captured at build time and embedded in
BuildConfig.GIT_HASH via:git is on your PATH before building.Open the Build Variants panel (bottom-left in Android Studio, or View → Tool Windows → Build Variants) and choose:
debug
Recommended for development. No signing configuration is required. The application ID gains a The output APK is named
.debug suffix (com.hardbacknutter.nevertoomanybooks.debug), so a debug build can be installed alongside a release build.NeverTooManyBooks-<versionName>-debug.apk and located in app/build/outputs/apk/debug/. beta
Initialised from the
release config with a -beta version name suffix. Requires the same signing configuration as release. Used for public beta distributions. release
Requires a signing configuration (see the next step). Enables minification (
minifyEnabled = true) and resource shrinking (shrinkResources = true) via ProGuard/R8.Without a valid signing configuration,
assembleRelease will print
"Keystore properties file not found. No signing configuration will be applied." and produce an unsigned APK that cannot be installed on a device. Always configure signing before distributing a release build.sign.storeFile=/path/to/keystore.jks
sign.storePassword=MyStorePassword
sign.keyAlias=MyKeyAlias
sign.keyPassword=MyKeyPassword
signingConfigs {
release {
if (!keystoreProperties.isEmpty()) {
storeFile = file(keystoreProperties["sign.storeFile"])
storePassword = keystoreProperties["sign.storePassword"]
keyAlias = keystoreProperties["sign.keyAlias"]
keyPassword = keystoreProperties["sign.keyPassword"]
}
}
}
# Run all unit tests (JVM)
./gradlew test
# Run unit tests for a specific module
./gradlew :app:testDebugUnitTest
Test XML reports are written to
<module>/build/reports/tests/; HTML reports are disabled by default (configured in the root build.gradle).The test runner is
androidx.test.runner.AndroidJUnitRunner with the JUnit 5 AndroidJUnit5Builder configured via testInstrumentationRunnerArguments.Fix any reported violations before submitting a pull request. The lint task is also available, though
abortOnError is set to false to allow command-line CI builds to complete even with warnings:Code Tags
The codebase uses a consistent set of inline comment tags (always followed by a colon) to annotate work items at the point of the relevant code:| Tag | Meaning |
|---|---|
URGENT: | Not a show-stopper, but really needs attention soon |
TEST: | Work done and minimally tested; full testing not yet complete |
RELEASE: | Must be checked before any public release |
NEWTHINGS: | Notes on how to wire in new functionality of a given type |
FIXME: | A workaround is present or the situation is acceptable, but needs a proper fix |
TODO: | Needs work — time permitting |
ENHANCE: | An improvement worth making — time permitting |
Gradle Performance Tuning
Thegradle.properties file enables several Gradle performance features that speed up incremental builds:
./gradlew --no-configuration-cache <task> to bypass the cache for that invocation.