diff --git a/app/android/build.gradle.kts b/app/android/build.gradle.kts index 1d7e97b..079fe13 100644 --- a/app/android/build.gradle.kts +++ b/app/android/build.gradle.kts @@ -1,44 +1,61 @@ -import com.android.build.gradle.tasks.MergeSourceSetFolders -import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import com.android.build.api.dsl.ApplicationExtension import java.io.FileInputStream import java.util.Properties plugins { alias(libs.plugins.android.application) - alias(libs.plugins.jetbrains.kotlin.android) alias(libs.plugins.compose.compiler) alias(libs.plugins.compose.multiplatform) alias(libs.plugins.google.services) } -kotlin { - compilerOptions { - jvmTarget = JvmTarget.JVM_17 +abstract class FixComposeResTask : DefaultTask() { + @get:InputFiles abstract val inputFiles: ConfigurableFileCollection + @get:OutputDirectory abstract val outputDirectory: DirectoryProperty + + @TaskAction + fun action() { + val outDir = outputDirectory.get().asFile.apply { + deleteRecursively() + mkdirs() + } + + inputFiles.forEach { file -> + if (file.exists()) { + file.copyRecursively( + File(outDir, "composeResources/ru.fromchat"), + overwrite = true + ) + } + } } } -val fixComposeResourcesStructure = tasks.register("fixComposeResourcesStructure") { +val fixComposeResourcesStructure = tasks.register("fixComposeResourcesStructure") { val sharedProject = rootProject.project(":app:shared") - from( + inputFiles.from( sharedProject .layout .buildDirectory - .dir("generated/compose/resourceGenerator/preparedResources/commonMain/composeResources") + .dir( + "generated/compose/resourceGenerator/preparedResources/commonMain/composeResources" + ) ) - into( - layout - .buildDirectory - .dir("intermediates/fixed_compose_res/composeResources/ru.fromchat") + outputDirectory.set( + layout.buildDirectory.dir("intermediates/fixed_compose_res") ) - dependsOn(sharedProject.tasks.matching { it.name.contains("prepareComposeResources", ignoreCase = true) }) - dependsOn(sharedProject.tasks.matching { it.name.contains("copyNonXmlValueResources", ignoreCase = true) }) + dependsOn( + sharedProject.tasks.matching { + it.name.contains("prepareComposeResources", ignoreCase = true) + } + ) } -android { +extensions.configure { namespace = "ru.fromchat" compileSdk = 36 @@ -87,22 +104,17 @@ android { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } +} - sourceSets["main"].apply { - assets.srcDirs( - layout.buildDirectory.dir("intermediates/fixed_compose_res") +androidComponents { + onVariants { variant -> + variant.sources.assets?.addGeneratedSourceDirectory( + fixComposeResourcesStructure, + FixComposeResTask::outputDirectory ) } } -tasks.withType().configureEach { - dependsOn(fixComposeResourcesStructure) -} - -tasks.matching { it.name.contains("lintVital", ignoreCase = true) }.configureEach { - dependsOn(fixComposeResourcesStructure) -} - dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) @@ -125,7 +137,4 @@ dependencies { implementation(project(":app:shared")) implementation(project(":utils:shared")) -} - -// Process `google-services.json` into resources so Firebase initializes automatically. -apply(plugin = "com.google.gms.google-services") \ No newline at end of file +} \ No newline at end of file diff --git a/app/shared/build.gradle.kts b/app/shared/build.gradle.kts index 8a8520d..3ac3d23 100644 --- a/app/shared/build.gradle.kts +++ b/app/shared/build.gradle.kts @@ -1,11 +1,8 @@ - -import com.android.build.api.dsl.androidLibrary - plugins { alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.compose.multiplatform) alias(libs.plugins.compose.compiler) - alias(libs.plugins.android.kotlin.multiplatform.library) + alias(libs.plugins.kotlin.multiplatform.library) alias(libs.plugins.kotlin.serialization) } @@ -43,15 +40,15 @@ kotlin { } commonMain.dependencies { - implementation(compose.runtime) - implementation(compose.foundation) - implementation(compose.material3) - implementation(compose.ui) - implementation(compose.components.resources) - implementation(compose.components.uiToolingPreview) + implementation(libs.compose.runtime) + implementation(libs.compose.foundation) + implementation(libs.compose.material3) + implementation(libs.compose.ui) + implementation(libs.compose.components.resources) + implementation(libs.compose.ui.tooling.preview) implementation(libs.constraintlayout) implementation(libs.navigation.compose) - implementation(compose.materialIconsExtended) + implementation(libs.compose.materialIconsExtended) implementation(libs.haze) implementation(libs.haze.materials) implementation(libs.androidx.core.ktx) diff --git a/build.gradle.kts b/build.gradle.kts index 7b921e4..2bef730 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,21 +1,19 @@ plugins { - // this is necessary to avoid the plugins to be loaded multiple times - // in each subproject's classloader alias(libs.plugins.android.application) apply false alias(libs.plugins.compose.multiplatform) apply false alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.kotlin.multiplatform) apply false - alias(libs.plugins.android.kotlin.multiplatform.library) apply false - alias(libs.plugins.jetbrains.kotlin.android) apply false + alias(libs.plugins.kotlin.multiplatform.library) apply false alias(libs.plugins.android.library) apply false + alias(libs.plugins.google.services) apply false } -// Ensure google-services plugin is available to subprojects that apply it. buildscript { repositories { google() mavenCentral() } + dependencies { classpath(libs.google.services) } diff --git a/gradle.properties b/gradle.properties index 20e2a01..f8128bf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,4 +20,8 @@ kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +android.uniquePackageNames=false +android.dependency.useConstraints=true +android.r8.strictFullModeForKeepRules=false +android.generateSyncIssueWhenLibraryConstraintsAreEnabled=false \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 086f61a..c5dfeb1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,10 +1,10 @@ [versions] -agp = "8.13.2" +agp = "9.0.0" androidx-activityCompose = "1.12.2" androidx-appcompat = "1.7.1" androidx-core-ktx = "1.17.0" coilCompose = "3.3.0" -compose-multiplatform = "1.9.3" +compose-multiplatform = "1.10.0" #noinspection NewerVersionAvailable constraintlayout = "0.6.1-shaded" coreSplashscreen = "1.2.0" @@ -13,7 +13,6 @@ googleServices = "4.4.4" haze = "1.7.1" kotlin = "2.3.0" adaptiveAndroid = "1.2.0" -kotlinStdlib = "2.3.0" biometric = "1.4.0-alpha05" gson = "2.13.2" kotlinxIoBytestring = "0.8.2" @@ -26,13 +25,16 @@ material = "1.13.0" activityKtx = "1.12.2" navigationCompose = "2.9.1" datastore = "1.2.0" -security-crypto = "1.1.0-alpha06" +security-crypto = "1.1.0" ktor = "3.3.3" slf4j = "1.7.36" kotlinxDatetime = "0.7.1" lifecycleRuntimeKtx = "2.10.0" -composeBom = "2025.12.01" -playServicesBase = "18.9.0" +composeBom = "2026.01.00" +composeMaterialIconsExtended = "1.7.3" +composeMaterial3 = "1.10.0-alpha05" +composeComponents = "1.10.0" +playServicesBase = "18.10.0" [libraries] androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" } @@ -49,7 +51,7 @@ haze-materials = { module = "dev.chrisbanes.haze:haze-materials", version.ref = jetbrains-kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" } jetbrains-kotlinx-io-bytestring = { module = "org.jetbrains.kotlinx:kotlinx-io-bytestring", version.ref = "kotlinxIoBytestring" } androidx-adaptive-android = { group = "androidx.compose.material3.adaptive", name = "adaptive-android", version.ref = "adaptiveAndroid" } -kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlinStdlib" } +kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" } biometric = { module = "androidx.biometric:biometric", version.ref = "biometric" } gson = { module = "com.google.code.gson:gson", version.ref = "gson" } kotlinx-io-core = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinxIoCore" } @@ -80,14 +82,20 @@ androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-toolin androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } play-services-base = { group = "com.google.android.gms", name = "play-services-base", version.ref = "playServicesBase" } +compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "compose-multiplatform" } +compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "compose-multiplatform" } +compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "composeMaterial3" } +compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "compose-multiplatform" } +compose-components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "composeComponents" } +compose-ui-tooling-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "compose-multiplatform" } +compose-materialIconsExtended = { module = "org.jetbrains.compose.material:material-icons-extended", version.ref = "composeMaterialIconsExtended" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } -android-kotlin-multiplatform-library = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" } -jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version = "2.3.0" } +kotlin-multiplatform-library = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" } android-library = { id = "com.android.library", version.ref = "agp" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "serialization" } -google-services = { id = "com.google.gms.google-services" } \ No newline at end of file +google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ccf3f33..40c3263 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sat Sep 06 13:57:07 MSK 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 23bdd6e..6a8eccf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,6 +19,9 @@ pluginManagement { gradlePluginPortal() } } +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} dependencyResolutionManagement { repositories { diff --git a/utils/android/build.gradle.kts b/utils/android/build.gradle.kts index 1295e7b..7702c9a 100644 --- a/utils/android/build.gradle.kts +++ b/utils/android/build.gradle.kts @@ -1,17 +1,19 @@ + +import com.android.build.api.dsl.LibraryExtension import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { alias(libs.plugins.android.library) - alias(libs.plugins.jetbrains.kotlin.android) } -kotlin { +tasks.withType { compilerOptions { jvmTarget = JvmTarget.JVM_17 } } -android { +extensions.configure { namespace = "com.pr0gramm3r101.utils" compileSdk = 36 diff --git a/utils/shared/build.gradle.kts b/utils/shared/build.gradle.kts index 9333662..88febdf 100644 --- a/utils/shared/build.gradle.kts +++ b/utils/shared/build.gradle.kts @@ -1,10 +1,8 @@ -import com.android.build.api.dsl.androidLibrary - plugins { alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.compose.multiplatform) alias(libs.plugins.compose.compiler) - alias(libs.plugins.android.kotlin.multiplatform.library) + alias(libs.plugins.kotlin.multiplatform.library) } kotlin { @@ -31,13 +29,13 @@ kotlin { sourceSets { commonMain.dependencies { implementation(libs.kotlin.stdlib) - implementation(compose.runtime) - implementation(compose.foundation) - implementation(compose.material3) - implementation(compose.ui) - implementation(compose.components.resources) + implementation(libs.compose.runtime) + implementation(libs.compose.foundation) + implementation(libs.compose.material3) + implementation(libs.compose.ui) + implementation(libs.compose.components.resources) implementation(libs.constraintlayout) - implementation(compose.materialIconsExtended) + implementation(libs.compose.materialIconsExtended) implementation(libs.jetbrains.kotlinx.coroutines.core) implementation(libs.navigation.compose) implementation(libs.kotlinx.serialization.json) diff --git a/utils/shared/src/commonMain/kotlin/com/pr0gramm3r101/utils/Utils.kt b/utils/shared/src/commonMain/kotlin/com/pr0gramm3r101/utils/Utils.kt index 256a297..053a614 100644 --- a/utils/shared/src/commonMain/kotlin/com/pr0gramm3r101/utils/Utils.kt +++ b/utils/shared/src/commonMain/kotlin/com/pr0gramm3r101/utils/Utils.kt @@ -7,12 +7,11 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Typography import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocal -import androidx.compose.runtime.ProvidableCompositionLocal import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusManager -import androidx.compose.ui.platform.ClipboardManager -import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.platform.Clipboard +import androidx.compose.ui.platform.LocalClipboard import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.SoftwareKeyboardController import androidx.compose.ui.text.AnnotatedString @@ -68,13 +67,10 @@ expect fun Modifier.clearFocusOnKeyboardDismiss(): Modifier operator fun Modifier.plus(other: Modifier) = then(other) -var ClipboardManager.text - get() = getText() - set(value) = setText(value!!) - @Composable expect fun ToggleNavScrimEffect(enabled: Boolean = false) +// TODO fix implementation interface SupportClipboardManager { suspend fun setText(string: String) suspend fun getText(): String? @@ -83,21 +79,21 @@ interface SupportClipboardManager { fun setTextListener(listener: (String) -> Unit) } -val LocalSupportClipboardManager: ProvidableCompositionLocal = compositionLocalOf { +val LocalSupportClipboardManager = compositionLocalOf { throw IllegalStateException("This CompositionLocal hasn't been provided.") } -val supportClipboardManagerImpl - @Composable get() = LocalClipboardManager().toSupport() +val supportClipboardManagerImpl @Composable get() = LocalClipboard().toSupport() -inline fun ClipboardManager.toSupport() = object : SupportClipboardManager { +inline fun Clipboard.toSupport() = object : SupportClipboardManager { private var listener: ((String) -> Unit)? = null override suspend fun setText(string: String) { - setText(string.toAnnotatedString()) + setText(string) listener?.invoke(string) } - override suspend fun getText() = this@toSupport.getText()?.toString() ?: "" + + override suspend fun getText() = this@toSupport.getClipEntry()?.toString() ?: "" override fun setTextListener(listener: (String) -> Unit) { this.listener = listener @@ -109,7 +105,9 @@ fun String.toAnnotatedString() = AnnotatedString(this) @Composable inline operator fun CompositionLocal.invoke() = current -val LocalSnackbar: ProvidableCompositionLocal = compositionLocalOf { throw NullPointerException() } +val LocalSnackbar = compositionLocalOf { + throw NullPointerException() +} inline fun resetFocus( keyboardController: SoftwareKeyboardController?, @@ -119,8 +117,7 @@ inline fun resetFocus( focusManager.clearFocus(true) } -val unsupported: Nothing - get() = throw UnsupportedOperationException() +val unsupported: Nothing get() = throw UnsupportedOperationException() val WindowWidthSizeClass.rawValue: Int get() = when (this) { @@ -145,26 +142,23 @@ val WindowSizeClass.width get() = windowWidthSizeClass val WindowSizeClass.height get() = windowHeightSizeClass val WindowAdaptiveInfo.widthSizeClass get() = windowSizeClass.width -@Suppress("unused") val WindowAdaptiveInfo.heightSizeClass get() = windowSizeClass.height -fun String.encodeJSON(): String { - val out = StringBuilder() - for (i in indices) { - when (val c: Char = get(i)) { - '"', '\\', '/' -> out.append('\\').append(c) - '\t' -> out.append("\\t") - '\b' -> out.append("\\b") - '\n' -> out.append("\\n") - '\r' -> out.append("\\r") - else -> if (c.code <= 0x1F) { - out.append("\\u${c.code.toString(16).padStart(4, '0')}") - } else { - out.append(c) +fun String.encodeJSON() = buildString { + for (i in this@encodeJSON.indices) { + append( + when (val c: Char = get(i)) { + '"', '\\', '/' -> "\\$c" + '\t' -> "\\t" + '\b' -> "\\b" + '\n' -> "\\n" + '\r' -> "\\r" + else -> if (c.code <= 0x1F) { + "\\u${c.code.toString(16).padStart(4, '0')}" + } else c } - } + ) } - return "$out" } @Composable