Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ captures
**/xcshareddata/WorkspaceSettings.xcsettings

iosApp/iosApp/GoogleService-Info.plist
androidApp/google-services.json
output
12 changes: 12 additions & 0 deletions androidApp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
plugins {
alias(libs.plugins.convention.android.application.compose)
alias(libs.plugins.google.services)
}

dependencies {
implementation(projects.composeApp)
implementation(libs.koin.android)
implementation(libs.core.splashscreen)
implementation(libs.core.ktx)
implementation(libs.androidx.activity.compose)
}
1 change: 1 addition & 0 deletions build-logic/convention/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ dependencies {
compileOnly(libs.android.tools.common)
compileOnly(libs.kotlin.gradlePlugin)
compileOnly(libs.compose.gradlePlugin)
compileOnly(libs.jetbrains.compose.gradlePlugin)
compileOnly(libs.ksp.gradlePlugin)
compileOnly(libs.androidx.room.gradle.plugin)
implementation(libs.buildkonfig.gradlePlugin)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class AndroidApplicationConventionPlugin: Plugin<Project> {
with(target) {
with(pluginManager) {
apply("com.android.application")
// AGP 9.0+ has built-in Kotlin support, no need for kotlin.android plugin
}

extensions.configure<ApplicationExtension> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import com.plcoding.chirp.convention.applyHierarchyTemplate
import com.plcoding.chirp.convention.configureAndroidTarget
import com.plcoding.chirp.convention.configureAndroidLibraryTarget
import com.plcoding.chirp.convention.configureDesktopTarget
import com.plcoding.chirp.convention.configureIosTargets
import com.plcoding.chirp.convention.libs
Expand All @@ -9,19 +9,27 @@ import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension

/**
* Convention plugin for Compose Multiplatform "application" modules.
*
* AGP 9.0 Compatibility:
* This plugin now uses com.android.kotlin.multiplatform.library instead of com.android.application.
* The actual Android application entry point (MainActivity, Application class) should be in a
* separate :androidApp module that depends on the module using this plugin.
*/
class CmpApplicationConventionPlugin: Plugin<Project> {

override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.plcoding.convention.android.application.compose")
apply("com.android.kotlin.multiplatform.library")
apply("org.jetbrains.kotlin.multiplatform")
apply("org.jetbrains.compose")
apply("org.jetbrains.kotlin.plugin.compose")
apply("org.jetbrains.kotlin.plugin.serialization")
}

configureAndroidTarget()
configureAndroidLibraryTarget()
configureIosTargets()
configureDesktopTarget()

Expand All @@ -30,7 +38,18 @@ class CmpApplicationConventionPlugin: Plugin<Project> {
}

dependencies {
"debugImplementation"(libs.findLibrary("androidx-compose-ui-tooling").get())
// Core Compose dependencies
"commonMainImplementation"(libs.findLibrary("jetbrains-compose-runtime").get())
"commonMainImplementation"(libs.findLibrary("jetbrains-compose-foundation").get())
"commonMainImplementation"(libs.findLibrary("jetbrains-compose-material3").get())
"commonMainImplementation"(libs.findLibrary("jetbrains-compose-ui").get())

// CMP 1.10.0+: Resources and preview tooling are now separate modules
"commonMainImplementation"(libs.findLibrary("jetbrains-compose-resources").get())
"commonMainImplementation"(libs.findLibrary("jetbrains-compose-ui-tooling-preview").get())

// Single-variant model: use androidMainImplementation instead of debugImplementation
"androidMainImplementation"(libs.findLibrary("jetbrains-compose-ui-tooling").get())
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@ class CmpLibraryConventionPlugin: Plugin<Project> {
}

dependencies {
// Core Compose dependencies
"commonMainImplementation"(libs.findLibrary("jetbrains-compose-ui").get())
"commonMainImplementation"(libs.findLibrary("jetbrains-compose-foundation").get())
"commonMainImplementation"(libs.findLibrary("jetbrains-compose-material3").get())
"commonMainImplementation"(libs.findLibrary("jetbrains-compose-material-icons-core").get())

"debugImplementation"(libs.findLibrary("androidx-compose-ui-tooling").get())
// CMP 1.10.0+: Resources and preview tooling are now separate modules
"commonMainImplementation"(libs.findLibrary("jetbrains-compose-resources").get())
"commonMainImplementation"(libs.findLibrary("jetbrains-compose-ui-tooling-preview").get())

// Single-variant model: use androidMainImplementation instead of debugImplementation
"androidMainImplementation"(libs.findLibrary("jetbrains-compose-ui-tooling").get())
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,21 @@
import com.android.build.api.dsl.LibraryExtension
import com.plcoding.chirp.convention.configureKotlinAndroid
import com.plcoding.chirp.convention.configureKotlinMultiplatform
import com.plcoding.chirp.convention.libs
import com.plcoding.chirp.convention.pathToResourcePrefix
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies

class KmpLibraryConventionPlugin: Plugin<Project> {

override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.android.library")
apply("com.android.kotlin.multiplatform.library")
apply("org.jetbrains.kotlin.multiplatform")
apply("org.jetbrains.kotlin.plugin.serialization")
}

configureKotlinMultiplatform()

extensions.configure<LibraryExtension> {
configureKotlinAndroid(this)

resourcePrefix = this@with.pathToResourcePrefix()

// Required to make debug build of app run in iOS simulator
experimentalProperties["android.experimental.kmp.enableAndroidResources"] = "true"
}

dependencies {
"commonMainImplementation"(libs.findLibrary("kotlinx-serialization-json").get())
"commonTestImplementation"(libs.findLibrary("kotlin-test").get())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.plcoding.chirp.convention

import com.android.build.api.dsl.CommonExtension
import com.android.build.api.dsl.ApplicationExtension
import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies

internal fun Project.configureAndroidCompose(
commonExtension: CommonExtension<*, *, *, *, *, *>
extension: ApplicationExtension
) {
with(commonExtension) {
with(extension) {
buildFeatures {
compose = true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,28 @@ private val hierarchyTemplate = KotlinHierarchyTemplate {
common {
withCompilations { true }

/*
AGP 9.0 Prep:
jvmCommon was removed because Android belonged to both mobile and jvmCommon,
creating overlapping source-set paths. With the new AGP KMP plugin, this ambiguity
forces actuals to exist in all intermediate source sets, leading to compilation
errors. Removing jvmCommon globally avoids this conflict, while modules that truly
need Android + Desktop JVM sharing can opt in explicitly using dependsOn().
*/
group("mobile") {
withAndroidTarget()
group("ios") {
withIos()
}
}

group("jvmCommon") {
withAndroidTarget()
withJvm()
}

/*
Android no longer automatically depends on intermediate source sets such as
mobileMain or jvmCommonMain, and jvmCommonMain has been removed from the global
hierarchy. As of Gradle 9.0, any module that needs to share code between Android
and Desktop JVM must explicitly configure this dependency, for example by
making androidMain depend on jvmCommonMain.
*/
group("native") {
withNative()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.plcoding.chirp.convention

import com.android.build.api.dsl.CommonExtension
import com.android.build.api.dsl.ApplicationExtension
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies
Expand All @@ -9,9 +9,9 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

internal fun Project.configureKotlinAndroid(
commonExtension: CommonExtension<*, *, *, *, *, *>
extension: ApplicationExtension
) {
with(commonExtension) {
with(extension) {
compileSdk = libs.findVersion("projectCompileSdkVersion").get().toString().toInt()

defaultConfig.minSdk = libs.findVersion("projectMinSdkVersion").get().toString().toInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ package com.plcoding.chirp.convention

import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension

/**
* Configures Android target for APPLICATION modules using com.android.application plugin.
* Uses the traditional androidTarget {} DSL.
*/
internal fun Project.configureAndroidTarget() {
extensions.configure<KotlinMultiplatformExtension> {
androidTarget {
Expand All @@ -15,4 +20,15 @@ internal fun Project.configureAndroidTarget() {
}
}
}
}

/**
* Configures Android target for LIBRARY modules using com.android.kotlin.multiplatform.library plugin.
* Android library settings (namespace, compileSdk, minSdk) must be configured in each module's
* build.gradle.kts using kotlin { androidLibrary { ... } }
*/
internal fun Project.configureAndroidLibraryTarget() {
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The configureAndroidLibraryTarget function only adds desugaring dependencies but doesn't configure compiler options like jvmTarget.set(JvmTarget.JVM_17) that are set in configureAndroidTarget for applications. Verify that the new 'com.android.kotlin.multiplatform.library' plugin automatically inherits these compiler settings, or add explicit configuration to ensure library modules compile with JVM 17 target.

Suggested change
internal fun Project.configureAndroidLibraryTarget() {
internal fun Project.configureAndroidLibraryTarget() {
extensions.configure<KotlinMultiplatformExtension> {
androidTarget {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}
}

Copilot uses AI. Check for mistakes.
dependencies {
"coreLibraryDesugaring"(libs.findLibrary("android-desugarJdkLibs").get())
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package com.plcoding.chirp.convention

import com.android.build.api.dsl.LibraryExtension
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension

/**
* Configures Kotlin Multiplatform for library modules.
* Note: Android library settings (namespace, compileSdk, minSdk, androidResources) must be
* configured in each module's build.gradle.kts using kotlin { androidLibrary { ... } }
*/
internal fun Project.configureKotlinMultiplatform() {
extensions.configure<LibraryExtension> {
namespace = this@configureKotlinMultiplatform.pathToPackageName()
}

configureAndroidTarget()
configureAndroidLibraryTarget()
configureDesktopTarget()

extensions.configure<KotlinMultiplatformExtension> {
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ plugins {
// in each subproject's classloader
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.compose.hot.reload) apply false
alias(libs.plugins.compose.multiplatform) apply false
alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.kotlin.multiplatform) apply false
Expand All @@ -13,4 +12,5 @@ plugins {
alias(libs.plugins.room) apply false
alias(libs.plugins.google.services) apply false
alias(libs.plugins.conveyor) apply false
alias(libs.plugins.kotlin.android) apply false
}
28 changes: 14 additions & 14 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
plugins {
alias(libs.plugins.convention.cmp.application)
alias(libs.plugins.compose.hot.reload)
alias(libs.plugins.google.services)
alias(libs.plugins.conveyor)
}

version = "1.0.0"

kotlin {
androidLibrary {
namespace = "com.plcoding.chirp.shared"
compileSdk = 36
minSdk = 26

androidResources {
enable = true
}
}

sourceSets {
androidMain.dependencies {
implementation(compose.preview)
implementation(libs.androidx.activity.compose)

implementation(libs.core.splashscreen)

implementation(libs.koin.android)
}
commonMain.dependencies {
implementation(projects.core.data)
Expand All @@ -33,13 +36,6 @@ kotlin {

implementation(libs.jetbrains.compose.navigation)
implementation(libs.bundles.koin.common)

implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material3)
implementation(compose.ui)
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
implementation(libs.jetbrains.compose.viewmodel)
implementation(libs.jetbrains.lifecycle.compose)
}
Expand All @@ -64,6 +60,10 @@ kotlin {
}
}

compose.resources {
packageOfResClass = "com.plcoding.chirp"
}

compose.desktop {
application {
mainClass = "com.plcoding.chirp.MainKt"
Expand Down
2 changes: 1 addition & 1 deletion composeApp/src/commonMain/kotlin/com/plcoding/chirp/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import com.plcoding.chirp.navigation.DeepLinkListener
import com.plcoding.chirp.navigation.NavigationRoot
import com.plcoding.core.designsystem.theme.ChirpTheme
import com.plcoding.core.presentation.util.ObserveAsEvents
import org.jetbrains.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.Preview
import org.koin.compose.viewmodel.koinViewModel

@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import androidx.compose.ui.window.Tray
import androidx.compose.ui.window.TrayState
import com.plcoding.core.domain.preferences.ThemePreference
import org.jetbrains.compose.resources.painterResource
import chirp.composeapp.generated.resources.Res
import chirp.composeapp.generated.resources.app_theme
import chirp.composeapp.generated.resources.logo
import com.plcoding.chirp.Res
import com.plcoding.chirp.app_theme
import com.plcoding.chirp.logo
import org.jetbrains.compose.resources.stringResource

@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.MenuBar
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.rememberWindowState
import chirp.composeapp.generated.resources.Res
import chirp.composeapp.generated.resources.file
import chirp.composeapp.generated.resources.logo
import chirp.composeapp.generated.resources.new_window
import chirp.core.designsystem.generated.resources.logo_chirp
import com.plcoding.chirp.Res
import com.plcoding.chirp.file
import com.plcoding.chirp.logo
import com.plcoding.chirp.new_window
import com.plcoding.core.designsystem.logo_chirp
import com.plcoding.chirp.App
import com.plcoding.chirp.theme.AppTheme
import org.jetbrains.compose.resources.painterResource
Expand Down
Loading