From e8d0425b3800dfc4080c1e642c7c41d54f3c7437 Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Mon, 11 Aug 2025 12:24:21 +0000 Subject: [PATCH] android, desktop: compose 1.8.2 + Kotlin 2.1.20 + compileSdk 35 (#5774) * android, desktop: compose 1.8.0-beta01 + Kotlin 2.1.20 + compileSdk 35 * more fixes * remove non-total methods * fix script path * switch to stable compose.ui, import icons separately --------- Co-authored-by: Evgeny Poberezkin --- .gitignore | 1 + apps/multiplatform/android/build.gradle.kts | 5 +++-- apps/multiplatform/common/build.gradle.kts | 5 ++++- .../simplex/common/views/call/CallView.android.kt | 6 ++++-- .../kotlin/chat/simplex/common/model/ChatModel.kt | 15 ++++++++++----- .../chat/simplex/common/platform/AppCommon.kt | 2 +- .../simplex/common/views/chat/ChatItemsLoader.kt | 3 ++- .../common/views/helpers/ProcessedErrors.kt | 1 + .../networkAndServers/AdvancedNetworkSettings.kt | 4 ++-- .../kotlin/chat/simplex/common/DesktopApp.kt | 2 +- .../common/platform/ScrollableColumn.desktop.kt | 4 ++-- .../simplex/common/views/call/CallView.desktop.kt | 6 ++++-- apps/multiplatform/desktop/build.gradle.kts | 5 +++-- apps/multiplatform/gradle.properties | 6 +++--- .../gradle/wrapper/gradle-wrapper.properties | 6 +++--- apps/multiplatform/settings.gradle.kts | 1 + 16 files changed, 45 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 645b55ec9d..4560272980 100644 --- a/.gitignore +++ b/.gitignore @@ -79,3 +79,4 @@ website/package-lock.json website/.cache website/test/stubs-layout-cache/_includes/*.js apps/android/app/release +apps/multiplatform/.kotlin/sessions diff --git a/apps/multiplatform/android/build.gradle.kts b/apps/multiplatform/android/build.gradle.kts index 2bac5f7ada..8168c92bf8 100644 --- a/apps/multiplatform/android/build.gradle.kts +++ b/apps/multiplatform/android/build.gradle.kts @@ -5,10 +5,11 @@ plugins { id("org.jetbrains.compose") kotlin("android") id("org.jetbrains.kotlin.plugin.serialization") + id("org.jetbrains.kotlin.plugin.compose") } android { - compileSdk = 34 + compileSdk = 35 defaultConfig { applicationId = "chat.simplex.app" @@ -191,7 +192,7 @@ tasks { outputDir = outputs.files.files.last() } exec { - workingDir("../../../scripts/android") + workingDir("../../scripts/android") environment = mapOf("JAVA_HOME" to "$javaHome") commandLine = listOf( "./compress-and-sign-apk.sh", diff --git a/apps/multiplatform/common/build.gradle.kts b/apps/multiplatform/common/build.gradle.kts index e2927e4aaf..8b4dd0e4d7 100644 --- a/apps/multiplatform/common/build.gradle.kts +++ b/apps/multiplatform/common/build.gradle.kts @@ -3,6 +3,7 @@ plugins { id("org.jetbrains.compose") id("com.android.library") id("org.jetbrains.kotlin.plugin.serialization") + id("org.jetbrains.kotlin.plugin.compose") id("dev.icerock.mobile.multiplatform-resources") id("com.github.gmazzo.buildconfig") version "5.3.5" } @@ -39,6 +40,8 @@ kotlin { api("com.russhwolf:multiplatform-settings:1.1.1") api("com.charleskorn.kaml:kaml:0.59.0") api("org.jetbrains.compose.ui:ui-text:${rootProject.extra["compose.version"] as String}") + implementation("org.jetbrains.compose.material:material-icons-core:1.7.3") + implementation("org.jetbrains.compose.material:material-icons-extended:1.7.3") implementation("org.jetbrains.compose.components:components-animatedimage:${rootProject.extra["compose.version"] as String}") //Barcode api("org.boofcv:boofcv-core:1.1.3") @@ -125,7 +128,7 @@ kotlin { android { namespace = "chat.simplex.common" - compileSdk = 34 + compileSdk = 35 sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") defaultConfig { minSdk = 26 diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt index 166f4ec355..22e53af849 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt @@ -709,9 +709,11 @@ fun WebRTCView(callCommand: SnapshotStateList, onResponse: (WVAPIM .filterNotNull() .collect { while (callCommand.isNotEmpty()) { - val cmd = callCommand.removeFirst() + val cmd = callCommand.removeFirstOrNull() Log.d(TAG, "WebRTCView LaunchedEffect executing $cmd") - processCommand(wv, cmd) + if (cmd != null) { + processCommand(wv, cmd) + } } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 1d00d7cdb0..94c44b3883 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -26,6 +26,8 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.sync.Mutex import kotlin.collections.removeAll as remAll import kotlinx.datetime.* +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant import kotlinx.datetime.TimeZone import kotlinx.serialization.* import kotlinx.serialization.descriptors.* @@ -472,14 +474,17 @@ object ChatModel { } fun removeLastChatItems() { - val removed: Triple + val remIndex: Int + val rem: ChatItem? chatItems.value = SnapshotStateList().apply { addAll(chatItems.value) - val remIndex = lastIndex - val rem = removeLast() - removed = Triple(rem.id, remIndex, rem.isRcvNew) + remIndex = lastIndex + rem = removeLastOrNull() + } + if (rem != null) { + val removed = Triple(rem.id, remIndex, rem.isRcvNew) + chatState.itemsRemoved(listOf(removed), chatItems.value) } - chatState.itemsRemoved(listOf(removed), chatItems.value) } suspend fun addChatItem(rhId: Long?, chatInfo: ChatInfo, cItem: ChatItem) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/AppCommon.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/AppCommon.kt index 780f8c25b4..7a96bd99d2 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/AppCommon.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/AppCommon.kt @@ -32,7 +32,7 @@ val databaseBackend: String = if (appPlatform == AppPlatform.ANDROID) "sqlite" e class FifoQueue(private var capacity: Int) : LinkedList() { override fun add(element: E): Boolean { - if(size > capacity) removeFirst() + if (size > capacity) removeFirstOrNull() return super.add(element) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatItemsLoader.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatItemsLoader.kt index 07cdc065a7..ed40150cb1 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatItemsLoader.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatItemsLoader.kt @@ -325,7 +325,8 @@ private fun removeDuplicatesAndUpperSplits( if (idsToTrim.last().isNotEmpty()) { // it has some elements to trim from currently visible range which means the items shouldn't be trimmed // Otherwise, the last set would be empty - idsToTrim.removeLast() + // note: removeLast() produce NoSuchMethodError on Android but removeLastOrNull() works + idsToTrim.removeLastOrNull() } val allItemsToDelete = idsToTrim.flatten() if (allItemsToDelete.isNotEmpty()) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ProcessedErrors.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ProcessedErrors.kt index 5fa097fb6b..3d21cfaf0f 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ProcessedErrors.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ProcessedErrors.kt @@ -58,6 +58,7 @@ class ProcessedErrors (val interval: Long) { text = generalGetString(MR.strings.agent_internal_error_desc).format(error.internalErr), ) } + else -> {} } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/AdvancedNetworkSettings.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/AdvancedNetworkSettings.kt index 40d664a257..8c38070c98 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/AdvancedNetworkSettings.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/AdvancedNetworkSettings.kt @@ -33,7 +33,7 @@ import chat.simplex.res.MR import java.text.DecimalFormat @Composable -fun ModalData.AdvancedNetworkSettingsView(showModal: (ModalData.() -> Unit) -> Unit, close: () -> Unit) { +fun ModalData.AdvancedNetworkSettingsView(showModal: (@Composable ModalData.() -> Unit) -> Unit, close: () -> Unit) { val currentRemoteHost by remember { chatModel.currentRemoteHost } val developerTools = remember { appPrefs.developerTools.get() } @@ -216,7 +216,7 @@ fun ModalData.AdvancedNetworkSettingsView(showModal: (ModalData.() -> Unit) -> U updateSessionMode: (TransportSessionMode) -> Unit, updateSMPProxyMode: (SMPProxyMode) -> Unit, updateSMPProxyFallback: (SMPProxyFallback) -> Unit, - showModal: (ModalData.() -> Unit) -> Unit, + showModal: (@Composable ModalData.() -> Unit) -> Unit, resetDisabled: Boolean, reset: () -> Unit, saveDisabled: Boolean, diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/DesktopApp.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/DesktopApp.kt index dfffb826f5..136a883035 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/DesktopApp.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/DesktopApp.kt @@ -171,7 +171,7 @@ private fun ApplicationScope.AppWindow(closedByError: MutableState) { // Shows toast in insertion order with preferred delay per toast. New one will be shown once previous one expires LaunchedEffect(toast, toasts.size) { delay(toast.second) - simplexWindowState.toasts.removeFirst() + simplexWindowState.toasts.removeFirstOrNull() } } var windowFocused by remember { simplexWindowState.windowFocused } diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/ScrollableColumn.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/ScrollableColumn.desktop.kt index 7a2a1dff0a..696e0efde8 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/ScrollableColumn.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/ScrollableColumn.desktop.kt @@ -93,7 +93,7 @@ actual fun LazyColumnWithScrollBar( } val modifier = if (fillMaxSize) Modifier.fillMaxSize().then(modifier) else modifier Box(Modifier.copyViewToAppBar(remember { appPrefs.appearanceBarsBlurRadius.state }.value, LocalAppBarHandler.current?.graphicsLayer).nestedScroll(connection)) { - LazyColumn(modifier.then(scrollModifier), state, contentPadding, reverseLayout, verticalArrangement, horizontalAlignment, flingBehavior, userScrollEnabled, content) + LazyColumn(modifier.then(scrollModifier), state, contentPadding, reverseLayout, verticalArrangement, horizontalAlignment, flingBehavior, userScrollEnabled, content = content) ScrollBar(reverseLayout, state, scrollBarAlpha, scrollJob, scrollBarDraggingState, additionalBarOffset, additionalTopBar, chatBottomBar) } } @@ -138,7 +138,7 @@ actual fun LazyColumnWithScrollBarNoAppBar( // (only first visible row is useful because LazyColumn doesn't have absolute scroll position, only relative to row) val scrollBarDraggingState = remember { mutableStateOf(false) } Box(contentAlignment = containerAlignment) { - LazyColumn(modifier.then(scrollModifier), state, contentPadding, reverseLayout, verticalArrangement, horizontalAlignment, flingBehavior, userScrollEnabled, content) + LazyColumn(modifier.then(scrollModifier), state, contentPadding, reverseLayout, verticalArrangement, horizontalAlignment, flingBehavior, userScrollEnabled, content = content) Box(if (maxHeight?.value != null) Modifier.height(maxHeight.value).fillMaxWidth() else Modifier.fillMaxSize(), contentAlignment = Alignment.CenterEnd) { DesktopScrollBar(rememberScrollbarAdapter(state), Modifier.fillMaxHeight(), scrollBarAlpha, scrollJob, reverseLayout, scrollBarDraggingState) } diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt index e3b0642547..9be10a584b 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt @@ -195,9 +195,11 @@ fun WebRTCController(callCommand: SnapshotStateList, onResponse: ( delay(100) } while (callCommand.isNotEmpty()) { - val cmd = callCommand.removeFirst() + val cmd = callCommand.removeFirstOrNull() Log.d(TAG, "WebRTCController LaunchedEffect executing $cmd") - processCommand(cmd) + if (cmd != null) { + processCommand(cmd) + } } } } diff --git a/apps/multiplatform/desktop/build.gradle.kts b/apps/multiplatform/desktop/build.gradle.kts index e39ba48a0b..60ff535e88 100644 --- a/apps/multiplatform/desktop/build.gradle.kts +++ b/apps/multiplatform/desktop/build.gradle.kts @@ -1,9 +1,10 @@ +import org.gradle.internal.extensions.stdlib.toDefaultLowerCase import org.jetbrains.compose.desktop.application.dsl.TargetFormat -import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly plugins { kotlin("multiplatform") id("org.jetbrains.compose") + id("org.jetbrains.kotlin.plugin.compose") id("io.github.tomtzook.gradle-cmake") version "1.2.2" } @@ -89,7 +90,7 @@ compose { } } } - val os = System.getProperty("os.name", "generic").toLowerCaseAsciiOnly() + val os = System.getProperty("os.name", "generic").toDefaultLowerCase() if (os.contains("mac") || os.contains("win")) { packageName = "SimpleX" } else { diff --git a/apps/multiplatform/gradle.properties b/apps/multiplatform/gradle.properties index 9c64695b00..078138c078 100644 --- a/apps/multiplatform/gradle.properties +++ b/apps/multiplatform/gradle.properties @@ -32,9 +32,9 @@ android.bundle=false desktop.version_name=6.4.3.1 desktop.version_code=117 -kotlin.version=1.9.23 -gradle.plugin.version=8.2.0 -compose.version=1.7.0 +kotlin.version=2.1.20 +gradle.plugin.version=8.7.0 +compose.version=1.8.2 # Choose sqlite or postgres backend database.backend=sqlite diff --git a/apps/multiplatform/gradle/wrapper/gradle-wrapper.properties b/apps/multiplatform/gradle/wrapper/gradle-wrapper.properties index 4e4a6a3f29..6183001f7b 100644 --- a/apps/multiplatform/gradle/wrapper/gradle-wrapper.properties +++ b/apps/multiplatform/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Feb 14 14:23:51 GMT 2022 +#Fri Mar 21 20:38:56 ICT 2025 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/apps/multiplatform/settings.gradle.kts b/apps/multiplatform/settings.gradle.kts index ba047edf1e..40446f1958 100644 --- a/apps/multiplatform/settings.gradle.kts +++ b/apps/multiplatform/settings.gradle.kts @@ -12,6 +12,7 @@ pluginManagement { id("com.android.application").version(extra["gradle.plugin.version"] as String) id("com.android.library").version(extra["gradle.plugin.version"] as String) id("org.jetbrains.compose").version(extra["compose.version"] as String) + id("org.jetbrains.kotlin.plugin.compose").version(extra["kotlin.version"] as String) id("org.jetbrains.kotlin.plugin.serialization").version(extra["kotlin.version"] as String) } }