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 5ff7a07e3c..f05e0843e8 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 @@ -46,6 +46,9 @@ 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 bodyDetails by bind(R.id.bottom_sheet_message_preview_body_details) 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/core/utils/TextUtils.kt b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt index 84dd793172..992a85679c 100644 --- a/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt +++ b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt @@ -19,6 +19,7 @@ package im.vector.app.core.utils import android.content.Context import android.os.Build import android.text.format.Formatter +import org.threeten.bp.Duration import java.util.TreeMap object TextUtils { @@ -68,4 +69,15 @@ object TextUtils { Formatter.formatFileSize(context, normalizedSize) } } + + fun formatDuration(duration: Duration): String { + val hours = duration.seconds / 3600 + val minutes = (duration.seconds % 3600) / 60 + val seconds = duration.seconds % 60 + return if (hours > 0) { + String.format("%d:%02d:%02d", hours, minutes, seconds) + } else { + String.format("%02d:%02d", minutes, seconds) + } + } } diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt index 469fba4d5e..fcd6bf0a77 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt @@ -21,6 +21,7 @@ import android.hardware.camera2.CameraManager import androidx.core.content.getSystemService import im.vector.app.core.services.CallService import im.vector.app.core.utils.CountUpTimer +import im.vector.app.core.utils.TextUtils.formatDuration import im.vector.app.features.call.CameraEventsHandlerAdapter import im.vector.app.features.call.CameraProxy import im.vector.app.features.call.CameraType @@ -829,17 +830,6 @@ class WebRtcCall(val mxCall: MxCall, } } - private fun formatDuration(duration: Duration): String { - val hours = duration.seconds / 3600 - val minutes = (duration.seconds % 3600) / 60 - val seconds = duration.seconds % 60 - return if (hours > 0) { - String.format("%d:%02d:%02d", hours, minutes, seconds) - } else { - String.format("%02d:%02d", minutes, seconds) - } - } - // MxCall.StateListener override fun onStateUpdate(call: MxCall) { 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 d62083de30..dcbc2c3293 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 @@ -33,6 +33,7 @@ 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.format.EventDetailsFormatter 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 @@ -53,6 +54,7 @@ class MessageActionsEpoxyController @Inject constructor( private val imageContentRenderer: ImageContentRenderer, private val dimensionConverter: DimensionConverter, private val errorFormatter: ErrorFormatter, + private val eventDetailsFormatter: EventDetailsFormatter, private val dateFormatter: VectorDateFormatter ) : TypedEpoxyController() { @@ -71,6 +73,7 @@ class MessageActionsEpoxyController @Inject constructor( data(state.timelineEvent()?.buildImageContentRendererData(dimensionConverter.dpToPx(66))) userClicked { listener?.didSelectMenuAction(EventSharedAction.OpenUserProfile(state.informationData.senderId)) } body(state.messageBody.linkify(listener)) + bodyDetails(eventDetailsFormatter.format(state.timelineEvent()?.root)) time(formattedDate) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/EventDetailsFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/EventDetailsFormatter.kt new file mode 100644 index 0000000000..d5b2367c15 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/EventDetailsFormatter.kt @@ -0,0 +1,92 @@ +/* + * Copyright 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.format + +import android.content.Context +import im.vector.app.core.utils.TextUtils +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.isAudioMessage +import org.matrix.android.sdk.api.session.events.model.isFileMessage +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.MessageAudioContent +import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent +import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent +import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent +import org.threeten.bp.Duration +import javax.inject.Inject + +class EventDetailsFormatter @Inject constructor( + private val context: Context +) { + + fun format(event: Event?): CharSequence? { + event ?: return null + + if (event.isRedacted()) { + return null + } + + if (event.isEncrypted() && event.mxDecryptionResult == null) { + return null + } + + return when { + event.isImageMessage() -> formatForImageMessage(event) + event.isVideoMessage() -> formatForVideoMessage(event) + event.isAudioMessage() -> formatForAudioMessage(event) + event.isFileMessage() -> formatForFileMessage(event) + else -> null + } + } + + /** + * Example: "1024 x 720 - 670 kB" + */ + private fun formatForImageMessage(event: Event): CharSequence? { + return event.getClearContent().toModel()?.info + ?.let { "${it.width} x ${it.height} - ${it.size.asFileSize()}" } + } + + /** + * Example: "02:45 - 1024 x 720 - 670 kB" + */ + private fun formatForVideoMessage(event: Event): CharSequence? { + return event.getClearContent().toModel()?.videoInfo + ?.let { "${it.duration.asDuration()} - ${it.width} x ${it.height} - ${it.size.asFileSize()}" } + } + + /** + * Example: "02:45 - 670 kB" + */ + private fun formatForAudioMessage(event: Event): CharSequence? { + return event.getClearContent().toModel()?.audioInfo + ?.let { "${it.duration.asDuration()} - ${it.size.asFileSize()}" } + } + + /** + * Example: "670 kB - application/pdf" + */ + private fun formatForFileMessage(event: Event): CharSequence? { + return event.getClearContent().toModel()?.info + ?.let { "${it.size.asFileSize()} - ${it.mimeType}" } + } + + private fun Long.asFileSize() = TextUtils.formatFileSize(context, this) + private fun Int.asDuration() = TextUtils.formatDuration(Duration.ofMillis(toLong())) +} 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 afd96d5690..c2175d495e 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 @@ -76,10 +76,29 @@ android:textColor="?riotx_text_secondary" android:textIsSelectable="false" android:textSize="14sp" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toTopOf="@+id/bottom_sheet_message_preview_body_details" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/bottom_sheet_message_preview_avatar" 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. " /> + app:layout_goneMarginBottom="4dp" + 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." /> + +