diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e90dd91c0b..6732b81e4b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,11 +67,13 @@ ## stream-chat-android-compose ### 🐞 Fixed +- Fix keyboard not closing when opening the attachments picker from `MessagesScreen`. [#5506](https://github.com/GetStream/stream-chat-android/pull/5506) ### ⬆️ Improved ### ✅ Added - The `StreamAttachmentFactories.defaultFactories()` method now accepts a `skipTypes` parameter to skip specific factory types. [#5494](https://github.com/GetStream/stream-chat-android/pull/5494) +- Add `ChatTheme.keyboardBehaviour` property to customize different keyboard behaviours. [#5506](https://github.com/GetStream/stream-chat-android/pull/5506) ### ⚠️ Changed diff --git a/stream-chat-android-compose/api/stream-chat-android-compose.api b/stream-chat-android-compose/api/stream-chat-android-compose.api index 22fd831c6eb..a276b9eaa77 100644 --- a/stream-chat-android-compose/api/stream-chat-android-compose.api +++ b/stream-chat-android-compose/api/stream-chat-android-compose.api @@ -2026,6 +2026,7 @@ public final class io/getstream/chat/android/compose/ui/theme/ChatTheme { public final fun getColors (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/compose/ui/theme/StreamColors; public final fun getDateFormatter (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/ui/common/helper/DateFormatter; public final fun getDimens (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/compose/ui/theme/StreamDimens; + public final fun getKeyboardBehaviour (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/compose/ui/theme/StreamKeyboardBehaviour; public final fun getMessageAlignmentProvider (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/compose/ui/util/MessageAlignmentProvider; public final fun getMessageComposerTheme (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/compose/ui/theme/MessageComposerTheme; public final fun getMessageContentFactory (Landroidx/compose/runtime/Composer;I)Lio/getstream/chat/android/compose/ui/components/messages/factory/MessageContentFactory; @@ -2060,7 +2061,7 @@ public final class io/getstream/chat/android/compose/ui/theme/ChatTheme { } public final class io/getstream/chat/android/compose/ui/theme/ChatThemeKt { - public static final fun ChatTheme (ZZZZLio/getstream/chat/android/compose/ui/theme/StreamColors;Lio/getstream/chat/android/compose/ui/theme/StreamDimens;Lio/getstream/chat/android/compose/ui/theme/StreamTypography;Lio/getstream/chat/android/compose/ui/theme/StreamShapes;Lio/getstream/chat/android/compose/ui/theme/StreamRippleConfiguration;Ljava/util/List;Lio/getstream/chat/android/compose/ui/components/messages/factory/MessageContentFactory;Ljava/util/List;Ljava/util/List;Lio/getstream/chat/android/compose/ui/util/ReactionIconFactory;Lio/getstream/chat/android/compose/ui/theme/ReactionOptionsTheme;Lio/getstream/chat/android/compose/ui/util/MessagePreviewIconFactory;Lio/getstream/chat/android/compose/ui/util/PollSwitchItemFactory;ZLio/getstream/chat/android/ui/common/helper/DateFormatter;Lio/getstream/chat/android/ui/common/helper/TimeProvider;Lio/getstream/chat/android/ui/common/utils/ChannelNameFormatter;Lio/getstream/chat/android/compose/ui/util/MessagePreviewFormatter;Lio/getstream/chat/android/compose/ui/util/SearchResultNameFormatter;Lio/getstream/chat/android/compose/ui/util/StreamCoilImageLoaderFactory;Lio/getstream/chat/android/ui/common/helper/ImageHeadersProvider;Lio/getstream/chat/android/ui/common/helper/DownloadAttachmentUriGenerator;Lio/getstream/chat/android/ui/common/helper/DownloadRequestInterceptor;Lio/getstream/chat/android/ui/common/helper/ImageAssetTransformer;Lio/getstream/chat/android/compose/ui/util/MessageAlignmentProvider;Lio/getstream/chat/android/compose/ui/theme/MessageOptionsTheme;Lio/getstream/chat/android/ui/common/state/messages/list/MessageOptionsUserReactionAlignment;Ljava/util/List;ZLio/getstream/chat/android/ui/common/images/resizing/StreamCdnImageResizing;ZLio/getstream/chat/android/compose/ui/theme/MessageTheme;Lio/getstream/chat/android/compose/ui/theme/MessageTheme;Lio/getstream/chat/android/compose/ui/theme/MessageDateSeparatorTheme;Lio/getstream/chat/android/compose/ui/theme/MessageUnreadSeparatorTheme;Lio/getstream/chat/android/compose/ui/theme/MessageComposerTheme;Lio/getstream/chat/android/compose/ui/theme/AttachmentPickerTheme;Lio/getstream/chat/android/compose/ui/util/MessageTextFormatter;Lio/getstream/chat/android/compose/ui/util/QuotedMessageTextFormatter;Lio/getstream/sdk/chat/audio/recording/StreamMediaRecorder;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;IIIIIII)V + public static final fun ChatTheme (ZZZZLio/getstream/chat/android/compose/ui/theme/StreamColors;Lio/getstream/chat/android/compose/ui/theme/StreamDimens;Lio/getstream/chat/android/compose/ui/theme/StreamTypography;Lio/getstream/chat/android/compose/ui/theme/StreamShapes;Lio/getstream/chat/android/compose/ui/theme/StreamRippleConfiguration;Ljava/util/List;Lio/getstream/chat/android/compose/ui/components/messages/factory/MessageContentFactory;Ljava/util/List;Ljava/util/List;Lio/getstream/chat/android/compose/ui/util/ReactionIconFactory;Lio/getstream/chat/android/compose/ui/theme/ReactionOptionsTheme;Lio/getstream/chat/android/compose/ui/util/MessagePreviewIconFactory;Lio/getstream/chat/android/compose/ui/util/PollSwitchItemFactory;ZLio/getstream/chat/android/ui/common/helper/DateFormatter;Lio/getstream/chat/android/ui/common/helper/TimeProvider;Lio/getstream/chat/android/ui/common/utils/ChannelNameFormatter;Lio/getstream/chat/android/compose/ui/util/MessagePreviewFormatter;Lio/getstream/chat/android/compose/ui/util/SearchResultNameFormatter;Lio/getstream/chat/android/compose/ui/util/StreamCoilImageLoaderFactory;Lio/getstream/chat/android/ui/common/helper/ImageHeadersProvider;Lio/getstream/chat/android/ui/common/helper/DownloadAttachmentUriGenerator;Lio/getstream/chat/android/ui/common/helper/DownloadRequestInterceptor;Lio/getstream/chat/android/ui/common/helper/ImageAssetTransformer;Lio/getstream/chat/android/compose/ui/util/MessageAlignmentProvider;Lio/getstream/chat/android/compose/ui/theme/MessageOptionsTheme;Lio/getstream/chat/android/ui/common/state/messages/list/MessageOptionsUserReactionAlignment;Ljava/util/List;ZLio/getstream/chat/android/ui/common/images/resizing/StreamCdnImageResizing;ZLio/getstream/chat/android/compose/ui/theme/MessageTheme;Lio/getstream/chat/android/compose/ui/theme/MessageTheme;Lio/getstream/chat/android/compose/ui/theme/MessageDateSeparatorTheme;Lio/getstream/chat/android/compose/ui/theme/MessageUnreadSeparatorTheme;Lio/getstream/chat/android/compose/ui/theme/MessageComposerTheme;Lio/getstream/chat/android/compose/ui/theme/AttachmentPickerTheme;Lio/getstream/chat/android/compose/ui/util/MessageTextFormatter;Lio/getstream/chat/android/compose/ui/util/QuotedMessageTextFormatter;Lio/getstream/sdk/chat/audio/recording/StreamMediaRecorder;Lio/getstream/chat/android/compose/ui/theme/StreamKeyboardBehaviour;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;IIIIIII)V } public final class io/getstream/chat/android/compose/ui/theme/ComponentOffset { @@ -2616,6 +2617,23 @@ public final class io/getstream/chat/android/compose/ui/theme/StreamDimens$Compa public final fun defaultDimens ()Lio/getstream/chat/android/compose/ui/theme/StreamDimens; } +public final class io/getstream/chat/android/compose/ui/theme/StreamKeyboardBehaviour { + public static final field $stable I + public static final field Companion Lio/getstream/chat/android/compose/ui/theme/StreamKeyboardBehaviour$Companion; + public fun (Z)V + public final fun component1 ()Z + public final fun copy (Z)Lio/getstream/chat/android/compose/ui/theme/StreamKeyboardBehaviour; + public static synthetic fun copy$default (Lio/getstream/chat/android/compose/ui/theme/StreamKeyboardBehaviour;ZILjava/lang/Object;)Lio/getstream/chat/android/compose/ui/theme/StreamKeyboardBehaviour; + public fun equals (Ljava/lang/Object;)Z + public final fun getCloseKeyboardOnAttachmentPickerOpen ()Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class io/getstream/chat/android/compose/ui/theme/StreamKeyboardBehaviour$Companion { + public final fun defaultBehaviour ()Lio/getstream/chat/android/compose/ui/theme/StreamKeyboardBehaviour; +} + public final class io/getstream/chat/android/compose/ui/theme/StreamRippleConfiguration { public static final field $stable I public static final field Companion Lio/getstream/chat/android/compose/ui/theme/StreamRippleConfiguration$Companion; diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt index e67a2c9d07c..b851c6462d1 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt @@ -18,7 +18,6 @@ package io.getstream.chat.android.compose.ui.messages import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.core.AnimationConstants import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.tween @@ -43,6 +42,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -53,6 +53,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -443,7 +444,6 @@ public fun BoxScope.MessageMenus( * displayed as a link attachment. False by default. */ @Suppress("LongMethod") -@OptIn(ExperimentalAnimationApi::class) @Composable private fun BoxScope.MessagesScreenMenus( listViewModel: MessageListViewModel, @@ -578,7 +578,6 @@ private fun BoxScope.MessagesScreenMenus( * @param skipEnrichUrl If the message should skip enriching the URL. If URL is not enriched, it will not be * displayed as a link attachment. False by default. */ -@OptIn(ExperimentalAnimationApi::class) @Composable private fun BoxScope.MessagesScreenReactionsPicker( listViewModel: MessageListViewModel, @@ -646,6 +645,15 @@ public fun BoxScope.AttachmentsPickerMenu( ) { val isShowingAttachments = attachmentsPickerViewModel.isShowingAttachments + // Ensure keyboard is closed when the attachments picker is shown (if instructed by ChatTheme) + val keyboardController = LocalSoftwareKeyboardController.current + val shouldCloseKeyboard = ChatTheme.keyboardBehaviour.closeKeyboardOnAttachmentPickerOpen + LaunchedEffect(isShowingAttachments) { + if (shouldCloseKeyboard && isShowingAttachments) { + keyboardController?.hide() + } + } + AnimatedVisibility( visible = isShowingAttachments, enter = fadeIn(), diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt index 7f128e9be66..71708483d12 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt @@ -218,6 +218,9 @@ private val LocalComposerLinkPreviewEnabled = compositionLocalOf { private val LocalStreamMediaRecorder = compositionLocalOf { error("No StreamMediaRecorder provided! Make sure to wrap all usages of Stream components in a ChatTheme.") } +private val LocalKeyboardBehaviour = compositionLocalOf { + error("No StreamKeyboardBehaviour provided! Make sure to wrap all usages of Stream components in a ChatTheme.") +} /** * Our theme that provides all the important properties for styling to the user. @@ -265,8 +268,10 @@ private val LocalStreamMediaRecorder = compositionLocalOf { * @param messageComposerTheme Theme of the message composer. * @param attachmentPickerTheme Theme of the attachment picker. * @param streamMediaRecorder Used for recording audio messages. + * @param keyboardBehaviour Configuration for different keyboard behaviours. * @param content The content shown within the theme wrapper. */ +@Suppress("LongMethod") @Composable public fun ChatTheme( isInDarkMode: Boolean = isSystemInDarkTheme(), @@ -363,6 +368,7 @@ public fun ChatTheme( otherMessageTheme = otherMessageTheme, ), streamMediaRecorder: StreamMediaRecorder = DefaultStreamMediaRecorder(LocalContext.current), + keyboardBehaviour: StreamKeyboardBehaviour = StreamKeyboardBehaviour.defaultBehaviour(), content: @Composable () -> Unit, ) { LaunchedEffect(Unit) { @@ -412,6 +418,7 @@ public fun ChatTheme( LocalStreamMediaRecorder provides streamMediaRecorder, LocalAutoTranslationEnabled provides autoTranslationEnabled, LocalComposerLinkPreviewEnabled provides isComposerLinkPreviewEnabled, + LocalKeyboardBehaviour provides keyboardBehaviour, ) { if (allowUIAutomationTest) { Box( @@ -738,4 +745,12 @@ public object ChatTheme { @Composable @ReadOnlyComposable get() = LocalStreamMediaRecorder.current + + /** + * Retrieves the current [StreamKeyboardBehaviour] at the call site's position in the hierarchy. + */ + public val keyboardBehaviour: StreamKeyboardBehaviour + @Composable + @ReadOnlyComposable + get() = LocalKeyboardBehaviour.current } diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamKeyboardBehaviour.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamKeyboardBehaviour.kt new file mode 100644 index 00000000000..0b8b42558d1 --- /dev/null +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamKeyboardBehaviour.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014-2024 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getstream.chat.android.compose.ui.theme + +/** + * Class holding configuration for different keyboard behaviours. + * + * @param closeKeyboardOnAttachmentPickerOpen If true, the keyboard will be closed when the attachment picker is opened. + */ +public data class StreamKeyboardBehaviour( + val closeKeyboardOnAttachmentPickerOpen: Boolean, +) { + + public companion object { + + /** + * Builds the default keyboard behaviour. + * + * @return A [StreamKeyboardBehaviour] instance holding the default configuration. + */ + public fun defaultBehaviour(): StreamKeyboardBehaviour { + return StreamKeyboardBehaviour(closeKeyboardOnAttachmentPickerOpen = true) + } + } +}