371 lines
14 KiB
Groovy
371 lines
14 KiB
Groovy
apply plugin: "com.android.application"
|
|
apply plugin: "org.jetbrains.kotlin.android"
|
|
apply plugin: "com.facebook.react"
|
|
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
|
apply plugin: "io.gitlab.arturbosch.detekt"
|
|
|
|
def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath()
|
|
|
|
/**
|
|
* This is the configuration block to customize your React Native Android app.
|
|
* By default you don't need to apply any configuration, just uncomment the lines you need.
|
|
*/
|
|
react {
|
|
entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim())
|
|
reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
|
|
hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc"
|
|
codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
|
|
|
|
// Use Expo CLI to bundle the app, this ensures the Metro config
|
|
// works correctly with Expo projects.
|
|
cliFile = new File(["node", "--print", "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim())
|
|
bundleCommand = "export:embed"
|
|
|
|
/* Folders */
|
|
// The root of your project, i.e. where "package.json" lives. Default is '../..'
|
|
// root = file("../../")
|
|
// The folder where the react-native NPM package is. Default is ../../node_modules/react-native
|
|
// reactNativeDir = file("../../node_modules/react-native")
|
|
// The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
|
|
// codegenDir = file("../../node_modules/@react-native/codegen")
|
|
|
|
/* Variants */
|
|
// The list of variants to that are debuggable. For those we're going to
|
|
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
|
|
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
|
|
// debuggableVariants = ["liteDebug", "prodDebug"]
|
|
|
|
/* Bundling */
|
|
// A list containing the node command and its flags. Default is just 'node'.
|
|
// nodeExecutableAndArgs = ["node"]
|
|
|
|
//
|
|
// The path to the CLI configuration file. Default is empty.
|
|
// bundleConfig = file(../rn-cli.config.js)
|
|
//
|
|
// The name of the generated asset file containing your JS bundle
|
|
// bundleAssetName = "MyApplication.android.bundle"
|
|
//
|
|
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
|
|
// entryFile = file("../js/MyApplication.android.js")
|
|
//
|
|
// A list of extra flags to pass to the 'bundle' commands.
|
|
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
|
|
// extraPackagerArgs = []
|
|
|
|
/* Hermes Commands */
|
|
// The hermes compiler command to run. By default it is 'hermesc'
|
|
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
|
|
//
|
|
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
|
|
// hermesFlags = ["-O", "-output-source-map"]
|
|
|
|
/* Autolinking */
|
|
autolinkLibrariesWithApp()
|
|
}
|
|
|
|
/**
|
|
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
|
|
*/
|
|
def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInReleaseBuilds') ?: false).toBoolean()
|
|
|
|
/**
|
|
* The preferred build flavor of JavaScriptCore (JSC)
|
|
*
|
|
* For example, to use the international variant, you can use:
|
|
* `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+`
|
|
*
|
|
* The international variant includes ICU i18n library and necessary data
|
|
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
|
* give correct results when using with locales other than en-US. Note that
|
|
* this variant is about 6MiB larger per architecture than default.
|
|
*/
|
|
def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'
|
|
|
|
android {
|
|
ndkVersion rootProject.ext.ndkVersion
|
|
|
|
buildToolsVersion rootProject.ext.buildToolsVersion
|
|
compileSdk rootProject.ext.compileSdkVersion
|
|
|
|
namespace 'net.aliasvault.app'
|
|
defaultConfig {
|
|
applicationId 'net.aliasvault.app'
|
|
minSdkVersion rootProject.ext.minSdkVersion
|
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
|
versionCode 2700100
|
|
versionName "0.27.0-alpha"
|
|
|
|
// Instrumented test configuration
|
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
}
|
|
signingConfigs {
|
|
debug {
|
|
storeFile file('debug.keystore')
|
|
storePassword 'android'
|
|
keyAlias 'androiddebugkey'
|
|
keyPassword 'android'
|
|
}
|
|
release {
|
|
if (project.hasProperty('ALIASVAULT_UPLOAD_STORE_FILE')) {
|
|
storeFile file(ALIASVAULT_UPLOAD_STORE_FILE)
|
|
storePassword ALIASVAULT_UPLOAD_STORE_PASSWORD
|
|
keyAlias ALIASVAULT_UPLOAD_KEY_ALIAS
|
|
keyPassword ALIASVAULT_UPLOAD_KEY_PASSWORD
|
|
}
|
|
}
|
|
}
|
|
buildTypes {
|
|
debug {
|
|
signingConfig signingConfigs.debug
|
|
}
|
|
release {
|
|
// Caution! In production, you need to generate your own keystore file.
|
|
// see https://reactnative.dev/docs/signed-apk-android.
|
|
signingConfig signingConfigs.debug
|
|
signingConfig signingConfigs.release
|
|
shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false)
|
|
minifyEnabled enableProguardInReleaseBuilds
|
|
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
|
crunchPngs (findProperty('android.enablePngCrunchInReleaseBuilds')?.toBoolean() ?: true)
|
|
}
|
|
}
|
|
packagingOptions {
|
|
jniLibs {
|
|
useLegacyPackaging (findProperty('expo.useLegacyPackaging')?.toBoolean() ?: false)
|
|
}
|
|
}
|
|
androidResources {
|
|
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~'
|
|
}
|
|
lintOptions {
|
|
checkReleaseBuilds true
|
|
abortOnError true
|
|
xmlReport true
|
|
htmlReport true
|
|
lintConfig file("lint.xml")
|
|
// Focus only on app/src and exclude expo modules
|
|
disable 'InvalidPackage'
|
|
}
|
|
testOptions {
|
|
unitTests.all {
|
|
useJUnitPlatform()
|
|
testLogging {
|
|
events "passed", "failed", "skipped"
|
|
exceptionFormat "full"
|
|
showStandardStreams true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply static values from `gradle.properties` to the `android.packagingOptions`
|
|
// Accepts values in comma delimited lists, example:
|
|
// android.packagingOptions.pickFirsts=/LICENSE,**/picasa.ini
|
|
["pickFirsts", "excludes", "merges", "doNotStrip"].each { prop ->
|
|
// Split option: 'foo,bar' -> ['foo', 'bar']
|
|
def options = (findProperty("android.packagingOptions.$prop") ?: "").split(",");
|
|
// Trim all elements in place.
|
|
for (i in 0..<options.size()) options[i] = options[i].trim();
|
|
// `[] - ""` is essentially `[""].filter(Boolean)` removing all empty strings.
|
|
options -= ""
|
|
|
|
if (options.length > 0) {
|
|
println "android.packagingOptions.$prop += $options ($options.length)"
|
|
// Ex: android.packagingOptions.pickFirsts += '**/SCCS/**'
|
|
options.each {
|
|
android.packagingOptions[prop] += it
|
|
}
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
// The version of react-native is set by the React Native Gradle Plugin
|
|
implementation("com.facebook.react:react-android")
|
|
|
|
// Add biometric dependency for credential management
|
|
implementation("androidx.biometric:biometric:1.1.0")
|
|
|
|
// Add Credential Manager dependencies for passkey support (Android 14+)
|
|
implementation("androidx.credentials:credentials:1.6.0-beta02")
|
|
|
|
// Add Material Design components for UI
|
|
implementation("com.google.android.material:material:1.11.0")
|
|
|
|
// Add Fragment and ViewModel dependencies for fragment-based UI
|
|
implementation("androidx.fragment:fragment-ktx:1.6.2")
|
|
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
|
|
|
|
// Add vector drawable support for SVG
|
|
implementation("com.caverock:androidsvg-aar:1.4")
|
|
|
|
// Add Argon2 library for password key derivation
|
|
implementation("com.lambdapioneer.argon2kt:argon2kt:1.6.0")
|
|
|
|
// Add modern SQLite library with VACUUM INTO and backup API support
|
|
implementation("com.github.requery:sqlite-android:3.49.0")
|
|
|
|
// Add ZXing library for QR code scanning (F-Droid compatible)
|
|
implementation("com.journeyapps:zxing-android-embedded:4.3.0")
|
|
|
|
// Add JNA library for UniFFI native bindings (Android-specific AAR with native libs)
|
|
// Version 5.17.0+ required for Android 15 16KB page size support
|
|
// See: https://github.com/java-native-access/jna/issues/1647
|
|
implementation("net.java.dev.jna:jna:5.17.0") {
|
|
artifact {
|
|
type = "aar"
|
|
}
|
|
}
|
|
|
|
// Test dependencies (unit tests)
|
|
testImplementation 'junit:junit:4.13.2'
|
|
testImplementation 'org.mockito:mockito-core:4.0.0'
|
|
testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0'
|
|
testImplementation 'androidx.test:core:1.5.0'
|
|
testImplementation 'androidx.test:runner:1.5.2'
|
|
testImplementation 'androidx.test:rules:1.5.0'
|
|
testImplementation 'androidx.test.ext:junit:1.1.5'
|
|
testImplementation 'org.robolectric:robolectric:4.9'
|
|
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
|
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2'
|
|
testImplementation 'org.junit.vintage:junit-vintage-engine:5.9.2'
|
|
|
|
// Instrumented test dependencies (androidTest)
|
|
androidTestImplementation 'androidx.test:core:1.5.0'
|
|
androidTestImplementation 'androidx.test:runner:1.5.2'
|
|
androidTestImplementation 'androidx.test:rules:1.5.0'
|
|
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
|
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
|
|
androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
|
|
|
|
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
|
|
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
|
|
def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true";
|
|
|
|
if (isGifEnabled) {
|
|
// For animated gif support
|
|
implementation("com.facebook.fresco:animated-gif:${reactAndroidLibs.versions.fresco.get()}")
|
|
}
|
|
|
|
if (isWebpEnabled) {
|
|
// For webp support
|
|
implementation("com.facebook.fresco:webpsupport:${reactAndroidLibs.versions.fresco.get()}")
|
|
if (isWebpAnimatedEnabled) {
|
|
// Animated webp support
|
|
implementation("com.facebook.fresco:animated-webp:${reactAndroidLibs.versions.fresco.get()}")
|
|
}
|
|
}
|
|
|
|
if (hermesEnabled.toBoolean()) {
|
|
implementation("com.facebook.react:hermes-android")
|
|
} else {
|
|
implementation jscFlavor
|
|
}
|
|
implementation "org.jetbrains.kotlin:kotlin-test:1.9.25"
|
|
}
|
|
|
|
ktlint {
|
|
android = true
|
|
verbose = true
|
|
outputToConsole = true
|
|
ignoreFailures = false
|
|
enableExperimentalRules = true
|
|
filter {
|
|
// Include all Kotlin and Java files first
|
|
include("**/kotlin/**")
|
|
include("**/java/**")
|
|
// Then exclude generated files
|
|
exclude("**/generated/**")
|
|
exclude("**/rustcore/uniffi/**") // Exclude UniFFI-generated Rust bindings
|
|
exclude("**/uniffi/**") // Exclude any uniffi directory
|
|
}
|
|
|
|
// Configure max line length
|
|
kotlinScriptAdditionalPaths {
|
|
include(fileTree("scripts/"))
|
|
}
|
|
|
|
// Set max line length to 160
|
|
version.set("0.50.0")
|
|
}
|
|
|
|
task lintFormat {
|
|
description = "Run all auto-fixers (ktlint and Android lint)"
|
|
group = "formatting"
|
|
dependsOn 'ktlintFormat'
|
|
dependsOn 'detekt'
|
|
}
|
|
|
|
task lintCheck {
|
|
description = "Run all linting checks (ktlint, Detekt and Android lint)"
|
|
group = "formatting"
|
|
dependsOn 'ktlintCheck'
|
|
dependsOn 'detekt'
|
|
}
|
|
|
|
// Add a task dependency to make ktlint run before build
|
|
tasks.named("preBuild") {
|
|
dependsOn("lintFormat")
|
|
dependsOn("lintCheck")
|
|
}
|
|
|
|
// Ensure codegen completes before ktlint tasks run
|
|
afterEvaluate {
|
|
tasks.withType(org.jlleitschuh.gradle.ktlint.tasks.KtLintCheckTask).configureEach {
|
|
dependsOn("generateCodegenArtifactsFromSchema")
|
|
// Exclude UniFFI-generated Rust bindings
|
|
exclude("**/rustcore/uniffi/**/*.kt")
|
|
exclude("**/uniffi/**/*.kt")
|
|
}
|
|
tasks.withType(org.jlleitschuh.gradle.ktlint.tasks.KtLintFormatTask).configureEach {
|
|
dependsOn("generateCodegenArtifactsFromSchema")
|
|
// Exclude UniFFI-generated Rust bindings
|
|
exclude("**/rustcore/uniffi/**/*.kt")
|
|
exclude("**/uniffi/**/*.kt")
|
|
}
|
|
}
|
|
|
|
detekt {
|
|
buildUponDefaultConfig = true
|
|
config = files("$rootProject.projectDir/detekt.yml")
|
|
baseline = file("$rootProject.projectDir/baseline.xml")
|
|
|
|
source.setFrom(
|
|
fileTree("src/main/java") {
|
|
exclude "**/uniffi/**"
|
|
}
|
|
)
|
|
reports {
|
|
html.required.set(true)
|
|
xml.required.set(true)
|
|
txt.required.set(false)
|
|
sarif.required.set(false)
|
|
}
|
|
}
|
|
|
|
// Rust Core build task - ensures native libraries and Kotlin bindings are up to date
|
|
// Uses checksum-based incremental builds to skip if sources haven't changed
|
|
task buildRustCore(type: Exec) {
|
|
description = "Build Rust Core native libraries and Kotlin bindings"
|
|
group = "build"
|
|
|
|
workingDir = file("${rootProject.projectDir}/scripts")
|
|
|
|
// Set up environment for cargo
|
|
environment "PATH", "${System.getProperty('user.home')}/.cargo/bin:/usr/local/bin:/opt/homebrew/bin:${System.getenv('PATH')}"
|
|
|
|
// Determine build mode based on variant
|
|
def isRelease = gradle.startParameter.taskNames.any { it.toLowerCase().contains('release') }
|
|
def buildArgs = isRelease ? ['./build-rust-core.sh', '--release'] : ['./build-rust-core.sh', '--debug']
|
|
|
|
commandLine buildArgs
|
|
|
|
// Only log if there are actual changes
|
|
ignoreExitValue = false
|
|
}
|
|
|
|
// Make Rust Core build run before Kotlin compilation
|
|
tasks.matching { it.name.startsWith('compile') && it.name.contains('Kotlin') }.configureEach {
|
|
dependsOn buildRustCore
|
|
}
|