Merge pull request #3034 from vector-im/feature/bma/reply_image_preview

Picture preview when replying.
This commit is contained in:
Benoit Marty 2021-03-22 20:49:40 +01:00 committed by GitHub
commit 2b70a8450c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 150 additions and 5 deletions

View File

@ -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 🐛:

View File

@ -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<BottomSheetMessa
@EpoxyAttribute
lateinit var body: CharSequence
@EpoxyAttribute
var imageContentRenderer: ImageContentRenderer? = null
@EpoxyAttribute
var data: ImageContentRenderer.Data? = null
@EpoxyAttribute
var time: CharSequence? = null
@ -59,16 +67,26 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
holder.avatar.setOnClickListener { userClicked?.invoke() }
holder.sender.setOnClickListener { userClicked?.invoke() }
holder.sender.setTextOrHide(matrixItem.displayName)
data?.let {
imageContentRenderer?.render(it, ImageContentRenderer.Mode.THUMBNAIL, holder.imagePreview)
}
holder.imagePreview.isVisible = data != null
holder.body.movementMethod = movementMethod
holder.body.text = body
body.findPillsAndProcess(coroutineScope) { it.bind(holder.body) }
holder.timestamp.setTextOrHide(time)
}
override fun unbind(holder: Holder) {
imageContentRenderer?.clear(holder.imagePreview)
super.unbind(holder)
}
class Holder : VectorEpoxyHolder() {
val avatar by bind<ImageView>(R.id.bottom_sheet_message_preview_avatar)
val sender by bind<TextView>(R.id.bottom_sheet_message_preview_sender)
val body by bind<TextView>(R.id.bottom_sheet_message_preview_body)
val timestamp by bind<TextView>(R.id.bottom_sheet_message_preview_timestamp)
val imagePreview by bind<ImageView>(R.id.bottom_sheet_message_preview_image)
}
}

View File

@ -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()

View File

@ -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<MessageActionState>() {
@ -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)

View File

@ -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<MessageImageContent>()
?.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<MessageVideoContent>()
?.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
}
}

View File

@ -68,6 +68,13 @@
app:tint="?riotx_text_primary"
tools:ignore="MissingConstraints,MissingPrefix" />
<ImageView
android:id="@+id/composerRelatedMessageImage"
android:layout_width="0dp"
android:layout_height="0dp"
android:importantForAccessibility="no"
tools:ignore="MissingPrefix" />
<ImageButton
android:id="@+id/composerRelatedMessageCloseButton"
android:layout_width="22dp"

View File

@ -83,6 +83,15 @@
tools:ignore="MissingConstraints,MissingPrefix"
tools:src="@drawable/ic_edit" />
<ImageView
android:id="@+id/composerRelatedMessageImage"
android:layout_width="0dp"
android:layout_height="0dp"
android:importantForAccessibility="no"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintStart_toEndOf="parent"
tools:ignore="MissingPrefix"
tools:src="@tools:sample/backgrounds/scenic" />
<ImageButton
android:id="@+id/composerRelatedMessageCloseButton"

View File

@ -60,6 +60,20 @@
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/first_names" />
<ImageView
android:id="@+id/composerRelatedMessageImage"
android:layout_width="100dp"
android:layout_height="66dp"
android:layout_marginTop="6dp"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@+id/composerRelatedMessageTitle"
app:layout_constraintTop_toBottomOf="@+id/composerRelatedMessageTitle"
tools:ignore="MissingPrefix"
tools:src="@tools:sample/backgrounds/scenic"
tools:visibility="visible" />
<TextView
android:id="@+id/composerRelatedMessageContent"
android:layout_width="0dp"
@ -70,7 +84,7 @@
app:layout_constrainedHeight="true"
app:layout_constraintEnd_toEndOf="@id/composerRelatedMessageTitle"
app:layout_constraintStart_toStartOf="@id/composerRelatedMessageTitle"
app:layout_constraintTop_toBottomOf="@id/composerRelatedMessageTitle"
app:layout_constraintTop_toBottomOf="@id/composerRelatedMessageImage"
tools:text="@tools:sample/lorem/random" />
<ImageView
@ -89,7 +103,6 @@
tools:ignore="MissingPrefix"
tools:src="@drawable/ic_edit" />
<ImageButton
android:id="@+id/composerRelatedMessageCloseButton"
android:layout_width="48dp"
@ -97,9 +110,9 @@
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/cancel"
android:src="@drawable/ic_close_round"
app:layout_constraintBottom_toBottomOf="@id/composerRelatedMessageContent"
app:layout_constraintBottom_toBottomOf="@id/composer_preview_barrier"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/composerRelatedMessageContent"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/riotx_notice"
tools:ignore="MissingPrefix" />

View File

@ -50,6 +50,20 @@
app:layout_constraintEnd_toEndOf="parent"
tools:text="Friday 8pm" />
<ImageView
android:id="@+id/bottom_sheet_message_preview_image"
android:layout_width="100dp"
android:layout_height="66dp"
android:layout_marginTop="6dp"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@+id/bottom_sheet_message_preview_sender"
app:layout_constraintTop_toBottomOf="@id/bottom_sheet_message_preview_sender"
tools:ignore="MissingPrefix"
tools:src="@tools:sample/backgrounds/scenic"
tools:visibility="visible" />
<TextView
android:id="@+id/bottom_sheet_message_preview_body"
android:layout_width="0dp"
@ -65,7 +79,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/bottom_sheet_message_preview_avatar"
app:layout_constraintTop_toBottomOf="@id/bottom_sheet_message_preview_sender"
app:layout_constraintTop_toBottomOf="@id/bottom_sheet_message_preview_image"
tools:text="Quis harum id autem cumque consequatur laboriosam aliquam sed. Sint accusamus dignissimos nobis ullam earum debitis aspernatur. Sint accusamus dignissimos nobis ullam earum debitis aspernatur. " />
</androidx.constraintlayout.widget.ConstraintLayout>