diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Modifier.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Modifier.android.kt index 2ff2a3e021..2aa66bc69b 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Modifier.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Modifier.android.kt @@ -21,7 +21,7 @@ actual fun ProvideWindowInsets( actual fun Modifier.desktopOnExternalDrag( enabled: Boolean, onFiles: (List) -> Unit, - onImage: (Painter) -> Unit, + onImage: (File) -> Unit, onText: (String) -> Unit ): Modifier = this diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/PlatformTextField.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/PlatformTextField.android.kt index 0b17a3aadf..5365db6a4c 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/PlatformTextField.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/PlatformTextField.android.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.core.graphics.drawable.DrawableCompat +import androidx.core.view.children import androidx.core.view.inputmethod.EditorInfoCompat import androidx.core.view.inputmethod.InputConnectionCompat import androidx.core.widget.doAfterTextChanged @@ -94,8 +95,8 @@ actual fun PlatformTextField( } val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl - AndroidView(modifier = Modifier, factory = { - val editText = @SuppressLint("AppCompatCustomView") object: EditText(it) { + AndroidView(modifier = Modifier, factory = { context -> + val editText = @SuppressLint("AppCompatCustomView") object: EditText(context) { override fun setOnReceiveContentListener( mimeTypes: Array?, listener: OnReceiveContentListener? @@ -148,8 +149,12 @@ actual fun PlatformTextField( } } editText.doAfterTextChanged { text -> if (composeState.value.preview is ComposePreview.VoicePreview && text.toString() != "") editText.setText("") } - editText + val workaround = WorkaroundFocusSearchLayout(context) + workaround.addView(editText) + workaround.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + workaround }) { + val it = it.children.first() as EditText it.setTextColor(textColor.toArgb()) it.setHintTextColor(hintColor.toArgb()) it.hint = placeholder 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 3fc5620222..f3a9be6132 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 @@ -22,7 +22,6 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* -import androidx.compose.material.ripple.rememberRipple import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.snapshots.SnapshotStateList @@ -411,6 +410,7 @@ private fun ControlButton(icon: Painter, iconText: StringResource, enabled: Bool @Composable private fun ControlButtonWrap(enabled: Boolean = true, action: () -> Unit, background: Color = controlButtonsBackground(), size: Dp, content: @Composable () -> Unit) { + val ripple = remember { ripple(bounded = false, radius = size / 2, color = background.lighter(0.1f)) } Box( Modifier .background(background, CircleShape) @@ -419,7 +419,7 @@ private fun ControlButtonWrap(enabled: Boolean = true, action: () -> Unit, backg onClick = action, role = Role.Button, interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(bounded = false, radius = size / 2, color = background.lighter(0.1f)), + indication = ripple, enabled = enabled ), contentAlignment = Alignment.Center diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.android.kt index e0fd81f7b6..4681a5a64d 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.android.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.* -import androidx.compose.material.ripple.rememberRipple import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -40,8 +39,8 @@ actual fun ActiveCallInteractiveArea(call: Call) { val onClick = { platform.androidStartCallActivity(false) } Box(Modifier.offset(y = CALL_TOP_OFFSET).height(CALL_INTERACTIVE_AREA_HEIGHT)) { val source = remember { MutableInteractionSource() } - val indication = rememberRipple(bounded = true, 3000.dp) - Box(Modifier.height(CALL_TOP_GREEN_LINE_HEIGHT).clickable(onClick = onClick, indication = indication, interactionSource = source)) { + val ripple = remember { ripple(bounded = true, 3000.dp) } + Box(Modifier.height(CALL_TOP_GREEN_LINE_HEIGHT).clickable(onClick = onClick, indication = ripple, interactionSource = source)) { GreenLine(call) } Box( @@ -50,7 +49,7 @@ actual fun ActiveCallInteractiveArea(call: Call) { .size(CALL_BOTTOM_ICON_HEIGHT) .background(SimplexGreen, CircleShape) .clip(CircleShape) - .clickable(onClick = onClick, indication = indication, interactionSource = source) + .clickable(onClick = onClick, indication = ripple, interactionSource = source) .align(Alignment.BottomCenter), contentAlignment = Alignment.Center ) { diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/WorkaroundFocusSearchLayout.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/WorkaroundFocusSearchLayout.kt new file mode 100644 index 0000000000..d111b99385 --- /dev/null +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/WorkaroundFocusSearchLayout.kt @@ -0,0 +1,41 @@ +package chat.simplex.common.views.helpers + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.widget.FrameLayout + +/** + * A workaround for the ANR issue on Compose 1.7.x. + * https://issuetracker.google.com/issues/369354336 + * Code from: + * https://issuetracker.google.com/issues/369354336#comment8 +*/ +class WorkaroundFocusSearchLayout : FrameLayout { + + constructor( + context: Context, + ) : super(context) + + constructor( + context: Context, + attrs: AttributeSet?, + ) : super(context, attrs) + + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int, + ) : super(context, attrs, defStyleAttr) + + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int, + defStyleRes: Int, + ) : super(context, attrs, defStyleAttr, defStyleRes) + + override fun focusSearch(focused: View?, direction: Int): View? { + return null + } +} \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Modifier.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Modifier.kt index 6683ea7d33..5281fcb1ad 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Modifier.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Modifier.kt @@ -27,7 +27,7 @@ expect fun ProvideWindowInsets( expect fun Modifier.desktopOnExternalDrag( enabled: Boolean = true, onFiles: (List) -> Unit = {}, - onImage: (Painter) -> Unit = {}, + onImage: (File) -> Unit = {}, onText: (String) -> Unit = {} ): Modifier diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/WelcomeView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/WelcomeView.kt index f5dcc6b54a..0d5350dbe0 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/WelcomeView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/WelcomeView.kt @@ -289,6 +289,7 @@ fun ProfileNameField(name: MutableState, placeholder: String = "", isVal enabled = true, isError = false, interactionSource = remember { MutableInteractionSource() }, + colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.Unspecified) ) } ) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index d89782148a..570f763e99 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -642,14 +642,7 @@ fun ChatLayout( .desktopOnExternalDrag( enabled = remember(attachmentDisabled.value, chatInfo.value?.userCanSend) { mutableStateOf(!attachmentDisabled.value && chatInfo.value?.userCanSend == true) }.value, onFiles = { paths -> composeState.onFilesAttached(paths.map { it.toURI() }) }, - onImage = { - // TODO: file is not saved anywhere?! - val tmpFile = File.createTempFile("image", ".bmp", tmpDir) - tmpFile.deleteOnExit() - chatModel.filesToDelete.add(tmpFile) - val uri = tmpFile.toURI() - CoroutineScope(Dispatchers.IO).launch { composeState.processPickedMedia(listOf(uri), null) } - }, + onImage = { file -> CoroutineScope(Dispatchers.IO).launch { composeState.processPickedMedia(listOf(file.toURI()), null) } }, onText = { // Need to parse HTML in order to correctly display the content //composeState.value = composeState.value.copy(message = composeState.value.message + it) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SendMsgView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SendMsgView.kt index 162e753b18..76c4fc4a62 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SendMsgView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SendMsgView.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.* import androidx.compose.material.* -import androidx.compose.material.ripple.rememberRipple import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.* @@ -423,6 +422,7 @@ private fun SendMsgButton( onLongClick: (() -> Unit)? = null ) { val interactionSource = remember { MutableInteractionSource() } + val ripple = remember { ripple(bounded = false, radius = 24.dp) } Box( modifier = Modifier.requiredSize(36.dp) .combinedClickable( @@ -431,7 +431,7 @@ private fun SendMsgButton( enabled = enabled, role = Role.Button, interactionSource = interactionSource, - indication = rememberRipple(bounded = false, radius = 24.dp) + indication = ripple ) .onRightClick { onLongClick?.invoke() }, contentAlignment = Alignment.Center @@ -454,6 +454,7 @@ private fun SendMsgButton( @Composable private fun StartLiveMessageButton(enabled: Boolean, onClick: () -> Unit) { val interactionSource = remember { MutableInteractionSource() } + val ripple = remember { ripple(bounded = false, radius = 24.dp) } Box( modifier = Modifier.requiredSize(36.dp) .clickable( @@ -461,7 +462,7 @@ private fun StartLiveMessageButton(enabled: Boolean, onClick: () -> Unit) { enabled = enabled, role = Role.Button, interactionSource = interactionSource, - indication = rememberRipple(bounded = false, radius = 24.dp) + indication = ripple ), contentAlignment = Alignment.Center ) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultBasicTextField.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultBasicTextField.kt index a6f0d2c9b6..b0366cceb3 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultBasicTextField.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultBasicTextField.kt @@ -3,7 +3,6 @@ package chat.simplex.common.views.helpers import androidx.compose.foundation.background import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.shape.ZeroCornerSize import androidx.compose.foundation.text.* import androidx.compose.material.* @@ -22,13 +21,11 @@ import androidx.compose.ui.text.input.* import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.* import chat.simplex.common.views.database.PassphraseStrength -import chat.simplex.common.views.database.validKey import chat.simplex.res.MR import kotlinx.coroutines.delay import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch -@OptIn(ExperimentalComposeUiApi::class) @Composable fun DefaultBasicTextField( modifier: Modifier, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ExposedDropDownSettingRow.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ExposedDropDownSettingRow.kt index 8349841973..7ed91adbd9 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ExposedDropDownSettingRow.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ExposedDropDownSettingRow.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.* -import androidx.compose.material.ripple.rememberRipple import dev.icerock.moko.resources.compose.painterResource import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -107,6 +106,7 @@ fun ExposedDropDownSettingWithIcon( expanded.value = !expanded.value && enabled.value } ) { + val ripple = remember { ripple(bounded = false, radius = boxSize / 2, color = background.lighter(0.1f)) } Box( Modifier .background(background, CircleShape) @@ -115,7 +115,7 @@ fun ExposedDropDownSettingWithIcon( onClick = {}, role = Role.Button, interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(bounded = false, radius = boxSize / 2, color = background.lighter(0.1f)), + indication = ripple, enabled = enabled.value ), contentAlignment = Alignment.Center diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/TextEditor.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/TextEditor.kt index 45accccc59..ab7e562697 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/TextEditor.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/TextEditor.kt @@ -87,6 +87,7 @@ fun TextEditor( enabled = true, isError = false, interactionSource = remember { MutableInteractionSource() }, + colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.Unspecified) ) } ) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt index 8acddc2aa6..5298e11e75 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt @@ -616,6 +616,7 @@ fun LinkTextView(link: String, share: Boolean) { enabled = false, isError = false, interactionSource = remember { MutableInteractionSource() }, + colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.Unspecified) ) }) } diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Images.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Images.desktop.kt index e65adea70e..0f53adaf0b 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Images.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Images.desktop.kt @@ -206,3 +206,35 @@ fun BufferedImage.flip(vertically: Boolean, horizontally: Boolean): BufferedImag } return AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR).filter(this, null) } + +fun BufferedImage.saveInTmpFile(): File? { + val formats = arrayOf("jpg", "png") + for (format in formats) { + val tmpFile = File.createTempFile("image", ".$format", tmpDir) + try { + // May fail on JPG, using PNG as an alternative + val success = ImageIO.write(this, format, tmpFile) + if (success) { + tmpFile.deleteOnExit() + chatModel.filesToDelete.add(tmpFile) + return tmpFile + } else { + tmpFile.delete() + } + } catch (e: Exception) { + Log.e(TAG, e.stackTraceToString()) + tmpFile.delete() + return null + } + } + return null +} + +fun BufferedImage.hasAlpha(): Boolean { + for (x in 0 until width) { + for (y in 0 until height) { + if (getRGB(x, y) == 0) return true + } + } + return false +} diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Modifier.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Modifier.desktop.kt index 97f8bc129a..150885cbc8 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Modifier.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Modifier.desktop.kt @@ -1,10 +1,17 @@ package chat.simplex.common.platform import androidx.compose.foundation.contextMenuOpenDetector +import androidx.compose.foundation.draganddrop.dragAndDropTarget import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.* -import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.draganddrop.* +import androidx.compose.ui.draganddrop.DragData import androidx.compose.ui.input.pointer.* +import java.awt.Image +import java.awt.datatransfer.DataFlavor +import java.awt.datatransfer.Transferable +import java.awt.image.BufferedImage import java.io.File import java.net.URI @@ -23,16 +30,61 @@ actual fun ProvideWindowInsets( actual fun Modifier.desktopOnExternalDrag( enabled: Boolean, onFiles: (List) -> Unit, - onImage: (Painter) -> Unit, + onImage: (File) -> Unit, onText: (String) -> Unit -): Modifier = -onExternalDrag(enabled) { - when(val data = it.dragData) { - // data.readFiles() returns filePath in URI format (where spaces replaces with %20). But it's an error-prone idea to work later - // with such format when everywhere we use absolutePath in File() format - is DragData.FilesList -> onFiles(data.readFiles().map { URI.create(it).toFile() }) - is DragData.Image -> onImage(data.readImage()) - is DragData.Text -> onText(data.readText()) +): Modifier { + val callback = remember { + object : DragAndDropTarget { + override fun onDrop(event: DragAndDropEvent): Boolean { + when (val data = event.dragData()) { + // data.readFiles() returns filePath in URI format (where spaces replaces with %20). But it's an error-prone idea to work later + // with such format when everywhere we use absolutePath in File() format + is DragData.FilesList -> { + val files = data.readFiles() + // When dragging and dropping an image from browser, it comes to FilesList section but no files inside + if (files.isNotEmpty()) { + onFiles(files.map { URI.create(it).toFile() }) + } else { + try { + val transferable = event.awtTransferable + if (transferable.isDataFlavorSupported(DataFlavor.imageFlavor)) { + onImage(DragDataImageImpl(transferable).bufferedImage().saveInTmpFile() ?: return false) + } else { + return false + } + } catch (e: Exception) { + Log.e(TAG, e.stackTraceToString()) + return false + } + } + } + is DragData.Image -> onImage(DragDataImageImpl(event.awtTransferable).bufferedImage().saveInTmpFile() ?: return false) + is DragData.Text -> onText(data.readText()) + } + return true + } + } + } + return dragAndDropTarget(shouldStartDragAndDrop = { true }, target = callback) +} + +// Copied from AwtDragData and modified +private class DragDataImageImpl(private val transferable: Transferable) { + fun bufferedImage(): BufferedImage = (transferable.getTransferData(DataFlavor.imageFlavor) as Image).bufferedImage() + private fun Image.bufferedImage(): BufferedImage { + if (this is BufferedImage && hasAlpha()) { + // Such image cannot be drawn as JPG, only PNG + return this + } + // Creating non-transparent image which can be drawn as JPG + val bufferedImage = BufferedImage(getWidth(null), getHeight(null), BufferedImage.TYPE_INT_RGB) + val g2 = bufferedImage.createGraphics() + try { + g2.drawImage(this, 0, 0, null) + } finally { + g2.dispose() + } + return bufferedImage } } diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/PlatformTextField.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/PlatformTextField.desktop.kt index 5b0db7c94a..e37e99f3e9 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/PlatformTextField.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/PlatformTextField.desktop.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.input.key.* import androidx.compose.ui.platform.* @@ -109,7 +110,7 @@ actual fun PlatformTextField( maxLines = 16, keyboardOptions = KeyboardOptions.Default.copy( capitalization = KeyboardCapitalization.Sentences, - autoCorrect = true + autoCorrectEnabled = true ), modifier = Modifier .padding(vertical = 4.dp) @@ -193,6 +194,7 @@ actual fun PlatformTextField( interactionSource = remember { MutableInteractionSource() }, contentPadding = PaddingValues(), visualTransformation = VisualTransformation.None, + colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.Unspecified) ) Spacer(Modifier.height(10.dp)) } diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.desktop.kt index 189f1842dd..9789fa3d1a 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.desktop.kt @@ -10,20 +10,20 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.drawscope.ContentDrawScope +import androidx.compose.ui.node.DelegatableNode +import androidx.compose.ui.node.DrawModifierNode import androidx.compose.ui.unit.dp import chat.simplex.common.platform.onRightClick import chat.simplex.common.views.helpers.* -object NoIndication : Indication { - private object NoIndicationInstance : IndicationInstance { - override fun ContentDrawScope.drawIndication() { - drawContent() - } - } - @Composable - override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance { - return NoIndicationInstance +object NoIndication : IndicationNodeFactory { + // Should be as a class, not an object. Otherwise, crash + private class NoIndicationInstance : Modifier.Node(), DrawModifierNode { + override fun ContentDrawScope.draw() { drawContent() } } + override fun create(interactionSource: InteractionSource): DelegatableNode = NoIndicationInstance() + override fun hashCode(): Int = -1 + override fun equals(other: Any?) = other === this } @Composable diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/Utils.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/Utils.desktop.kt index 641ddb8744..d541a5780e 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/Utils.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/Utils.desktop.kt @@ -156,7 +156,9 @@ actual fun getFileSize(uri: URI): Long? = uri.toFile().length() actual fun getBitmapFromUri(uri: URI, withAlertOnException: Boolean): ImageBitmap? = try { - ImageIO.read(uri.inputStream()).toComposeImageBitmap() + uri.inputStream().use { + ImageIO.read(it).toComposeImageBitmap() + } } catch (e: Exception) { Log.e(TAG, "Error while decoding drawable: ${e.stackTraceToString()}") if (withAlertOnException) showImageDecodingException() diff --git a/apps/multiplatform/gradle.properties b/apps/multiplatform/gradle.properties index 32bd7579c8..d795257a76 100644 --- a/apps/multiplatform/gradle.properties +++ b/apps/multiplatform/gradle.properties @@ -21,8 +21,6 @@ kotlin.code.style=official # 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 -# Automatically convert third-party libraries to use AndroidX -android.enableJetifier=true kotlin.mpp.androidSourceSetLayoutVersion=2 kotlin.jvm.target=11 @@ -34,4 +32,4 @@ desktop.version_code=74 kotlin.version=1.9.23 gradle.plugin.version=8.2.0 -compose.version=1.6.1 +compose.version=1.7.0