Set up project skeleton (milestone 1)
- Architecture and feature overview in docs/ARCHITECTURE.md - Gradle 9.8 / AGP 9.4 / Kotlin 2.4 with version catalog and convention plugins - Modules :app, :core:*, :device-profiles, :feature:* (placeholder screens) - Flavors core (no INTERNET, enforced by a build check) and full - Material You theme, navigation, expert mode and dry-run settings (DataStore) - Pixel 8-10 device detection, unit tests, GPLv3 license, English README with Obtainium badge Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
group = "org.pixelwall.buildlogic"
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.android.gradle.plugin)
|
||||
compileOnly(libs.kotlin.gradle.plugin)
|
||||
compileOnly(libs.compose.gradle.plugin)
|
||||
compileOnly(libs.ksp.gradle.plugin)
|
||||
compileOnly(libs.hilt.gradle.plugin)
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
register("androidApplication") {
|
||||
id = "pixelwall.android.application"
|
||||
implementationClass = "AndroidApplicationConventionPlugin"
|
||||
}
|
||||
register("androidLibrary") {
|
||||
id = "pixelwall.android.library"
|
||||
implementationClass = "AndroidLibraryConventionPlugin"
|
||||
}
|
||||
register("androidCompose") {
|
||||
id = "pixelwall.android.compose"
|
||||
implementationClass = "AndroidComposeConventionPlugin"
|
||||
}
|
||||
register("androidHilt") {
|
||||
id = "pixelwall.android.hilt"
|
||||
implementationClass = "AndroidHiltConventionPlugin"
|
||||
}
|
||||
register("androidFeature") {
|
||||
id = "pixelwall.android.feature"
|
||||
implementationClass = "AndroidFeatureConventionPlugin"
|
||||
}
|
||||
register("jvmLibrary") {
|
||||
id = "pixelwall.jvm.library"
|
||||
implementationClass = "JvmLibraryConventionPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import com.android.build.api.artifact.SingleArtifact
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.register
|
||||
|
||||
class AndroidApplicationConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) = with(target) {
|
||||
pluginManager.apply("com.android.application")
|
||||
extensions.configure<ApplicationExtension> {
|
||||
compileSdk = libs.int("compileSdk")
|
||||
defaultConfig {
|
||||
minSdk = libs.int("minSdk")
|
||||
targetSdk = libs.int("targetSdk")
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JAVA_VERSION
|
||||
targetCompatibility = JAVA_VERSION
|
||||
}
|
||||
lint {
|
||||
warningsAsErrors = false
|
||||
abortOnError = true
|
||||
checkDependencies = true
|
||||
}
|
||||
}
|
||||
configureKotlin()
|
||||
verifyCoreFlavorHasNoNetwork()
|
||||
}
|
||||
|
||||
/** Varianten des Flavors "core" dürfen nie Netzwerkzugriff anfordern (siehe docs/ARCHITECTURE.md §9). */
|
||||
private fun Project.verifyCoreFlavorHasNoNetwork() {
|
||||
val verifyAll = tasks.register("verifyNoNetworkPermission")
|
||||
tasks.named("check").configure { dependsOn(verifyAll) }
|
||||
extensions.configure<ApplicationAndroidComponentsExtension> {
|
||||
onVariants { variant ->
|
||||
if (variant.productFlavors.none { it.second == "core" }) return@onVariants
|
||||
val name = variant.name.replaceFirstChar(Char::uppercase)
|
||||
val task = tasks.register<VerifyNoInternetPermissionTask>("verify${name}NoNetworkPermission") {
|
||||
mergedManifest.set(variant.artifacts.get(SingleArtifact.MERGED_MANIFEST))
|
||||
report.set(layout.buildDirectory.file("reports/no-network/${variant.name}.txt"))
|
||||
}
|
||||
verifyAll.configure { dependsOn(task) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import com.android.build.api.dsl.CommonExtension
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
class AndroidComposeConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) = with(target) {
|
||||
pluginManager.apply("org.jetbrains.kotlin.plugin.compose")
|
||||
extensions.getByType(CommonExtension::class.java).buildFeatures.compose = true
|
||||
dependencies {
|
||||
val bom = platform(libs.lib("compose-bom"))
|
||||
add("implementation", bom)
|
||||
add("androidTestImplementation", bom)
|
||||
add("implementation", libs.lib("compose-ui"))
|
||||
add("implementation", libs.lib("compose-material3"))
|
||||
add("implementation", libs.lib("compose-material-icons"))
|
||||
add("implementation", libs.lib("compose-ui-tooling-preview"))
|
||||
add("debugImplementation", libs.lib("compose-ui-tooling"))
|
||||
add("debugImplementation", libs.lib("compose-ui-test-manifest"))
|
||||
add("androidTestImplementation", libs.lib("compose-ui-test-junit4"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
class AndroidFeatureConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) = with(target) {
|
||||
pluginManager.apply("pixelwall.android.library")
|
||||
pluginManager.apply("pixelwall.android.compose")
|
||||
pluginManager.apply("pixelwall.android.hilt")
|
||||
dependencies {
|
||||
add("implementation", project(":core:model"))
|
||||
add("implementation", project(":core:domain"))
|
||||
add("implementation", project(":core:ui"))
|
||||
add("implementation", libs.lib("androidx-hilt-navigation-compose"))
|
||||
add("implementation", libs.lib("androidx-navigation-compose"))
|
||||
add("implementation", libs.lib("androidx-lifecycle-runtime-compose"))
|
||||
add("implementation", libs.lib("androidx-lifecycle-viewmodel-compose"))
|
||||
add("implementation", libs.lib("kotlinx-coroutines-android"))
|
||||
add("testImplementation", project(":core:testing"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
class AndroidHiltConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) = with(target) {
|
||||
pluginManager.apply("com.google.devtools.ksp")
|
||||
pluginManager.apply("com.google.dagger.hilt.android")
|
||||
dependencies {
|
||||
add("implementation", libs.lib("hilt-android"))
|
||||
add("ksp", libs.lib("hilt-compiler"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import com.android.build.api.dsl.LibraryExtension
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
class AndroidLibraryConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) = with(target) {
|
||||
pluginManager.apply("com.android.library")
|
||||
extensions.configure<LibraryExtension> {
|
||||
compileSdk = libs.int("compileSdk")
|
||||
defaultConfig {
|
||||
minSdk = libs.int("minSdk")
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JAVA_VERSION
|
||||
targetCompatibility = JAVA_VERSION
|
||||
}
|
||||
testOptions.unitTests.isReturnDefaultValues = true
|
||||
}
|
||||
configureKotlin()
|
||||
dependencies {
|
||||
add("testImplementation", libs.lib("junit"))
|
||||
add("testImplementation", libs.lib("truth"))
|
||||
add("testImplementation", libs.lib("kotlinx-coroutines-test"))
|
||||
add("testImplementation", libs.lib("turbine"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
class JvmLibraryConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) = with(target) {
|
||||
pluginManager.apply("org.jetbrains.kotlin.jvm")
|
||||
pluginManager.apply("com.android.lint")
|
||||
extensions.configure<JavaPluginExtension> {
|
||||
sourceCompatibility = JAVA_VERSION
|
||||
targetCompatibility = JAVA_VERSION
|
||||
}
|
||||
configureKotlin()
|
||||
dependencies {
|
||||
add("testImplementation", libs.lib("junit"))
|
||||
add("testImplementation", libs.lib("truth"))
|
||||
add("testImplementation", libs.lib("kotlinx-coroutines-test"))
|
||||
add("testImplementation", libs.lib("turbine"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.VersionCatalog
|
||||
import org.gradle.api.artifacts.VersionCatalogsExtension
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
internal val Project.libs: VersionCatalog
|
||||
get() = extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
|
||||
internal fun VersionCatalog.lib(alias: String) = findLibrary(alias).get()
|
||||
|
||||
internal fun VersionCatalog.int(alias: String) = findVersion(alias).get().requiredVersion.toInt()
|
||||
|
||||
internal val JAVA_VERSION = JavaVersion.VERSION_17
|
||||
|
||||
internal fun Project.configureKotlin() {
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_17)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
/** Bricht den Build ab, wenn das zusammengeführte Manifest Netzwerkberechtigungen enthält. */
|
||||
abstract class VerifyNoInternetPermissionTask : DefaultTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val mergedManifest: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val report: RegularFileProperty
|
||||
|
||||
@TaskAction
|
||||
fun verify() {
|
||||
val manifest = mergedManifest.get().asFile.readText()
|
||||
val found = FORBIDDEN.filter { manifest.contains("\"$it\"") }
|
||||
if (found.isNotEmpty()) {
|
||||
throw GradleException("Merged manifest must not request: ${found.joinToString()}")
|
||||
}
|
||||
report.get().asFile.writeText("OK: no network permissions\n")
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val FORBIDDEN = listOf("android.permission.INTERNET", "android.permission.ACCESS_NETWORK_STATE")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "build-logic"
|
||||
include(":convention")
|
||||
Reference in New Issue
Block a user