/* * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Tobias Kaminsky * SPDX-FileCopyrightText: 2024 Andy Scherzinger * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ import com.github.spotbugs.snom.Confidence import com.github.spotbugs.snom.Effort import com.github.spotbugs.snom.SpotBugsTask import org.gradle.internal.jvm.Jvm buildscript { dependencies { classpath "com.android.tools.build:gradle:$androidPluginVersion" classpath libs.spotbugs.gradle.plugin classpath libs.kotlin.gradle.plugin classpath libs.detekt.gradle.plugin classpath libs.commons.httpclient.commons.httpclient // remove after entire switch to lib v2 classpath libs.shot classpath "org.jacoco:org.jacoco.core:$jacoco_version" classpath "org.jacoco:org.jacoco.report:$jacoco_version" classpath "org.jacoco:org.jacoco.agent:$jacoco_version" } } plugins { alias(libs.plugins.kotlin.compose) alias(libs.plugins.spotless) alias(libs.plugins.kapt) alias(libs.plugins.ksp) apply false } apply plugin: "com.android.application" apply plugin: "kotlin-android" apply plugin: "kotlin-parcelize" apply plugin: "checkstyle" apply plugin: "pmd" apply from: "$rootProject.projectDir/jacoco.gradle" apply plugin: "com.github.spotbugs" apply plugin: "io.gitlab.arturbosch.detekt" // needed to make renovate run without shot, as shot requires Android SDK // https://github.com/pedrovgs/Shot/issues/300 if (shotTest) { apply plugin: "shot" } apply plugin: "com.google.devtools.ksp" println "Gradle uses Java ${Jvm.current()}" configurations { configureEach { exclude group: "org.jetbrains", module: "annotations-java5" // via prism4j, already using annotations explicitly } } configurations.configureEach { resolutionStrategy.eachDependency { if (requested.group == "org.checkerframework" && requested.name != "checker-compat-qual") { useVersion(checkerVersion) because("https://github.com/google/ExoPlayer/issues/10007") } if (requested.group == "commons-logging" && requested.name == "commons-logging") { useTarget("org.slf4j:jcl-over-slf4j:1.7.4") } } } // semantic versioning for version code def versionMajor = 3 def versionMinor = 35 def versionPatch = 0 def versionBuild = 0 // 0-50=Alpha / 51-98=RC / 90-99=stable def ndkEnv = new HashMap() file("$project.rootDir/ndk.env").readLines().each() { def (key, value) = it.tokenize("=") ndkEnv.put(key, value) } def perfAnalysis = project.hasProperty("perfAnalysis") def getConfigProperties() { def props = new Properties() def file = rootProject.file(".gradle/config.properties") if (file.exists()) { props.load(new FileInputStream(file)) } return props } def configProps = getConfigProperties() android { // install this NDK version and Cmake to produce smaller APKs. Build will still work if not installed ndkVersion = "${ndkEnv.get("NDK_VERSION")}" namespace = "com.owncloud.android" testNamespace = "${namespace}.test" androidResources { generateLocaleConfig = true } defaultConfig { applicationId = "com.nextcloud.client" minSdk = 27 targetSdk = 35 compileSdk = 35 buildConfigField "boolean", "CI", ciBuild.toString() buildConfigField "boolean", "RUNTIME_PERF_ANALYSIS", perfAnalysis.toString() javaCompileOptions { annotationProcessorOptions { arguments += ["room.schemaLocation": "$projectDir/schemas".toString()] } } // arguments to be passed to functional tests if (shotTest) { testInstrumentationRunner "com.karumi.shot.ShotTestRunner" } else { testInstrumentationRunner "com.nextcloud.client.TestRunner" } testInstrumentationRunnerArgument "TEST_SERVER_URL", "${NC_TEST_SERVER_BASEURL}" testInstrumentationRunnerArgument "TEST_SERVER_USERNAME", "${NC_TEST_SERVER_USERNAME}" testInstrumentationRunnerArgument "TEST_SERVER_PASSWORD", "${NC_TEST_SERVER_PASSWORD}" testInstrumentationRunnerArguments disableAnalytics: "true" versionCode versionMajor * 10000000 + versionMinor * 10000 + versionPatch * 100 + versionBuild if (versionBuild > 89) { versionName "${versionMajor}.${versionMinor}.${versionPatch}" } else if (versionBuild > 50) { versionName "${versionMajor}.${versionMinor}.${versionPatch} RC" + (versionBuild - 50) } else { versionName "${versionMajor}.${versionMinor}.${versionPatch} Alpha" + (versionBuild + 1) } // adapt structure from Eclipse to Gradle/Android Studio expectations; // see http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Configuring-the-Structure flavorDimensions += "default" buildTypes { release { buildConfigField "String", "NC_TEST_SERVER_DATA_STRING", "\"\"" } debug { testCoverageEnabled = project.hasProperty("coverage") resConfigs "xxxhdpi" buildConfigField "String", "NC_TEST_SERVER_DATA_STRING", "\"nc://login/user:${configProps['NC_TEST_SERVER_USERNAME']}&password:${configProps['NC_TEST_SERVER_PASSWORD']}&server:${configProps['NC_TEST_SERVER_BASEURL']}\"" } } buildFeatures { buildConfig = true } productFlavors { // used for f-droid generic { applicationId "com.nextcloud.client" dimension "default" } gplay { applicationId "com.nextcloud.client" dimension "default" } huawei { applicationId "com.nextcloud.client" dimension "default" } versionDev { applicationId "com.nextcloud.android.beta" dimension "default" versionCode 20220322 versionName "20220322" } qa { applicationId "com.nextcloud.android.qa" dimension "default" versionCode 1 versionName "1" } } testOptions { unitTests.returnDefaultValues = true animationsDisabled = true } } // adapt structure from Eclipse to Gradle/Android Studio expectations; // see http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Configuring-the-Structure packagingOptions { resources { excludes += "META-INF/LICENSE*" excludes += "META-INF/versions/9/OSGI-INF/MANIFEST*" pickFirst "MANIFEST.MF" // workaround for duplicated manifest on some dependencies } } tasks.register("checkstyle", Checkstyle) { configFile = file("${rootProject.projectDir}/checkstyle.xml") configProperties.checkstyleSuppressionsPath = file("${project.rootDir}/config/quality/checkstyle/suppressions.xml").absolutePath source "src" include "**/*.java" exclude "**/gen/**" classpath = files() } tasks.register("pmd", Pmd) { ruleSetFiles = files("${project.rootDir}/ruleset.xml") ignoreFailures = true // should continue checking ruleSets = [] source "src" include "**/*.java" exclude "**/gen/**" reports { xml { destination = layout.buildDirectory.file("reports/pmd/pmd.xml").get().asFile } html { destination = layout.buildDirectory.file("reports/pmd/pmd.html").get().asFile } } } check.dependsOn "checkstyle", "spotbugsGplayDebug", "pmd", "lint", "spotlessKotlinCheck", "detekt" buildFeatures { dataBinding = true viewBinding = true aidl = true compose = true } compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = "17" } lint { abortOnError = false checkGeneratedSources = true disable "MissingTranslation", "GradleDependency", "VectorPath", "IconMissingDensityFolder", "IconDensities", "GoogleAppIndexingWarning", "MissingDefaultResource", "InvalidPeriodicWorkRequestInterval", "StringFormatInvalid", "MissingQuantity" htmlOutput = layout.buildDirectory.file("reports/lint/lint.html").get().asFile htmlReport = true } sourceSets { // Adds exported schema location as test app assets. androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } kapt { useBuildCache = true } } dependencies { // region Nextcloud library implementation("com.github.nextcloud:android-library:$androidLibraryVersion") { exclude group: "org.ogce", module: "xpp3" // unused in Android and brings wrong Junit version } // endregion // region Splash Screen implementation libs.splashscreen // endregion // region Jetpack Compose implementation(platform(libs.compose.bom)) implementation(libs.compose.ui) implementation(libs.compose.ui.graphics) implementation(libs.compose.material3) debugImplementation(libs.compose.ui.tooling) implementation(libs.compose.ui.tooling.preview) // endregion // region Media3 implementation libs.media3.ui implementation libs.media3.session implementation libs.media3.exoplayer implementation libs.media3.datasource // endregion // region Room implementation libs.room.runtime ksp "androidx.room:room-compiler:$roomVersion" androidTestImplementation libs.room.testing // endregion // region Espresso androidTestImplementation libs.espresso.core androidTestImplementation libs.espresso.contrib androidTestImplementation libs.espresso.web androidTestImplementation libs.espresso.accessibility androidTestImplementation libs.espresso.intents androidTestImplementation libs.espresso.idling.resource // endregion // region Glide implementation libs.glide ksp libs.ksp // endregion // region UI implementation libs.appcompat implementation libs.webkit implementation libs.cardview implementation libs.exifinterface implementation libs.fragment.ktx // endregion // region Worker implementation libs.work.runtime implementation libs.work.runtime.ktx // endregion // region Lifecycle implementation libs.lifecycle.viewmodel.ktx implementation libs.lifecycle.service implementation(libs.lifecycle.runtime.ktx) // endregion // region JUnit androidTestImplementation libs.junit androidTestImplementation libs.rules androidTestImplementation libs.runner androidTestUtil libs.orchestrator androidTestImplementation libs.core.ktx androidTestImplementation libs.core.testing // endregion // region other libraries compileOnly libs.org.jbundle.util.osgi.wrapped.org.apache.http.client implementation libs.commons.httpclient.commons.httpclient // remove after entire switch to lib v2 implementation libs.jackrabbit.webdav // remove after entire switch to lib v2 implementation libs.constraintlayout implementation libs.legacy.support.v4 implementation libs.material implementation libs.disklrucache implementation libs.juniversalchardet // need this version for Android <7 compileOnly libs.annotations implementation libs.commons.io implementation libs.eventbus implementation libs.ez.vcard implementation libs.nnio implementation libs.bcpkix.jdk18on implementation libs.gson implementation libs.sectioned.recyclerview implementation libs.photoview implementation libs.android.gif.drawable implementation libs.qrcodescanner // "com.github.blikoon:QRCodeScanner:0.1.2" implementation libs.flexbox implementation libs.androidsvg implementation libs.annotation implementation libs.emoji.google // endregion // region AppScan, document scanner not available on FDroid (generic) due to OpenCV binaries gplayImplementation project(":appscan") huaweiImplementation project(":appscan") qaImplementation project(":appscan") // endregion // region SpotBugs spotbugsPlugins libs.findsecbugs.plugin spotbugsPlugins libs.fb.contrib // endregion // region Dagger implementation libs.dagger implementation libs.dagger.android implementation libs.dagger.android.support kapt "com.google.dagger:dagger-compiler:$daggerVersion" kapt "com.google.dagger:dagger-android-processor:$daggerVersion" // endregion // region Crypto implementation libs.conscrypt.android // endregion // region Library implementation libs.library // endregion // region Shimmer implementation libs.loaderviewlibrary // endregion // region Markdown rendering implementation libs.core implementation libs.ext.strikethrough implementation libs.ext.tables implementation libs.ext.tasklist implementation libs.html implementation libs.syntax.highlight implementation libs.prism4j kapt "io.noties:prism4j-bundler:$prismVersion" // endregion // region Image cropping / rotation implementation libs.android.image.cropper // endregion // region Maps implementation libs.osmdroid.android // endregion // region iCal4j implementation(libs.ical4j) { ["org.apache.commons", "commons-logging"].each { exclude group: "$it" } } // endregion // region LeakCanary if (perfAnalysis) { debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14" } // endregion // region Local Unit Test testImplementation libs.junit.junit testImplementation libs.mockito.core testImplementation libs.test.core testImplementation libs.json testImplementation libs.mockito.kotlin testImplementation libs.core.testing testImplementation "io.mockk:mockk:$mockkVersion" testImplementation libs.mockk.android // endregion // region Mocking support androidTestImplementation libs.dexopener // required to allow mocking on API 27 and older androidTestImplementation libs.mockito.kotlin androidTestImplementation libs.mockito.core androidTestImplementation(libs.mockito.android) androidTestImplementation libs.mockk.android androidTestImplementation libs.screenshot.core // endregion // region UIAutomator // UIAutomator - for cross-app UI tests, and to grant screen is turned on in Espresso tests // androidTestImplementation "androidx.test.uiautomator:uiautomator:2.2.0" // fix conflict in dependencies; see http://g.co/androidstudio/app-test-app-conflict for details // androidTestImplementation "com.android.support:support-annotations:${supportLibraryVersion}" androidTestImplementation libs.screengrab // endregion // region Kotlin implementation libs.kotlin.stdlib // endregion // region Stateless implementation libs.stateless4j // endregion // region Google Play dependencies, upon each update first test: new registration, receive push gplayImplementation libs.firebase.messaging gplayImplementation libs.play.services.base gplayImplementation libs.review.ktx // endregion // region UI implementation libs.ui // endregion // region Image loading implementation libs.coil // endregion } configurations.configureEach { resolutionStrategy { force "org.objenesis:objenesis:3.4" eachDependency { details -> if ("org.jacoco" == details.requested.group) { details.useVersion "$jacoco_version" } } } } // Run the compiler as a separate process tasks.withType(JavaCompile).configureEach { options.fork = true // Enable Incremental Compilation options.incremental = true } tasks.withType(Test).configureEach { // Run tests in parallel maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 // increased logging for tests testLogging { events "passed", "skipped", "failed" } } android.applicationVariants.configureEach { variant -> variant.outputs.configureEach { output -> outputFileName = "${output.baseName}-${variant.versionCode}.apk" } } spotless { kotlin { target "**/*.kt" ktlint() } } detekt { config.setFrom("detekt.yml") } if (shotTest) { shot { showOnlyFailingTestsInReports = ciBuild // CI environment renders some shadows slightly different from local VMs // Add a 0.5% tolerance to account for that tolerance = ciBuild ? 0.1 : 0 } } jacoco { toolVersion = "$jacoco_version" } spotbugs { ignoreFailures = true // should continue checking effort = Effort.MAX reportLevel = Confidence.valueOf("MEDIUM") } tasks.withType(SpotBugsTask){task -> String variantNameCap = task.name.replace("spotbugs", "") String variantName = variantNameCap.substring(0, 1).toLowerCase() + variantNameCap.substring(1) dependsOn "compile${variantNameCap}Sources" classes = fileTree(layout.buildDirectory.get().asFile.toString()+"/intermediates/javac/${variantName}/compile${variantNameCap}JavaWithJavac/classes/") excludeFilter = file("${project.rootDir}/scripts/analysis/spotbugs-filter.xml") reports { xml { required = true } html { required = true outputLocation = layout.buildDirectory.file("reports/spotbugs/spotbugs.html").get().asFile stylesheet = "fancy.xsl" } } } ksp { arg("room.schemaLocation", "$projectDir/schemas") }