This document explains how the Android app signing is configured for the Formulus app.
The app uses the application ID: org.opendataensemble.formulus
This is consistently set in:
android/app/build.gradle-applicationIdandnamespace- All Kotlin source files in
android/app/src/main/java/org/opendataensemble/formulus/
Debug builds use the default Android debug keystore located at:
android/app/debug.keystore- Alias:
androiddebugkey - Password:
android
Release builds use a production keystore configured via android/local.properties.
Required properties in local.properties:
FORMULUS_RELEASE_STORE_FILE=keystores/formulus-signing.jks
FORMULUS_RELEASE_STORE_PASSWORD=your_keystore_password
FORMULUS_RELEASE_KEY_ALIAS=your_key_alias
FORMULUS_RELEASE_KEY_PASSWORD=your_key_passwordImportant Notes:
- The
local.propertiesfile is gitignored and should never be committed - The
FORMULUS_RELEASE_STORE_FILEpath is relative to theandroid/directory (project root) - Recommended location:
android/keystores/formulus-signing.jks - If
local.propertiesdoesn't exist or is missing signing properties, release builds will fall back to the debug keystore
The signing configuration is loaded in android/app/build.gradle:
// Load keystore properties from local.properties
def keystorePropertiesFile = rootProject.file("local.properties")
def keystoreProperties = new Properties()
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
signingConfigs {
debug {
storeFile = file('debug.keystore')
storePassword = 'android'
keyAlias = 'androiddebugkey'
keyPassword = 'android'
}
release {
if (keystorePropertiesFile.exists()) {
storeFile = file(keystoreProperties['FORMULUS_RELEASE_STORE_FILE'])
storePassword = keystoreProperties['FORMULUS_RELEASE_STORE_PASSWORD']
keyAlias = keystoreProperties['FORMULUS_RELEASE_KEY_ALIAS']
keyPassword = keystoreProperties['FORMULUS_RELEASE_KEY_PASSWORD']
}
}
}
buildTypes {
debug {
signingConfig = signingConfigs.debug
}
release {
signingConfig = keystorePropertiesFile.exists() ? signingConfigs.release : signingConfigs.debug
minifyEnabled = enableProguardInReleaseBuilds
proguardFiles = [getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"]
}
}
}To build a release version:
# Build release APK
cd android
.\gradlew assembleRelease
# Build release AAB (for Play Store)
.\gradlew bundleReleaseThe signed outputs will be located at:
- APK:
android/app/build/outputs/apk/release/formulus-v{version}-{versionCode}-release-{date}.apk- Example:
formulus-v1.0-1-release-20251006.apk
- Example:
- AAB:
android/app/build/outputs/bundle/release/app-release.aab
The APK filename includes:
- App name:
formulus - Version name: from
versionNamein build.gradle (e.g.,v1.0) - Version code: from
versionCodein build.gradle (e.g.,1) - Build type:
debugorrelease - Build date: in
yyyyMMddformat
- Never commit
local.properties- It's already in.gitignore - Never commit the keystore file - Keep it in a secure location
- Use strong passwords for both keystore and key alias
- Backup your keystore - If you lose it, you cannot update your app on Play Store
- Consider using environment variables for CI/CD pipelines instead of local.properties
To verify your signing configuration is working:
# Clean build
cd android
.\gradlew clean
# Build release
.\gradlew assembleRelease
# Verify the APK signature
# Using keytool (comes with JDK)
# Replace the filename with your actual build output
keytool -printcert -jarfile app\build\outputs\apk\release\formulus-v1.0-1-release-20251006.apkYou should see your certificate details (not the debug certificate).