Merge pull request #3034 from vector-im/feature/bma/reply_image_preview
Picture preview when replying.
This commit is contained in:
commit
2b70a8450c
|
@ -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 🐛:
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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" />
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue