From 94454fc4f118318b44a4854bc78c86b751dfddd9 Mon Sep 17 00:00:00 2001 From: Max Kahan Date: Wed, 21 Oct 2026 16:20:28 +0200 Subject: [PATCH] chore: bump Stream Chat Android to 7.13.0 Move every sample from Stream Chat Android 6.1.1 to 7.13.0 and update the toolchain v7 needs: Kotlin 2.2.21, AGP 8.13.2, Gradle 8.13, compileSdk 36 and navigation 2.8.4. The Compose sample now uses the Kotlin Compose compiler plugin instead of composeOptions. The Gradle heap goes to 4g because D8 ran out of memory at 2g with the larger v7 dependency graph. API moves, following the v6 to v7 migration guide: - The offline and state artifacts are gone. Clients are configured with ChatClientConfig(offlineEnabled = true, userPresence = true) instead of withPlugins(...). - XML: MessageListViewModelFactory -> ChannelViewModelFactory, MessageListHeaderView/ViewModel -> ChannelHeaderView/ViewModel (layout attrs streamUiMessageListHeader* -> streamUiChannelHeader*), MessageListActivity/Fragment -> ChannelActivity/Fragment, MessageListListenerContainer -> MessageListListeners, addSelectedAttachments -> addAttachments, and bindData now takes a non-null MessageListItemPayloadDiff. - Compose: AttachmentFactory and the attachmentFactories ChatTheme params are gone, so the date attachment rendering moves into a ChatComponentFactory (CustomAttachmentContent, MessageComposerAttachments, MessageQuotedContent, MessageComposerQuotedMessage, MessageComposerLeadingContent). MessagesViewModelFactory -> ChannelViewModelFactory, MessageListHeader -> ChannelHeader, the composer integrations slot -> onAttachmentsClick, ChannelsScreen onItemClick -> onChannelClick, CancelIcon -> ComposerCancelIcon, and theme tokens move to StreamDesign names. Co-Authored-By: Claude Opus 5.5 --- build.gradle | 1 + compose-custom-attachments/build.gradle | 11 +- .../customattachments/CustomAttachmentsApp.kt | 14 +- .../activity/ChannelsActivity.kt | 10 +- .../activity/MessagesActivity.kt | 15 +- .../ui/CustomChatComponentFactory.kt | 142 ++++++++++++++++++ .../ui/CustomMessagesScreen.kt | 59 +++----- .../ui/DateAttachmentFactory.kt | 42 ++---- .../ui/QuotedDateAttachmentFactory.kt | 33 ++-- .../build.gradle | 6 +- .../chat/android/customattachments/App.kt | 11 +- .../activity/MessagesActivity.kt | 20 +-- .../factory/DateAttachmentFactory.kt | 6 +- .../src/main/res/layout/activity_messages.xml | 6 +- custom-attachments-message-input/build.gradle | 6 +- .../chat/android/customattachments/App.kt | 11 +- .../activity/MessagesActivity.kt | 16 +- .../factory/DateAttachmentFactory.kt | 6 +- gradle.properties | 2 +- gradle/libs.versions.toml | 9 +- gradle/wrapper/gradle-wrapper.properties | 2 +- video-chat-sample/build.gradle | 4 +- virtual-event-sample/build.gradle | 6 +- .../chat/virtualevent/VirtualEventApp.kt | 11 +- .../feature/dm/DirectChatActivity.kt | 24 +-- .../event/detail/EventDetailsActivity.kt | 4 +- .../detail/LivestreamMessageItemVhFactory.kt | 2 +- .../main/res/layout/activity_direct_chat.xml | 12 +- whatsapp-clone-sample/build.gradle | 6 +- .../io/getstream/whatsappclone/WhatsApp.kt | 13 +- .../ui/messages/MessageListFragment.kt | 10 +- 31 files changed, 272 insertions(+), 248 deletions(-) create mode 100644 compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomChatComponentFactory.kt diff --git a/build.gradle b/build.gradle index 249e680f..6c8f001b 100644 --- a/build.gradle +++ b/build.gradle @@ -9,6 +9,7 @@ buildscript { dependencies { classpath libs.androidGradlePlugin classpath libs.kotlinGradlePlugin + classpath libs.composeCompilerGradlePlugin classpath libs.navigationSafeArgsGradlePlugin classpath libs.spotlessPlugin } diff --git a/compose-custom-attachments/build.gradle b/compose-custom-attachments/build.gradle index af502b43..210244c0 100644 --- a/compose-custom-attachments/build.gradle +++ b/compose-custom-attachments/build.gradle @@ -1,10 +1,11 @@ plugins { id 'com.android.application' id 'kotlin-android' + id 'org.jetbrains.kotlin.plugin.compose' } android { - compileSdk 34 + compileSdk 36 defaultConfig { applicationId "io.getstream.chat.android.compose.customattachments" @@ -24,9 +25,6 @@ android { buildFeatures { compose true } - composeOptions { - kotlinCompilerExtensionVersion libs.versions.composeCompiler.get() - } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -35,16 +33,13 @@ android { dependencies { // Stream SDK - implementation "io.getstream:stream-chat-android-compose:6.1.1" - implementation "io.getstream:stream-chat-android-state:6.1.1" - implementation "io.getstream:stream-chat-android-offline:6.1.1" + implementation "io.getstream:stream-chat-android-compose:7.13.0" implementation libs.androidxCoreKtx implementation libs.androidxAppCompat implementation libs.materialComponents // Compose - implementation libs.composeCompiler implementation libs.composeUi implementation libs.composeUiTooling implementation libs.composeFoundation diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/CustomAttachmentsApp.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/CustomAttachmentsApp.kt index 2940a968..bb0cd83e 100644 --- a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/CustomAttachmentsApp.kt +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/CustomAttachmentsApp.kt @@ -26,12 +26,10 @@ package io.getstream.chat.android.compose.customattachments import android.app.Application import io.getstream.chat.android.client.ChatClient +import io.getstream.chat.android.client.api.ChatClientConfig import io.getstream.chat.android.client.logger.ChatLogLevel import io.getstream.chat.android.models.UploadAttachmentsNetworkType import io.getstream.chat.android.models.User -import io.getstream.chat.android.offline.plugin.factory.StreamOfflinePluginFactory -import io.getstream.chat.android.state.plugin.config.StatePluginConfig -import io.getstream.chat.android.state.plugin.factory.StreamStatePluginFactory class CustomAttachmentsApp : Application() { override fun onCreate() { @@ -41,18 +39,10 @@ class CustomAttachmentsApp : Application() { } private fun setupStreamSdk() { - val offlinePluginFactory = StreamOfflinePluginFactory(appContext = applicationContext) - val statePluginFactory = StreamStatePluginFactory( - config = StatePluginConfig( - backgroundSyncEnabled = true, - userPresence = true, - ), - appContext = applicationContext, - ) ChatClient.Builder("qx5us2v6xvmh", applicationContext) .logLevel(ChatLogLevel.ALL) .uploadAttachmentsNetworkType(UploadAttachmentsNetworkType.NOT_ROAMING) - .withPlugins(offlinePluginFactory, statePluginFactory) + .config(ChatClientConfig(offlineEnabled = true, userPresence = true)) .build() } diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/ChannelsActivity.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/ChannelsActivity.kt index cb7ad3fc..56884e76 100644 --- a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/ChannelsActivity.kt +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/ChannelsActivity.kt @@ -27,8 +27,7 @@ package io.getstream.chat.android.compose.customattachments.activity import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity -import io.getstream.chat.android.compose.customattachments.ui.dateAttachmentFactory -import io.getstream.chat.android.compose.ui.attachments.StreamAttachmentFactories +import io.getstream.chat.android.compose.customattachments.ui.CustomChatComponentFactory import io.getstream.chat.android.compose.ui.channels.ChannelsScreen import io.getstream.chat.android.compose.ui.theme.ChatTheme @@ -37,13 +36,10 @@ class ChannelsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val customFactories = listOf(dateAttachmentFactory) - val defaultFactories = StreamAttachmentFactories.defaultFactories() - setContent { - ChatTheme(attachmentFactories = customFactories + defaultFactories) { + ChatTheme(componentFactory = CustomChatComponentFactory) { ChannelsScreen( - onItemClick = { channel -> + onChannelClick = { channel -> startActivity(MessagesActivity.getIntent(this, channel.cid)) }, onBackPressed = { finish() } diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/MessagesActivity.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/MessagesActivity.kt index b4eccbd0..3c28c380 100644 --- a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/MessagesActivity.kt +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/activity/MessagesActivity.kt @@ -29,10 +29,8 @@ import android.content.Intent import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity +import io.getstream.chat.android.compose.customattachments.ui.CustomChatComponentFactory import io.getstream.chat.android.compose.customattachments.ui.CustomMessagesScreen -import io.getstream.chat.android.compose.customattachments.ui.dateAttachmentFactory -import io.getstream.chat.android.compose.customattachments.ui.quotedDateAttachmentFactory -import io.getstream.chat.android.compose.ui.attachments.StreamAttachmentFactories import io.getstream.chat.android.compose.ui.theme.ChatTheme class MessagesActivity : AppCompatActivity() { @@ -41,17 +39,8 @@ class MessagesActivity : AppCompatActivity() { super.onCreate(savedInstanceState) val channelId = requireNotNull(intent.getStringExtra(KEY_CHANNEL_ID)) - val customFactories = listOf(dateAttachmentFactory) - val defaultFactories = StreamAttachmentFactories.defaultFactories() - - val customQuotedFactories = listOf(quotedDateAttachmentFactory) - val defaultQuotedFactories = StreamAttachmentFactories.defaultQuotedFactories() - setContent { - ChatTheme( - attachmentFactories = customFactories + defaultFactories, - quotedAttachmentFactories = customQuotedFactories + defaultQuotedFactories - ) { + ChatTheme(componentFactory = CustomChatComponentFactory) { CustomMessagesScreen( channelId = channelId, onBackPressed = { finish() } diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomChatComponentFactory.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomChatComponentFactory.kt new file mode 100644 index 00000000..e0a15105 --- /dev/null +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomChatComponentFactory.kt @@ -0,0 +1,142 @@ +/* + * The MIT License (MIT) + * + * Copyright 2022 Stream.IO, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package io.getstream.chat.android.compose.customattachments.ui + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import io.getstream.chat.android.compose.customattachments.R +import io.getstream.chat.android.compose.ui.components.ComposerCancelIcon +import io.getstream.chat.android.compose.ui.theme.ChatComponentFactory +import io.getstream.chat.android.compose.ui.theme.ChatTheme +import io.getstream.chat.android.compose.ui.theme.CustomAttachmentContentParams +import io.getstream.chat.android.compose.ui.theme.MessageComposerAttachmentsParams +import io.getstream.chat.android.compose.ui.theme.MessageComposerLeadingContentParams +import io.getstream.chat.android.compose.ui.theme.MessageComposerQuotedMessageParams +import io.getstream.chat.android.compose.ui.theme.MessageQuotedContentParams + +/** + * A custom [ChatComponentFactory] that adds support for date attachments. + * + * It renders date attachments in the message list, in the composer preview and in quoted + * messages, and replaces the composer's leading button with a calendar button. + */ +object CustomChatComponentFactory : ChatComponentFactory { + + @Composable + override fun CustomAttachmentContent(params: CustomAttachmentContentParams) { + if (params.state.message.attachments.any { it.type == "date" }) { + DateAttachmentContent( + modifier = params.modifier, + attachmentState = params.state + ) + } + } + + @Composable + override fun MessageComposerAttachments(params: MessageComposerAttachmentsParams) { + val (dateAttachments, otherAttachments) = params.attachments.partition { it.type == "date" } + + Column(modifier = params.modifier) { + if (dateAttachments.isNotEmpty()) { + DateAttachmentPreviewContent( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + attachments = dateAttachments, + onAttachmentRemoved = params.onAttachmentRemoved + ) + } + if (otherAttachments.isNotEmpty()) { + super.MessageComposerAttachments( + params = params.copy( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + attachments = otherAttachments + ) + ) + } + } + } + + @Composable + override fun MessageQuotedContent(params: MessageQuotedContentParams) { + if (params.message.attachments.any { it.type == "date" }) { + QuotedDateAttachmentContent( + modifier = params.modifier, + message = params.message + ) + } else { + super.MessageQuotedContent(params) + } + } + + @Composable + override fun MessageComposerQuotedMessage(params: MessageComposerQuotedMessageParams) { + if (params.quotedMessage.attachments.any { it.type == "date" }) { + Box(modifier = params.modifier.padding(horizontal = 8.dp)) { + QuotedDateAttachmentContent(message = params.quotedMessage) + + ComposerCancelIcon( + modifier = Modifier + .align(Alignment.TopEnd) + .offset(x = 4.dp, y = (-4).dp), + onClick = params.onCancelClick + ) + } + } else { + super.MessageComposerQuotedMessage(params) + } + } + + @Composable + override fun MessageComposerLeadingContent(params: MessageComposerLeadingContentParams) { + IconButton( + modifier = params.modifier + .size(48.dp) + .padding(12.dp), + content = { + Icon( + painter = painterResource(id = R.drawable.ic_calendar), + contentDescription = null, + tint = ChatTheme.colors.textSecondary + ) + }, + onClick = params.onAttachmentsClick + ) + } +} diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomMessagesScreen.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomMessagesScreen.kt index b4915e39..db3fbe8e 100644 --- a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomMessagesScreen.kt +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/CustomMessagesScreen.kt @@ -32,30 +32,25 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.material.Icon -import androidx.compose.material.IconButton import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.google.android.material.datepicker.MaterialDatePicker import io.getstream.chat.android.client.ChatClient -import io.getstream.chat.android.compose.customattachments.R -import io.getstream.chat.android.compose.ui.messages.MessagesScreen +import io.getstream.chat.android.compose.ui.messages.ChannelScreen import io.getstream.chat.android.compose.ui.messages.composer.MessageComposer -import io.getstream.chat.android.compose.ui.messages.header.MessageListHeader +import io.getstream.chat.android.compose.ui.messages.header.ChannelHeader import io.getstream.chat.android.compose.ui.messages.list.MessageList import io.getstream.chat.android.compose.ui.theme.ChatTheme +import io.getstream.chat.android.compose.viewmodel.messages.ChannelViewModelFactory import io.getstream.chat.android.compose.viewmodel.messages.MessageComposerViewModel import io.getstream.chat.android.compose.viewmodel.messages.MessageListViewModel -import io.getstream.chat.android.compose.viewmodel.messages.MessagesViewModelFactory import io.getstream.chat.android.models.Attachment import io.getstream.chat.android.ui.common.state.messages.MessageMode import io.getstream.chat.android.ui.common.state.messages.Reply @@ -63,7 +58,7 @@ import java.text.DateFormat import java.util.Date /** - * A custom [MessagesScreen] with the support for date attachments. + * A custom [ChannelScreen] with the support for date attachments. * * @param channelId The ID of the opened channel. * @param onBackPressed Handler for the back action. @@ -73,7 +68,7 @@ fun CustomMessagesScreen( channelId: String, onBackPressed: () -> Unit = {} ) { - val factory = MessagesViewModelFactory( + val factory = ChannelViewModelFactory( context = LocalContext.current, channelId = channelId ) @@ -91,7 +86,7 @@ fun CustomMessagesScreen( Scaffold( modifier = Modifier.fillMaxSize(), topBar = { - MessageListHeader( + ChannelHeader( modifier = Modifier.height(56.dp), channel = messageListViewModel.channel, currentUser = currentUser, @@ -107,12 +102,15 @@ fun CustomMessagesScreen( val date = DateFormat .getDateInstance(DateFormat.LONG) .format(Date(it)) + // fallback is the text shown for this attachment in message previews, + // such as the channel list val attachment = Attachment( type = "date", + fallback = date, extraData = mutableMapOf("payload" to date) ) - composerViewModel.addSelectedAttachments(listOf(attachment)) + composerViewModel.addAttachments(listOf(attachment)) } ) } @@ -120,7 +118,7 @@ fun CustomMessagesScreen( MessageList( modifier = Modifier .padding(it) - .background(ChatTheme.colors.appBackground) + .background(ChatTheme.colors.backgroundCoreApp) .fillMaxSize(), viewModel = messageListViewModel, onThreadClick = { message -> @@ -136,7 +134,8 @@ fun CustomMessagesScreen( } /** - * A custom [MessageComposer] with a button that shows [MaterialDatePicker]. + * A custom [MessageComposer] that shows [MaterialDatePicker] when the leading button is clicked. + * The calendar icon for that button is provided by [CustomChatComponentFactory]. * * @param viewModel The ViewModel that provides pieces of data to show in the composer, like the * currently selected integration data or the user input. It also handles sending messages. @@ -154,30 +153,16 @@ private fun CustomMessageComposer( .fillMaxWidth() .wrapContentHeight(), viewModel = viewModel, - integrations = { - IconButton( - modifier = Modifier - .size(48.dp) - .padding(12.dp), - content = { - Icon( - painter = painterResource(id = R.drawable.ic_calendar), - contentDescription = null, - tint = ChatTheme.colors.textLowEmphasis - ) - }, - onClick = { - MaterialDatePicker.Builder - .datePicker() - .build() - .apply { - show(activity.supportFragmentManager, null) - addOnPositiveButtonClickListener { - onDateSelected(it) - } - } + onAttachmentsClick = { + MaterialDatePicker.Builder + .datePicker() + .build() + .apply { + show(activity.supportFragmentManager, null) + addOnPositiveButtonClickListener { + onDateSelected(it) + } } - ) } ) } diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/DateAttachmentFactory.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/DateAttachmentFactory.kt index f5dec19d..561be221 100644 --- a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/DateAttachmentFactory.kt +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/DateAttachmentFactory.kt @@ -44,32 +44,10 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import io.getstream.chat.android.compose.customattachments.R import io.getstream.chat.android.compose.state.messages.attachments.AttachmentState -import io.getstream.chat.android.compose.ui.attachments.AttachmentFactory -import io.getstream.chat.android.compose.ui.components.CancelIcon +import io.getstream.chat.android.compose.ui.components.ComposerCancelIcon import io.getstream.chat.android.compose.ui.theme.ChatTheme import io.getstream.chat.android.models.Attachment -/** - * A custom [AttachmentFactory] that adds support for date attachments. - */ -val dateAttachmentFactory: AttachmentFactory = AttachmentFactory( - canHandle = { attachments -> attachments.any { it.type == "date" } }, - content = @Composable { modifier, attachmentState -> - DateAttachmentContent( - modifier = modifier, - attachmentState = attachmentState - ) - }, - previewContent = { modifier, attachments, onAttachmentRemoved -> - DateAttachmentPreviewContent( - modifier = modifier, - attachments = attachments, - onAttachmentRemoved = onAttachmentRemoved - ) - }, - textFormatter = { attachment -> attachment.extraData["payload"].toString() } -) - /** * Represents the UI shown in the message input preview before sending. * @@ -90,7 +68,7 @@ fun DateAttachmentPreviewContent( modifier = modifier .wrapContentHeight() .clip(RoundedCornerShape(16.dp)) - .background(color = ChatTheme.colors.barsBackground) + .background(color = ChatTheme.colors.backgroundCoreSurfaceDefault) ) { Text( modifier = Modifier @@ -98,12 +76,12 @@ fun DateAttachmentPreviewContent( .padding(16.dp) .fillMaxWidth(), text = formattedDate, - style = ChatTheme.typography.body, + style = ChatTheme.typography.bodyDefault, maxLines = 1, - color = ChatTheme.colors.textHighEmphasis + color = ChatTheme.colors.textPrimary ) - CancelIcon( + ComposerCancelIcon( modifier = Modifier .align(Alignment.TopEnd) .padding(4.dp), @@ -130,8 +108,8 @@ fun DateAttachmentContent( modifier = modifier .fillMaxWidth() .padding(4.dp) - .clip(ChatTheme.shapes.attachment) - .background(ChatTheme.colors.infoAccent) + .clip(RoundedCornerShape(12.dp)) + .background(ChatTheme.colors.accentSuccess) .padding(8.dp) ) { Row( @@ -142,14 +120,14 @@ fun DateAttachmentContent( modifier = Modifier.size(16.dp), painter = painterResource(id = R.drawable.ic_calendar), contentDescription = null, - tint = ChatTheme.colors.textHighEmphasis + tint = ChatTheme.colors.textPrimary ) Text( text = formattedDate, - style = ChatTheme.typography.body, + style = ChatTheme.typography.bodyDefault, maxLines = 1, - color = ChatTheme.colors.textHighEmphasis + color = ChatTheme.colors.textPrimary ) } } diff --git a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/QuotedDateAttachmentFactory.kt b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/QuotedDateAttachmentFactory.kt index 5eb2a6b4..35b23cce 100644 --- a/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/QuotedDateAttachmentFactory.kt +++ b/compose-custom-attachments/src/main/java/io/getstream/chat/android/compose/customattachments/ui/QuotedDateAttachmentFactory.kt @@ -28,6 +28,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -38,39 +39,25 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import io.getstream.chat.android.compose.customattachments.R -import io.getstream.chat.android.compose.state.messages.attachments.AttachmentState -import io.getstream.chat.android.compose.ui.attachments.AttachmentFactory import io.getstream.chat.android.compose.ui.theme.ChatTheme +import io.getstream.chat.android.models.Message import java.text.ParseException import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale -/** - * A custom [AttachmentFactory] that adds support for quoted date attachments. - */ -val quotedDateAttachmentFactory: AttachmentFactory = AttachmentFactory( - canHandle = { attachments -> attachments.any { it.type == "date" } }, - content = @Composable { modifier, attachmentState -> - QuotedDateAttachmentContent( - modifier = modifier, - attachmentState = attachmentState - ) - } -) - /** * Represents the UI shown in the message list for quoted date attachments. * - * @param attachmentState The state of the attachment. + * @param message The quoted message that contains the date attachment. * @param modifier Modifier for styling. */ @Composable fun QuotedDateAttachmentContent( - attachmentState: AttachmentState, + message: Message, modifier: Modifier = Modifier ) { - val attachment = attachmentState.message.attachments.first { it.type == "date" } + val attachment = message.attachments.first { it.type == "date" } val date = attachment.extraData["payload"].toString() val formattedDate = StringBuilder().apply { try { @@ -95,8 +82,8 @@ fun QuotedDateAttachmentContent( Column( modifier = modifier .padding(4.dp) - .clip(ChatTheme.shapes.attachment) - .background(ChatTheme.colors.infoAccent) + .clip(RoundedCornerShape(12.dp)) + .background(ChatTheme.colors.accentSuccess) .padding(8.dp) ) { Column( @@ -106,13 +93,13 @@ fun QuotedDateAttachmentContent( modifier = Modifier.size(16.dp), painter = painterResource(id = R.drawable.ic_calendar), contentDescription = null, - tint = ChatTheme.colors.textHighEmphasis + tint = ChatTheme.colors.textPrimary ) Text( text = formattedDate, - style = ChatTheme.typography.body, - color = ChatTheme.colors.textHighEmphasis, + style = ChatTheme.typography.bodyDefault, + color = ChatTheme.colors.textPrimary, textAlign = TextAlign.Center ) } diff --git a/custom-attachments-message-composer/build.gradle b/custom-attachments-message-composer/build.gradle index 26ad935f..ff9d51f3 100644 --- a/custom-attachments-message-composer/build.gradle +++ b/custom-attachments-message-composer/build.gradle @@ -4,7 +4,7 @@ plugins { } android { - compileSdk 34 + compileSdk 36 defaultConfig { applicationId "io.getstream.chat.android.customattachmentsv2" @@ -27,9 +27,7 @@ android { dependencies { // Stream SDK dependency - implementation "io.getstream:stream-chat-android-ui-components:6.1.1" - implementation "io.getstream:stream-chat-android-state:6.1.1" - implementation "io.getstream:stream-chat-android-offline:6.1.1" + implementation "io.getstream:stream-chat-android-ui-components:7.13.0" implementation libs.androidxCoreKtx implementation libs.androidxActivityKtx diff --git a/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/App.kt b/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/App.kt index 190ddbfa..14695dda 100644 --- a/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/App.kt +++ b/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/App.kt @@ -26,14 +26,12 @@ package io.getstream.chat.android.customattachments import android.app.Application import io.getstream.chat.android.client.ChatClient +import io.getstream.chat.android.client.api.ChatClientConfig import io.getstream.chat.android.client.logger.ChatLogLevel import io.getstream.chat.android.customattachments.factory.DateAttachmentFactory import io.getstream.chat.android.customattachments.factory.DateAttachmentPreviewFactory import io.getstream.chat.android.customattachments.factory.QuotedDateAttachmentFactory import io.getstream.chat.android.models.User -import io.getstream.chat.android.offline.plugin.factory.StreamOfflinePluginFactory -import io.getstream.chat.android.state.plugin.config.StatePluginConfig -import io.getstream.chat.android.state.plugin.factory.StreamStatePluginFactory import io.getstream.chat.android.ui.ChatUI import io.getstream.chat.android.ui.feature.messages.composer.attachment.preview.AttachmentPreviewFactoryManager import io.getstream.chat.android.ui.feature.messages.composer.attachment.preview.factory.FileAttachmentPreviewFactory @@ -49,14 +47,9 @@ class App : Application() { } private fun setupStreamSdk() { - val offlinePluginFactory = StreamOfflinePluginFactory(appContext = applicationContext) - val statePluginFactory = StreamStatePluginFactory( - config = StatePluginConfig(), - appContext = applicationContext, - ) ChatClient.Builder("qx5us2v6xvmh", applicationContext) .logLevel(ChatLogLevel.ALL) - .withPlugins(offlinePluginFactory, statePluginFactory) + .config(ChatClientConfig(offlineEnabled = true, userPresence = true)) .build() ChatUI.attachmentFactoryManager = AttachmentFactoryManager( diff --git a/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/activity/MessagesActivity.kt b/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/activity/MessagesActivity.kt index 1e5d44a6..ceba4371 100644 --- a/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/activity/MessagesActivity.kt +++ b/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/activity/MessagesActivity.kt @@ -47,10 +47,10 @@ import io.getstream.chat.android.ui.common.state.messages.composer.MessageCompos import io.getstream.chat.android.ui.feature.messages.composer.MessageComposerContext import io.getstream.chat.android.ui.feature.messages.composer.MessageComposerViewStyle import io.getstream.chat.android.ui.feature.messages.composer.content.MessageComposerContent +import io.getstream.chat.android.ui.viewmodel.messages.ChannelHeaderViewModel +import io.getstream.chat.android.ui.viewmodel.messages.ChannelViewModelFactory import io.getstream.chat.android.ui.viewmodel.messages.MessageComposerViewModel -import io.getstream.chat.android.ui.viewmodel.messages.MessageListHeaderViewModel import io.getstream.chat.android.ui.viewmodel.messages.MessageListViewModel -import io.getstream.chat.android.ui.viewmodel.messages.MessageListViewModelFactory import io.getstream.chat.android.ui.viewmodel.messages.bindView import java.text.DateFormat import java.util.Date @@ -58,10 +58,10 @@ import java.util.Date class MessagesActivity : AppCompatActivity() { private lateinit var binding: ActivityMessagesBinding - private val factory: MessageListViewModelFactory by lazy { - MessageListViewModelFactory(this, requireNotNull(intent.getStringExtra(EXTRA_CID))) + private val factory: ChannelViewModelFactory by lazy { + ChannelViewModelFactory(this, requireNotNull(intent.getStringExtra(EXTRA_CID))) } - private val messageListHeaderViewModel: MessageListHeaderViewModel by viewModels { factory } + private val channelHeaderViewModel: ChannelHeaderViewModel by viewModels { factory } private val messageListViewModel: MessageListViewModel by viewModels { factory } private val messageComposerViewModel: MessageComposerViewModel by viewModels { factory } @@ -71,18 +71,18 @@ class MessagesActivity : AppCompatActivity() { binding = ActivityMessagesBinding.inflate(layoutInflater) setContentView(binding.root) - messageListHeaderViewModel.bindView(binding.messageListHeaderView, this) + channelHeaderViewModel.bindView(binding.channelHeaderView, this) messageListViewModel.bindView(binding.messageListView, this) messageComposerViewModel.bindView(binding.messageComposerView, this) messageListViewModel.mode.observe(this) { when (it) { is MessageMode.MessageThread -> { - messageListHeaderViewModel.setActiveThread(it.parentMessage) + channelHeaderViewModel.setActiveThread(it.parentMessage) messageComposerViewModel.setMessageMode(MessageMode.MessageThread(it.parentMessage)) } is MessageMode.Normal -> { - messageListHeaderViewModel.resetThread() + channelHeaderViewModel.resetThread() messageComposerViewModel.leaveThread() } } @@ -103,7 +103,7 @@ class MessagesActivity : AppCompatActivity() { val backHandler = { messageListViewModel.onEvent(MessageListViewModel.Event.BackButtonPressed) } - binding.messageListHeaderView.setBackButtonClickListener(backHandler) + binding.channelHeaderView.setBackButtonClickListener(backHandler) onBackPressedDispatcher.addCallback(this) { backHandler() } @@ -130,7 +130,7 @@ class MessagesActivity : AppCompatActivity() { type = "date", extraData = mutableMapOf("payload" to date) ) - messageComposerViewModel.addSelectedAttachments(listOf(attachment)) + messageComposerViewModel.addAttachments(listOf(attachment)) } // Show the date picker dialog when the attachment button is clicked diff --git a/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/factory/DateAttachmentFactory.kt b/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/factory/DateAttachmentFactory.kt index bee455aa..b19cfdd3 100644 --- a/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/factory/DateAttachmentFactory.kt +++ b/custom-attachments-message-composer/src/main/java/io/getstream/chat/android/customattachments/factory/DateAttachmentFactory.kt @@ -28,7 +28,7 @@ import android.view.LayoutInflater import android.view.ViewGroup import io.getstream.chat.android.customattachments.databinding.ItemDateAttachmentBinding import io.getstream.chat.android.models.Message -import io.getstream.chat.android.ui.feature.messages.list.adapter.MessageListListenerContainer +import io.getstream.chat.android.ui.feature.messages.list.adapter.MessageListListeners import io.getstream.chat.android.ui.feature.messages.list.adapter.viewholder.attachment.AttachmentFactory import io.getstream.chat.android.ui.feature.messages.list.adapter.viewholder.attachment.InnerAttachmentViewHolder @@ -40,7 +40,7 @@ class DateAttachmentFactory : AttachmentFactory { override fun createViewHolder( message: Message, - listeners: MessageListListenerContainer?, + listeners: MessageListListeners?, parent: ViewGroup ): InnerAttachmentViewHolder { return ItemDateAttachmentBinding @@ -50,7 +50,7 @@ class DateAttachmentFactory : AttachmentFactory { class DateAttachmentViewHolder( private val binding: ItemDateAttachmentBinding, - listeners: MessageListListenerContainer? + listeners: MessageListListeners? ) : InnerAttachmentViewHolder(binding.root) { private lateinit var message: Message diff --git a/custom-attachments-message-composer/src/main/res/layout/activity_messages.xml b/custom-attachments-message-composer/src/main/res/layout/activity_messages.xml index 05fec8c8..2659e4f5 100644 --- a/custom-attachments-message-composer/src/main/res/layout/activity_messages.xml +++ b/custom-attachments-message-composer/src/main/res/layout/activity_messages.xml @@ -27,8 +27,8 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + app:layout_constraintTop_toBottomOf="@+id/channelHeaderView" /> { - messageListHeaderViewModel.setActiveThread(it.parentMessage) + channelHeaderViewModel.setActiveThread(it.parentMessage) } is MessageMode.Normal -> { - messageListHeaderViewModel.resetThread() + channelHeaderViewModel.resetThread() } } messageInputViewModel.setMessageMode(it) diff --git a/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/EventDetailsActivity.kt b/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/EventDetailsActivity.kt index 9519a655..9d2dbfce 100644 --- a/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/EventDetailsActivity.kt +++ b/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/EventDetailsActivity.kt @@ -33,9 +33,9 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.SimpleExoPlayer +import io.getstream.chat.android.ui.viewmodel.messages.ChannelViewModelFactory import io.getstream.chat.android.ui.viewmodel.messages.MessageComposerViewModel import io.getstream.chat.android.ui.viewmodel.messages.MessageListViewModel -import io.getstream.chat.android.ui.viewmodel.messages.MessageListViewModelFactory import io.getstream.chat.android.ui.viewmodel.messages.bindView import io.getstream.chat.virtualevent.databinding.ActivityEventDetailsBinding import io.getstream.chat.virtualevent.util.setupToolbar @@ -58,7 +58,7 @@ class EventDetailsActivity : AppCompatActivity() { setContentView(binding.root) val cid = intent.getStringExtra(KEY_EXTRA_CID)!! - val factory = MessageListViewModelFactory(this, cid = cid) + val factory = ChannelViewModelFactory(this, cid = cid) messageListViewModel = factory.create(MessageListViewModel::class.java) messageInputViewModel = factory.create(MessageComposerViewModel::class.java) diff --git a/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/LivestreamMessageItemVhFactory.kt b/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/LivestreamMessageItemVhFactory.kt index f3fce4a9..cfda9e63 100644 --- a/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/LivestreamMessageItemVhFactory.kt +++ b/virtual-event-sample/src/main/java/io/getstream/chat/virtualevent/feature/event/detail/LivestreamMessageItemVhFactory.kt @@ -55,7 +55,7 @@ class PlainTextViewHolder( ), ) : BaseMessageItemViewHolder(binding.root) { - override fun bindData(data: MessageListItem.MessageItem, diff: MessageListItemPayloadDiff?) { + override fun bindData(data: MessageListItem.MessageItem, diff: MessageListItemPayloadDiff) { binding.messageTextView.text = data.message.text binding.usernameTextView.text = data.message.user.name binding.avatarView.setUser(data.message.user) diff --git a/virtual-event-sample/src/main/res/layout/activity_direct_chat.xml b/virtual-event-sample/src/main/res/layout/activity_direct_chat.xml index 8b92797e..d6fa15f1 100644 --- a/virtual-event-sample/src/main/res/layout/activity_direct_chat.xml +++ b/virtual-event-sample/src/main/res/layout/activity_direct_chat.xml @@ -30,16 +30,16 @@ android:layout_height="match_parent" > - diff --git a/whatsapp-clone-sample/build.gradle b/whatsapp-clone-sample/build.gradle index 619b700d..fd659dad 100644 --- a/whatsapp-clone-sample/build.gradle +++ b/whatsapp-clone-sample/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: "androidx.navigation.safeargs.kotlin" android { - compileSdk 34 + compileSdk 36 defaultConfig { applicationId "io.getstream.whatsappclone" @@ -37,9 +37,7 @@ android { dependencies { // Stream SDK dependency - implementation "io.getstream:stream-chat-android-ui-components:6.1.1" - implementation "io.getstream:stream-chat-android-state:6.1.1" - implementation "io.getstream:stream-chat-android-offline:6.1.1" + implementation "io.getstream:stream-chat-android-ui-components:7.13.0" implementation libs.kotlinStdLib implementation libs.androidxViewPager2 diff --git a/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/WhatsApp.kt b/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/WhatsApp.kt index 0c915f7e..9c5d6b75 100644 --- a/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/WhatsApp.kt +++ b/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/WhatsApp.kt @@ -26,25 +26,16 @@ package io.getstream.whatsappclone import android.app.Application import io.getstream.chat.android.client.ChatClient +import io.getstream.chat.android.client.api.ChatClientConfig import io.getstream.chat.android.models.User -import io.getstream.chat.android.offline.plugin.factory.StreamOfflinePluginFactory -import io.getstream.chat.android.state.plugin.config.StatePluginConfig -import io.getstream.chat.android.state.plugin.factory.StreamStatePluginFactory class WhatsApp : Application() { override fun onCreate() { super.onCreate() - val offlinePluginFactory = StreamOfflinePluginFactory( - appContext = this - ) - val statePluginFactory = StreamStatePluginFactory( - config = StatePluginConfig(), - appContext = this, - ) ChatClient.Builder("qx5us2v6xvmh", this) - .withPlugins(offlinePluginFactory, statePluginFactory) + .config(ChatClientConfig(offlineEnabled = true, userPresence = true)) .build() val user = User( diff --git a/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/ui/messages/MessageListFragment.kt b/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/ui/messages/MessageListFragment.kt index 7a8c8882..c1e35287 100644 --- a/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/ui/messages/MessageListFragment.kt +++ b/whatsapp-clone-sample/src/main/kotlin/io/getstream/whatsappclone/ui/messages/MessageListFragment.kt @@ -38,10 +38,10 @@ import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import io.getstream.chat.android.client.ChatClient import io.getstream.chat.android.ui.ChatUI +import io.getstream.chat.android.ui.viewmodel.messages.ChannelHeaderViewModel +import io.getstream.chat.android.ui.viewmodel.messages.ChannelViewModelFactory import io.getstream.chat.android.ui.viewmodel.messages.MessageComposerViewModel -import io.getstream.chat.android.ui.viewmodel.messages.MessageListHeaderViewModel import io.getstream.chat.android.ui.viewmodel.messages.MessageListViewModel -import io.getstream.chat.android.ui.viewmodel.messages.MessageListViewModelFactory import io.getstream.chat.android.ui.viewmodel.messages.bindView import io.getstream.whatsappclone.R import io.getstream.whatsappclone.databinding.FragmentMessageListBinding @@ -50,8 +50,8 @@ class MessageListFragment : Fragment() { private val args: MessageListFragmentArgs by navArgs() - private val factory: MessageListViewModelFactory by lazy { MessageListViewModelFactory(requireContext(), args.cid) } - private val messageListHeaderViewModel: MessageListHeaderViewModel by viewModels { factory } + private val factory: ChannelViewModelFactory by lazy { ChannelViewModelFactory(requireContext(), args.cid) } + private val channelHeaderViewModel: ChannelHeaderViewModel by viewModels { factory } private val messageListViewModel: MessageListViewModel by viewModels { factory } private val messageInputViewModel: MessageComposerViewModel by viewModels { factory } @@ -101,7 +101,7 @@ class MessageListFragment : Fragment() { setDisplayShowTitleEnabled(false) } - messageListHeaderViewModel.channel.observe(viewLifecycleOwner) { channel -> + channelHeaderViewModel.channel.observe(viewLifecycleOwner) { channel -> val user = ChatClient.instance().clientState.user.value binding.channelNameTextView.text = ChatUI.channelNameFormatter.formatChannelName(channel, user)