Skip to content
Snippets Groups Projects
Commit 4c9559ea authored by Taras's avatar Taras
Browse files

Merge branch 'feature/media_thumbnails' into develop

parents bcb723b0 664d1176
No related branches found
No related tags found
No related merge requests found
Showing
with 124 additions and 103 deletions
......@@ -9,16 +9,16 @@ import android.widget.TextView
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.RecyclerView
import org.futo.circles.core.extensions.gone
import org.futo.circles.core.extensions.loadEncryptedIntoWithAspect
import org.futo.circles.core.extensions.loadEncryptedThumbOrFullIntoWithAspect
import org.futo.circles.core.extensions.setIsVisible
import org.futo.circles.core.extensions.visible
import org.futo.circles.core.list.ViewBindingHolder
import org.futo.circles.core.list.context
import org.futo.circles.core.model.MediaContent
import org.futo.circles.core.model.MediaType
import org.futo.circles.core.model.PollContent
import org.futo.circles.core.model.Post
import org.futo.circles.core.model.TextContent
import org.futo.circles.core.model.MediaType
import org.futo.circles.databinding.ViewPollPostBinding
import org.futo.circles.databinding.ViewTextMediaPostBinding
import org.futo.circles.feature.timeline.post.markdown.MarkdownParser
......@@ -97,12 +97,12 @@ class TextMediaPostViewHolder(
bindMediaCaption(content)
bindMediaCover(content)
binding.vMediaContent.videoGroup.setIsVisible(content.getMediaType() == MediaType.Video)
binding.vMediaContent.tvDuration.text = content.mediaContentInfo.duration
binding.vMediaContent.tvDuration.text = content.mediaFileData.duration
}
private fun bindMediaCaption(content: MediaContent) {
binding.tvTextContent.apply {
val caption = content.mediaContentInfo.caption
val caption = content.caption
setIsVisible(caption != null)
caption?.let { setText(markwon.toMarkdown(it), TextView.BufferType.SPANNABLE) }
}
......@@ -111,17 +111,13 @@ class TextMediaPostViewHolder(
private fun bindMediaCover(content: MediaContent) {
val image = binding.vMediaContent.ivCover
image.post {
val size = content.calculateSize(image.width)
val size = content.thumbnailOrFullSize(image.width)
image.updateLayoutParams {
width = size.width
height = size.height
}
}
content.mediaFileData.loadEncryptedIntoWithAspect(
image,
content.aspectRatio,
content.mediaContentInfo.thumbHash
)
content.loadEncryptedThumbOrFullIntoWithAspect(image)
}
}
......
......@@ -113,7 +113,7 @@ class PostLayout(
private fun setMentionBorder(content: PostContent) {
val hasMention = when (content) {
is MediaContent -> content.mediaContentInfo.caption?.let {
is MediaContent -> content.caption?.let {
MarkdownParser.hasCurrentUserMention(it)
} ?: false
......
......@@ -11,6 +11,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.updateLayoutParams
import androidx.core.widget.doAfterTextChanged
import org.futo.circles.core.extensions.loadEncryptedIntoWithAspect
import org.futo.circles.core.extensions.loadEncryptedThumbOrFullIntoWithAspect
import org.futo.circles.core.extensions.loadImage
import org.futo.circles.core.extensions.notEmptyDisplayName
import org.futo.circles.core.extensions.setIsVisible
......@@ -103,7 +104,7 @@ class PreviewPostView(
fun setMediaFromExistingPost(mediaContent: MediaContent) {
canEditMedia = false
val caption = mediaContent.mediaContentInfo.caption ?: ""
val caption = mediaContent.caption ?: ""
setText(caption)
val uri = Uri.parse(mediaContent.mediaFileData.fileUrl)
val mediaType = mediaContent.getMediaType()
......@@ -113,7 +114,7 @@ class PreviewPostView(
val isVideo = mediaType == MediaType.Video
binding.lMediaContent.videoGroup.setIsVisible(isVideo)
if (isVideo)
binding.lMediaContent.tvDuration.text = mediaContent.mediaContentInfo.duration
binding.lMediaContent.tvDuration.text = mediaContent.mediaFileData.duration
listener?.onPostContentAvailable(true)
}
......@@ -173,17 +174,13 @@ class PreviewPostView(
private fun loadMediaCover(mediaContent: MediaContent) {
val image = binding.lMediaContent.ivCover
image.post {
val size = mediaContent.calculateSize(image.width)
val size = mediaContent.thumbnailOrFullSize(image.width)
image.updateLayoutParams {
width = size.width
height = size.height
}
}
mediaContent.mediaFileData.loadEncryptedIntoWithAspect(
image,
mediaContent.aspectRatio,
mediaContent.mediaContentInfo.thumbHash
)
mediaContent.loadEncryptedThumbOrFullIntoWithAspect(image)
}
private fun requestFocusOnText() {
......
......@@ -69,7 +69,7 @@ dependencies {
kapt "com.google.dagger:hilt-compiler:$rootProject.ext.hilt_version"
//Matrix
api 'org.matrix.android:matrix-sdk-android:1.5.30.12'
api 'org.matrix.android:matrix-sdk-android:1.5.30.14'
//Retrofit2
def retrofit_version = '2.9.0'
......
package org.futo.circles.core.extensions
import android.util.Size
import android.widget.ImageView
import org.futo.circles.core.model.MediaContent
import org.futo.circles.core.model.MediaFileData
fun MediaContent.loadEncryptedThumbOrFullIntoWithAspect(imageView: ImageView) {
val fileContent = thumbnailFileData ?: mediaFileData
fileContent.loadEncryptedIntoWithAspect(imageView, thumbHash)
}
fun MediaFileData.loadEncryptedIntoWithAspect(
imageView: ImageView,
aspectRatio: Float,
thumbHash: String? = null
) {
imageView.post {
if (fileUrl.startsWith(UriContentScheme)) {
imageView.loadImage(fileUrl)
} else {
val imageWith = imageView.width
val size = Size(imageWith, (imageWith / aspectRatio).toInt())
val size = calculateSize(imageView.width)
imageView.loadEncryptedImage(this, size, thumbHash = thumbHash)
}
}
......
......@@ -3,7 +3,6 @@ package org.futo.circles.core.mapping
import com.bumptech.glide.request.target.Target
import org.futo.circles.core.MediaCaptionFieldKey
import org.futo.circles.core.model.MediaContent
import org.futo.circles.core.model.MediaContentInfo
import org.futo.circles.core.model.MediaFileData
import org.futo.circles.core.model.MediaType
import org.futo.circles.core.model.PostContentType
......@@ -16,22 +15,13 @@ import org.matrix.android.sdk.api.session.room.model.message.getFileName
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
fun TimelineEvent.toMediaContent(mediaType: MediaType): MediaContent {
val messageContentInfo = root.getClearContent().let {
when (mediaType) {
MediaType.Image -> it.toModel<MessageImageContent>()
.toMediaContentInfo(getCaption())
MediaType.Video -> it.toModel<MessageVideoContent>()
.toMediaContentInfo(getCaption())
}
}
return MediaContent(
type = if (mediaType == MediaType.Image) PostContentType.IMAGE_CONTENT else PostContentType.VIDEO_CONTENT,
mediaFileData = toMediaContentData(mediaType),
mediaContentInfo = messageContentInfo
)
}
fun TimelineEvent.toMediaContent(mediaType: MediaType): MediaContent = MediaContent(
type = if (mediaType == MediaType.Image) PostContentType.IMAGE_CONTENT else PostContentType.VIDEO_CONTENT,
caption = getCaption(),
mediaFileData = toMediaFileData(mediaType),
thumbnailFileData = toThumbnailFileData(mediaType),
thumbHash = getThumbHash(mediaType)
)
private fun TimelineEvent.getCaption(): String? {
val lastContent =
......@@ -39,37 +29,81 @@ private fun TimelineEvent.getCaption(): String? {
return lastContent?.get(MediaCaptionFieldKey)?.toString()
}
private fun MessageImageContent?.toMediaContentInfo(caption: String?): MediaContentInfo =
MediaContentInfo(
caption = caption,
thumbnailUrl = this?.info?.thumbnailFile?.url ?: "",
width = this?.info?.width ?: Target.SIZE_ORIGINAL,
height = this?.info?.height ?: Target.SIZE_ORIGINAL,
duration = "",
thumbHash = this?.info?.blurHash
)
private fun TimelineEvent.getThumbHash(mediaType: MediaType) = when (mediaType) {
MediaType.Image -> {
val info = root.getClearContent()?.toModel<MessageImageContent>()?.info
info?.thumbHash ?: info?.blurHash
}
private fun MessageVideoContent?.toMediaContentInfo(caption: String?): MediaContentInfo =
MediaContentInfo(
caption = caption,
thumbnailUrl = this?.videoInfo?.thumbnailFile?.url ?: "",
width = this?.videoInfo?.width ?: Target.SIZE_ORIGINAL,
height = this?.videoInfo?.height ?: Target.SIZE_ORIGINAL,
duration = VideoUtils.getVideoDurationString(this?.videoInfo?.duration?.toLong() ?: 0L),
thumbHash = this?.videoInfo?.blurHash
)
MediaType.Video -> {
val info = root.getClearContent()?.toModel<MessageVideoContent>()?.videoInfo
info?.thumbHash ?: info?.blurHash
}
}
private fun TimelineEvent.toMediaContentData(mediaType: MediaType): MediaFileData {
val messageContent = root.getClearContent().let {
when (mediaType) {
MediaType.Image -> it.toModel<MessageImageContent>()
MediaType.Video -> it.toModel<MessageVideoContent>()
}
private fun TimelineEvent.toMediaFileData(mediaType: MediaType): MediaFileData {
val content = root.getClearContent()
return when (mediaType) {
MediaType.Image -> content.toModel<MessageImageContent>().toMediaFileData()
MediaType.Video -> content.toModel<MessageVideoContent>().toMediaFileData()
}
}
private fun MessageImageContent?.toMediaFileData() = MediaFileData(
fileName = this?.getFileName() ?: "",
mimeType = this?.mimeType ?: "",
fileUrl = this?.getFileUrl() ?: "",
elementToDecrypt = this?.encryptedFileInfo?.toElementToDecrypt(),
width = this?.info?.width ?: Target.SIZE_ORIGINAL,
height = this?.info?.height ?: Target.SIZE_ORIGINAL,
duration = ""
)
private fun MessageVideoContent?.toMediaFileData() = MediaFileData(
fileName = this?.getFileName() ?: "",
mimeType = this?.mimeType ?: "",
fileUrl = this?.getFileUrl() ?: "",
elementToDecrypt = this?.encryptedFileInfo?.toElementToDecrypt(),
width = this?.videoInfo?.width ?: Target.SIZE_ORIGINAL,
height = this?.videoInfo?.height ?: Target.SIZE_ORIGINAL,
duration = VideoUtils.getVideoDurationString(this?.videoInfo?.duration?.toLong() ?: 0L)
)
private fun MessageImageContent.toThumbnailFileData(): MediaFileData? {
val imageInfo = info ?: return null
val file = imageInfo.thumbnailFile?.toElementToDecrypt() ?: return null
val url = imageInfo.thumbnailFile?.url ?: imageInfo.thumbnailUrl
val mimeType = imageInfo.thumbnailInfo?.mimeType ?: ""
return MediaFileData(
fileName = getFileName(),
mimeType = mimeType,
fileUrl = url ?: "",
elementToDecrypt = file,
width = imageInfo.thumbnailInfo?.width ?: Target.SIZE_ORIGINAL,
height = imageInfo.thumbnailInfo?.height ?: Target.SIZE_ORIGINAL,
duration = ""
)
}
private fun MessageVideoContent.toThumbnailFileData(): MediaFileData? {
val videoInfo = videoInfo ?: return null
val file = videoInfo.thumbnailFile?.toElementToDecrypt() ?: return null
val url = videoInfo.thumbnailFile?.url ?: videoInfo.thumbnailUrl
val mimeType = videoInfo.thumbnailInfo?.mimeType ?: ""
return MediaFileData(
fileName = messageContent?.getFileName() ?: "",
mimeType = messageContent?.mimeType ?: "",
fileUrl = messageContent?.getFileUrl() ?: "",
elementToDecrypt = messageContent?.encryptedFileInfo?.toElementToDecrypt(),
fileName = getFileName(),
mimeType = mimeType,
fileUrl = url ?: "",
elementToDecrypt = file,
width = videoInfo.thumbnailInfo?.width ?: Target.SIZE_ORIGINAL,
height = videoInfo.thumbnailInfo?.height ?: Target.SIZE_ORIGINAL,
duration = ""
)
}
private fun TimelineEvent.toThumbnailFileData(mediaType: MediaType) = root.getClearContent().let {
when (mediaType) {
MediaType.Image -> it.toModel<MessageImageContent>()?.toThumbnailFileData()
MediaType.Video -> it.toModel<MessageVideoContent>()?.toThumbnailFileData()
}
}
\ No newline at end of file
package org.futo.circles.core.model
import android.util.Size
import org.matrix.android.sdk.api.session.crypto.attachments.ElementToDecrypt
data class MediaFileData(
val fileName: String,
val mimeType: String,
val fileUrl: String,
val elementToDecrypt: ElementToDecrypt?
)
\ No newline at end of file
val elementToDecrypt: ElementToDecrypt?,
val width: Int,
val height: Int,
val duration: String
) {
val aspectRatio = width.toFloat() / height.toFloat()
fun calculateSize(width: Int) = Size(width, (width / aspectRatio).toInt())
}
\ No newline at end of file
......@@ -23,24 +23,21 @@ data class TextContent(
data class MediaContent(
override val type: PostContentType,
val caption: String?,
val mediaFileData: MediaFileData,
val mediaContentInfo: MediaContentInfo,
val thumbnailFileData: MediaFileData?,
val thumbHash: String?
) : PostContent(type) {
val aspectRatio = mediaContentInfo.width.toFloat() / mediaContentInfo.height.toFloat()
fun calculateSize(width: Int) = Size(width, (width / aspectRatio).toInt())
fun thumbnailOrFullSize(width: Int) = thumbnailFileData?.let {
Size(width, (width / it.aspectRatio).toInt())
} ?: Size(width, (width / mediaFileData.aspectRatio).toInt())
fun getMediaType(): MediaType =
if (type == PostContentType.VIDEO_CONTENT) MediaType.Video else MediaType.Image
}
data class MediaContentInfo(
val caption: String?,
val thumbnailUrl: String,
val width: Int,
val height: Int,
val duration: String,
val thumbHash: String?
)
data class PollContent(
val question: String,
val state: PollState,
......
......@@ -11,7 +11,7 @@ import androidx.recyclerview.widget.RecyclerView
import org.futo.circles.core.R
import org.futo.circles.core.databinding.ListItemGalleryMediaBinding
import org.futo.circles.core.databinding.ListItemGalleryMediaMultiselectBinding
import org.futo.circles.core.extensions.loadEncryptedIntoWithAspect
import org.futo.circles.core.extensions.loadEncryptedThumbOrFullIntoWithAspect
import org.futo.circles.core.extensions.onClick
import org.futo.circles.core.extensions.setIsVisible
import org.futo.circles.core.list.ViewBindingHolder
......@@ -32,24 +32,20 @@ abstract class GridMediaItemViewHolder(view: View) : RecyclerView.ViewHolder(vie
private fun bindCover(id: String, mediaContent: MediaContent) {
ivCover.transitionName = id
ivCover.post {
val size = mediaContent.calculateSize(ivCover.width)
val size = mediaContent.thumbnailOrFullSize(ivCover.width)
ivCover.updateLayoutParams {
width = size.width
height = size.height
}
}
mediaContent.mediaFileData.loadEncryptedIntoWithAspect(
ivCover,
mediaContent.aspectRatio,
mediaContent.mediaContentInfo.thumbHash
)
mediaContent.loadEncryptedThumbOrFullIntoWithAspect(ivCover)
}
private fun bindVideoParams(
mediaContent: MediaContent
) {
videoGroup.setIsVisible(mediaContent.type == PostContentType.VIDEO_CONTENT)
tvDuration.text = mediaContent.mediaContentInfo.duration
tvDuration.text = mediaContent.mediaFileData.duration
}
}
......
......@@ -85,14 +85,12 @@ class SendMessageDataSource @Inject constructor(@ApplicationContext private val
MediaType.Image -> uri.toImageContentAttachmentData(context)
MediaType.Video -> uri.toVideoContentAttachmentData(context)
} ?: return null
val shouldCompress =
if (compressBeforeSending) content.mimeType != WEBP_MIME_TYPE else false
val additionalContent = mutableMapOf<String, Any>()
caption?.let { additionalContent[MediaCaptionFieldKey] = it }
return roomForMessage.sendService().sendMedia(
content,
shouldCompress,
compressBeforeSending,
emptySet(),
rootThreadEventId = threadEventId,
additionalContent = additionalContent
......@@ -128,8 +126,4 @@ class SendMessageDataSource @Inject constructor(@ApplicationContext private val
roomForMessage.relationService()
.editPoll(event, pollContent.pollType, pollContent.question, pollContent.options)
}
companion object {
private const val WEBP_MIME_TYPE = "image/webp"
}
}
\ No newline at end of file
......@@ -76,11 +76,7 @@ class FullScreenMediaFragment : Fragment(R.layout.fragment_full_screen_media) {
transitionName = null
gone()
}
it.mediaFileData.loadEncryptedIntoWithAspect(
binding.ivImage,
it.aspectRatio,
it.mediaContentInfo.thumbHash
)
it.mediaFileData.loadEncryptedIntoWithAspect(binding.ivImage, it.thumbHash)
binding.ivImage.post { parentFragment?.startPostponedEnterTransition() }
}
viewModel.videoLiveData.observeData(this) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment