From 8559beaec3223145da29a5e0ac72a7092c4436e3 Mon Sep 17 00:00:00 2001 From: MarinTolic Date: Wed, 14 Sep 2022 19:13:54 +0200 Subject: [PATCH 01/56] [3369] Duplicate `ImageAttachmentViewHolder`. The duplicate is renamed to `MediaAttachmentViewHolder` --- DEPRECATIONS.md | 1 + .../api/stream-chat-android-ui-components.api | 2 + .../MessageListItemViewHolderFactory.kt | 32 ++- .../list/adapter/MessageListItemViewType.kt | 1 + .../internal/MessageListItemViewTypeMapper.kt | 13 +- .../internal/MediaAttachmentsViewHolder.kt | 174 +++++++++++++++ ...tream_ui_item_message_media_attachment.xml | 202 ++++++++++++++++++ 7 files changed, 417 insertions(+), 8 deletions(-) create mode 100644 stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/internal/MediaAttachmentsViewHolder.kt create mode 100644 stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_message_media_attachment.xml diff --git a/DEPRECATIONS.md b/DEPRECATIONS.md index f73d19dd815..50011be0cd7 100644 --- a/DEPRECATIONS.md +++ b/DEPRECATIONS.md @@ -4,6 +4,7 @@ This document lists deprecated constructs in the SDK, with their expected time | API / Feature | Deprecated (warning) | Deprecated (error) | Removed | Notes | | --- | --- | --- | --- | --- | +| `MessageListItemViewHolderFactory.createImageAttachmentsViewHolder` | 2022.09.28
5.10.1 | 2022.10.12
5.10.1 | 2022.10.26
5.10.1 | The function `MessageListItemViewHolderFactory.createImageAttachmentsViewHolder` has been deprecated in favor of the function `MessageListItemViewHolderFactory.createMediaAttachmentsViewHolder` which returns the new `ViewHolder` capable of previewing both images and videos. | | `StreamDimens` constructor containing parameter `attachmentsContentImageHeight` | 2022.08.16
5.8.0 | 2022.08.30
5.9.0 | 2022.09.13
5.10.0 | This constructor has been deprecated. Use the constructor that does not contain the parameter `attachmentsContentImageHeight`. | | `QueryChannelsState.chatEventHandler` | 2022.08.16
5.8.0 | 2022.08.30
5.9.0 | 2022.09.13
5.10.0 | Use `QueryChannelsState.chatEventHandlerFactory` instead. | | Multiple event specific `BaseChatEventHandler` methods | 2022.08.16
5.8.0 | 2022.08.30
5.9.0 | 2022.09.13
5.10.0 | Use `handleChatEvent()` or `handleCidEvent()` instead. | diff --git a/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api b/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api index c8bb7e5f2cc..395cdf9e126 100644 --- a/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api +++ b/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api @@ -3279,6 +3279,7 @@ public class io/getstream/chat/android/ui/message/list/adapter/MessageListItemVi protected final fun createGiphyMessageItemViewHolder (Landroid/view/ViewGroup;)Lio/getstream/chat/android/ui/message/list/adapter/BaseMessageItemViewHolder; protected final fun createImageAttachmentsViewHolder (Landroid/view/ViewGroup;)Lio/getstream/chat/android/ui/message/list/adapter/BaseMessageItemViewHolder; protected final fun createLinkAttachmentsViewHolder (Landroid/view/ViewGroup;)Lio/getstream/chat/android/ui/message/list/adapter/BaseMessageItemViewHolder; + protected final fun createMediaAttachmentsViewHolder (Landroid/view/ViewGroup;)Lio/getstream/chat/android/ui/message/list/adapter/BaseMessageItemViewHolder; protected final fun createMessageDeletedViewHolder (Landroid/view/ViewGroup;)Lio/getstream/chat/android/ui/message/list/adapter/BaseMessageItemViewHolder; protected final fun createPlainTextViewHolder (Landroid/view/ViewGroup;)Lio/getstream/chat/android/ui/message/list/adapter/BaseMessageItemViewHolder; protected final fun createSystemMessageItemViewHolder (Landroid/view/ViewGroup;)Lio/getstream/chat/android/ui/message/list/adapter/BaseMessageItemViewHolder; @@ -3300,6 +3301,7 @@ public final class io/getstream/chat/android/ui/message/list/adapter/MessageList public static final field INSTANCE Lio/getstream/chat/android/ui/message/list/adapter/MessageListItemViewType; public static final field LINK_ATTACHMENTS I public static final field LOADING_INDICATOR I + public static final field MEDIA_ATTACHMENT I public static final field MESSAGE_DELETED I public static final field PLAIN_TEXT I public static final field SYSTEM_MESSAGE I diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/MessageListItemViewHolderFactory.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/MessageListItemViewHolderFactory.kt index e602624ef04..4f44d207bc0 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/MessageListItemViewHolderFactory.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/MessageListItemViewHolderFactory.kt @@ -29,9 +29,9 @@ import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.FILE_ATTACHMENTS import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.GIPHY import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.GIPHY_ATTACHMENT -import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.IMAGE_ATTACHMENT import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.LINK_ATTACHMENTS import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.LOADING_INDICATOR +import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.MEDIA_ATTACHMENT import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.MESSAGE_DELETED import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.PLAIN_TEXT import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.SYSTEM_MESSAGE @@ -49,6 +49,7 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.Gip import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.GiphyViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.ImageAttachmentViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.LinkAttachmentsViewHolder +import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MediaAttachmentsViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessageDeletedViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessagePlainTextViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.SystemMessageViewHolder @@ -167,7 +168,7 @@ public open class MessageListItemViewHolderFactory { LINK_ATTACHMENTS -> createLinkAttachmentsViewHolder(parentView) GIPHY_ATTACHMENT -> createGiphyAttachmentViewHolder(parentView) FILE_ATTACHMENTS -> createFileAttachmentsViewHolder(parentView) - IMAGE_ATTACHMENT -> createImageAttachmentsViewHolder(parentView) + MEDIA_ATTACHMENT -> createMediaAttachmentsViewHolder(parentView) else -> throw IllegalArgumentException("Unhandled MessageList view type: $viewType") } } @@ -214,6 +215,15 @@ public open class MessageListItemViewHolderFactory { * @param parentView The parent container. * @return The [BaseMessageItemViewHolder] that displays messages with image attachments. */ + @Deprecated( + message = "This function has been deprecated in favor of the function 'createMediaAttachmentsViewHolder'." + + "The new functions returns the new media 'ViewHolder' which is capable of previewing both image" + + "and video attachments.", + replaceWith = ReplaceWith( + "createMediaAttachmentsViewHolder(parentView = parentView)", + "this::createMediaAttachmentsViewHolder" + ) + ) protected fun createImageAttachmentsViewHolder( parentView: ViewGroup, ): BaseMessageItemViewHolder { @@ -225,6 +235,24 @@ public open class MessageListItemViewHolderFactory { ) } + /** + * Creates a ViewHolder for messages containing image and/ or video attachments and no other type + * of attachments. + * + * @param parentView The parent container. + * @return The [BaseMessageItemViewHolder] that displays messages with image and/or video attachments. + */ + protected fun createMediaAttachmentsViewHolder( + parentView: ViewGroup, + ): BaseMessageItemViewHolder { + return MediaAttachmentsViewHolder( + parentView, + decoratorProvider.decorators, + listenerContainer, + textTransformer, + ) + } + /** * Creates a date divider view holder. * diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/MessageListItemViewType.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/MessageListItemViewType.kt index b8aba4695d2..46491e94781 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/MessageListItemViewType.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/MessageListItemViewType.kt @@ -38,4 +38,5 @@ public object MessageListItemViewType { public const val IMAGE_ATTACHMENT: Int = OFFSET + 13 public const val FILE_ATTACHMENTS: Int = OFFSET + 14 public const val LINK_ATTACHMENTS: Int = OFFSET + 15 + public const val MEDIA_ATTACHMENT: Int = OFFSET + 16 } diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/internal/MessageListItemViewTypeMapper.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/internal/MessageListItemViewTypeMapper.kt index de162bc9de1..a891e403537 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/internal/MessageListItemViewTypeMapper.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/internal/MessageListItemViewTypeMapper.kt @@ -29,9 +29,9 @@ import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.FILE_ATTACHMENTS import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.GIPHY import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.GIPHY_ATTACHMENT -import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.IMAGE_ATTACHMENT import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.LINK_ATTACHMENTS import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.LOADING_INDICATOR +import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.MEDIA_ATTACHMENT import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.MESSAGE_DELETED import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.PLAIN_TEXT import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.SYSTEM_MESSAGE @@ -39,6 +39,7 @@ import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.THREAD_SEPARATOR import io.getstream.chat.android.ui.message.list.adapter.MessageListItemViewType.TYPING_INDICATOR import io.getstream.chat.android.ui.message.list.adapter.viewholder.attachment.AttachmentFactoryManager +import io.getstream.chat.android.uiutils.constant.AttachmentType import io.getstream.chat.android.uiutils.extension.hasLink import io.getstream.chat.android.uiutils.extension.isUploading @@ -82,19 +83,19 @@ internal object MessageListItemViewTypeMapper { message.isGiphyEphemeral() -> GIPHY containsGiphy -> GIPHY_ATTACHMENT containsOnlyLinks -> LINK_ATTACHMENTS - message.isImageAttachment() -> IMAGE_ATTACHMENT + message.isMediaAttachment() -> MEDIA_ATTACHMENT hasAttachments -> FILE_ATTACHMENTS else -> PLAIN_TEXT } } /** - * Checks if the message contains only image attachments (Can also optionally contain links). + * Checks if the message contains only image or video attachments (Can also optionally contain links). */ - private fun Message.isImageAttachment(): Boolean { + private fun Message.isMediaAttachment(): Boolean { return attachments.isNotEmpty() && - attachments.any { it.isImage() } && - attachments.all { it.isImage() || it.hasLink() } && + attachments.any { it.isImage() || it.type == AttachmentType.VIDEO } && + attachments.all { it.isImage() || it.type == AttachmentType.VIDEO || it.hasLink() } && attachments.none { it.isUploading() } } diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/internal/MediaAttachmentsViewHolder.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/internal/MediaAttachmentsViewHolder.kt new file mode 100644 index 00000000000..68387524bde --- /dev/null +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/internal/MediaAttachmentsViewHolder.kt @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2014-2022 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.ui.message.list.adapter.viewholder.internal + +import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.isVisible +import androidx.core.view.setPadding +import androidx.core.view.updateLayoutParams +import com.getstream.sdk.chat.adapter.MessageListItem +import io.getstream.chat.android.client.models.Attachment +import io.getstream.chat.android.ui.R +import io.getstream.chat.android.ui.common.extensions.internal.dpToPx +import io.getstream.chat.android.ui.common.extensions.internal.streamThemeInflater +import io.getstream.chat.android.ui.common.internal.LongClickFriendlyLinkMovementMethod +import io.getstream.chat.android.ui.databinding.StreamUiItemMessageMediaAttachmentBinding +import io.getstream.chat.android.ui.message.list.adapter.MessageListItemPayloadDiff +import io.getstream.chat.android.ui.message.list.adapter.MessageListListenerContainer +import io.getstream.chat.android.ui.message.list.adapter.internal.DecoratedBaseMessageItemViewHolder +import io.getstream.chat.android.ui.message.list.adapter.view.internal.AttachmentClickListener +import io.getstream.chat.android.ui.message.list.adapter.view.internal.AttachmentLongClickListener +import io.getstream.chat.android.ui.message.list.adapter.viewholder.decorator.internal.Decorator +import io.getstream.chat.android.ui.transformer.ChatMessageTextTransformer + +/** + * ViewHolder used for displaying messages that contain image and/ or video attachments. + * + * @param parent The parent container. + * @param decorators List of decorators applied to the ViewHolder. + * @param listeners Listeners used by the ViewHolder. + * @param messageTextTransformer Formats strings and sets them on the respective TextView. + * @param binding Binding generated for the layout. + */ +internal class MediaAttachmentsViewHolder( + parent: ViewGroup, + decorators: List, + private val listeners: MessageListListenerContainer?, + private val messageTextTransformer: ChatMessageTextTransformer, + internal val binding: StreamUiItemMessageMediaAttachmentBinding = StreamUiItemMessageMediaAttachmentBinding.inflate( + parent.streamThemeInflater, + parent, + false + ), +) : DecoratedBaseMessageItemViewHolder(binding.root, decorators) { + + /** + * Initializes the ViewHolder class. + */ + init { + initializeListeners() + setLinkMovementMethod() + } + + override fun bindData(data: MessageListItem.MessageItem, diff: MessageListItemPayloadDiff?) { + super.bindData(data, diff) + + bindMessageText() + bindHorizontalBias() + bindMediaAttachments(diff) + bindUploadingIndicator() + } + + /** + * Updates the text section of the message. + */ + private fun bindMessageText() { + binding.messageText.isVisible = data.message.text.isNotEmpty() + messageTextTransformer.transformAndApply(binding.messageText, data) + } + + /** + * Updates the horizontal bias of the message according to the owner + * of the message. + */ + private fun bindHorizontalBias() { + binding.messageContainer.updateLayoutParams { + this.horizontalBias = if (data.isMine) 1f else 0f + } + } + + /** + * Updates the media attachments section of the message. + */ + private fun bindMediaAttachments(diff: MessageListItemPayloadDiff?) { + if (diff?.attachments != false) { + binding.mediaAttachmentView.setPadding(1.dpToPx()) + binding.mediaAttachmentView.setupBackground(data) + binding.mediaAttachmentView.showAttachments(data.message.attachments) + } + } + + /** + * Update the uploading status section of the message. + */ + private fun bindUploadingIndicator() { + val totalAttachmentsCount = data.message.attachments.size + val completedAttachmentsCount = + data.message.attachments.count { it.uploadState == null || it.uploadState == Attachment.UploadState.Success } + if (completedAttachmentsCount == totalAttachmentsCount) { + binding.sentFiles.isVisible = false + } else { + binding.sentFiles.text = + context.getString( + R.string.stream_ui_message_list_attachment_uploading, + completedAttachmentsCount, + totalAttachmentsCount + ) + } + } + + /** + * Initializes listeners that enable handling clicks on various + * elements such as reactions, threads, message containers, etc. + */ + private fun initializeListeners() { + binding.run { + listeners?.let { container -> + messageContainer.setOnClickListener { + container.messageClickListener.onMessageClick(data.message) + } + reactionsView.setReactionClickListener { + container.reactionViewClickListener.onReactionViewClick(data.message) + } + footnote.setOnThreadClickListener { + container.threadClickListener.onThreadClick(data.message) + } + messageContainer.setOnLongClickListener { + container.messageLongClickListener.onMessageLongClick(data.message) + true + } + avatarView.setOnClickListener { + container.userClickListener.onUserClick(data.message.user) + } + mediaAttachmentView.attachmentClickListener = AttachmentClickListener { attachment -> + container.attachmentClickListener.onAttachmentClick(data.message, attachment) + } + mediaAttachmentView.attachmentLongClickListener = AttachmentLongClickListener { + container.messageLongClickListener.onMessageLongClick(data.message) + } + } + } + } + + /** + * Enables clicking on links. + */ + private fun setLinkMovementMethod() { + listeners?.let { container -> + LongClickFriendlyLinkMovementMethod.set( + textView = binding.messageText, + longClickTarget = binding.messageContainer, + onLinkClicked = container.linkClickListener::onLinkClick + ) + } + } + + override fun onAttachedToWindow() { + bindUploadingIndicator() + } +} diff --git a/stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_message_media_attachment.xml b/stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_message_media_attachment.xml new file mode 100644 index 00000000000..17f826e965c --- /dev/null +++ b/stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_message_media_attachment.xml @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 0f3ffb846aed5a59aa727828b2ac9ebb22ce175b Mon Sep 17 00:00:00 2001 From: MarinTolic Date: Wed, 14 Sep 2022 19:16:22 +0200 Subject: [PATCH 02/56] [3369] Appease detekt --- .../adapter/viewholder/internal/MediaAttachmentsViewHolder.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/internal/MediaAttachmentsViewHolder.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/internal/MediaAttachmentsViewHolder.kt index 68387524bde..1513c903029 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/internal/MediaAttachmentsViewHolder.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/internal/MediaAttachmentsViewHolder.kt @@ -109,7 +109,9 @@ internal class MediaAttachmentsViewHolder( private fun bindUploadingIndicator() { val totalAttachmentsCount = data.message.attachments.size val completedAttachmentsCount = - data.message.attachments.count { it.uploadState == null || it.uploadState == Attachment.UploadState.Success } + data.message.attachments.count { + it.uploadState == null || it.uploadState == Attachment.UploadState.Success + } if (completedAttachmentsCount == totalAttachmentsCount) { binding.sentFiles.isVisible = false } else { From 94cae9a66deae7f937f17f113f60293bb3636aba Mon Sep 17 00:00:00 2001 From: MarinTolic Date: Wed, 14 Sep 2022 19:27:55 +0200 Subject: [PATCH 03/56] [3369] Update deprecations and the changelog --- CHANGELOG.md | 2 ++ DEPRECATIONS.md | 2 +- .../ui/message/list/adapter/MessageListItemViewHolderFactory.kt | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09356ed0b12..479dd8abc1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,8 +60,10 @@ ### ⬆️ Improved ### ✅ Added +- Added the function `MessageListItemViewHolderFactory.createMediaAttachmentsViewHolder()` which returns a `ViewHolder` capable of previewing both image and video attachments. [#4158](https://github.com/GetStream/stream-chat-android/pull/4158) ### ⚠️ Changed +- The function `MessageListItemViewHolderFactory.createImageAttachmentsViewHolder()` has been deprecated in favor of the function `MessageListItemViewHolderFactory.createMediaAttachmentsViewHolder()` which returns a `ViewHolder` capable of previewing both images and videos. [#4158](https://github.com/GetStream/stream-chat-android/pull/4158) ### ❌ Removed diff --git a/DEPRECATIONS.md b/DEPRECATIONS.md index 50011be0cd7..7954c4fb0d9 100644 --- a/DEPRECATIONS.md +++ b/DEPRECATIONS.md @@ -4,7 +4,7 @@ This document lists deprecated constructs in the SDK, with their expected time | API / Feature | Deprecated (warning) | Deprecated (error) | Removed | Notes | | --- | --- | --- | --- | --- | -| `MessageListItemViewHolderFactory.createImageAttachmentsViewHolder` | 2022.09.28
5.10.1 | 2022.10.12
5.10.1 | 2022.10.26
5.10.1 | The function `MessageListItemViewHolderFactory.createImageAttachmentsViewHolder` has been deprecated in favor of the function `MessageListItemViewHolderFactory.createMediaAttachmentsViewHolder` which returns the new `ViewHolder` capable of previewing both images and videos. | +| `MessageListItemViewHolderFactory.createImageAttachmentsViewHolder()` | 2022.09.28
5.10.1 | 2022.10.12
5.10.1 | 2022.10.26
5.10.1 | The function `MessageListItemViewHolderFactory.createImageAttachmentsViewHolder()` has been deprecated in favor of the function `MessageListItemViewHolderFactory.createMediaAttachmentsViewHolder()` which returns a `ViewHolder` capable of previewing both images and videos. | | `StreamDimens` constructor containing parameter `attachmentsContentImageHeight` | 2022.08.16
5.8.0 | 2022.08.30
5.9.0 | 2022.09.13
5.10.0 | This constructor has been deprecated. Use the constructor that does not contain the parameter `attachmentsContentImageHeight`. | | `QueryChannelsState.chatEventHandler` | 2022.08.16
5.8.0 | 2022.08.30
5.9.0 | 2022.09.13
5.10.0 | Use `QueryChannelsState.chatEventHandlerFactory` instead. | | Multiple event specific `BaseChatEventHandler` methods | 2022.08.16
5.8.0 | 2022.08.30
5.9.0 | 2022.09.13
5.10.0 | Use `handleChatEvent()` or `handleCidEvent()` instead. | diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/MessageListItemViewHolderFactory.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/MessageListItemViewHolderFactory.kt index 4f44d207bc0..1df7b9a9df2 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/MessageListItemViewHolderFactory.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/MessageListItemViewHolderFactory.kt @@ -217,7 +217,7 @@ public open class MessageListItemViewHolderFactory { */ @Deprecated( message = "This function has been deprecated in favor of the function 'createMediaAttachmentsViewHolder'." + - "The new functions returns the new media 'ViewHolder' which is capable of previewing both image" + + "The new functions returns a 'ViewHolder' which is capable of previewing both image" + "and video attachments.", replaceWith = ReplaceWith( "createMediaAttachmentsViewHolder(parentView = parentView)", From a238717d01496c1ffdc717fd042bd1d1cf189a4c Mon Sep 17 00:00:00 2001 From: MarinTolic Date: Wed, 14 Sep 2022 20:22:10 +0200 Subject: [PATCH 04/56] [3369] Implement decorators for the new `MediaAttachmentsViewHolder` --- .../decorator/internal/AvatarDecorator.kt | 15 +++++++++++++++ .../decorator/internal/BackgroundDecorator.kt | 18 ++++++++++++++++++ .../decorator/internal/BaseDecorator.kt | 13 +++++++++++++ .../internal/FailedIndicatorDecorator.kt | 15 +++++++++++++++ .../decorator/internal/FootnoteDecorator.kt | 18 ++++++++++++++++++ .../decorator/internal/GapDecorator.kt | 12 ++++++++++++ .../internal/MaxPossibleWidthDecorator.kt | 13 +++++++++++++ .../MessageContainerMarginDecorator.kt | 14 ++++++++++++++ .../internal/PinIndicatorDecorator.kt | 14 ++++++++++++++ .../decorator/internal/ReactionsDecorator.kt | 14 ++++++++++++++ .../decorator/internal/ReplyDecorator.kt | 12 ++++++++++++ .../decorator/internal/TextDecorator.kt | 12 ++++++++++++ 12 files changed, 170 insertions(+) diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/AvatarDecorator.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/AvatarDecorator.kt index dcba83256d0..bd338f993fc 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/AvatarDecorator.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/AvatarDecorator.kt @@ -26,6 +26,7 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.Gip import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.GiphyViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.ImageAttachmentViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.LinkAttachmentsViewHolder +import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MediaAttachmentsViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessageDeletedViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessagePlainTextViewHolder @@ -89,6 +90,20 @@ internal class AvatarDecorator( setupAvatar(getAvatarView(viewHolder.binding.avatarMineView, viewHolder.binding.avatarView, data.isMine), data) } + /** + * Decorates the avatars of messages containing image and/ or video attachments, based on the message owner. + * + * @param viewHolder The holder to decorate. + * @param data The item that holds all the information. + */ + override fun decorateMediaAttachmentsMessage( + viewHolder: MediaAttachmentsViewHolder, + data: MessageListItem.MessageItem, + ) { + controlVisibility(viewHolder.binding.avatarMineView, viewHolder.binding.avatarView, data.isMine) + setupAvatar(getAvatarView(viewHolder.binding.avatarMineView, viewHolder.binding.avatarView, data.isMine), data) + } + /** * Decorates the avatar of the plain text message, based on the message owner. * diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/BackgroundDecorator.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/BackgroundDecorator.kt index 91d1b3913a3..a41f306f2c3 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/BackgroundDecorator.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/BackgroundDecorator.kt @@ -24,6 +24,7 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.Gip import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.GiphyViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.ImageAttachmentViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.LinkAttachmentsViewHolder +import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MediaAttachmentsViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessageDeletedViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessagePlainTextViewHolder import io.getstream.chat.android.ui.message.list.background.MessageBackgroundFactory @@ -100,6 +101,23 @@ internal class BackgroundDecorator( ) } + /** + * Decorates the backgrounds of messages containing image and/ or video attachments. + * + * @param viewHolder The holder to decorate. + * @param data The item that holds all the information. + */ + override fun decorateMediaAttachmentsMessage( + viewHolder: MediaAttachmentsViewHolder, + data: MessageListItem.MessageItem, + ) { + viewHolder.binding.messageContainer.background = + messageBackgroundFactory.imageAttachmentMessageBackground( + viewHolder.binding.messageContainer.context, + data + ) + } + /** * Decorates the background of the deleted message. * diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/BaseDecorator.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/BaseDecorator.kt index 888c193385f..554b70f2f22 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/BaseDecorator.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/BaseDecorator.kt @@ -26,6 +26,7 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.Gip import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.GiphyViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.ImageAttachmentViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.LinkAttachmentsViewHolder +import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MediaAttachmentsViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessageDeletedViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessagePlainTextViewHolder @@ -47,6 +48,7 @@ internal abstract class BaseDecorator : Decorator { is GiphyAttachmentViewHolder -> decorateGiphyAttachmentMessage(viewHolder, data) is FileAttachmentsViewHolder -> decorateFileAttachmentsMessage(viewHolder, data) is ImageAttachmentViewHolder -> decorateImageAttachmentsMessage(viewHolder, data) + is MediaAttachmentsViewHolder -> decorateMediaAttachmentsMessage(viewHolder, data) is DateDividerViewHolder -> Unit else -> Unit }.exhaustive @@ -96,6 +98,17 @@ internal abstract class BaseDecorator : Decorator { data: MessageListItem.MessageItem, ) + /** + * Applies various decorations to the [MediaAttachmentsViewHolder]. + * + * @param viewHolder The holder to be decorated. + * @param data The data used to define various decorations. + */ + abstract fun decorateMediaAttachmentsMessage( + viewHolder: MediaAttachmentsViewHolder, + data: MessageListItem.MessageItem, + ) + protected abstract fun decoratePlainTextMessage( viewHolder: MessagePlainTextViewHolder, data: MessageListItem.MessageItem, diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/FailedIndicatorDecorator.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/FailedIndicatorDecorator.kt index 7f135c5743d..573b38a37c4 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/FailedIndicatorDecorator.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/FailedIndicatorDecorator.kt @@ -27,6 +27,7 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.Gip import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.GiphyViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.ImageAttachmentViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.LinkAttachmentsViewHolder +import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MediaAttachmentsViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessageDeletedViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessagePlainTextViewHolder @@ -90,6 +91,20 @@ internal class FailedIndicatorDecorator( setupFailedIndicator(viewHolder.binding.deliveryFailedIcon, data) } + /** + * Decorates the visibility of the "failed" sections of messages containing + * image and/ or video attachments. + * + * @param viewHolder The holder to decorate. + * @param data The item that holds all the information. + */ + override fun decorateMediaAttachmentsMessage( + viewHolder: MediaAttachmentsViewHolder, + data: MessageListItem.MessageItem, + ) { + setupFailedIndicator(viewHolder.binding.deliveryFailedIcon, data) + } + /** * Decorates the visibility of the "failed" section of the plain text message. * diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/FootnoteDecorator.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/FootnoteDecorator.kt index d90af86f46c..5997311fbeb 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/FootnoteDecorator.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/FootnoteDecorator.kt @@ -45,6 +45,7 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.Gip import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.GiphyViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.ImageAttachmentViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.LinkAttachmentsViewHolder +import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MediaAttachmentsViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessageDeletedViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessagePlainTextViewHolder @@ -127,6 +128,23 @@ internal class FootnoteDecorator( data, ) + /** + * Decorates the footnotes of messages containing image and/ or video attachments. + * + * @param viewHolder The holder to decorate. + * @param data The item that holds all the information. + */ + override fun decorateMediaAttachmentsMessage( + viewHolder: MediaAttachmentsViewHolder, + data: MessageListItem.MessageItem, + ) = setupFootnote( + viewHolder.binding.footnote, + viewHolder.binding.root, + viewHolder.binding.threadGuideline, + viewHolder.binding.messageContainer, + data, + ) + /** * Decorates the footnote of the plain text message. * diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/GapDecorator.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/GapDecorator.kt index e9a5ded5fa8..66217d9a86b 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/GapDecorator.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/GapDecorator.kt @@ -24,6 +24,7 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.Gip import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.GiphyViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.ImageAttachmentViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.LinkAttachmentsViewHolder +import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MediaAttachmentsViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessageDeletedViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessagePlainTextViewHolder @@ -73,6 +74,17 @@ internal class GapDecorator : BaseDecorator() { data: MessageListItem.MessageItem, ) = setupGapView(viewHolder.binding.gapView, data) + /** + * Decorates the gap sections of messages containing image and/ or video attachments. + * + * @param viewHolder The holder to decorate. + * @param data The item that holds all the information. + */ + override fun decorateMediaAttachmentsMessage( + viewHolder: MediaAttachmentsViewHolder, + data: MessageListItem.MessageItem, + ) = setupGapView(viewHolder.binding.gapView, data) + override fun decorateDeletedMessage( viewHolder: MessageDeletedViewHolder, data: MessageListItem.MessageItem, diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/MaxPossibleWidthDecorator.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/MaxPossibleWidthDecorator.kt index 172d68e8965..b32f63698fc 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/MaxPossibleWidthDecorator.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/MaxPossibleWidthDecorator.kt @@ -25,6 +25,7 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.Gip import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.GiphyViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.ImageAttachmentViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.LinkAttachmentsViewHolder +import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MediaAttachmentsViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessageDeletedViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessagePlainTextViewHolder @@ -78,6 +79,18 @@ internal class MaxPossibleWidthDecorator(private val style: MessageListItemStyle data: MessageListItem.MessageItem, ) = applyMaxPossibleWidth(viewHolder.binding.marginStart, viewHolder.binding.marginEnd, data) + /** + * Decorates the maximum width of messages containing image and/ or video attachments, by changing + * the start and end margins of the container. + * + * @param viewHolder The holder to decorate. + * @param data The item that holds all the information. + */ + override fun decorateMediaAttachmentsMessage( + viewHolder: MediaAttachmentsViewHolder, + data: MessageListItem.MessageItem, + ) = applyMaxPossibleWidth(viewHolder.binding.marginStart, viewHolder.binding.marginEnd, data) + /** * Decorates the maximum width of the plain text message, by changing * the start and end margins of the container. diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/MessageContainerMarginDecorator.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/MessageContainerMarginDecorator.kt index 47d73f6b356..48c363b400a 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/MessageContainerMarginDecorator.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/MessageContainerMarginDecorator.kt @@ -27,6 +27,7 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.Gip import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.GiphyViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.ImageAttachmentViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.LinkAttachmentsViewHolder +import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MediaAttachmentsViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessageDeletedViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessagePlainTextViewHolder @@ -86,6 +87,19 @@ internal class MessageContainerMarginDecorator( viewHolder.binding.run { configMargins(messageContainer, footnote, style) } } + /** + * Decorates the message containers of messages containing image and/ or video attachments. + * + * @param viewHolder The holder to decorate. + * @param data The item that holds all the information. + */ + override fun decorateMediaAttachmentsMessage( + viewHolder: MediaAttachmentsViewHolder, + data: MessageListItem.MessageItem, + ) { + viewHolder.binding.run { configMargins(messageContainer, footnote, style) } + } + /** * Decorates the message container of the image attachments message. * diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/PinIndicatorDecorator.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/PinIndicatorDecorator.kt index aa770184d9c..87ed2d0e1fe 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/PinIndicatorDecorator.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/PinIndicatorDecorator.kt @@ -33,6 +33,7 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.Gip import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.GiphyViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.ImageAttachmentViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.LinkAttachmentsViewHolder +import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MediaAttachmentsViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessageDeletedViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessagePlainTextViewHolder @@ -94,6 +95,19 @@ internal class PinIndicatorDecorator(private val style: MessageListItemStyle) : setupPinIndicator(root, pinIndicatorTextView, data) } + /** + * Decorates the pin indicators of messages containing image and/ or video attachments. + * + * @param viewHolder The holder to decorate. + * @param data The item that holds all the information. + */ + override fun decorateMediaAttachmentsMessage( + viewHolder: MediaAttachmentsViewHolder, + data: MessageListItem.MessageItem, + ) = with(viewHolder.binding) { + setupPinIndicator(root, pinIndicatorTextView, data) + } + /** * Decorates the pin indicator of the plain text message. * diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/ReactionsDecorator.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/ReactionsDecorator.kt index 1effe92fede..847596e5771 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/ReactionsDecorator.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/ReactionsDecorator.kt @@ -35,6 +35,7 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.Gip import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.GiphyViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.ImageAttachmentViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.LinkAttachmentsViewHolder +import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MediaAttachmentsViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessageDeletedViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessagePlainTextViewHolder import io.getstream.chat.android.ui.message.list.reactions.view.internal.ViewReactionsView @@ -93,6 +94,19 @@ internal class ReactionsDecorator(private val style: MessageListItemStyle) : Bas setupReactionsView(root, messageContainer, reactionsSpace, reactionsView, data) } + /** + * Decorates the reactions sections of messages containing image and/ or video attachments. + * + * @param viewHolder The holder to decorate. + * @param data The item that holds all the information. + */ + override fun decorateMediaAttachmentsMessage( + viewHolder: MediaAttachmentsViewHolder, + data: MessageListItem.MessageItem, + ) = with(viewHolder.binding) { + setupReactionsView(root, messageContainer, reactionsSpace, reactionsView, data) + } + /** * Decorates the reactions section of the plain text message. * diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/ReplyDecorator.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/ReplyDecorator.kt index 7e53db9ae24..2d2b39fca76 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/ReplyDecorator.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/ReplyDecorator.kt @@ -26,6 +26,7 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.Gip import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.GiphyViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.ImageAttachmentViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.LinkAttachmentsViewHolder +import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MediaAttachmentsViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessagePlainTextViewHolder internal class ReplyDecorator( @@ -76,6 +77,17 @@ internal class ReplyDecorator( data: MessageListItem.MessageItem, ) = setupReplyView(viewHolder.binding.replyView, data) + /** + * Decorates the reply sections of messages containing image and/ or video attachments. + * + * @param viewHolder The holder to decorate. + * @param data The item that holds all the information. + */ + override fun decorateMediaAttachmentsMessage( + viewHolder: MediaAttachmentsViewHolder, + data: MessageListItem.MessageItem, + ) = setupReplyView(viewHolder.binding.replyView, data) + /** * Decorates the reply section of the plain text message. * diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/TextDecorator.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/TextDecorator.kt index b1842325e00..9dcf4e89288 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/TextDecorator.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/TextDecorator.kt @@ -26,6 +26,7 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.Gip import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.GiphyViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.ImageAttachmentViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.LinkAttachmentsViewHolder +import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MediaAttachmentsViewHolder import io.getstream.chat.android.ui.message.list.adapter.viewholder.internal.MessagePlainTextViewHolder internal class TextDecorator(private val style: MessageListItemStyle) : BaseDecorator() { @@ -74,6 +75,17 @@ internal class TextDecorator(private val style: MessageListItemStyle) : BaseDeco data: MessageListItem.MessageItem, ) = setupTextView(viewHolder.binding.messageText, data) + /** + * Decorates the texts of messages containing image and/ or video attachments. + * + * @param viewHolder The holder to decorate. + * @param data The item that holds all the information. + */ + override fun decorateMediaAttachmentsMessage( + viewHolder: MediaAttachmentsViewHolder, + data: MessageListItem.MessageItem, + ) = setupTextView(viewHolder.binding.messageText, data) + /** * Decorates the text of the plain text message. * From 9001f6b2bd60cf25133a2ab1fdce0ab9ce110daf Mon Sep 17 00:00:00 2001 From: MarinTolic Date: Tue, 4 Oct 2022 18:34:57 +0200 Subject: [PATCH 05/56] [3369] Switch to using the new user avatar. --- .../viewholder/decorator/internal/AvatarDecorator.kt | 4 ++-- .../viewholder/internal/MediaAttachmentsViewHolder.kt | 2 +- .../layout/stream_ui_item_message_media_attachment.xml | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/AvatarDecorator.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/AvatarDecorator.kt index 21cb74b11a0..c069db40c8d 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/AvatarDecorator.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/decorator/internal/AvatarDecorator.kt @@ -100,8 +100,8 @@ internal class AvatarDecorator( viewHolder: MediaAttachmentsViewHolder, data: MessageListItem.MessageItem, ) { - controlVisibility(viewHolder.binding.avatarMineView, viewHolder.binding.avatarView, data.isMine) - setupAvatar(getAvatarView(viewHolder.binding.avatarMineView, viewHolder.binding.avatarView, data.isMine), data) + controlVisibility(viewHolder.binding.userAvatarMineView, viewHolder.binding.userAvatarView, data.isMine) + setupAvatar(getAvatarView(viewHolder.binding.userAvatarMineView, viewHolder.binding.userAvatarView, data.isMine), data) } /** diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/internal/MediaAttachmentsViewHolder.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/internal/MediaAttachmentsViewHolder.kt index 1513c903029..677f9bbeb25 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/internal/MediaAttachmentsViewHolder.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/viewholder/internal/MediaAttachmentsViewHolder.kt @@ -144,7 +144,7 @@ internal class MediaAttachmentsViewHolder( container.messageLongClickListener.onMessageLongClick(data.message) true } - avatarView.setOnClickListener { + userAvatarView.setOnClickListener { container.userClickListener.onUserClick(data.message.user) } mediaAttachmentView.attachmentClickListener = AttachmentClickListener { attachment -> diff --git a/stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_message_media_attachment.xml b/stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_message_media_attachment.xml index 17f826e965c..a3dafb615c4 100644 --- a/stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_message_media_attachment.xml +++ b/stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_message_media_attachment.xml @@ -115,8 +115,8 @@ app:layout_constraintTop_toTopOf="@id/reactionsSpace" /> - - Date: Wed, 5 Oct 2022 18:28:18 +0200 Subject: [PATCH 06/56] [3369] Implemented the ability to display video thumbnails inside the message list. --- CHANGELOG.md | 1 + .../api/stream-chat-android-ui-components.api | 8 +- .../io/getstream/chat/android/ui/ChatUI.kt | 5 + .../chat/android/ui/TransformStyle.kt | 4 +- ...ewStyle.kt => MediaAttachmentViewStyle.kt} | 12 +- ...tachmentView.kt => MediaAttachmentView.kt} | 63 ++++---- ...upView.kt => MediaAttachmentsGroupView.kt} | 145 ++++++++++-------- .../stream_ui_item_image_attachment.xml | 2 +- ...tream_ui_item_message_media_attachment.xml | 2 +- ...ml => stream_ui_media_attachment_view.xml} | 0 .../src/main/res/values-night/colors.xml | 2 + .../src/main/res/values/colors.xml | 3 +- 12 files changed, 138 insertions(+), 109 deletions(-) rename stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/{ImageAttachmentViewStyle.kt => MediaAttachmentViewStyle.kt} (94%) rename stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/{ImageAttachmentView.kt => MediaAttachmentView.kt} (71%) rename stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/{ImageAttachmentsGroupView.kt => MediaAttachmentsGroupView.kt} (65%) rename stream-chat-android-ui-components/src/main/res/layout/{stream_ui_image_attachment_view.xml => stream_ui_media_attachment_view.xml} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index bacdec59ae9..f45d8701e21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,7 @@ ### ⚠️ Changed - The function `MessageListItemViewHolderFactory.createImageAttachmentsViewHolder()` has been deprecated in favor of the function `MessageListItemViewHolderFactory.createMediaAttachmentsViewHolder()` which returns a `ViewHolder` capable of previewing both images and videos. [#4158](https://github.com/GetStream/stream-chat-android/pull/4158) +- `ImageAttachmentViewStyle` has been replaced by `MediaAttachmentViewStyle`. The new style controls how previews of both image and video attachments are displayed inside the message list. [#4158](https://github.com/GetStream/stream-chat-android/pull/4158) ### ❌ Removed - Removed `AvatarView` in favor of `UserAvatarView` and `ChannelAvatarView` to keep consistency with the Compose UI SDK. [#4165](https://github.com/GetStream/stream-chat-android/pull/4165) diff --git a/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api b/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api index 51f4b84cd2e..756ec3d75d6 100644 --- a/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api +++ b/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api @@ -641,6 +641,7 @@ public final class io/getstream/chat/android/ui/ChatUI { public final fun getQuotedAttachmentFactoryManager ()Lio/getstream/chat/android/ui/message/list/adapter/viewholder/attachment/QuotedAttachmentFactoryManager; public final fun getStyle ()Lio/getstream/chat/android/ui/common/style/ChatStyle; public final fun getSupportedReactions ()Lio/getstream/chat/android/ui/SupportedReactions; + public final fun getVideoThumbnailsEnabled ()Z public final fun setAttachmentFactoryManager (Lio/getstream/chat/android/ui/message/list/adapter/viewholder/attachment/AttachmentFactoryManager;)V public final fun setAttachmentPreviewFactoryManager (Lio/getstream/chat/android/ui/message/composer/attachment/AttachmentPreviewFactoryManager;)V public final fun setChannelNameFormatter (Lio/getstream/chat/android/ui/common/ChannelNameFormatter;)V @@ -655,6 +656,7 @@ public final class io/getstream/chat/android/ui/ChatUI { public final fun setQuotedAttachmentFactoryManager (Lio/getstream/chat/android/ui/message/list/adapter/viewholder/attachment/QuotedAttachmentFactoryManager;)V public final fun setStyle (Lio/getstream/chat/android/ui/common/style/ChatStyle;)V public final fun setSupportedReactions (Lio/getstream/chat/android/ui/SupportedReactions;)V + public final fun setVideoThumbnailsEnabled (Z)V } public final class io/getstream/chat/android/ui/CurrentUserProvider$Companion { @@ -3292,15 +3294,15 @@ public final class io/getstream/chat/android/ui/message/list/adapter/view/GiphyM public final class io/getstream/chat/android/ui/message/list/adapter/view/GiphyMediaAttachmentViewStyle$Companion { } -public final class io/getstream/chat/android/ui/message/list/adapter/view/ImageAttachmentViewStyle { +public final class io/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle { public fun (Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IILio/getstream/chat/android/ui/common/style/TextStyle;)V public final fun component1 ()Landroid/graphics/drawable/Drawable; public final fun component2 ()Landroid/graphics/drawable/Drawable; public final fun component3 ()I public final fun component4 ()I public final fun component5 ()Lio/getstream/chat/android/ui/common/style/TextStyle; - public final fun copy (Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IILio/getstream/chat/android/ui/common/style/TextStyle;)Lio/getstream/chat/android/ui/message/list/adapter/view/ImageAttachmentViewStyle; - public static synthetic fun copy$default (Lio/getstream/chat/android/ui/message/list/adapter/view/ImageAttachmentViewStyle;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IILio/getstream/chat/android/ui/common/style/TextStyle;ILjava/lang/Object;)Lio/getstream/chat/android/ui/message/list/adapter/view/ImageAttachmentViewStyle; + public final fun copy (Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IILio/getstream/chat/android/ui/common/style/TextStyle;)Lio/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle; + public static synthetic fun copy$default (Lio/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IILio/getstream/chat/android/ui/common/style/TextStyle;ILjava/lang/Object;)Lio/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle; public fun equals (Ljava/lang/Object;)Z public final fun getImageBackgroundColor ()I public final fun getMoreCountOverlayColor ()I diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/ChatUI.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/ChatUI.kt index 997301117a2..6d1d696a094 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/ChatUI.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/ChatUI.kt @@ -127,4 +127,9 @@ public object ChatUI { * Provides the currently logged in user. */ public var currentUserProvider: CurrentUserProvider = CurrentUserProvider.defaultCurrentUserProvider() + + /** + * Whether thumbnails for video attachments will be displayed in previews. + */ + public var videoThumbnailsEnabled: Boolean = true } diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/TransformStyle.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/TransformStyle.kt index 040cacb4a06..fab25f582af 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/TransformStyle.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/TransformStyle.kt @@ -31,7 +31,7 @@ import io.getstream.chat.android.ui.message.list.MessageListItemStyle import io.getstream.chat.android.ui.message.list.MessageListViewStyle import io.getstream.chat.android.ui.message.list.MessageReplyStyle import io.getstream.chat.android.ui.message.list.ScrollButtonViewStyle -import io.getstream.chat.android.ui.message.list.adapter.view.ImageAttachmentViewStyle +import io.getstream.chat.android.ui.message.list.adapter.view.MediaAttachmentViewStyle import io.getstream.chat.android.ui.message.list.header.MessageListHeaderViewStyle import io.getstream.chat.android.ui.message.list.reactions.edit.EditReactionsViewStyle import io.getstream.chat.android.ui.message.list.reactions.user.SingleReactionViewStyle @@ -54,7 +54,7 @@ public object TransformStyle { public var singleReactionViewStyleTransformer: StyleTransformer = noopTransformer() public var channelActionsDialogStyleTransformer: StyleTransformer = noopTransformer() public var giphyViewHolderStyleTransformer: StyleTransformer = noopTransformer() - public var imageAttachmentStyleTransformer: StyleTransformer = noopTransformer() + public var imageAttachmentStyleTransformer: StyleTransformer = noopTransformer() public var messageReplyStyleTransformer: StyleTransformer = noopTransformer() public var fileAttachmentStyleTransformer: StyleTransformer = noopTransformer() public var suggestionListStyleTransformer: StyleTransformer = noopTransformer() diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/ImageAttachmentViewStyle.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle.kt similarity index 94% rename from stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/ImageAttachmentViewStyle.kt rename to stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle.kt index 9264932de15..38f672c1439 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/ImageAttachmentViewStyle.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle.kt @@ -30,16 +30,16 @@ import io.getstream.chat.android.ui.common.extensions.internal.use import io.getstream.chat.android.ui.common.style.TextStyle /** - * Style for [io.getstream.chat.android.ui.message.list.adapter.view.internal.ImageAttachmentView]. + * Style for [io.getstream.chat.android.ui.message.list.adapter.view.internal.MediaAttachmentView]. * Use this class together with [TransformStyle.imageAttachmentStyleTransformer] to change styles programmatically. * * @param progressIcon Animated progress drawable. Default value is [R.drawable.stream_ui_rotating_indeterminate_progress_gradient]. - * @param placeholderIcon Displayed while the image is Loading. + * @param placeholderIcon Displayed while the media preview is Loading. * @param imageBackgroundColor Image background. Default value is [R.color.stream_ui_grey]. * @param moreCountOverlayColor More count semi-transparent overlay color. Default value is [R.color.stream_ui_overlay]. * @param moreCountTextStyle Appearance for "more count" text. */ -public data class ImageAttachmentViewStyle( +public data class MediaAttachmentViewStyle( public val progressIcon: Drawable, public val placeholderIcon: Drawable, @ColorInt val imageBackgroundColor: Int, @@ -48,9 +48,9 @@ public data class ImageAttachmentViewStyle( ) { internal companion object { /** - * Fetches styled attributes and returns them wrapped inside of [ImageAttachmentViewStyle]. + * Fetches styled attributes and returns them wrapped inside of [MediaAttachmentViewStyle]. */ - operator fun invoke(context: Context, attrs: AttributeSet?): ImageAttachmentViewStyle { + operator fun invoke(context: Context, attrs: AttributeSet?): MediaAttachmentViewStyle { context.obtainStyledAttributes( attrs, R.styleable.ImageAttachmentView, @@ -93,7 +93,7 @@ public data class ImageAttachmentViewStyle( a.getDrawable(R.styleable.ImageAttachmentView_streamUiImageAttachmentPlaceHolderIcon) ?: context.getDrawableCompat(R.drawable.stream_ui_picture_placeholder)!! - return ImageAttachmentViewStyle( + return MediaAttachmentViewStyle( progressIcon = progressIcon, imageBackgroundColor = imageBackgroundColor, moreCountOverlayColor = moreCountOverlayColor, diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/ImageAttachmentView.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentView.kt similarity index 71% rename from stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/ImageAttachmentView.kt rename to stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentView.kt index 9ec020badca..cc8cd0b1d56 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/ImageAttachmentView.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentView.kt @@ -22,42 +22,44 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.core.view.isVisible import com.getstream.sdk.chat.images.load +import com.getstream.sdk.chat.model.ModelType import com.getstream.sdk.chat.utils.extensions.constrainViewToParentBySide import com.getstream.sdk.chat.utils.extensions.imagePreviewUrl import com.getstream.sdk.chat.utils.extensions.updateConstraints import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.ShapeAppearanceModel import io.getstream.chat.android.client.models.Attachment +import io.getstream.chat.android.ui.ChatUI import io.getstream.chat.android.ui.R import io.getstream.chat.android.ui.common.extensions.internal.createStreamThemeWrapper import io.getstream.chat.android.ui.common.extensions.internal.dpToPx import io.getstream.chat.android.ui.common.extensions.internal.streamThemeInflater import io.getstream.chat.android.ui.common.style.setTextStyle -import io.getstream.chat.android.ui.databinding.StreamUiImageAttachmentViewBinding -import io.getstream.chat.android.ui.message.list.adapter.view.ImageAttachmentViewStyle +import io.getstream.chat.android.ui.databinding.StreamUiMediaAttachmentViewBinding +import io.getstream.chat.android.ui.message.list.adapter.view.MediaAttachmentViewStyle /** - * View used to display image attachments. + * View used to display image and video attachments. * * Giphy images are handled by a separate View. * @see GiphyMediaAttachmentView */ -internal class ImageAttachmentView : ConstraintLayout { +internal class MediaAttachmentView : ConstraintLayout { /** - * Handles image attachment clicks. + * Handles clicks on media attachment previews. */ var attachmentClickListener: AttachmentClickListener? = null /** - * Handles image attachment long clicks. + * Handles media attachment long clicks. */ var attachmentLongClickListener: AttachmentLongClickListener? = null /** - * Binding for [R.layout.stream_ui_image_attachment_view]. + * Binding for [R.layout.stream_ui_media_attachment_view]. */ - internal val binding: StreamUiImageAttachmentViewBinding = - StreamUiImageAttachmentViewBinding.inflate(streamThemeInflater).also { + internal val binding: StreamUiMediaAttachmentViewBinding = + StreamUiMediaAttachmentViewBinding.inflate(streamThemeInflater).also { it.root.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) val padding = 1.dpToPx() it.root.setPadding(padding, padding, padding, padding) @@ -69,9 +71,9 @@ internal class ImageAttachmentView : ConstraintLayout { } /** - * Style applied to [ImageAttachmentView]. + * Style applied to [MediaAttachmentView]. */ - private lateinit var style: ImageAttachmentViewStyle + private lateinit var style: MediaAttachmentViewStyle constructor(context: Context) : this(context, null, 0) @@ -86,24 +88,31 @@ internal class ImageAttachmentView : ConstraintLayout { } private fun init(attrs: AttributeSet?) { - style = ImageAttachmentViewStyle(context, attrs) + style = MediaAttachmentViewStyle(context, attrs) binding.loadingProgressBar.indeterminateDrawable = style.progressIcon binding.moreCountLabel.setTextStyle(style.moreCountTextStyle) } /** - * Displays the images in a message. Displays a count saying how many more - * images the message contains in case they don't all fit in the preview. + * Displays the media previews in a message. Displays a count saying how many more + * media attachments the message contains in case they don't all fit in the preview. */ fun showAttachment(attachment: Attachment, andMoreCount: Int = NO_MORE_COUNT) { - val url = attachment.imagePreviewUrl ?: attachment.titleLink ?: attachment.ogUrl ?: attachment.upload ?: return + val url = + if (attachment.type == ModelType.attach_image || + (attachment.type == ModelType.attach_video && ChatUI.videoThumbnailsEnabled) + ) { + attachment.imagePreviewUrl ?: attachment.titleLink ?: attachment.ogUrl ?: attachment.upload ?: return + } else { + null + } val showMore = { if (andMoreCount > NO_MORE_COUNT) { showMoreCount(andMoreCount) } } - showImage(url) { + showMediaPreview(url) { showMore() } @@ -122,11 +131,11 @@ internal class ImageAttachmentView : ConstraintLayout { } /** - * Loads the images. + * Loads the media preview. */ - private fun showImage(imageUrl: Any, onCompleteCallback: () -> Unit) { + private fun showMediaPreview(mediaUrl: Any?, onCompleteCallback: () -> Unit) { binding.imageView.load( - data = imageUrl, + data = mediaUrl, placeholderDrawable = style.placeholderIcon, onStart = { showLoading(true) }, onComplete = { @@ -137,7 +146,7 @@ internal class ImageAttachmentView : ConstraintLayout { } /** - * Displays how many more images the message contains that are not + * Displays how many more media attachments the message contains that are not * able to fit inside the preview. */ private fun showMoreCount(andMoreCount: Int) { @@ -147,9 +156,9 @@ internal class ImageAttachmentView : ConstraintLayout { } /** - * Creates and sets the shape of the image/s container. + * Creates and sets the shape of the media preview containers. */ - fun setImageShapeByCorners( + fun setMediaPreviewShapeByCorners( topLeft: Float, topRight: Float, bottomRight: Float, @@ -161,15 +170,15 @@ internal class ImageAttachmentView : ConstraintLayout { .setBottomRightCornerSize(bottomRight) .setBottomLeftCornerSize(bottomLeft) .build() - .let(this::setImageShape) + .let(this::setMediaPreviewShape) } /** - * Applies the shape to the image/s container. Also sets the container + * Applies the shape to the media preview containers. Also sets the container * background color and the overlay color for the label that displays - * how many more images there are in a message. + * how many more media attachments there are in a message. */ - private fun setImageShape(shapeAppearanceModel: ShapeAppearanceModel) { + private fun setMediaPreviewShape(shapeAppearanceModel: ShapeAppearanceModel) { binding.imageView.shapeAppearanceModel = shapeAppearanceModel binding.loadImage.background = MaterialShapeDrawable(shapeAppearanceModel).apply { setTint(style.imageBackgroundColor) @@ -181,7 +190,7 @@ internal class ImageAttachmentView : ConstraintLayout { companion object { /** - * When all images in a message are able to fit in the preview. + * When all media attachments in a message are able to fit in the preview. */ private const val NO_MORE_COUNT = 0 } diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/ImageAttachmentsGroupView.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentsGroupView.kt similarity index 65% rename from stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/ImageAttachmentsGroupView.kt rename to stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentsGroupView.kt index 909c6593dde..34cb25de073 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/ImageAttachmentsGroupView.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentsGroupView.kt @@ -35,6 +35,7 @@ import com.google.android.material.shape.CornerSize import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.ShapeAppearanceModel import io.getstream.chat.android.client.models.Attachment +import io.getstream.chat.android.ui.ChatUI import io.getstream.chat.android.ui.R import io.getstream.chat.android.ui.common.extensions.internal.createStreamThemeWrapper import io.getstream.chat.android.ui.common.extensions.internal.displayMetrics @@ -45,10 +46,10 @@ import io.getstream.chat.android.ui.message.list.adapter.viewholder.decorator.in import io.getstream.chat.android.ui.message.list.background.ShapeAppearanceModelFactory import io.getstream.chat.android.uiutils.extension.hasLink -internal class ImageAttachmentsGroupView : ConstraintLayout { +internal class MediaAttachmentsGroupView : ConstraintLayout { var attachmentClickListener: AttachmentClickListener? = null var attachmentLongClickListener: AttachmentLongClickListener? = null - private val maxImageAttachmentHeight: Int by lazy { + private val maxMediaAttachmentHeight: Int by lazy { (displayMetrics().heightPixels * MAX_HEIGHT_PERCENTAGE).toInt() } @@ -70,59 +71,67 @@ internal class ImageAttachmentsGroupView : ConstraintLayout { ) fun showAttachments(attachments: List) { - val images = - attachments.filter { attachment -> !attachment.hasLink() && attachment.type == ModelType.attach_image } - when (images.size) { + val media = + attachments.filter { attachment -> + !attachment.hasLink() && ( + attachment.type == ModelType.attach_image || + attachment.type == ModelType.attach_video + ) + } + when (media.size) { 0 -> Unit - 1 -> showOne(images.first()) - 2 -> showTwo(images.first(), images[1]) - 3 -> showThree(images.first(), images[1], images[2]) + 1 -> showOne(media.first()) + 2 -> showTwo(media.first(), media[1]) + 3 -> showThree(media.first(), media[1], media[2]) else -> showFour( - images.first(), - images[1], - images[2], - images[3], - images.size - MAX_PREVIEW_COUNT + media.first(), + media[1], + media[2], + media[3], + media.size - MAX_PREVIEW_COUNT ) } - (background as? MaterialShapeDrawable)?.shapeAppearanceModel?.let(::applyToImages) + (background as? MaterialShapeDrawable)?.shapeAppearanceModel?.let(::applyToMediaPreviews) } private fun showOne(first: Attachment) { removeAllViews() - val imageAttachmentView = createImageAttachmentView() - addView(imageAttachmentView) - state = State.OneView(imageAttachmentView) + val mediaAttachmentView = createMediaAttachmentView() + addView(mediaAttachmentView) + state = State.OneView(mediaAttachmentView) ConstraintSet().apply { - constrainMaxHeight(imageAttachmentView.id, maxImageAttachmentHeight) - constrainWidth(imageAttachmentView.id, ViewGroup.LayoutParams.MATCH_PARENT) - constrainViewToParentBySide(imageAttachmentView, ConstraintSet.LEFT) - constrainViewToParentBySide(imageAttachmentView, ConstraintSet.RIGHT) - constrainViewToParentBySide(imageAttachmentView, ConstraintSet.TOP) - constrainViewToParentBySide(imageAttachmentView, ConstraintSet.BOTTOM) + constrainMaxHeight(mediaAttachmentView.id, maxMediaAttachmentHeight) + constrainWidth(mediaAttachmentView.id, ViewGroup.LayoutParams.MATCH_PARENT) + constrainViewToParentBySide(mediaAttachmentView, ConstraintSet.LEFT) + constrainViewToParentBySide(mediaAttachmentView, ConstraintSet.RIGHT) + constrainViewToParentBySide(mediaAttachmentView, ConstraintSet.TOP) + constrainViewToParentBySide(mediaAttachmentView, ConstraintSet.BOTTOM) - val imageWidth = first.originalWidth?.toFloat() - val imageHeight = first.originalHeight?.toFloat() + val mediaWidth = first.originalWidth?.toFloat() + val mediaHeight = first.originalHeight?.toFloat() - // Used to set a dimension ratio before we load an image + // Used to set a dimension ratio before we load the media // so that message positions don't jump after we load it. - if (imageWidth != null && imageHeight != null) { - val ratio = (imageWidth / imageHeight).toString() - this.setDimensionRatio(imageAttachmentView.id, ratio) - } else { - constrainHeight(imageAttachmentView.id, LayoutParams.WRAP_CONTENT) + when { + mediaWidth != null && mediaHeight != null -> { + val ratio = (mediaWidth / mediaHeight).toString() + this.setDimensionRatio(mediaAttachmentView.id, ratio) + } + first.type == ModelType.attach_video && !ChatUI.videoThumbnailsEnabled -> + this.setDimensionRatio(mediaAttachmentView.id, "1") + else -> constrainHeight(mediaAttachmentView.id, LayoutParams.WRAP_CONTENT) } - applyTo(this@ImageAttachmentsGroupView) + applyTo(this@MediaAttachmentsGroupView) } - imageAttachmentView.showAttachment(first) + mediaAttachmentView.showAttachment(first) } private fun showTwo(first: Attachment, second: Attachment) { removeAllViews() - val viewOne = createImageAttachmentView().also { addView(it) } - val viewTwo = createImageAttachmentView().also { addView(it) } + val viewOne = createMediaAttachmentView().also { addView(it) } + val viewTwo = createMediaAttachmentView().also { addView(it) } state = State.TwoViews(viewOne, viewTwo) ConstraintSet().apply { setupMinHeight(viewOne, false) @@ -132,7 +141,7 @@ internal class ImageAttachmentsGroupView : ConstraintLayout { constrainViewToParentBySide(viewOne, ConstraintSet.BOTTOM) constrainViewToParentBySide(viewTwo, ConstraintSet.BOTTOM) horizontalChainInParent(viewOne, viewTwo) - applyTo(this@ImageAttachmentsGroupView) + applyTo(this@MediaAttachmentsGroupView) } viewOne.showAttachment(first) viewTwo.showAttachment(second) @@ -140,9 +149,9 @@ internal class ImageAttachmentsGroupView : ConstraintLayout { private fun showThree(first: Attachment, second: Attachment, third: Attachment) { removeAllViews() - val viewOne = createImageAttachmentView().also { addView(it) } - val viewTwo = createImageAttachmentView().also { addView(it) } - val viewThree = createImageAttachmentView().also { addView(it) } + val viewOne = createMediaAttachmentView().also { addView(it) } + val viewTwo = createMediaAttachmentView().also { addView(it) } + val viewThree = createMediaAttachmentView().also { addView(it) } state = State.ThreeViews(viewOne, viewTwo, viewThree) ConstraintSet().apply { setupMinHeight(viewTwo, true) @@ -152,7 +161,7 @@ internal class ImageAttachmentsGroupView : ConstraintLayout { verticalChainInParent(viewTwo, viewThree) connect(viewOne.id, ConstraintSet.TOP, viewTwo.id, ConstraintSet.TOP) connect(viewOne.id, ConstraintSet.BOTTOM, viewThree.id, ConstraintSet.BOTTOM) - applyTo(this@ImageAttachmentsGroupView) + applyTo(this@MediaAttachmentsGroupView) } viewOne.showAttachment(first) viewTwo.showAttachment(second) @@ -167,10 +176,10 @@ internal class ImageAttachmentsGroupView : ConstraintLayout { andMoreCount: Int = 0, ) { removeAllViews() - val viewOne = createImageAttachmentView().also { addView(it) } - val viewTwo = createImageAttachmentView().also { addView(it) } - val viewThree = createImageAttachmentView().also { addView(it) } - val viewFour = createImageAttachmentView().also { addView(it) } + val viewOne = createMediaAttachmentView().also { addView(it) } + val viewTwo = createMediaAttachmentView().also { addView(it) } + val viewThree = createMediaAttachmentView().also { addView(it) } + val viewFour = createMediaAttachmentView().also { addView(it) } state = State.FourViews(viewOne, viewTwo, viewThree, viewFour) ConstraintSet().apply { setupMinHeight(viewOne, true) @@ -181,7 +190,7 @@ internal class ImageAttachmentsGroupView : ConstraintLayout { horizontalChainInParent(viewThree, viewFour) verticalChainInParent(viewOne, viewThree) verticalChainInParent(viewTwo, viewFour) - applyTo(this@ImageAttachmentsGroupView) + applyTo(this@MediaAttachmentsGroupView) } viewOne.showAttachment(first) viewTwo.showAttachment(second) @@ -192,36 +201,36 @@ internal class ImageAttachmentsGroupView : ConstraintLayout { override fun setBackground(background: Drawable) { super.setBackground(background) if (background is MaterialShapeDrawable) { - applyToImages(background.shapeAppearanceModel) + applyToMediaPreviews(background.shapeAppearanceModel) } } - private fun applyToImages(shapeAppearanceModel: ShapeAppearanceModel) { + private fun applyToMediaPreviews(shapeAppearanceModel: ShapeAppearanceModel) { val topLeftCorner = shapeAppearanceModel.getCornerSize(ShapeAppearanceModel::getTopLeftCornerSize) val topRightCorner = shapeAppearanceModel.getCornerSize(ShapeAppearanceModel::getTopRightCornerSize) val bottomRightCorner = shapeAppearanceModel.getCornerSize(ShapeAppearanceModel::getBottomRightCornerSize) val bottomLeftCorner = shapeAppearanceModel.getCornerSize(ShapeAppearanceModel::getBottomLeftCornerSize) when (val stateCopy = state) { - is State.OneView -> stateCopy.view.setImageShapeByCorners( + is State.OneView -> stateCopy.view.setMediaPreviewShapeByCorners( topLeftCorner, topRightCorner, bottomRightCorner, bottomLeftCorner ) is State.TwoViews -> { - stateCopy.viewOne.setImageShapeByCorners(topLeftCorner, 0f, 0f, bottomLeftCorner) - stateCopy.viewTwo.setImageShapeByCorners(0f, topRightCorner, bottomRightCorner, 0f) + stateCopy.viewOne.setMediaPreviewShapeByCorners(topLeftCorner, 0f, 0f, bottomLeftCorner) + stateCopy.viewTwo.setMediaPreviewShapeByCorners(0f, topRightCorner, bottomRightCorner, 0f) } is State.ThreeViews -> { - stateCopy.viewOne.setImageShapeByCorners(topLeftCorner, 0f, 0f, bottomLeftCorner) - stateCopy.viewTwo.setImageShapeByCorners(0f, topRightCorner, 0f, 0f) - stateCopy.viewThree.setImageShapeByCorners(0f, 0f, bottomRightCorner, 0f) + stateCopy.viewOne.setMediaPreviewShapeByCorners(topLeftCorner, 0f, 0f, bottomLeftCorner) + stateCopy.viewTwo.setMediaPreviewShapeByCorners(0f, topRightCorner, 0f, 0f) + stateCopy.viewThree.setMediaPreviewShapeByCorners(0f, 0f, bottomRightCorner, 0f) } is State.FourViews -> { - stateCopy.viewOne.setImageShapeByCorners(topLeftCorner, 0f, 0f, 0f) - stateCopy.viewTwo.setImageShapeByCorners(0f, topRightCorner, 0f, 0f) - stateCopy.viewThree.setImageShapeByCorners(0f, 0f, 0f, bottomLeftCorner) - stateCopy.viewFour.setImageShapeByCorners(0f, 0f, bottomRightCorner, 0f) + stateCopy.viewOne.setMediaPreviewShapeByCorners(topLeftCorner, 0f, 0f, 0f) + stateCopy.viewTwo.setMediaPreviewShapeByCorners(0f, topRightCorner, 0f, 0f) + stateCopy.viewThree.setMediaPreviewShapeByCorners(0f, 0f, 0f, bottomLeftCorner) + stateCopy.viewFour.setMediaPreviewShapeByCorners(0f, 0f, bottomRightCorner, 0f) } else -> Unit } @@ -232,8 +241,8 @@ internal class ImageAttachmentsGroupView : ConstraintLayout { .getOrDefault(0f) } - private fun createImageAttachmentView(): ImageAttachmentView { - return ImageAttachmentView(context).also { + private fun createMediaAttachmentView(): MediaAttachmentView { + return MediaAttachmentView(context).also { it.id = generateViewId() it.attachmentClickListener = attachmentClickListener it.attachmentLongClickListener = attachmentLongClickListener @@ -259,19 +268,19 @@ internal class ImageAttachmentsGroupView : ConstraintLayout { private sealed class State { object Empty : State() - data class OneView(val view: ImageAttachmentView) : State() - data class TwoViews(val viewOne: ImageAttachmentView, val viewTwo: ImageAttachmentView) : State() + data class OneView(val view: MediaAttachmentView) : State() + data class TwoViews(val viewOne: MediaAttachmentView, val viewTwo: MediaAttachmentView) : State() data class ThreeViews( - val viewOne: ImageAttachmentView, - val viewTwo: ImageAttachmentView, - val viewThree: ImageAttachmentView, + val viewOne: MediaAttachmentView, + val viewTwo: MediaAttachmentView, + val viewThree: MediaAttachmentView, ) : State() data class FourViews( - val viewOne: ImageAttachmentView, - val viewTwo: ImageAttachmentView, - val viewThree: ImageAttachmentView, - val viewFour: ImageAttachmentView, + val viewOne: MediaAttachmentView, + val viewTwo: MediaAttachmentView, + val viewThree: MediaAttachmentView, + val viewFour: MediaAttachmentView, ) : State() } diff --git a/stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_image_attachment.xml b/stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_image_attachment.xml index 3ef5932439f..a2ce68b95cd 100644 --- a/stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_image_attachment.xml +++ b/stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_image_attachment.xml @@ -81,7 +81,7 @@ android:visibility="gone" /> - - #99FFFFFF #101214 #070A0D + #1C1E22 + #1C1E22 #FFFFFF diff --git a/stream-chat-android-ui-components/src/main/res/values/colors.xml b/stream-chat-android-ui-components/src/main/res/values/colors.xml index 5f74e8192ac..1fa4139d25e 100644 --- a/stream-chat-android-ui-components/src/main/res/values/colors.xml +++ b/stream-chat-android-ui-components/src/main/res/values/colors.xml @@ -41,6 +41,8 @@ #99000000 #F7F7F7 #FCFCFC + #E9EAED + #E9EAED #FFFFFF @@ -49,7 +51,6 @@ #E8E8E8 #FBF4DD - #FF8A65 #81C784 From 2e2c207fe17d12d600fd1ac2c62ec8ce4e41931a Mon Sep 17 00:00:00 2001 From: MarinTolic Date: Wed, 5 Oct 2022 18:34:52 +0200 Subject: [PATCH 07/56] [3369] Appease Detekt --- .../ui/message/list/adapter/view/MediaAttachmentViewStyle.kt | 3 ++- .../list/adapter/view/internal/MediaAttachmentsGroupView.kt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle.kt index 38f672c1439..b14a2d8a24b 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle.kt @@ -33,7 +33,8 @@ import io.getstream.chat.android.ui.common.style.TextStyle * Style for [io.getstream.chat.android.ui.message.list.adapter.view.internal.MediaAttachmentView]. * Use this class together with [TransformStyle.imageAttachmentStyleTransformer] to change styles programmatically. * - * @param progressIcon Animated progress drawable. Default value is [R.drawable.stream_ui_rotating_indeterminate_progress_gradient]. + * @param progressIcon Animated progress drawable. Default value is + * [R.drawable.stream_ui_rotating_indeterminate_progress_gradient]. * @param placeholderIcon Displayed while the media preview is Loading. * @param imageBackgroundColor Image background. Default value is [R.color.stream_ui_grey]. * @param moreCountOverlayColor More count semi-transparent overlay color. Default value is [R.color.stream_ui_overlay]. diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentsGroupView.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentsGroupView.kt index 34cb25de073..aaf6073d7b9 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentsGroupView.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentsGroupView.kt @@ -70,6 +70,7 @@ internal class MediaAttachmentsGroupView : ConstraintLayout { defStyleRes ) + @Suppress("MagicNumber") fun showAttachments(attachments: List) { val media = attachments.filter { attachment -> From 91743bb11657eddd60b0576efe5baef32d0a24b0 Mon Sep 17 00:00:00 2001 From: MarinTolic Date: Thu, 6 Oct 2022 20:13:18 +0200 Subject: [PATCH 08/56] [3369] Expand the customizability of the new media attachment view --- .../api/stream-chat-android-ui-components.api | 28 ++++- .../sdk/chat/images/ViewExtensions.kt | 2 +- .../adapter/view/MediaAttachmentViewStyle.kt | 105 +++++++++++++++--- .../view/internal/MediaAttachmentView.kt | 54 ++++++++- .../main/res/drawable/stream_ui_ic_play.xml | 25 +++++ .../stream_ui_picture_placeholder.xml | 8 +- .../stream_ui_media_attachment_view.xml | 25 ++++- .../values/attrs_image_attachment_view.xml | 59 ++++++---- .../src/main/res/values/styles.xml | 29 +++-- 9 files changed, 281 insertions(+), 54 deletions(-) create mode 100644 stream-chat-android-ui-components/src/main/res/drawable/stream_ui_ic_play.xml diff --git a/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api b/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api index 756ec3d75d6..039ecb17c38 100644 --- a/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api +++ b/stream-chat-android-ui-components/api/stream-chat-android-ui-components.api @@ -3295,20 +3295,40 @@ public final class io/getstream/chat/android/ui/message/list/adapter/view/GiphyM } public final class io/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle { - public fun (Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IILio/getstream/chat/android/ui/common/style/TextStyle;)V + public fun (Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IIIILio/getstream/chat/android/ui/common/style/TextStyle;Landroid/graphics/drawable/Drawable;IFIIIIF)V public final fun component1 ()Landroid/graphics/drawable/Drawable; + public final fun component10 ()F + public final fun component11 ()I + public final fun component12 ()I + public final fun component13 ()I + public final fun component14 ()I + public final fun component15 ()F public final fun component2 ()Landroid/graphics/drawable/Drawable; public final fun component3 ()I public final fun component4 ()I - public final fun component5 ()Lio/getstream/chat/android/ui/common/style/TextStyle; - public final fun copy (Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IILio/getstream/chat/android/ui/common/style/TextStyle;)Lio/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle; - public static synthetic fun copy$default (Lio/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IILio/getstream/chat/android/ui/common/style/TextStyle;ILjava/lang/Object;)Lio/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle; + public final fun component5 ()I + public final fun component6 ()I + public final fun component7 ()Lio/getstream/chat/android/ui/common/style/TextStyle; + public final fun component8 ()Landroid/graphics/drawable/Drawable; + public final fun component9 ()I + public final fun copy (Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IIIILio/getstream/chat/android/ui/common/style/TextStyle;Landroid/graphics/drawable/Drawable;IFIIIIF)Lio/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle; + public static synthetic fun copy$default (Lio/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;IIIILio/getstream/chat/android/ui/common/style/TextStyle;Landroid/graphics/drawable/Drawable;IFIIIIFILjava/lang/Object;)Lio/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle; public fun equals (Ljava/lang/Object;)Z public final fun getImageBackgroundColor ()I public final fun getMoreCountOverlayColor ()I public final fun getMoreCountTextStyle ()Lio/getstream/chat/android/ui/common/style/TextStyle; public final fun getPlaceholderIcon ()Landroid/graphics/drawable/Drawable; + public final fun getPlaceholderIconTint ()I + public final fun getPlayVideoIcon ()Landroid/graphics/drawable/Drawable; + public final fun getPlayVideoIconBackgroundColor ()I + public final fun getPlayVideoIconCornerRadius ()F + public final fun getPlayVideoIconElevation ()F + public final fun getPlayVideoIconPaddingBottom ()I + public final fun getPlayVideoIconPaddingEnd ()I + public final fun getPlayVideoIconPaddingStart ()I + public final fun getPlayVideoIconPaddingTop ()I public final fun getProgressIcon ()Landroid/graphics/drawable/Drawable; + public final fun getVideoBackgroundColor ()I public fun hashCode ()I public fun toString ()Ljava/lang/String; } diff --git a/stream-chat-android-ui-components/src/main/kotlin/com/getstream/sdk/chat/images/ViewExtensions.kt b/stream-chat-android-ui-components/src/main/kotlin/com/getstream/sdk/chat/images/ViewExtensions.kt index 94a50d50413..52e2135c886 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/com/getstream/sdk/chat/images/ViewExtensions.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/com/getstream/sdk/chat/images/ViewExtensions.kt @@ -45,7 +45,7 @@ public fun ImageView.load( @InternalStreamChatApi public fun ImageView.load( data: Any?, - placeholderDrawable: Drawable, + placeholderDrawable: Drawable?, transformation: ImageTransformation = ImageTransformation.None, onStart: () -> Unit = {}, onComplete: () -> Unit = {}, diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle.kt index b14a2d8a24b..977924d3c72 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/MediaAttachmentViewStyle.kt @@ -36,16 +36,48 @@ import io.getstream.chat.android.ui.common.style.TextStyle * @param progressIcon Animated progress drawable. Default value is * [R.drawable.stream_ui_rotating_indeterminate_progress_gradient]. * @param placeholderIcon Displayed while the media preview is Loading. - * @param imageBackgroundColor Image background. Default value is [R.color.stream_ui_grey]. + * @param placeholderIconTint The tint applied to the placeholder icon displayed before a media + * attachment preview was loaded or after loading had failed. + * Default value is [R.drawable.stream_ui_picture_placeholder]. + * @param imageBackgroundColor Controls the background color of image attachment previews. + * Default value is [R.color.stream_ui_grey]. + * @param videoBackgroundColor Controls the background color of video attachment previews. + * Default value is [R.color.stream_ui_grey]. * @param moreCountOverlayColor More count semi-transparent overlay color. Default value is [R.color.stream_ui_overlay]. * @param moreCountTextStyle Appearance for "more count" text. + * @param playVideoIcon The icon overlaid above previews of video attachments. + * Default value is [R.drawable.stream_ui_ic_play] + * @param playVideoIconBackgroundColor Applies a background colour to the View hosting the play video icon. + * Default value is [R.color.stream_ui_literal_white] + * @param playVideoIconElevation Determines the elevation of the play video button. + * @param playVideoIconPaddingTop Determines the padding set between the top of the play video icon and its + * parent. + * @param playVideoIconPaddingBottom Determines the padding set between the bottom of the play video icon and its + * parent. + * @param playVideoIconPaddingStart Determines the padding set between the start of the play video icon and its + * parent. + * @param playVideoIconPaddingEnd Determines the padding set between the end of the play video icon and its + * parent. + * @param playVideoIconCornerRadius Determines the corner radius of the play video icon. */ public data class MediaAttachmentViewStyle( public val progressIcon: Drawable, public val placeholderIcon: Drawable, + @ColorInt public val placeholderIconTint: Int, @ColorInt val imageBackgroundColor: Int, + // TODO - Might be difficult to implement due to the way we apply border radius + // TODO - Check and decide + @ColorInt val videoBackgroundColor: Int, @ColorInt val moreCountOverlayColor: Int, public val moreCountTextStyle: TextStyle, + public val playVideoIcon: Drawable?, + @ColorInt public val playVideoIconBackgroundColor: Int, + public val playVideoIconElevation: Float, + public val playVideoIconPaddingTop: Int, + public val playVideoIconPaddingBottom: Int, + public val playVideoIconPaddingStart: Int, + public val playVideoIconPaddingEnd: Int, + public val playVideoIconCornerRadius: Float ) { internal companion object { /** @@ -54,52 +86,99 @@ public data class MediaAttachmentViewStyle( operator fun invoke(context: Context, attrs: AttributeSet?): MediaAttachmentViewStyle { context.obtainStyledAttributes( attrs, - R.styleable.ImageAttachmentView, + R.styleable.MediaAttachmentView, R.attr.streamUiMessageListImageAttachmentStyle, - R.style.StreamUi_MessageList_ImageAttachment + R.style.StreamUi_MessageList_MediaAttachment ).use { a -> - val progressIcon = a.getDrawable(R.styleable.ImageAttachmentView_streamUiImageAttachmentProgressIcon) + val progressIcon = a.getDrawable(R.styleable.MediaAttachmentView_streamUiMediaAttachmentProgressIcon) ?: context.getDrawableCompat(R.drawable.stream_ui_rotating_indeterminate_progress_gradient)!! val imageBackgroundColor = a.getColor( - R.styleable.ImageAttachmentView_streamUiImageAttachmentImageBackgroundColor, - context.getColorCompat(R.color.stream_ui_grey) + R.styleable.MediaAttachmentView_streamUiMediaAttachmentImageBackgroundColor, + context.getColorCompat(R.color.stream_ui_message_list_image_attachment_background) + ) + + val videoBackgroundColor = a.getColor( + R.styleable.MediaAttachmentView_streamUiMediaAttachmentVideoBackgroundColor, + context.getColorCompat(R.color.stream_ui_message_list_video_attachment_background) ) val moreCountOverlayColor = a.getColor( - R.styleable.ImageAttachmentView_streamUiImageAttachmentMoreCountOverlayColor, + R.styleable.MediaAttachmentView_streamUiMediaAttachmentMoreCountOverlayColor, context.getColorCompat(R.color.stream_ui_overlay) ) val moreCountTextStyle = TextStyle.Builder(a) .size( - R.styleable.ImageAttachmentView_streamUiImageAttachmentMoreCountTextSize, + R.styleable.MediaAttachmentView_streamUiMediaAttachmentMoreCountTextSize, context.getDimension(R.dimen.stream_ui_message_image_attachment_more_count_text_size) ) .color( - R.styleable.ImageAttachmentView_streamUiImageAttachmentMoreCountTextColor, + R.styleable.MediaAttachmentView_streamUiMediaAttachmentMoreCountTextColor, context.getColorCompat(R.color.stream_ui_literal_white) ) .font( - R.styleable.ImageAttachmentView_streamUiImageAttachmentMoreCountFontAssets, - R.styleable.ImageAttachmentView_streamUiImageAttachmentMoreCountTextFont + R.styleable.MediaAttachmentView_streamUiMediaAttachmentMoreCountFontAssets, + R.styleable.MediaAttachmentView_streamUiMediaAttachmentMoreCountTextFont ) .style( - R.styleable.ImageAttachmentView_streamUiImageAttachmentMoreCountTextStyle, + R.styleable.MediaAttachmentView_streamUiMediaAttachmentMoreCountTextStyle, Typeface.NORMAL ) .build() val placeholderIcon = - a.getDrawable(R.styleable.ImageAttachmentView_streamUiImageAttachmentPlaceHolderIcon) + a.getDrawable(R.styleable.MediaAttachmentView_streamUiMediaAttachmentPlaceHolderIcon) ?: context.getDrawableCompat(R.drawable.stream_ui_picture_placeholder)!! + val placeholderIconColor = a.getColor( + R.styleable.MediaAttachmentView_streamUiMediaAttachmentPlaceHolderIconTint, + context.getColorCompat(R.color.stream_ui_literal_transparent) + ) + + val playVideoIcon = a.getDrawable(R.styleable.MediaAttachmentView_streamUiMediaAttachmentPlayVideoIcon) + ?: context.getDrawableCompat(R.drawable.stream_ui_ic_play) + + val playVideoIconBackgroundColor = + a.getColor( + R.styleable.MediaAttachmentView_streamUiMediaAttachmentPlayVideoIconBackgroundColor, + context.getColorCompat(R.color.stream_ui_literal_white) + ) + + val playVideoIconCornerRadius = + a.getDimension(R.styleable.MediaAttachmentView_streamUiMediaAttachmentPlayVideoIconCornerRadius, 0f) + + val playVideoIconElevation = + a.getDimension(R.styleable.MediaAttachmentView_streamUiMediaAttachmentPlayVideoIconElevation, 0f) + + val playVideoIconPaddingTop = + a.getDimensionPixelSize(R.styleable.MediaAttachmentView_streamUiMediaAttachmentPlayVideoIconPaddingTop, 0) + + val playVideoIconPaddingBottom = + a.getDimensionPixelSize(R.styleable.MediaAttachmentView_streamUiMediaAttachmentPlayVideoIconPaddingBottom, 0) + + val playVideoIconPaddingStart = + a.getDimensionPixelSize(R.styleable.MediaAttachmentView_streamUiMediaAttachmentPlayVideoIconPaddingStart, 0) + + val playVideoIconPaddingEnd = + a.getDimensionPixelSize(R.styleable.MediaAttachmentView_streamUiMediaAttachmentPlayVideoIconPaddingEnd, 0) + return MediaAttachmentViewStyle( progressIcon = progressIcon, imageBackgroundColor = imageBackgroundColor, + videoBackgroundColor = videoBackgroundColor, moreCountOverlayColor = moreCountOverlayColor, moreCountTextStyle = moreCountTextStyle, placeholderIcon = placeholderIcon, + placeholderIconTint = placeholderIconColor, + playVideoIcon = playVideoIcon, + playVideoIconBackgroundColor = playVideoIconBackgroundColor, + playVideoIconElevation = playVideoIconElevation, + playVideoIconPaddingTop = playVideoIconPaddingTop, + playVideoIconPaddingBottom = playVideoIconPaddingBottom, + playVideoIconPaddingStart = playVideoIconPaddingStart, + playVideoIconPaddingEnd = playVideoIconPaddingEnd, + playVideoIconCornerRadius = playVideoIconCornerRadius ).let(TransformStyle.imageAttachmentStyleTransformer::transform) } } diff --git a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentView.kt b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentView.kt index cc8cd0b1d56..c8f0f40d046 100644 --- a/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentView.kt +++ b/stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/message/list/adapter/view/internal/MediaAttachmentView.kt @@ -91,6 +91,29 @@ internal class MediaAttachmentView : ConstraintLayout { style = MediaAttachmentViewStyle(context, attrs) binding.loadingProgressBar.indeterminateDrawable = style.progressIcon binding.moreCountLabel.setTextStyle(style.moreCountTextStyle) + setupPlayIcon() + } + + /** + * Sets up the play icon overlaid above video attachment previews + * by pulling relevant values from [MediaAttachmentViewStyle]. + **/ + private fun setupPlayIcon() { + with(binding.playIconCardView) { + elevation = style.playVideoIconElevation + setCardBackgroundColor(style.playVideoIconBackgroundColor) + radius = style.playVideoIconCornerRadius + } + + with(binding.playIconImageView) { + setImageDrawable(style.playVideoIcon) + setPaddingRelative( + style.playVideoIconPaddingStart, + style.playVideoIconPaddingTop, + style.playVideoIconPaddingEnd, + style.playVideoIconPaddingBottom + ) + } } /** @@ -112,8 +135,16 @@ internal class MediaAttachmentView : ConstraintLayout { } } - showMediaPreview(url) { + showMediaPreview( + mediaUrl = url, + showImagePlaceholder = attachment.type == ModelType.attach_image + ) { showMore() + if (attachment.type == ModelType.attach_video) { + binding.playIconImageView.visibility = VISIBLE + } else { + binding.playIconImageView.visibility = GONE + } } setOnClickListener { attachmentClickListener?.onAttachmentClick(attachment) } @@ -133,10 +164,22 @@ internal class MediaAttachmentView : ConstraintLayout { /** * Loads the media preview. */ - private fun showMediaPreview(mediaUrl: Any?, onCompleteCallback: () -> Unit) { + private fun showMediaPreview( + mediaUrl: Any?, + showImagePlaceholder: Boolean, + onCompleteCallback: () -> Unit, + ) { + val placeholder = if (showImagePlaceholder) { + style.placeholderIcon.mutate().apply { + this.setTint(style.placeholderIconTint) + } + } else { + null + } + binding.imageView.load( data = mediaUrl, - placeholderDrawable = style.placeholderIcon, + placeholderDrawable = placeholder, onStart = { showLoading(true) }, onComplete = { showLoading(false) @@ -179,7 +222,12 @@ internal class MediaAttachmentView : ConstraintLayout { * how many more media attachments there are in a message. */ private fun setMediaPreviewShape(shapeAppearanceModel: ShapeAppearanceModel) { + // TODO - add as a customizable point to the relevant style + binding.imageView.setContentPadding(1, 1, 1, 1) binding.imageView.shapeAppearanceModel = shapeAppearanceModel + binding.imageView.background = MaterialShapeDrawable(shapeAppearanceModel).apply { + setTint(style.imageBackgroundColor) + } binding.loadImage.background = MaterialShapeDrawable(shapeAppearanceModel).apply { setTint(style.imageBackgroundColor) } diff --git a/stream-chat-android-ui-components/src/main/res/drawable/stream_ui_ic_play.xml b/stream-chat-android-ui-components/src/main/res/drawable/stream_ui_ic_play.xml new file mode 100644 index 00000000000..efce09a1876 --- /dev/null +++ b/stream-chat-android-ui-components/src/main/res/drawable/stream_ui_ic_play.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/stream-chat-android-ui-components/src/main/res/drawable/stream_ui_picture_placeholder.xml b/stream-chat-android-ui-components/src/main/res/drawable/stream_ui_picture_placeholder.xml index 76bc180da67..7f85de5afc9 100644 --- a/stream-chat-android-ui-components/src/main/res/drawable/stream_ui_picture_placeholder.xml +++ b/stream-chat-android-ui-components/src/main/res/drawable/stream_ui_picture_placeholder.xml @@ -17,17 +17,17 @@ - + diff --git a/stream-chat-android-ui-components/src/main/res/layout/stream_ui_media_attachment_view.xml b/stream-chat-android-ui-components/src/main/res/layout/stream_ui_media_attachment_view.xml index 950a485bd3e..6c48c37a263 100644 --- a/stream-chat-android-ui-components/src/main/res/layout/stream_ui_media_attachment_view.xml +++ b/stream-chat-android-ui-components/src/main/res/layout/stream_ui_media_attachment_view.xml @@ -34,6 +34,29 @@ app:layout_constraintTop_toTopOf="parent" /> + + + + + diff --git a/stream-chat-android-ui-components/src/main/res/values/attrs_image_attachment_view.xml b/stream-chat-android-ui-components/src/main/res/values/attrs_image_attachment_view.xml index a7c6d6f0e61..d57e91fb23a 100644 --- a/stream-chat-android-ui-components/src/main/res/values/attrs_image_attachment_view.xml +++ b/stream-chat-android-ui-components/src/main/res/values/attrs_image_attachment_view.xml @@ -15,30 +15,51 @@ limitations under the License. --> - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - + + + + + + + + + + + + + + + + + + diff --git a/stream-chat-android-ui-components/src/main/res/values/styles.xml b/stream-chat-android-ui-components/src/main/res/values/styles.xml index cb408f7a468..1a535a60347 100644 --- a/stream-chat-android-ui-components/src/main/res/values/styles.xml +++ b/stream-chat-android-ui-components/src/main/res/values/styles.xml @@ -504,15 +504,26 @@ 75% - + - - @drawable/stream_ui_ic_play - 3dp - 2dp - 2dp - 3dp - 2dp - 24dp - @color/stream_ui_literal_white +