diff --git a/build.gradle.kts b/build.gradle.kts index db96744..27ed778 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ plugins { -// id("root.publication") + id("root.publication") // trick: for the same plugin versions in all sub-modules alias(libs.plugins.androidApplication).apply(false) alias(libs.plugins.androidLibrary).apply(false) @@ -11,5 +11,3 @@ plugins { alias(libs.plugins.composeMultiplatform).apply(false) alias(libs.plugins.conventionPlugin).apply(false) } - -rootPublicationSetup() diff --git a/calf-core/build.gradle.kts b/calf-core/build.gradle.kts index f9c72b1..b77f962 100644 --- a/calf-core/build.gradle.kts +++ b/calf-core/build.gradle.kts @@ -1,13 +1,8 @@ plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.composeCompiler) - alias(libs.plugins.composeMultiplatform) - alias(libs.plugins.androidLibrary) + id("compose.multiplatform") + id("module.publication") } -composeMultiplatformSetup() -modulePublicationSetup() - kotlin { sourceSets.commonMain.dependencies { implementation(compose.runtime) diff --git a/calf-file-picker-coil/build.gradle.kts b/calf-file-picker-coil/build.gradle.kts index 3866445..21a436c 100644 --- a/calf-file-picker-coil/build.gradle.kts +++ b/calf-file-picker-coil/build.gradle.kts @@ -1,11 +1,8 @@ plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.androidLibrary) + id("kotlin.multiplatform") + id("module.publication") } -kotlinMultiplatformSetup() -modulePublicationSetup() - kotlin { sourceSets.commonMain.dependencies { api(projects.calfCore) diff --git a/calf-file-picker/build.gradle.kts b/calf-file-picker/build.gradle.kts index cdceaca..db3cc41 100644 --- a/calf-file-picker/build.gradle.kts +++ b/calf-file-picker/build.gradle.kts @@ -1,13 +1,8 @@ plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.composeCompiler) - alias(libs.plugins.composeMultiplatform) - alias(libs.plugins.androidLibrary) + id("compose.multiplatform") + id("module.publication") } -composeMultiplatformSetup() -modulePublicationSetup() - kotlin { sourceSets.commonMain.dependencies { api(projects.calfIo) diff --git a/calf-file-picker/src/iosMain/kotlin/com.mohamedrejeb.calf.picker/FilePickerLauncher.ios.kt b/calf-file-picker/src/iosMain/kotlin/com.mohamedrejeb.calf.picker/FilePickerLauncher.ios.kt index 6ffb6e9..cb7acd9 100644 --- a/calf-file-picker/src/iosMain/kotlin/com.mohamedrejeb.calf.picker/FilePickerLauncher.ios.kt +++ b/calf-file-picker/src/iosMain/kotlin/com.mohamedrejeb.calf.picker/FilePickerLauncher.ios.kt @@ -6,6 +6,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.interop.LocalUIViewController import com.mohamedrejeb.calf.core.InternalCalfApi import com.mohamedrejeb.calf.io.KmpFile +import kotlinx.cinterop.BetaInteropApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -37,6 +38,7 @@ import platform.UniformTypeIdentifiers.UTTypeVideo import platform.darwin.NSObject import kotlin.coroutines.resume +@OptIn(BetaInteropApi::class) @Composable actual fun rememberFilePickerLauncher( type: FilePickerFileType, @@ -49,7 +51,7 @@ actual fun rememberFilePickerLauncher( rememberDocumentPickerLauncher(type, selectionMode, onResult) } -@OptIn(InternalCalfApi::class) +@OptIn(InternalCalfApi::class, BetaInteropApi::class) @Composable private fun rememberDocumentPickerLauncher( type: FilePickerFileType, @@ -137,6 +139,7 @@ private fun rememberDocumentPickerLauncher( } } +@OptIn(BetaInteropApi::class) @Composable private fun rememberImageVideoPickerLauncher( type: FilePickerFileType, diff --git a/calf-geo/build.gradle.kts b/calf-geo/build.gradle.kts index cdbfb96..f43fb27 100644 --- a/calf-geo/build.gradle.kts +++ b/calf-geo/build.gradle.kts @@ -1,12 +1,7 @@ plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.composeCompiler) - alias(libs.plugins.composeMultiplatform) - alias(libs.plugins.androidLibrary) + id("compose.multiplatform") } -composeMultiplatformSetup() - kotlin { sourceSets.commonMain.dependencies { implementation(compose.runtime) diff --git a/calf-io/build.gradle.kts b/calf-io/build.gradle.kts index 5fc10ae..a8d233e 100644 --- a/calf-io/build.gradle.kts +++ b/calf-io/build.gradle.kts @@ -1,11 +1,8 @@ plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.androidLibrary) + id("kotlin.multiplatform") + id("module.publication") } -kotlinMultiplatformSetup() -modulePublicationSetup() - kotlin { sourceSets.commonMain.dependencies { api(projects.calfCore) diff --git a/calf-maps/build.gradle.kts b/calf-maps/build.gradle.kts index 7b55ae1..1faccd5 100644 --- a/calf-maps/build.gradle.kts +++ b/calf-maps/build.gradle.kts @@ -1,12 +1,7 @@ plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.composeCompiler) - alias(libs.plugins.composeMultiplatform) - alias(libs.plugins.androidLibrary) + id("compose.multiplatform") } -composeMultiplatformSetup() - kotlin { sourceSets.commonMain.dependencies { implementation(compose.runtime) diff --git a/calf-navigation/build.gradle.kts b/calf-navigation/build.gradle.kts index 15cf1ae..f207f64 100644 --- a/calf-navigation/build.gradle.kts +++ b/calf-navigation/build.gradle.kts @@ -1,12 +1,7 @@ plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.composeCompiler) - alias(libs.plugins.composeMultiplatform) - alias(libs.plugins.androidLibrary) + id("compose.multiplatform") } -composeMultiplatformSetup() - kotlin { sourceSets.commonMain.dependencies { implementation(compose.runtime) diff --git a/calf-permissions/build.gradle.kts b/calf-permissions/build.gradle.kts index 7d5c133..7097177 100644 --- a/calf-permissions/build.gradle.kts +++ b/calf-permissions/build.gradle.kts @@ -1,13 +1,8 @@ plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.composeCompiler) - alias(libs.plugins.composeMultiplatform) - alias(libs.plugins.androidLibrary) + id("compose.multiplatform") + id("module.publication") } -composeMultiplatformSetup() -modulePublicationSetup() - kotlin { sourceSets.commonMain.dependencies { implementation(compose.runtime) diff --git a/calf-sf-symbols/build.gradle.kts b/calf-sf-symbols/build.gradle.kts index 9621f82..feb6d21 100644 --- a/calf-sf-symbols/build.gradle.kts +++ b/calf-sf-symbols/build.gradle.kts @@ -1,12 +1,7 @@ plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.composeCompiler) - alias(libs.plugins.composeMultiplatform) - alias(libs.plugins.androidLibrary) + id("compose.multiplatform") } -composeMultiplatformSetup() - kotlin { sourceSets.commonMain.dependencies { implementation(compose.runtime) diff --git a/calf-ui/build.gradle.kts b/calf-ui/build.gradle.kts index 35584d0..17034c4 100644 --- a/calf-ui/build.gradle.kts +++ b/calf-ui/build.gradle.kts @@ -1,13 +1,8 @@ plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.composeCompiler) - alias(libs.plugins.composeMultiplatform) - alias(libs.plugins.androidLibrary) + id("compose.multiplatform") + id("module.publication") } -composeMultiplatformSetup() -modulePublicationSetup() - kotlin { sourceSets.commonMain.dependencies { implementation(projects.calfCore) diff --git a/calf-webview/build.gradle.kts b/calf-webview/build.gradle.kts index 3e0c777..e281bf5 100644 --- a/calf-webview/build.gradle.kts +++ b/calf-webview/build.gradle.kts @@ -1,15 +1,10 @@ import org.gradle.internal.os.OperatingSystem plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.composeCompiler) - alias(libs.plugins.composeMultiplatform) - alias(libs.plugins.androidLibrary) + id("compose.multiplatform") + id("module.publication") } -composeMultiplatformSetup() -modulePublicationSetup() - val os: OperatingSystem = OperatingSystem.current() val arch: String = System.getProperty("os.arch") val isAarch64: Boolean = arch.contains("aarch64") diff --git a/convention-plugins/build.gradle.kts b/convention-plugins/build.gradle.kts index 226f890..5b15e8e 100644 --- a/convention-plugins/build.gradle.kts +++ b/convention-plugins/build.gradle.kts @@ -1,8 +1,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - `kotlin-dsl-base` - `java-gradle-plugin` + `kotlin-dsl` } java { @@ -23,6 +22,7 @@ dependencies { implementation(libs.gradlePlugin.android) implementation(libs.gradlePlugin.jetbrainsCompose) implementation(libs.gradlePlugin.kotlin) + implementation(libs.gradlePlugin.composeCompiler) implementation(libs.nexus.publish) // hack to access version catalogue https://github.com/gradle/gradle/issues/15383 diff --git a/convention-plugins/src/main/kotlin/ComposeMultiplatform.kt b/convention-plugins/src/main/kotlin/ComposeMultiplatform.kt deleted file mode 100644 index 1edb787..0000000 --- a/convention-plugins/src/main/kotlin/ComposeMultiplatform.kt +++ /dev/null @@ -1,18 +0,0 @@ -import org.gradle.api.Project - -fun Project.composeMultiplatformSetup() { - require(plugins.any { it.toString().startsWith("org.jetbrains.kotlin") }) { - "Kotlin plugin must be applied before to use Compose Multiplatform" - } - require(plugins.any { it.toString().startsWith("org.jetbrains.compose") }) { - "Compose plugin must be applied before to use Compose Multiplatform" - } - - kotlin { - applyHierarchyTemplate() - - applyTargets() - } - - androidLibrarySetup() -} diff --git a/convention-plugins/src/main/kotlin/KotlinMultiplatform.kt b/convention-plugins/src/main/kotlin/KotlinMultiplatform.kt deleted file mode 100644 index 5bb2600..0000000 --- a/convention-plugins/src/main/kotlin/KotlinMultiplatform.kt +++ /dev/null @@ -1,15 +0,0 @@ -import org.gradle.api.Project - -fun Project.kotlinMultiplatformSetup() { - require(plugins.any { it.toString().startsWith("org.jetbrains.kotlin") }) { - "Kotlin plugin must be applied before to use Compose Multiplatform" - } - - kotlin { - applyHierarchyTemplate() - - applyTargets() - } - - androidLibrarySetup() -} diff --git a/convention-plugins/src/main/kotlin/Publication.kt b/convention-plugins/src/main/kotlin/Publication.kt deleted file mode 100644 index 2362d77..0000000 --- a/convention-plugins/src/main/kotlin/Publication.kt +++ /dev/null @@ -1,92 +0,0 @@ -import org.gradle.api.Project -import org.gradle.api.publish.maven.MavenPublication -import org.gradle.api.publish.maven.tasks.AbstractPublishToMaven -import org.gradle.api.tasks.bundling.Jar -import org.gradle.kotlin.dsl.apply -import org.gradle.kotlin.dsl.register -import org.gradle.kotlin.dsl.withType -import org.gradle.plugins.signing.Sign - -fun Project.rootPublicationSetup() { - apply(plugin = "io.github.gradle-nexus.publish-plugin") - - allprojects { - group = "com.mohamedrejeb.calf" - version = System.getenv("VERSION") ?: "0.5.3" - } - - nexusPublishing { - // Configure maven central repository - // https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-ossrh - repositories { - sonatype { - nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) - snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) - stagingProfileId.set(System.getenv("OSSRH_STAGING_PROFILE_ID")) - username.set(System.getenv("OSSRH_USERNAME")) - password.set(System.getenv("OSSRH_PASSWORD")) - } - } - } -} - -fun Project.modulePublicationSetup() { - apply(plugin = "maven-publish") - apply(plugin = "signing") - - publishing { - // Configure all publications - publications.withType { - // Stub javadoc.jar artifact - artifact( - tasks.register("${name}JavadocJar", Jar::class) { - archiveClassifier.set("javadoc") - archiveAppendix.set(this@withType.name) - }, - ) - - // Provide artifacts information required by Maven Central - pom { - name.set("Calf - Compose Adaptive Look & Feel") - description.set("Calf is a library that allows you to easily create adaptive UIs for your Compose Multiplatform apps.") - url.set("https://github.com/MohamedRejeb/Calf") - - licenses { - license { - name.set("Apache-2.0") - url.set("https://opensource.org/licenses/Apache-2.0") - } - } - developers { - developer { - id.set("MohamedRejeb") - name.set("Mohamed Rejeb") - email.set("mohamedrejeb445@gmail.com") - } - } - issueManagement { - system.set("Github") - url.set("https://github.com/MohamedRejeb/Calf/issues") - } - scm { - connection.set("https://github.com/MohamedRejeb/Calf.git") - url.set("https://github.com/MohamedRejeb/Calf") - } - } - } - } - - signing { - useInMemoryPgpKeys( - System.getenv("OSSRH_GPG_SECRET_KEY_ID"), - System.getenv("OSSRH_GPG_SECRET_KEY"), - System.getenv("OSSRH_GPG_SECRET_KEY_PASSWORD"), - ) - sign(publishing.publications) - } - - // TODO: remove after https://youtrack.jetbrains.com/issue/KT-46466 is fixed - project.tasks.withType(AbstractPublishToMaven::class.java).configureEach { - dependsOn(project.tasks.withType(Sign::class.java)) - } -} diff --git a/convention-plugins/src/main/kotlin/Targets.kt b/convention-plugins/src/main/kotlin/Targets.kt index 79eae6e..d68dd84 100644 --- a/convention-plugins/src/main/kotlin/Targets.kt +++ b/convention-plugins/src/main/kotlin/Targets.kt @@ -15,14 +15,10 @@ fun KotlinMultiplatformExtension.applyTargets() { jvmToolchain(11) jvm("desktop") - js { - browser() - } + js().browser() @OptIn(ExperimentalWasmDsl::class) - wasmJs { - browser() - } + wasmJs().browser() iosX64() iosArm64() diff --git a/convention-plugins/src/main/kotlin/Utils.kt b/convention-plugins/src/main/kotlin/Utils.kt deleted file mode 100644 index 641126e..0000000 --- a/convention-plugins/src/main/kotlin/Utils.kt +++ /dev/null @@ -1,68 +0,0 @@ -import io.github.gradlenexus.publishplugin.NexusPublishExtension -import org.gradle.api.Action -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension - -/** - * Retrieves the [kotlin][org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension] extension. - */ -internal val org.gradle.api.Project.kotlin: org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension get() = - (this as org.gradle.api.plugins.ExtensionAware).extensions.getByName( - "kotlin", - ) as org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension - -/** - * Configures the [kotlin][org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension] extension. - */ -internal fun org.gradle.api.Project.kotlin(configure: Action): Unit = - (this as org.gradle.api.plugins.ExtensionAware).extensions.configure("kotlin", configure) - -/** - * Retrieves the [compose][org.jetbrains.compose.ComposePlugin.Dependencies] extension. - */ -internal val org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension.compose: org.jetbrains.compose.ComposePlugin.Dependencies get() = - (this as org.gradle.api.plugins.ExtensionAware).extensions.getByName("compose") as org.jetbrains.compose.ComposePlugin.Dependencies - -/** - * Configures the [compose][org.jetbrains.compose.ComposePlugin.Dependencies] extension. - */ -internal fun org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension.compose( - configure: Action, -): Unit = (this as org.gradle.api.plugins.ExtensionAware).extensions.configure("compose", configure) - -/** - * Retrieves the [nexusPublishing][io.github.gradlenexus.publishplugin.NexusPublishExtension] extension. - */ -internal val org.gradle.api.Project.`nexusPublishing`: io.github.gradlenexus.publishplugin.NexusPublishExtension get() = - (this as org.gradle.api.plugins.ExtensionAware).extensions.getByName( - "nexusPublishing", - ) as io.github.gradlenexus.publishplugin.NexusPublishExtension - -/** - * Configures the [nexusPublishing][io.github.gradlenexus.publishplugin.NexusPublishExtension] extension. - */ -internal fun org.gradle.api.Project.`nexusPublishing`(configure: Action): Unit = - (this as org.gradle.api.plugins.ExtensionAware).extensions.configure("nexusPublishing", configure) - -/** - * Retrieves the [publishing][org.gradle.api.publish.PublishingExtension] extension. - */ -internal val org.gradle.api.Project.`publishing`: org.gradle.api.publish.PublishingExtension get() = - (this as org.gradle.api.plugins.ExtensionAware).extensions.getByName("publishing") as org.gradle.api.publish.PublishingExtension - -/** - * Configures the [publishing][org.gradle.api.publish.PublishingExtension] extension. - */ -internal fun org.gradle.api.Project.`publishing`(configure: Action): Unit = - (this as org.gradle.api.plugins.ExtensionAware).extensions.configure("publishing", configure) - -/** - * Retrieves the [signing][org.gradle.plugins.signing.SigningExtension] extension. - */ -internal val org.gradle.api.Project.`signing`: org.gradle.plugins.signing.SigningExtension get() = - (this as org.gradle.api.plugins.ExtensionAware).extensions.getByName("signing") as org.gradle.plugins.signing.SigningExtension - -/** - * Configures the [signing][org.gradle.plugins.signing.SigningExtension] extension. - */ -internal fun org.gradle.api.Project.`signing`(configure: Action): Unit = - (this as org.gradle.api.plugins.ExtensionAware).extensions.configure("signing", configure) diff --git a/convention-plugins/src/main/kotlin/android.library.gradle.kts b/convention-plugins/src/main/kotlin/android.library.gradle.kts new file mode 100644 index 0000000..9f07d35 --- /dev/null +++ b/convention-plugins/src/main/kotlin/android.library.gradle.kts @@ -0,0 +1,5 @@ +plugins { + `android-library` +} + +androidLibrarySetup() \ No newline at end of file diff --git a/convention-plugins/src/main/kotlin/compose.multiplatform.gradle.kts b/convention-plugins/src/main/kotlin/compose.multiplatform.gradle.kts new file mode 100644 index 0000000..bb845a4 --- /dev/null +++ b/convention-plugins/src/main/kotlin/compose.multiplatform.gradle.kts @@ -0,0 +1,5 @@ +plugins { + id("kotlin.multiplatform") + id("org.jetbrains.compose") + `kotlin-composecompiler` +} diff --git a/convention-plugins/src/main/kotlin/kotlin.multiplatform.gradle.kts b/convention-plugins/src/main/kotlin/kotlin.multiplatform.gradle.kts new file mode 100644 index 0000000..4412f02 --- /dev/null +++ b/convention-plugins/src/main/kotlin/kotlin.multiplatform.gradle.kts @@ -0,0 +1,9 @@ +plugins { + `kotlin-multiplatform` + id("android.library") +} + +kotlin { + applyHierarchyTemplate() + applyTargets() +} \ No newline at end of file diff --git a/convention-plugins/src/main/kotlin/module.publication.gradle.kts b/convention-plugins/src/main/kotlin/module.publication.gradle.kts new file mode 100644 index 0000000..e50291e --- /dev/null +++ b/convention-plugins/src/main/kotlin/module.publication.gradle.kts @@ -0,0 +1,64 @@ +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.api.tasks.bundling.Jar +import org.gradle.kotlin.dsl.`maven-publish` + +plugins { + `maven-publish` + signing +} + +publishing { + // Configure all publications + publications.withType { + // Stub javadoc.jar artifact + artifact( + tasks.register("${name}JavadocJar", Jar::class) { + archiveClassifier.set("javadoc") + archiveAppendix.set(this@withType.name) + }, + ) + + // Provide artifacts information required by Maven Central + pom { + name.set("Calf - Compose Adaptive Look & Feel") + description.set("Calf is a library that allows you to easily create adaptive UIs for your Compose Multiplatform apps.") + url.set("https://github.com/MohamedRejeb/Calf") + + licenses { + license { + name.set("Apache-2.0") + url.set("https://opensource.org/licenses/Apache-2.0") + } + } + developers { + developer { + id.set("MohamedRejeb") + name.set("Mohamed Rejeb") + email.set("mohamedrejeb445@gmail.com") + } + } + issueManagement { + system.set("Github") + url.set("https://github.com/MohamedRejeb/Calf/issues") + } + scm { + connection.set("https://github.com/MohamedRejeb/Calf.git") + url.set("https://github.com/MohamedRejeb/Calf") + } + } + } +} + +signing { + useInMemoryPgpKeys( + System.getenv("OSSRH_GPG_SECRET_KEY_ID"), + System.getenv("OSSRH_GPG_SECRET_KEY"), + System.getenv("OSSRH_GPG_SECRET_KEY_PASSWORD"), + ) + sign(publishing.publications) +} + +// TODO: remove after https://youtrack.jetbrains.com/issue/KT-46466 is fixed +project.tasks.withType(AbstractPublishToMaven::class.java).configureEach { + dependsOn(project.tasks.withType(Sign::class.java)) +} diff --git a/convention-plugins/src/main/kotlin/root.publication.gradle.kts b/convention-plugins/src/main/kotlin/root.publication.gradle.kts new file mode 100644 index 0000000..f316c2a --- /dev/null +++ b/convention-plugins/src/main/kotlin/root.publication.gradle.kts @@ -0,0 +1,22 @@ +plugins { + id("io.github.gradle-nexus.publish-plugin") +} + +allprojects { + group = "com.mohamedrejeb.calf" + version = System.getenv("VERSION") ?: "0.5.3" +} + +nexusPublishing { + // Configure maven central repository + // https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-ossrh + repositories { + sonatype { + nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) + snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) + stagingProfileId.set(System.getenv("OSSRH_STAGING_PROFILE_ID")) + username.set(System.getenv("OSSRH_USERNAME")) + password.set(System.getenv("OSSRH_PASSWORD")) + } + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ffb11d4..a276229 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ calf = "0.5.3" # For sample compose-compiler = "1.5.4" -activity-compose = "1.9.0" +activity-compose = "1.9.1" navigation-compose = "2.7.7" android-minSdk = "21" android-compileSdk = "34" @@ -27,6 +27,7 @@ play-services-location = "21.3.0" gradlePlugin-android = { module = "com.android.tools.build:gradle", version.ref = "agp" } gradlePlugin-jetbrainsCompose = { module = "org.jetbrains.compose:compose-gradle-plugin", version.ref = "compose" } gradlePlugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +gradlePlugin-composeCompiler = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity-compose" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3499ded..171d876 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/sample/android/build.gradle.kts b/sample/android/build.gradle.kts index 7c6e496..3c968fd 100644 --- a/sample/android/build.gradle.kts +++ b/sample/android/build.gradle.kts @@ -21,9 +21,6 @@ android { buildFeatures { compose = true } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get() - } compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 diff --git a/sample/common/build.gradle.kts b/sample/common/build.gradle.kts index cc5980a..e6c7e65 100644 --- a/sample/common/build.gradle.kts +++ b/sample/common/build.gradle.kts @@ -1,12 +1,7 @@ plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.composeCompiler) - alias(libs.plugins.composeMultiplatform) - alias(libs.plugins.androidLibrary) + id("compose.multiplatform") } -composeMultiplatformSetup() - kotlin { listOf( iosX64(), diff --git a/sample/common/src/commonMain/kotlin/com.mohamedrejeb.calf.sample/App.kt b/sample/common/src/commonMain/kotlin/com.mohamedrejeb.calf.sample/App.kt index b41a59d..e32cd0d 100644 --- a/sample/common/src/commonMain/kotlin/com.mohamedrejeb.calf.sample/App.kt +++ b/sample/common/src/commonMain/kotlin/com.mohamedrejeb.calf.sample/App.kt @@ -3,9 +3,18 @@ package com.mohamedrejeb.calf.sample import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import com.mohamedrejeb.calf.navigation.rememberNavController import com.mohamedrejeb.calf.sample.navigation.AppNavGraph +import com.mohamedrejeb.calf.sample.screens.DateSelector import com.mohamedrejeb.calf.sample.ui.theme.CalfTheme +import kotlinx.datetime.Clock +import kotlinx.datetime.LocalDate +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime @Composable fun App() = @@ -17,5 +26,23 @@ fun App() = contentColor = MaterialTheme.colorScheme.onSurface, ) { AppNavGraph(navController = navController) + +// var date by remember { +// val dateTime = Clock.System.now().toLocalDateTime(TimeZone.UTC) +// mutableStateOf( +// LocalDate( +// year = dateTime.year, +// monthNumber = dateTime.monthNumber, +// dayOfMonth = dateTime.dayOfMonth - 5 +// ) +// ) +// } +// +// DateSelector( +// initialDate = date, +// onDateSelected = { +// date = it +// } +// ) } } diff --git a/sample/common/src/commonMain/kotlin/com.mohamedrejeb.calf.sample/screens/DateSelector.kt b/sample/common/src/commonMain/kotlin/com.mohamedrejeb.calf.sample/screens/DateSelector.kt new file mode 100644 index 0000000..d174dfe --- /dev/null +++ b/sample/common/src/commonMain/kotlin/com.mohamedrejeb.calf.sample/screens/DateSelector.kt @@ -0,0 +1,176 @@ +package com.mohamedrejeb.calf.sample.screens + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBackIos +import androidx.compose.material.icons.automirrored.filled.ArrowForwardIos +import androidx.compose.material.icons.filled.ArrowBackIos +import androidx.compose.material.icons.filled.ArrowBackIosNew +import androidx.compose.material.icons.filled.ArrowForwardIos +import androidx.compose.material.icons.filled.DateRange +import androidx.compose.material3.DatePickerColors +import androidx.compose.material3.DatePickerDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableLongStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment.Companion.CenterVertically +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mohamedrejeb.calf.ui.datepicker.AdaptiveDatePicker +import com.mohamedrejeb.calf.ui.datepicker.rememberAdaptiveDatePickerState +import com.mohamedrejeb.calf.ui.sheet.AdaptiveBottomSheet +import com.mohamedrejeb.calf.ui.sheet.rememberAdaptiveSheetState +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.datetime.Clock +import kotlinx.datetime.DatePeriod +import kotlinx.datetime.Instant +import kotlinx.datetime.LocalDate +import kotlinx.datetime.TimeZone +import kotlinx.datetime.TimeZone.Companion.UTC +import kotlinx.datetime.atStartOfDayIn +import kotlinx.datetime.minus +import kotlinx.datetime.plus +import kotlinx.datetime.toLocalDateTime + +val LocalTestString = compositionLocalOf { "" } + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun DateSelector( + initialDate: LocalDate, + modifier: Modifier = Modifier, + onDateSelected: (LocalDate) -> Unit, +) { + val currentInitialDate by rememberUpdatedState(initialDate) + var showDatePicker by rememberSaveable { mutableStateOf(false) } + var skipPartiallyExpanded by remember { mutableStateOf(false) } + if (showDatePicker) { + val current by remember { + derivedStateOf { + currentInitialDate.atStartOfDayIn(TimeZone.UTC).toEpochMilliseconds().also { + println("Initial millis: $it") + } + } + } + var currentSelection by remember { mutableLongStateOf(current) } + val sheetState = rememberAdaptiveSheetState(skipPartiallyExpanded = skipPartiallyExpanded) + var hideSheet by remember { mutableStateOf(false) } + // TODO track min/max dates available and limit to those + val datePickerState = rememberAdaptiveDatePickerState(current) + AdaptiveBottomSheet( + onDismissRequest = { showDatePicker = false }, + adaptiveSheetState = sheetState, + ) { +// Surface( +// color = MaterialTheme.colorScheme.surface, +// contentColor = MaterialTheme.colorScheme.onSurface, +// ) { + AdaptiveDatePicker( + datePickerState, + modifier = Modifier.fillMaxWidth(), + colors = DatePickerDefaults.colors( + selectedDayContainerColor = Color.Red, + dayContentColor = Color.DarkGray, + containerColor = Color.LightGray + ), + headline = { Text("Select a date", Modifier.padding(start = 16.dp)) }, + ) + + Row(Modifier.padding(bottom = 16.dp)) { + Spacer(Modifier.weight(1f)) + TextButton(onClick = { hideSheet = true }) { Text("Cancel") } + Spacer(Modifier.width(4.dp)) + val confirmEnabled by remember { + derivedStateOf { datePickerState.selectedDateMillis != current } + } + TextButton( + onClick = { + currentSelection = datePickerState.selectedDateMillis ?: current + hideSheet = true + }, + enabled = confirmEnabled, + ) { + Text("Confirm") + } + } +// } + } + if (hideSheet) { + LaunchedEffect(Unit) { + try { + sheetState.hide() + } catch (e: Exception) { + println("But why?!") // because now it's animating back up?!? + } + hideSheet = false + showDatePicker = false + val selected = + currentSelection + .takeIf { it != current } + ?.let { Instant.fromEpochMilliseconds(it).toLocalDateTime(TimeZone.UTC).date } + ?: currentInitialDate + onDateSelected(selected) + } + } + } + NavigationBar(modifier = modifier) { + IconButton( + onClick = { onDateSelected(currentInitialDate.minus(DatePeriod(days = 1))) }, + modifier = Modifier.weight(0.2f), + ) { + Icon(Icons.AutoMirrored.Filled.ArrowBackIos, "Previous Day") + } + val scope = rememberCoroutineScope() + TextButton(onClick = { + showDatePicker = true +// scope.launch { +// delay(2000) +// skipPartiallyExpanded = !skipPartiallyExpanded +// } + }, modifier = Modifier.weight(0.6f)) { + Row(verticalAlignment = CenterVertically) { + Icon(Icons.Default.DateRange, contentDescription = "Select date") + Spacer(Modifier.width(4.dp)) + val today = remember { Clock.System.now().toLocalDateTime(TimeZone.UTC).date } + val text = + if (currentInitialDate == today) { + "$currentInitialDate (Today)" + } else { + currentInitialDate.toString() + } + Text(text, modifier = Modifier.animateContentSize()) + } + } + IconButton( + onClick = { onDateSelected(currentInitialDate.plus(DatePeriod(days = 1))) }, + modifier = Modifier.weight(0.2f), + ) { + Icon(Icons.AutoMirrored.Filled.ArrowForwardIos, "Next Day") + } + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index df2c6c4..77b9af7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,7 +10,6 @@ pluginManagement { } mavenCentral() gradlePluginPortal() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") } } @@ -19,12 +18,11 @@ dependencyResolutionManagement { repositories { google() mavenCentral() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") } } plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "0.4.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" } include( @@ -49,3 +47,4 @@ include( ) enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") +enableFeaturePreview("STABLE_CONFIGURATION_CACHE")