diff --git a/.editorconfig b/.editorconfig index b1b9eb9a..8ec45b59 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,3 +7,12 @@ end_of_line = lf indent_style = space insert_final_newline = true trim_trailing_whitespace = true +indent_size = 4 +max_line_length = 120 + +[*.{kt,kts}] +ij_kotlin_allow_trailing_comma = true +ij_kotlin_allow_trailing_comma_on_call_site = true + + + diff --git a/.gitignore b/.gitignore index 29e85bd2..47aa1275 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ local.properties .idea/ .DS_Store build/ +**/build/ *.iml /*/.externalNativeBuild/ /*/.cxx diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5d915bee..72503b8d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,6 +4,8 @@ plugins { alias(libs.plugins.ksp) alias(libs.plugins.hilt.android) alias(libs.plugins.compose.compiler) + alias(libs.plugins.ktlint) + alias(libs.plugins.detekt) } android { @@ -40,12 +42,12 @@ android { "tr", "vi", "zh-rCN", - "zh-rTW" + "zh-rTW", ) buildConfigField( "String[]", "LOCALE_ARRAY", - "new String[]{\"" + locales.joinToString("\",\"") + "\"}" + "new String[]{\"" + locales.joinToString("\",\"") + "\"}", ) resourceConfigurations.addAll(locales) @@ -85,8 +87,11 @@ android { compileOptions { encoding = "UTF-8" - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 + sourceCompatibility = JavaVersion.VERSION_20 + targetCompatibility = JavaVersion.VERSION_20 + } + kotlin { + jvmToolchain(20) } bundle { @@ -101,6 +106,76 @@ android { } ndkVersion = "28.0.13004108" + + lint { + abortOnError = false + checkReleaseBuilds = false + warningsAsErrors = false + checkDependencies = true +// baseline = file("lint-baseline.xml") no base line till satisfactory state is reached + + // Reports +// textReport = true + htmlReport = true + + htmlOutput = file("${layout.buildDirectory}/reports/code-quality/lint.html") + textOutput = file("${layout.buildDirectory}/reports/code-quality/lint.txt") + + // Compose-specific checks + enable += setOf( + "ComposableNaming", + "ComposableFunctionName", + "ModifierOrder", + "UnrememberedMutableState", + ) + } + ktlint { + android.set(true) + ignoreFailures.set(true) + coloredOutput.set(true) + this.outputToConsole.set(true) + + + reporters { +// reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.PLAIN) + reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.HTML) + } + + outputToConsole.set(true) + outputColorName.set("RED") + } +} +detekt { + toolVersion = libs.versions.detekt.get() + buildUponDefaultConfig = true + allRules = false +// ignoredFlavors = listOf("screenshot") +// ignoredBuildTypes = listOf("release") + + config.setFrom(files("$rootDir/config/detekt/detekt.yml")) + baseline = file("$rootDir/config/detekt/detekt-baseline.xml") + basePath = projectDir.path + ignoreFailures = true +} + +// Add this to handle the deprecated reports block issue +tasks.withType().configureEach { + reports { + // Enable HTML report + html.required.set(true) + html.outputLocation.set(file("${layout.buildDirectory.get()}/reports/detekt/detekt.html")) + +// // Enable XML report (often used for CI) + xml.required.set(false) +// xml.outputLocation.set(file("${layout.buildDirectory.get()}/reports/detekt/detekt.xml")) +// +// // Enable SARIF report (best for GitHub Actions) + sarif.required.set(false) +// sarif.outputLocation.set(file("${layout.buildDirectory.get()}/reports/detekt/detekt.sarif")) + + // Disable TXT report if not needed + txt.required.set(false) + } } dependencies { @@ -134,6 +209,7 @@ dependencies { implementation(libs.bundles.compose) debugImplementation(libs.androidx.compose.ui.tooling) debugImplementation(libs.androidx.compose.ui.test.manifest) + detektPlugins(libs.detekt.compose) add("androidTestScreenshotImplementation", libs.junit) add("androidTestScreenshotImplementation", libs.fastlane.screengrab) @@ -147,3 +223,14 @@ dependencies { testImplementation(libs.junit) testImplementation(libs.google.truth) } + +tasks.register("lintCheck") { + group = "verification" + description = "Run ktlint, detekt, and Android Lint sequentially" + + dependsOn( + ":app:detekt", + ":app:ktlintCheck" +// ":app:lintRegularDebug", + ) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/theme/Theme.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/theme/Theme.kt index 30ec94c0..e102b1f3 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/ui/theme/Theme.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/theme/Theme.kt @@ -54,6 +54,7 @@ data class PhyphoxColors( val LocalPhyphoxColors = staticCompositionLocalOf { PhyphoxColors() } +@Suppress("Unused", "UnusedReceiverParameter") val MaterialTheme.customColors: PhyphoxColors @Composable @ReadOnlyComposable diff --git a/build.gradle.kts b/build.gradle.kts index 58f44467..71b5a97f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,4 +4,6 @@ plugins { alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.hilt.android) apply false alias(libs.plugins.ksp) apply false + alias(libs.plugins.ktlint) apply false + alias(libs.plugins.detekt) apply false } diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml new file mode 100644 index 00000000..36113d3e --- /dev/null +++ b/config/detekt/detekt.yml @@ -0,0 +1,70 @@ +# Detekt Default Rules Configuration +# You can find the full default config at: https://github.com/detekt/detekt/blob/main/detekt-core/src/main/resources/default-detekt-config.yml + +build: + maxIssues: 0 + excludeCorrectable: false + weights: + # complexity: 2 + # LongParameterList: 1 + +# Jetpack Compose Custom Rules +# If you are using 'io.nlopez.compose.rules', the ruleset name is 'Compose' +Compose: + active: true + ComposableAnnotationNaming: + active: true + ComposableNaming: + active: true + ComposableParamOrder: + active: true + ContentEmitterReturningValues: + active: true + ModifierMissing: + active: true + ModifierReused: + active: true + ModifierWithoutDefault: + active: true + MultipleEmitters: + active: true + MutableParams: + active: true + PreviewNaming: + active: true + PreviewPublic: + active: true + RememberMissing: + active: true + UnstableCollections: + active: true + ViewModelForwarding: + active: true + ViewModelInjection: + active: true + + +# Adjusting standard Detekt rules for Compose compatibility +naming: + FunctionNaming: + # Allow Composable functions to start with Uppercase + ignoreAnnotated: ['Composable'] + TopLevelPropertyNaming: + # Compose often uses PascalCase for top-level constants/previews + constantPattern: '[A-Z][A-Za-z0-9]*|[A-Z][_A-Z0-9]*' + +complexity: + LongParameterList: + # Composables often have many parameters with defaults + ignoreAnnotated: ['Composable'] + functionThreshold: 12 + #MagicNumber: + # Ignore magic numbers in colors or preview sizes + #ignoreAnnotated: ['Composable', 'Preview'] + #ignorePropertyDeclaration: true + +style: + UnusedPrivateMember: + # Prevents flagging @Preview functions as unused + ignoreAnnotated: ['Preview'] + diff --git a/gradle.properties b/gradle.properties index 6a8a32c3..153a524d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,3 +19,4 @@ org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M" # need to suppress as required by the update to gradle 8.6.0 android.ndk.suppressMinSdkVersionError=21 android.suppressUnsupportedCompileSdk=35 +ksp.useKSP2=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3d90af06..2bb743d8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ targetSdk = "35" # Plugin Versions androidGradlePlugin = "8.13.2" kotlin = "2.2.21" -ksp = "2.2.10-2.0.2" +ksp = "2.2.21-2.0.4" # Dependency Versions multidex = "2.0.1" @@ -37,6 +37,10 @@ hilt-android = "2.57.2" composeBom = "2025.02.00" activityCompose = "1.10.0" lifecycleRuntimeCompose = "2.8.7" +agp = "31.13.2" +ktlint = "14.0.1" +detekt = "1.23.8" +detektCompose = "0.5.3" [libraries] androidx-multidex = { group = "androidx.multidex", name = "multidex", version.ref = "multidex" } @@ -86,12 +90,19 @@ androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-toolin androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } +#lint +androidx-lint = { module = "com.android.tools.lint:lint", version.ref = "agp" } +detekt-compose = { group = "io.nlopez.compose.rules", name = "detekt", version.ref = "detektCompose" } + [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt-android" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } + [bundles] camerax = ["androidx-camera-core", "androidx-camera-camera2", "androidx-camera-lifecycle", "androidx-camera-view"] diff --git a/settings.gradle.kts b/settings.gradle.kts index 92d8dcb8..20129036 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,7 @@ pluginManagement { } plugins { - + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)