diff --git a/CHANGES.md b/CHANGES.md index ce3281d987..fdf65b9f1c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ Improvements 🙌: - Crypto improvement | Bulk send NO_OLM withheld code - Display the room shield in all room setting screens - Improve message with Emoji only detection (#3017) + - Picture preview when replying. Also add the image preview in the message detail bottomsheet (#2916) - Api interceptor to allow app developers peek responses (#2986) Bugfix 🐛: diff --git a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt index a323ce995b..5ff7a07e3c 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt @@ -19,6 +19,7 @@ package im.vector.app.core.epoxy.bottomsheet import android.text.method.MovementMethod import android.widget.ImageView import android.widget.TextView +import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R @@ -27,6 +28,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.extensions.setTextOrHide import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess +import im.vector.app.features.media.ImageContentRenderer import org.matrix.android.sdk.api.util.MatrixItem /** @@ -44,6 +46,12 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel(R.id.bottom_sheet_message_preview_avatar) val sender by bind(R.id.bottom_sheet_message_preview_sender) val body by bind(R.id.bottom_sheet_message_preview_body) val timestamp by bind(R.id.bottom_sheet_message_preview_timestamp) + val imagePreview by bind(R.id.bottom_sheet_message_preview_image) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index a80aeb65b0..64bffcb49c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -98,6 +98,7 @@ import im.vector.app.core.ui.views.FailedMessagesWarningView import im.vector.app.core.ui.views.JumpToReadMarkerView import im.vector.app.core.ui.views.NotificationAreaView import im.vector.app.core.utils.Debouncer +import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.KeyboardStateUtils import im.vector.app.core.utils.PERMISSIONS_FOR_WRITING_FILES import im.vector.app.core.utils.TextUtils @@ -137,6 +138,7 @@ import im.vector.app.features.home.room.detail.timeline.action.MessageActionsBot import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActionViewModel import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider +import im.vector.app.features.home.room.detail.timeline.image.buildImageContentRendererData import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem import im.vector.app.features.home.room.detail.timeline.item.MessageFileItem import im.vector.app.features.home.room.detail.timeline.item.MessageImageVideoItem @@ -223,6 +225,7 @@ class RoomDetailFragment @Inject constructor( private val eventHtmlRenderer: EventHtmlRenderer, private val vectorPreferences: VectorPreferences, private val colorProvider: ColorProvider, + private val dimensionConverter: DimensionConverter, private val notificationUtils: NotificationUtils, private val matrixItemColorProvider: MatrixItemColorProvider, private val imageContentRenderer: ImageContentRenderer, @@ -873,6 +876,15 @@ class RoomDetailFragment @Inject constructor( } views.composerLayout.views.composerRelatedMessageContent.text = (formattedBody ?: nonFormattedBody) + // Image Event + val data = event.buildImageContentRendererData(dimensionConverter.dpToPx(66)) + val isImageVisible = if (data != null) { + imageContentRenderer.render(data, ImageContentRenderer.Mode.THUMBNAIL, views.composerLayout.views.composerRelatedMessageImage) + true + } else { + false + } + updateComposerText(defaultContent) views.composerLayout.views.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), iconRes)) @@ -884,6 +896,7 @@ class RoomDetailFragment @Inject constructor( if (isAdded) { // need to do it here also when not using quick reply focusComposerAndShowKeyboard() + views.composerLayout.views.composerRelatedMessageImage.isVisible = isImageVisible } } focusComposerAndShowKeyboard() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt index 4e1492aaba..30587e6659 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt @@ -29,11 +29,14 @@ import im.vector.app.core.epoxy.bottomsheet.bottomSheetQuickReactionsItem import im.vector.app.core.epoxy.bottomsheet.bottomSheetSendStateItem import im.vector.app.core.epoxy.dividerItem import im.vector.app.core.resources.StringProvider +import im.vector.app.core.utils.DimensionConverter import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.TimelineEventController +import im.vector.app.features.home.room.detail.timeline.image.buildImageContentRendererData import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovementMethod import im.vector.app.features.home.room.detail.timeline.tools.linkify +import im.vector.app.features.media.ImageContentRenderer import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.room.send.SendState import javax.inject.Inject @@ -45,6 +48,8 @@ class MessageActionsEpoxyController @Inject constructor( private val stringProvider: StringProvider, private val avatarRenderer: AvatarRenderer, private val fontProvider: EmojiCompatFontProvider, + private val imageContentRenderer: ImageContentRenderer, + private val dimensionConverter: DimensionConverter, private val dateFormatter: VectorDateFormatter ) : TypedEpoxyController() { @@ -59,6 +64,8 @@ class MessageActionsEpoxyController @Inject constructor( avatarRenderer(avatarRenderer) matrixItem(state.informationData.matrixItem) movementMethod(createLinkMovementMethod(listener)) + imageContentRenderer(imageContentRenderer) + data(state.timelineEvent()?.buildImageContentRendererData(dimensionConverter.dpToPx(66))) userClicked { listener?.didSelectMenuAction(EventSharedAction.OpenUserProfile(state.informationData.senderId)) } body(state.messageBody.linkify(listener)) time(formattedDate) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/image/ImageContentRendererFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/image/ImageContentRendererFactory.kt new file mode 100644 index 0000000000..7ff184f664 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/image/ImageContentRendererFactory.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 im.vector.app.features.home.room.detail.timeline.image + +import im.vector.app.features.media.ImageContentRenderer +import org.matrix.android.sdk.api.session.events.model.isImageMessage +import org.matrix.android.sdk.api.session.events.model.isVideoMessage +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent +import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent +import org.matrix.android.sdk.api.session.room.model.message.getFileUrl +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt + +fun TimelineEvent.buildImageContentRendererData(maxHeight: Int): ImageContentRenderer.Data? { + return when { + root.isImageMessage() -> root.getClearContent().toModel() + ?.let { messageImageContent -> + ImageContentRenderer.Data( + eventId = eventId, + filename = messageImageContent.body, + mimeType = messageImageContent.mimeType, + url = messageImageContent.getFileUrl(), + elementToDecrypt = messageImageContent.encryptedFileInfo?.toElementToDecrypt(), + height = messageImageContent.info?.height, + maxHeight = maxHeight, + width = messageImageContent.info?.width, + maxWidth = maxHeight * 2, + allowNonMxcUrls = false + ) + } + root.isVideoMessage() -> root.getClearContent().toModel() + ?.let { messageVideoContent -> + ImageContentRenderer.Data( + eventId = eventId, + filename = messageVideoContent.body, + mimeType = messageVideoContent.mimeType, + url = messageVideoContent.getFileUrl(), + elementToDecrypt = messageVideoContent.encryptedFileInfo?.toElementToDecrypt(), + height = messageVideoContent.videoInfo?.height, + maxHeight = maxHeight, + width = messageVideoContent.videoInfo?.width, + maxWidth = maxHeight * 2, + allowNonMxcUrls = false + ) + } + else -> null + } +} diff --git a/vector/src/main/res/layout/composer_layout.xml b/vector/src/main/res/layout/composer_layout.xml index 0db905d015..e837d2d80b 100644 --- a/vector/src/main/res/layout/composer_layout.xml +++ b/vector/src/main/res/layout/composer_layout.xml @@ -68,6 +68,13 @@ app:tint="?riotx_text_primary" tools:ignore="MissingConstraints,MissingPrefix" /> + + + + + - diff --git a/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml b/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml index 5fbed68955..afd96d5690 100644 --- a/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml +++ b/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml @@ -50,6 +50,20 @@ app:layout_constraintEnd_toEndOf="parent" tools:text="Friday 8pm" /> + +