From 5fd2e2942c2b375b86c52256f7c8921395f5594c Mon Sep 17 00:00:00 2001 From: Jimly Asshiddiqy Date: Tue, 28 Oct 2025 09:39:21 +0700 Subject: [PATCH 1/4] chore(gradle): Improve Gradle Configuration - Rename build.gradle to build.gradle.kts - Rename settings.gradle to settings.gradle - Rename jacoco.gradle to jacoco.gradle.kts - Refactor some entries in the libs.versions.toml - Change running test task from to run jacoco task to trigger task, now it's reversed... we run the test task that will trigger the jacoco task Signed-off-by: Jimly Asshiddiqy --- .drone.yml | 2 +- app/build.gradle | 592 ------------------------------- app/build.gradle.kts | 517 +++++++++++++++++++++++++++ appscan/build.gradle | 51 --- appscan/build.gradle.kts | 47 +++ build.gradle | 71 ---- build.gradle.kts | 37 ++ gradle/libs.versions.toml | 90 ++++- gradle/verification-metadata.xml | 150 +++++++- jacoco.gradle | 106 ------ jacoco.gradle.kts | 116 ++++++ settings.gradle | 22 -- settings.gradle.kts | 54 +++ 13 files changed, 997 insertions(+), 858 deletions(-) delete mode 100644 app/build.gradle create mode 100644 app/build.gradle.kts delete mode 100644 appscan/build.gradle create mode 100644 appscan/build.gradle.kts delete mode 100644 build.gradle create mode 100644 build.gradle.kts delete mode 100644 jacoco.gradle create mode 100644 jacoco.gradle.kts delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts diff --git a/.drone.yml b/.drone.yml index 569a7ae7724d..9347864809c2 100644 --- a/.drone.yml +++ b/.drone.yml @@ -148,7 +148,7 @@ steps: commands: - emulator -avd android -no-snapshot -gpu swiftshader_indirect -no-window -no-audio -skin 500x833 & - sed -i s'#false#true#'g app/src/main/res/values/setup.xml - - sed -i s'#showOnlyFailingTestsInReports = ciBuild#showOnlyFailingTestsInReports = false#' build.gradle + - sed -i s'#showOnlyFailingTestsInReports = ciBuild#showOnlyFailingTestsInReports = false#' build.gradle.kts - scripts/wait_for_emulator.sh - scripts/runAllScreenshotCombinations noCI false - scripts/screenshotSummary.sh diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 2ea044d01c42..000000000000 --- a/app/build.gradle +++ /dev/null @@ -1,592 +0,0 @@ -/* - * 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 - alias(libs.plugins.kotlin.serialization) -} - -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.36") - } - } -} - -// 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 = 36 - compileSdk = 36 - - 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.material.icons.core) - 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 - - // kotlinx.serialization - implementation(libs.kotlinx.serialization.json) -} - - -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") -} diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 000000000000..23cf315b382f --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,517 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +@file:Suppress("UnstableApiUsage", "DEPRECATION") + +import com.android.build.gradle.internal.api.ApkVariantOutputImpl +import com.github.spotbugs.snom.Confidence +import com.github.spotbugs.snom.Effort +import com.github.spotbugs.snom.SpotBugsTask +import com.karumi.shot.ShotExtension +import org.gradle.internal.jvm.Jvm +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import java.io.FileInputStream +import java.util.Properties + +val shotTest = System.getenv("SHOT_TEST") == "true" +val ciBuild = System.getenv("CI") == "true" +val perfAnalysis = project.hasProperty("perfAnalysis") + +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.compose) + alias(libs.plugins.spotless) + alias(libs.plugins.kapt) + alias(libs.plugins.ksp) + alias(libs.plugins.kotlin.serialization) + alias(libs.plugins.kotlin.parcelize) + alias(libs.plugins.jetbrains.kotlin.android) + alias(libs.plugins.spotbugs) + alias(libs.plugins.detekt) + // needed to make renovate run without shot, as shot requires Android SDK + // https://github.com/pedrovgs/Shot/issues/300 + if (System.getenv("SHOT_TEST") == "true") alias(libs.plugins.shot) + id("checkstyle") + id("pmd") +} +apply(from = "${rootProject.projectDir}/jacoco.gradle.kts") + +println("Gradle uses Java ${Jvm.current()}") + +configurations.configureEach { + // via prism4j, already using annotations explicitly + exclude(group = "org.jetbrains", module = "annotations-java5") + + resolutionStrategy { + force(libs.objenesis) + + eachDependency { + if (requested.group == "org.checkerframework" && requested.name != "checker-compat-qual") { + useVersion(libs.versions.checker.get()) + because("https://github.com/google/ExoPlayer/issues/10007") + } else if (requested.group == "org.jacoco") { + useVersion(libs.versions.jacoco.get()) + } else if (requested.group == "commons-logging" && requested.name == "commons-logging") { + useTarget(libs.slfj) + } + } + } +} + +// semantic versioning for version code +val versionMajor = 3 +val versionMinor = 35 +val versionPatch = 0 +val versionBuild = 0 // 0-50=Alpha / 51-98=RC / 90-99=stable + +val ndkEnv = buildMap { + file("${project.rootDir}/ndk.env").readLines().forEach { + val (key, value) = it.split("=") + put(key, value) + } +} + +val configProps = Properties().apply { + val file = rootProject.file(".gradle/config.properties") + if (file.exists()) load(FileInputStream(file)) +} + +val ncTestServerUsername = configProps["NC_TEST_SERVER_USERNAME"] +val ncTestServerPassword = configProps["NC_TEST_SERVER_PASSWORD"] +val ncTestServerBaseUrl = configProps["NC_TEST_SERVER_BASEURL"] + +android { + // install this NDK version and Cmake to produce smaller APKs. Build will still work if not installed + ndkVersion = "${ndkEnv["NDK_VERSION"]}" + + namespace = "com.owncloud.android" + testNamespace = "${namespace}.test" + + androidResources.generateLocaleConfig = true + + defaultConfig { + applicationId = "com.nextcloud.client" + minSdk = 27 + targetSdk = 36 + compileSdk = 36 + + buildConfigField("boolean", "CI", ciBuild.toString()) + buildConfigField("boolean", "RUNTIME_PERF_ANALYSIS", perfAnalysis.toString()) + + javaCompileOptions.annotationProcessorOptions { + arguments += mapOf("room.schemaLocation" to "$projectDir/schemas") + } + + // arguments to be passed to functional tests + testInstrumentationRunner = if (shotTest) "com.karumi.shot.ShotTestRunner" + else "com.nextcloud.client.TestRunner" + + testInstrumentationRunnerArguments += mapOf( + "TEST_SERVER_URL" to ncTestServerBaseUrl.toString(), + "TEST_SERVER_USERNAME" to ncTestServerUsername.toString(), + "TEST_SERVER_PASSWORD" to ncTestServerPassword.toString() + ) + testInstrumentationRunnerArguments["disableAnalytics"] = "true" + + versionCode = versionMajor * 10000000 + versionMinor * 10000 + versionPatch * 100 + versionBuild + versionName = when { + versionBuild > 89 -> "${versionMajor}.${versionMinor}.${versionPatch}" + versionBuild > 50 -> "${versionMajor}.${versionMinor}.${versionPatch} RC" + (versionBuild - 50) + else -> "${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 { + enableUnitTestCoverage = project.hasProperty("coverage") + resConfigs("xxxhdpi") + + buildConfigField( + "String", + "NC_TEST_SERVER_DATA_STRING", + "\"nc://login/user:${ncTestServerUsername}&password:${ncTestServerPassword}&server:${ncTestServerBaseUrl}\"" + ) + } + } + + productFlavors { + // used for f-droid + register("generic") { + applicationId = "com.nextcloud.client" + dimension = "default" + } + + register("gplay") { + applicationId = "com.nextcloud.client" + dimension = "default" + } + + register("huawei") { + applicationId = "com.nextcloud.client" + dimension = "default" + } + + register("versionDev") { + applicationId = "com.nextcloud.android.beta" + dimension = "default" + versionCode = 20220322 + versionName = "20220322" + } + + register("qa") { + applicationId = "com.nextcloud.android.qa" + dimension = "default" + versionCode = 1 + versionName = "1" + } + } + } + + applicationVariants.configureEach { + outputs.configureEach { + if (this is ApkVariantOutputImpl) this.outputFileName = "${this.baseName}-${this.versionCode}.apk" + } + } + + testOptions { + unitTests.isReturnDefaultValues = 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 + packaging.resources { + excludes.addAll(listOf("META-INF/LICENSE*", "META-INF/versions/9/OSGI-INF/MANIFEST*")) + pickFirsts.add("MANIFEST.MF") // workaround for duplicated manifest on some dependencies + } + + buildFeatures { + buildConfig = true + dataBinding = true + viewBinding = true + aidl = true + compose = true + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + lint { + abortOnError = false + checkGeneratedSources = true + disable.addAll( + listOf( + "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. + getByName("androidTest") { + assets.srcDirs(files("$projectDir/schemas")) + } + } + +} + +kapt.useBuildCache = true + +ksp.arg("room.schemaLocation", "$projectDir/schemas") + +kotlin.compilerOptions.jvmTarget.set(JvmTarget.JVM_17) + +spotless.kotlin { + target("**/*.kt") + ktlint() +} + +detekt.config.setFrom("detekt.yml") + +if (shotTest) configure { + showOnlyFailingTestsInReports = ciBuild + // CI environment renders some shadows slightly different from local VMs + // Add a 0.5% tolerance to account for that + tolerance = if (ciBuild) 0.1 else 0.0 +} + + +spotbugs { + ignoreFailures = true // should continue checking + effort = Effort.MAX + reportLevel = Confidence.valueOf("MEDIUM") +} + +tasks.register("checkstyle") { + configFile = file("${rootProject.projectDir}/checkstyle.xml") + setConfigProperties( + "checkstyleSuppressionsPath" to file("${rootProject.rootDir}/suppressions.xml").absolutePath + ) + source("src") + include("**/*.java") + exclude("**/gen/**") + classpath = files() +} + +tasks.register("pmd") { + ruleSetFiles = files("${rootProject.rootDir}/ruleset.xml") + ignoreFailures = true // should continue checking + ruleSets = emptyList() + + source("src") + include("**/*.java") + exclude("**/gen/**") + + reports { + xml.outputLocation.set(layout.buildDirectory.file("reports/pmd/pmd.xml").get().asFile) + html.outputLocation.set(layout.buildDirectory.file("reports/pmd/pmd.html").get().asFile) + } +} + +tasks.withType().configureEach { + val variantNameCap = name.replace("spotbugs", "") + val variantName = variantNameCap.substring(0, 1).lowercase() + 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 { + register("xml") { + required = true + } + register("html") { + required = true + outputLocation = layout.buildDirectory.file("reports/spotbugs/spotbugs.html").get().asFile + setStylesheet("fancy.xsl") + } + } +} + +// Run the compiler as a separate process +tasks.withType().configureEach { + options.isFork = true + + // Enable Incremental Compilation + options.isIncremental = true +} + +tasks.withType().configureEach { + // Run tests in parallel + maxParallelForks = Runtime.getRuntime().availableProcessors().div(2) + + // increased logging for tests + testLogging.events("passed", "skipped", "failed") +} + +tasks.named("check").configure { + dependsOn("checkstyle", "spotbugsGplayDebug", "pmd", "lint", "spotlessKotlinCheck", "detekt") +} + +dependencies { + // region Nextcloud library + implementation(libs.android.library) { + 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.material.icons.core) + implementation(libs.compose.ui) + implementation(libs.compose.ui.graphics) + implementation(libs.compose.material3) + implementation(libs.compose.ui.tooling.preview) + debugImplementation(libs.compose.ui.tooling) + // endregion + + // region Media3 + implementation(libs.bundles.media3) + // endregion + + // region Room + implementation(libs.room.runtime) + ksp(libs.room.compiler) + androidTestImplementation(libs.room.testing) + // endregion + + // region Espresso + androidTestImplementation(libs.bundles.espresso) + // endregion + + // region Glide + implementation(libs.glide) + ksp(libs.ksp) + // endregion + + // region UI + implementation(libs.bundles.ui) + // 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) + ksp(libs.dagger.compiler) + ksp(libs.dagger.processor) + // 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.bundles.markdown.rendering) + kapt(libs.prism4j.bundler) + // endregion + + // region Image cropping / rotation + implementation(libs.android.image.cropper) + // endregion + + // region Maps + implementation(libs.osmdroid.android) + // endregion + + // region iCal4j + implementation(libs.ical4j) { + listOf("org.apache.commons", "commons-logging").forEach { groupName -> exclude(group = groupName) } + } + // endregion + + // region LeakCanary + if (perfAnalysis) debugImplementation(libs.leakcanary) + // endregion + + // region Local Unit Test + testImplementation(libs.bundles.unit.test) + // endregion + + // region Mocking support + androidTestImplementation(libs.bundles.mocking) + // 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.bundles.gplay) + // endregion + + // region UI + implementation(libs.ui) + // endregion + + // region Image loading + implementation(libs.coil) + // endregion + + // kotlinx.serialization + implementation(libs.kotlinx.serialization.json) +} \ No newline at end of file diff --git a/appscan/build.gradle b/appscan/build.gradle deleted file mode 100644 index 00984b58de8d..000000000000 --- a/appscan/build.gradle +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2024 Alper Ozturk - * SPDX-FileCopyrightText: 2023 Álvaro Brey - * SPDX-FileCopyrightText: 2023 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only - */ -buildscript { - dependencies { - classpath "com.android.tools.build:gradle:$androidPluginVersion" - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -apply plugin: "com.android.library" -apply plugin: "kotlin-android" - -android { - namespace = "com.nextcloud.appscan" - - defaultConfig { - minSdk = 27 - targetSdk = 36 - compileSdk = 36 - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled = false - } - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - } - - kotlinOptions { - jvmTarget = "17" - } -} - -dependencies { - implementation "androidx.appcompat:appcompat:$appCompatVersion" - implementation "com.github.Hazzatur:Document-Scanning-Android-SDK:$documentScannerVersion" - implementation "com.github.nextcloud.android-common:ui:$androidCommonLibraryVersion" -} diff --git a/appscan/build.gradle.kts b/appscan/build.gradle.kts new file mode 100644 index 000000000000..9d14f2127ac8 --- /dev/null +++ b/appscan/build.gradle.kts @@ -0,0 +1,47 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.jetbrains.kotlin.android) +} + +android { + namespace = "com.nextcloud.appscan" + + defaultConfig { + minSdk = 27 + compileSdk = 36 + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + lint.targetSdk = 35 + testOptions.targetSdk = 35 +} + +kotlin.compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + freeCompilerArgs.add("-opt-in=kotlin.RequiresOptIn") +} + +dependencies { + implementation(libs.appcompat) + implementation(libs.document.scanning.android.sdk) + implementation(libs.ui) +} diff --git a/build.gradle b/build.gradle deleted file mode 100644 index ef00fbe90d1e..000000000000 --- a/build.gradle +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2024 Alper Ozturk - * SPDX-FileCopyrightText: 2023 Tobias Kaminsky - * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas - * SPDX-FileCopyrightText: 2016 Andy Scherzinger - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only - */ -buildscript { - ext { - androidLibraryVersion ="2f798d3f4b0b18458af0c917eb39ad8ca45e5b55" - androidCommonLibraryVersion = "0.29.0" - androidPluginVersion = '8.13.0' - androidxMediaVersion = "1.5.1" - androidxTestVersion = "1.7.0" - appCompatVersion = "1.7.1" - checkerVersion = "3.21.2" - daggerVersion = "2.57.2" - documentScannerVersion = "1.2.3" - espressoVersion = "3.6.1" - jacoco_version = "0.8.14" - kotlin_version = "2.2.21" - markwonVersion = "4.6.2" - mockitoVersion = "4.11.0" - mockitoKotlinVersion = "4.1.0" - mockkVersion = "1.14.6" - prismVersion = "2.0.0" - roomVersion = "2.8.0" - workRuntime = "2.10.3" - - ciBuild = System.getenv("CI") == "true" - shotTest = System.getenv("SHOT_TEST") == "true" - } -} - -subprojects { - buildscript { - repositories { - google() - mavenCentral() - gradlePluginPortal() - } - } - repositories { - google() - mavenCentral() - maven { url = "https://jitpack.io" } - } -} - -tasks.register("clean", Delete) { - delete rootProject.layout.buildDirectory -} - -tasks.register("installGitHooks", Copy) { - def sourceFolder = "${rootProject.projectDir}/scripts/hooks" - def destFolder = "${rootProject.projectDir}/.git/hooks" - - description = "Install git hooks" - - from(sourceFolder) { - include "*" - } - into destFolder - - eachFile { file -> - println "${sourceFolder}/${file.relativeSourcePath} -> ${destFolder}/${file.path}" - } -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 000000000000..96fdf0573608 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,37 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +plugins { + alias(libs.plugins.android.application) apply false + alias(libs.plugins.android.library) apply false + alias(libs.plugins.jetbrains.kotlin.android) apply false + alias(libs.plugins.kotlin.compose) apply false + alias(libs.plugins.spotless) apply false + alias(libs.plugins.kapt) apply false + alias(libs.plugins.ksp) apply false + alias(libs.plugins.kotlin.serialization) apply false + alias(libs.plugins.kotlin.parcelize) apply false + alias(libs.plugins.spotbugs) apply false + alias(libs.plugins.detekt) apply false + // needed to make renovate run without shot, as shot requires Android SDK + // https://github.com/pedrovgs/Shot/issues/300 + alias(libs.plugins.shot) apply false +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} + +tasks.register("installGitHooks") { + description = "Install git hooks" + + val sourceFolder = "${rootProject.projectDir}/scripts/hooks" + val destFolder = "${rootProject.projectDir}/.git/hooks" + + from(sourceFolder) { include("*") } + into(destFolder) + eachFile { println("${sourceFolder}/${file.path} -> ${destFolder}/${file.path}") } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 513283130f07..795ec7edb894 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,32 +2,39 @@ # SPDX-License-Identifier: AGPL-3.0-or-later [versions] -androidCommonLibraryVersion = "0.29.0" +androidCommonLibraryVersion = "0.28.0" androidGifDrawableVersion = "1.2.29" androidImageCropperVersion = "4.6.0" +androidLibraryVersion = "3546bd82fc" +androidPluginVersion = '8.13.0' androidsvgVersion = "1.4" +androidxMediaVersion = "1.5.1" androidxTestVersion = "1.7.0" -annotationsVersion = "3.0.1u2" annotationVersion = "1.9.1" +annotationsVersion = "3.0.1u2" appCompatVersion = "1.7.1" bcpkixJdk18onVersion = "1.81" cardviewVersion = "1.0.0" +checker = "3.21.2" coilVersion = "2.7.0" commonsHttpclient = "3.1" commonsIoVersion = "2.20.0" +composeBom = "2025.10.01" conscryptAndroidVersion = "2.5.3" constraintlayoutVersion = "2.2.1" coreTestingVersion = "2.2.0" coreVersion = "0.15.0" daggerVersion = "2.57.2" +detektGradlePlugin = "1.23.8" dexopenerVersion = "2.0.5" disklrucacheVersion = "2.0.2" +documentScannerVersion = "1.2.3" emojiGoogleVersion = "0.21.0" espressoVersion = "3.7.0" eventbusVersion = "3.3.1" exifinterfaceVersion = "1.4.1" ezVcardVersion = "0.12.1" -fbContribVersion = "7.6.15" +fbContribVersion = "7.6.14" findsecbugsPluginVersion = "1.14.0" firebaseMessagingVersion = "25.0.1" flexboxVersion = "3.0.0" @@ -36,10 +43,15 @@ glide = "5.0.5" gsonVersion = "2.13.2" ical4jVersion = "3.2.19" jackrabbitWebdavVersion = "2.13.5" +jacoco = "0.8.13" jsonVersion = "20250517" junit = "4.13.2" junitVersion = "1.3.0" juniversalchardetVersion = "2.5.0" +kotlin = "2.2.20" +kotlinxSerializationJson = "1.9.0" +ksp = "2.2.20-2.0.3" +leakcanary = "2.14" legacySupportV4Version = "1.0.0" libraryVersion = "1.3.0" lifecycleViewmodelKtxVersion = "2.9.4" @@ -50,8 +62,9 @@ materialVersion = "1.13.0" media3 = "1.8.0" mockitoKotlinVersion = "4.1.0" mockitoVersion = "4.11.0" -mockkVersion = "1.14.6" +mockkVersion = "1.14.5" nnioVersion = "0.3.1" +objenesis = "3.4" orchestratorVersion = "1.6.1" orgJbundleUtilOsgiWrappedOrgApacheHttpClientVersion = "4.1.2" osmdroidAndroidVersion = "6.1.20" @@ -64,23 +77,17 @@ roomVersion = "2.8.0" screengrabVersion = "2.1.1" sectionedRecyclerviewVersion = "0.6.1" shotVersion = "6.1.0" +slfj = "1.7.36" splash-screen = "1.0.1" -composeBom = "2025.10.01" -materialIcons = "1.7.8" -spotbugsGradlePlugin = "6.4.4" -detektGradlePlugin = "1.23.8" +spotbugsGradlePlugin = "6.4.2" spotless = "7.2.1" stateless4jVersion = "2.6.0" webkitVersion = "1.14.0" workRuntime = "2.10.4" -kotlinxSerializationJson = "1.9.0" - -kotlin = "2.2.21" -ksp = "2.3.0" [libraries] - # Crypto +android-library = { module = "com.github.nextcloud:android-library", version.ref = "androidLibraryVersion" } conscrypt-android = { module = "org.conscrypt:conscrypt-android", version.ref = "conscryptAndroidVersion" } bcpkix-jdk18on = { module = "org.bouncycastle:bcpkix-jdk18on", version.ref = "bcpkixJdk18onVersion" } @@ -88,6 +95,7 @@ bcpkix-jdk18on = { module = "org.bouncycastle:bcpkix-jdk18on", version.ref = "bc appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appCompatVersion" } cardview = { module = "androidx.cardview:cardview", version.ref = "cardviewVersion" } core-ktx = { module = "androidx.test:core-ktx", version.ref = "androidxTestVersion" } +document-scanning-android-sdk = { module = "com.github.Hazzatur:Document-Scanning-Android-SDK", version.ref = "documentScannerVersion" } fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtxVersion" } exifinterface = { module = "androidx.exifinterface:exifinterface", version.ref = "exifinterfaceVersion" } material-icons-core = { module = "androidx.compose.material:material-icons-core", version.ref = "materialIconsCoreVersion" } @@ -132,20 +140,26 @@ jackrabbit-webdav = { module = "org.apache.jackrabbit:jackrabbit-webdav", versio json = { module = "org.json:json", version.ref = "jsonVersion" } juniversalchardet = { module = "com.github.albfernandez:juniversalchardet", version.ref = "juniversalchardetVersion" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } +leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" } nnio = { module = "org.lukhnos:nnio", version.ref = "nnioVersion" } org-jbundle-util-osgi-wrapped-org-apache-http-client = { module = "org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client", version.ref = "orgJbundleUtilOsgiWrappedOrgApacheHttpClientVersion" } osmdroid-android = { module = "org.osmdroid:osmdroid-android", version.ref = "osmdroidAndroidVersion" } +objenesis = { module = "org.objenesis:objenesis", version.ref = "objenesis" } play-services-base = { module = "com.google.android.gms:play-services-base", version.ref = "playServicesBaseVersion" } review-ktx = { module = "com.google.android.play:review-ktx", version.ref = "reviewKtxVersion" } +slfj = { module = "org.slf4j:jcl-over-slf4j", version.ref = "slfj" } # Mockito mockito-android = { module = "org.mockito:mockito-android", version.ref = "mockitoVersion" } mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockitoVersion" } mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoKotlinVersion" } mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockkVersion" } +mockk = { module = "io.mockk:mockk", version.ref = "mockkVersion" } # Dagger dagger = { module = "com.google.dagger:dagger", version.ref = "daggerVersion" } +dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "daggerVersion" } +dagger-processor = { module = "com.google.dagger:dagger-android-processor", version.ref = "daggerVersion" } dagger-android = { module = "com.google.dagger:dagger-android", version.ref = "daggerVersion" } dagger-android-support = { module = "com.google.dagger:dagger-android-support", version.ref = "daggerVersion" } @@ -167,7 +181,6 @@ compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics" } compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } compose-material3 = { module = "androidx.compose.material3:material3" } -material-icons = { module = "androidx.compose.material:material-icons-core-1.7.8"} # Media3 media3-datasource = { module = "androidx.media3:media3-datasource-okhttp", version.ref = "media3" } @@ -177,6 +190,7 @@ media3-ui = { module = "androidx.media3:media3-ui", version.ref = "media3" } # Room room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomVersion" } +room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomVersion" } room-testing = { module = "androidx.room:room-testing", version.ref = "roomVersion" } # Espresso @@ -189,6 +203,7 @@ espresso-web = { module = "androidx.test.espresso:espresso-web", version.ref = " # Test junit = { module = "androidx.test.ext:junit", version.ref = "junitVersion" } + core-testing = { module = "androidx.arch.core:core-testing", version.ref = "coreTestingVersion" } orchestrator = { module = "androidx.test:orchestrator", version.ref = "orchestratorVersion" } rules = { module = "androidx.test:rules", version.ref = "androidxTestVersion" } @@ -212,6 +227,7 @@ stateless4j = { module = "com.github.stateless4j:stateless4j", version.ref = "st syntax-highlight = { module = "io.noties.markwon:syntax-highlight", version.ref = "markwonVersion" } core = { module = "io.noties.markwon:core", version.ref = "markwonVersion" } prism4j = { module = "io.noties:prism4j", version.ref = "prismVersion" } +prism4j-bundler = { module = "io.noties:prism4j-bundler", version.ref = "prismVersion" } # Nextcloud libraries ui = { module = "com.github.nextcloud.android-common:ui", version.ref = "androidCommonLibraryVersion" } @@ -221,9 +237,55 @@ qrcodescanner = { module = "com.github.nextcloud-deps:qrcodescanner", version.re work-runtime = { module = "androidx.work:work-runtime", version.ref = "workRuntime" } work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntime" } +[bundles] +media3 = ["media3-ui", "media3-session", "media3-exoplayer", "media3-datasource"] +espresso = ["espresso-core", "espresso-contrib", "espresso-web", "espresso-accessibility", "espresso-intents", "espresso-idling-resource"] +ui = ["appcompat", "webkit", "cardview", "exifinterface", "fragment-ktx"] +markdown-rendering = [ + "core", + "ext-strikethrough", + "ext-tables", + "ext-tasklist", + "html", + "syntax-highlight", + "prism4j" +] +unit-test = [ + "junit-junit", + "test-core", + "json", + "mockito-kotlin", + "mockk", + "mockk-android", + "mockito-core", + "mockito-android", + "core-testing" +] +mocking = [ + "dexopener", # required to allow mocking on API 27 and older + "mockito-kotlin", + "mockk", + "mockk-android", + "mockito-core", + "mockito-android", + "screenshot-core" +] +gplay = [ + "firebase-messaging", + "play-services-base", + "review-ktx" +] + [plugins] +android-application = { id = "com.android.application", version.ref = "androidPluginVersion" } +android-library = { id = "com.android.library", version.ref = "androidPluginVersion" } +kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } +spotbugs = { id = "com.github.spotbugs", version.ref = "spotbugsGradlePlugin" } +jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detektGradlePlugin" } +shot = { id = "shot", version.ref = "shotVersion" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } \ No newline at end of file diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 67119508d0eb..662173539512 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -1,4 +1,10 @@ - + + true @@ -10034,6 +10040,12 @@ + + + + + @@ -10158,6 +10170,12 @@ + + + + + @@ -10524,6 +10542,16 @@ + + + + + + + + @@ -11437,6 +11465,10 @@ + + + @@ -13143,6 +13175,16 @@ + + + + + + + + @@ -13191,6 +13233,16 @@ + + + + + + + + @@ -13240,6 +13292,16 @@ + + + + + + + + @@ -13408,6 +13470,16 @@ + + + + + + + + @@ -13455,6 +13527,16 @@ + + + + + + + + @@ -13503,6 +13585,16 @@ + + + + + + + + @@ -13552,6 +13644,16 @@ + + + + + + + + @@ -18600,6 +18702,12 @@ + + + + + @@ -21033,6 +21141,16 @@ + + + + + + + + @@ -21320,6 +21438,10 @@ + + + @@ -23201,6 +23323,12 @@ + + + + + @@ -23953,6 +24081,12 @@ + + + + + @@ -31163,6 +31297,13 @@ + + + + + @@ -31319,6 +31460,13 @@ + + + + + diff --git a/jacoco.gradle b/jacoco.gradle deleted file mode 100644 index f3a7090b679b..000000000000 --- a/jacoco.gradle +++ /dev/null @@ -1,106 +0,0 @@ -apply plugin: "jacoco" - -jacoco { - toolVersion = "$jacoco_version" -} - -// Force Jacoco Version - -subprojects { - configurations.all { - resolutionStrategy { - eachDependency { details -> - if ("org.jacoco" == details.requested.group) { - details.useVersion "$jacocoVersion" - } - } - } - } -} - -project.afterEvaluate { project -> - - tasks.withType(Test).configureEach { - jacoco.includeNoLocationClasses = true - jacoco.excludes = ["jdk.internal.*"] - } - - final flavor = "Gplay" - final buildType = "Debug" - final variant = "$flavor${buildType.capitalize()}" - final taskName = "jacocoTest${variant.capitalize()}UnitTestReport" - - task "$taskName"(type: JacocoReport, dependsOn: "test${variant.capitalize()}UnitTest") { - - reports { - csv.required = Boolean.FALSE - xml.required = Boolean.TRUE - html.required = Boolean.TRUE - } - - final fileFilter = [ - // data binding - "**/databinding/*", - "android/databinding/**/*.class", - "**/android/databinding/*Binding.class", - "**/android/databinding/*", - "**/androidx/databinding/*", - "**/BR.*", - // android - "**/R.class", - "**/R\$*.class", - "**/BuildConfig.*", - "**/Manifest*.*", - "**/*Test*.*", - "android/**/*.*", - // kotlin - "**/*MapperImpl*.*", - "**/*\$ViewInjector*.*", - "**/*\$ViewBinder*.*", - "**/BuildConfig.*", - "**/*Component*.*", - "**/*BR*.*", - "**/Manifest*.*", - "**/*\$Lambda\$*.*", - "**/*Companion*.*", - "**/*Module*.*", - "**/*Dagger*.*", - "**/*Hilt*.*", - "**/*MembersInjector*.*", - "**/*_MembersInjector.class", - "**/*_Factory*.*", - "**/*_Provide*Factory*.*", - "**/*Extensions*.*", - // sealed and data classes - "**/*\$Result.*", - "**/*\$Result\$*.*", - // adapters generated by moshi - "**/*JsonAdapter.*", - // Hilt - "**/*Module.kt", - "**/di/**", - "dagger.hilt.internal/*", - "hilt_aggregated_deps/*", - - "**/*\$Result.*", /* filtering `sealed` and `data` classes */ - "**/*\$Result\$*.*",/* filtering `sealed` and `data` classes */ - "**/*Args*.*", /* filtering Navigation Component generated classes */ - "**/*Directions*.*", /* filtering Navigation Component generated classes */ - "**/*inlined*.class", /* filtering inlined classes */ - "**/composables/**" - /* INSERT ANY OTHER JUNK YOU WANT FILTERED OUT HERE */] - - final androidKotlinTree = fileTree(dir: "${project.buildDir}/tmp/kotlin-classes/${variant}", excludes: fileFilter) - final kotlinTree = fileTree(dir: "${project.buildDir}/classes/kotlin/main", excludes: fileFilter) - final javacTree = fileTree(dir: "${project.buildDir}/intermediates/javac/${variant}/classes", excludes: fileFilter) - - final mainSrc = "${project.projectDir}/src/main/java" - final productFlavorSrc = "${project.projectDir}/src/${flavor}/java" - final buildTypeSrc = "${project.projectDir}/src/${buildType}/java" - - sourceDirectories.setFrom files([mainSrc, productFlavorSrc, buildTypeSrc]) - classDirectories.setFrom files([androidKotlinTree, kotlinTree, javacTree]) - executionData.setFrom fileTree(dir: project.buildDir, includes: ["jacoco/test${variant.capitalize()}UnitTest.exec", - "outputs/unit_test_code_coverage/${variant}UnitTest/test${variant.capitalize()}UnitTest.exec",]) - } -} diff --git a/jacoco.gradle.kts b/jacoco.gradle.kts new file mode 100644 index 000000000000..a4e450ef6d41 --- /dev/null +++ b/jacoco.gradle.kts @@ -0,0 +1,116 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +apply() + +configure { + toolVersion = extensions + .getByType() + .find("libs") + .get() + .findVersion("jacoco") + .get() + .displayName +} + +val flavor = "gplay" +val buildType = "Debug" +val variant = "$flavor${buildType.capitalize()}" + +val jacocoTask = tasks.register("jacocoTest${variant.capitalize()}UnitTestReport") { + reports { + csv.required = false + xml.required = true + html.required = true + } + + val fileFilter = listOf( + // data binding + "**/databinding/*", + "android/databinding/**/*.class", + "**/android/databinding/*Binding.class", + "**/android/databinding/*", + "**/androidx/databinding/*", + "**/BR.*", + // android + "**/R.class", + "**/R\$*.class", + "**/BuildConfig.*", + "**/Manifest*.*", + "**/*Test*.*", + "android/**/*.*", + // kotlin + "**/*MapperImpl*.*", + "**/*\$ViewInjector*.*", + "**/*\$ViewBinder*.*", + "**/BuildConfig.*", + "**/*Component*.*", + "**/*BR*.*", + "**/Manifest*.*", + "**/*\$Lambda\$*.*", + "**/*Companion*.*", + "**/*Module*.*", + "**/*Dagger*.*", + "**/*Hilt*.*", + "**/*MembersInjector*.*", + "**/*_MembersInjector.class", + "**/*_Factory*.*", + "**/*_Provide*Factory*.*", + "**/*Extensions*.*", + // sealed and data classes + "**/*\$Result.*", + "**/*\$Result\$*.*", + // adapters generated by moshi + "**/*JsonAdapter.*", + // Hilt + "**/*Module.kt", + "**/di/**", + "dagger.hilt.internal/*", + "hilt_aggregated_deps/*", + + "**/*\$Result.*", /* filtering `sealed` and `data` classes */ + "**/*\$Result\$*.*",/* filtering `sealed` and `data` classes */ + "**/*Args*.*", /* filtering Navigation Component generated classes */ + "**/*Directions*.*", /* filtering Navigation Component generated classes */ + "**/*inlined*.class", /* filtering inlined classes */ + "**/composables/**" + /* INSERT ANY OTHER JUNK YOU WANT FILTERED OUT HERE */ + ) + + val androidKotlinTree = project.fileTree("${project.layout.buildDirectory}/tmp/kotlin-classes/${variant}") { + exclude(fileFilter) + } + val javacTree = project.fileTree("${project.layout.buildDirectory}/intermediates/javac/${variant}") { + exclude(fileFilter) + } + + sourceDirectories.setFrom( + project.layout.projectDirectory.dir("src/main/java"), + project.layout.projectDirectory.dir("src/${flavor}/java"), + project.layout.projectDirectory.dir("src/${buildType}/java") + ) + classDirectories.setFrom( + project.layout.projectDirectory.files( + project.fileTree(project.layout.buildDirectory.dir("intermediates/javac/${variant}")) { exclude(fileFilter) }, + project.fileTree(project.layout.buildDirectory.dir("tmp/kotlin-classes/$variant")) { exclude(fileFilter) } + ) + ) + executionData.setFrom( + project.fileTree(project.layout.buildDirectory.dir("jacoco")) { + include("**/test${variant.capitalize()}UnitTest.exec") + } + ) +} + +tasks.withType() + .matching { task -> task.name == "test${variant.capitalize()}UnitTest" } + .configureEach { + extensions.getByType().isIncludeNoLocationClasses = true + extensions.getByType().setExcludes(listOf("jdk.internal.*")) + + finalizedBy(jacocoTask) + } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index f8c7dc28b281..000000000000 --- a/settings.gradle +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2014-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only - */ -rootProject.name = "Nextcloud" - -include ":app" -include ":appscan" - -//includeBuild("../android-common") { -// dependencySubstitution { -// substitute module("com.github.nextcloud.android-common:ui") using project(":ui") -// } -//} - -//includeBuild("../android-library") { -// dependencySubstitution { -// substitute module('com.github.nextcloud:android-library') using project(':library') // broken on gradle 8.14.2, so use 8.13 if needed -// } -//} diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 000000000000..77a316a910d4 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,54 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +rootProject.name = "Nextcloud" + +pluginManagement { + resolutionStrategy.eachPlugin { + if (requested.id.id == "shot") useModule("com.karumi:shot:${requested.version}") + } + + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + gradlePluginPortal() + mavenCentral() + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + maven("https://jitpack.io") + } +} +//includeBuild("../android-common") { +// dependencySubstitution { +// substitute module("com.github.nextcloud.android-common:ui") using project(":ui") +// } +//} + +//includeBuild("../android-library") { +// dependencySubstitution { +// substitute module('com.github.nextcloud:android-library') using project(':library') // broken on gradle 8.14.2, so use 8.13 if needed +// } +//} + +include(":app", ":appscan") \ No newline at end of file From 07c09e1e994d2487d020b98c8c2b0b487885d6b4 Mon Sep 17 00:00:00 2001 From: Jimly Asshiddiqy Date: Wed, 29 Oct 2025 08:35:45 +0700 Subject: [PATCH 2/4] chore(gradle): Resolve comments Signed-off-by: Jimly Asshiddiqy --- appscan/build.gradle.kts | 4 ++-- gradle/libs.versions.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/appscan/build.gradle.kts b/appscan/build.gradle.kts index 9d14f2127ac8..46dacfefabbf 100644 --- a/appscan/build.gradle.kts +++ b/appscan/build.gradle.kts @@ -31,8 +31,8 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - lint.targetSdk = 35 - testOptions.targetSdk = 35 + lint.targetSdk = 36 + testOptions.targetSdk = 36 } kotlin.compilerOptions { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 795ec7edb894..cdd2670d765b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: AGPL-3.0-or-later [versions] -androidCommonLibraryVersion = "0.28.0" +androidCommonLibraryVersion = "0.29.0" androidGifDrawableVersion = "1.2.29" androidImageCropperVersion = "4.6.0" androidLibraryVersion = "3546bd82fc" From bdc9bb18291434e7814bf0296fa601279b084651 Mon Sep 17 00:00:00 2001 From: Jimly Asshiddiqy Date: Wed, 29 Oct 2025 08:41:27 +0700 Subject: [PATCH 3/4] chore(gradle): update header Signed-off-by: Jimly Asshiddiqy --- app/build.gradle.kts | 2 +- appscan/build.gradle.kts | 2 +- build.gradle.kts | 2 +- jacoco.gradle.kts | 2 +- settings.gradle.kts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 23cf315b382f..f7294858fb1e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-FileCopyrightText: 2025 Jimly Asshiddiqy * SPDX-License-Identifier: AGPL-3.0-or-later */ @file:Suppress("UnstableApiUsage", "DEPRECATION") diff --git a/appscan/build.gradle.kts b/appscan/build.gradle.kts index 46dacfefabbf..5311613b85c7 100644 --- a/appscan/build.gradle.kts +++ b/appscan/build.gradle.kts @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-FileCopyrightText: 2025 Jimly Asshiddiqy * SPDX-License-Identifier: AGPL-3.0-or-later */ import org.jetbrains.kotlin.gradle.dsl.JvmTarget diff --git a/build.gradle.kts b/build.gradle.kts index 96fdf0573608..75f16a6b6ae1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-FileCopyrightText: 2025 Jimly Asshiddiqy * SPDX-License-Identifier: AGPL-3.0-or-later */ plugins { diff --git a/jacoco.gradle.kts b/jacoco.gradle.kts index a4e450ef6d41..fca6ee1c4eaa 100644 --- a/jacoco.gradle.kts +++ b/jacoco.gradle.kts @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-FileCopyrightText: 2025 Jimly Asshiddiqy * SPDX-License-Identifier: AGPL-3.0-or-later */ diff --git a/settings.gradle.kts b/settings.gradle.kts index 77a316a910d4..c0ebf04488ac 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-FileCopyrightText: 2025 Jimly Asshiddiqy * SPDX-License-Identifier: AGPL-3.0-or-later */ rootProject.name = "Nextcloud" From 96debdd3a5c5c77f29494345f0bcf98e2ecf8a07 Mon Sep 17 00:00:00 2001 From: Jimly Asshiddiqy Date: Thu, 30 Oct 2025 13:35:10 +0700 Subject: [PATCH 4/4] chore(gradle): update libraries Signed-off-by: Jimly Asshiddiqy --- gradle/libs.versions.toml | 10 +- gradle/verification-metadata.xml | 248 +++++++++++++++---------------- 2 files changed, 122 insertions(+), 136 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cdd2670d765b..dc1a6e3909d8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,7 +34,7 @@ espressoVersion = "3.7.0" eventbusVersion = "3.3.1" exifinterfaceVersion = "1.4.1" ezVcardVersion = "0.12.1" -fbContribVersion = "7.6.14" +fbContribVersion = "7.6.15" findsecbugsPluginVersion = "1.14.0" firebaseMessagingVersion = "25.0.1" flexboxVersion = "3.0.0" @@ -48,9 +48,9 @@ jsonVersion = "20250517" junit = "4.13.2" junitVersion = "1.3.0" juniversalchardetVersion = "2.5.0" -kotlin = "2.2.20" +kotlin = "2.2.21" kotlinxSerializationJson = "1.9.0" -ksp = "2.2.20-2.0.3" +ksp = "2.3.0" leakcanary = "2.14" legacySupportV4Version = "1.0.0" libraryVersion = "1.3.0" @@ -62,7 +62,7 @@ materialVersion = "1.13.0" media3 = "1.8.0" mockitoKotlinVersion = "4.1.0" mockitoVersion = "4.11.0" -mockkVersion = "1.14.5" +mockkVersion = "1.14.6" nnioVersion = "0.3.1" objenesis = "3.4" orchestratorVersion = "1.6.1" @@ -79,7 +79,7 @@ sectionedRecyclerviewVersion = "0.6.1" shotVersion = "6.1.0" slfj = "1.7.36" splash-screen = "1.0.1" -spotbugsGradlePlugin = "6.4.2" +spotbugsGradlePlugin = "6.4.4" spotless = "7.2.1" stateless4jVersion = "2.6.0" webkitVersion = "1.14.0" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 662173539512..7f7c63031fe9 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -1,10 +1,10 @@ - - true @@ -10040,12 +10040,11 @@ - - - - - + + + + + @@ -10171,10 +10170,9 @@ - - - + + + @@ -10542,16 +10540,14 @@ - - - - - - - - + + + + + + + + @@ -11465,10 +11461,9 @@ - - - + + + @@ -13176,14 +13171,12 @@ - - - - - - + + + + + + @@ -13234,14 +13227,12 @@ - - - - - - + + + + + + @@ -13292,16 +13283,14 @@ - - - - - - - - + + + + + + + + @@ -13470,16 +13459,14 @@ - - - - - - - - + + + + + + + + @@ -13528,15 +13515,13 @@ - - - - - - - + + + + + + + @@ -13586,15 +13571,13 @@ - - - - - - - + + + + + + + @@ -13644,16 +13627,14 @@ - - - - - - - - + + + + + + + + @@ -18702,12 +18683,16 @@ - - - - - + + + + + + + + + + @@ -21142,15 +21127,13 @@ - - - - - - - + + + + + + + @@ -21438,10 +21421,9 @@ - - - + + + @@ -23324,10 +23306,9 @@ - - - + + + @@ -24081,12 +24062,11 @@ - - - - - + + + + + @@ -31297,13 +31277,16 @@ - - - - - + + + + + + + + + + @@ -31461,11 +31444,14 @@ - - - + + + + + + + +