Bubbles: change again url preview

This commit is contained in:
ganfra 2022-01-20 15:13:54 +01:00
parent 2d9454c5b6
commit 8c4dff4db9
11 changed files with 123 additions and 49 deletions

View file

@ -44,6 +44,7 @@
<!-- Preview Url -->
<dimen name="preview_url_view_corner_radius">8dp</dimen>
<dimen name="preview_url_view_image_max_height">160dp</dimen>
<!-- Composer -->
<dimen name="composer_min_height">56dp</dimen>

View file

@ -47,5 +47,9 @@ data class PreviewUrlData(
// Value of field "og:description"
val description: String?,
// Value of field "og:image"
val mxcUrl: String?
val mxcUrl: String?,
// Value of field "og:image:width"
val imageWidth: Int?,
// Value of field "og:image:height"
val imageHeight: Int?
)

View file

@ -56,7 +56,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
) : RealmMigration {
companion object {
const val SESSION_STORE_SCHEMA_VERSION = 21L
const val SESSION_STORE_SCHEMA_VERSION = 22L
}
/**
@ -90,6 +90,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion <= 18) migrateTo19(realm)
if (oldVersion <= 19) migrateTo20(realm)
if (oldVersion <= 20) migrateTo21(realm)
if (oldVersion <= 21) migrateTo22(realm)
}
private fun migrateTo1(realm: DynamicRealm) {
@ -445,4 +446,15 @@ internal class RealmSessionStoreMigration @Inject constructor(
}
}
}
private fun migrateTo22(realm: DynamicRealm) {
Timber.d("Step 21 -> 22")
realm.schema.get("PreviewUrlCacheEntity")
?.addField(PreviewUrlCacheEntityFields.IMAGE_WIDTH, Int::class.java)
?.setNullable(PreviewUrlCacheEntityFields.IMAGE_WIDTH, true)
?.addField(PreviewUrlCacheEntityFields.IMAGE_HEIGHT, Int::class.java)
?.setNullable(PreviewUrlCacheEntityFields.IMAGE_HEIGHT, true)
}
}

View file

@ -28,7 +28,8 @@ internal open class PreviewUrlCacheEntity(
var title: String? = null,
var description: String? = null,
var mxcUrl: String? = null,
var imageWidth: Int? = null,
var imageHeight: Int? = null,
var lastUpdatedTimestamp: Long = 0L
) : RealmObject() {

View file

@ -48,8 +48,8 @@ internal class DefaultGetPreviewUrlTask @Inject constructor(
override suspend fun execute(params: GetPreviewUrlTask.Params): PreviewUrlData {
return when (params.cacheStrategy) {
CacheStrategy.NoCache -> doRequest(params.url, params.timestamp)
is CacheStrategy.TtlCache -> doRequestWithCache(
CacheStrategy.NoCache -> doRequest(params.url, params.timestamp)
is CacheStrategy.TtlCache -> doRequestWithCache(
params.url,
params.timestamp,
params.cacheStrategy.validityDurationInMillis,
@ -77,7 +77,9 @@ internal class DefaultGetPreviewUrlTask @Inject constructor(
siteName = (get("og:site_name") as? String)?.unescapeHtml(),
title = (get("og:title") as? String)?.unescapeHtml(),
description = (get("og:description") as? String)?.unescapeHtml(),
mxcUrl = get("og:image") as? String
mxcUrl = get("og:image") as? String,
imageHeight = (get("og:image:height") as? Double)?.toInt(),
imageWidth = (get("og:image:width") as? Double)?.toInt(),
)
}
@ -114,7 +116,8 @@ internal class DefaultGetPreviewUrlTask @Inject constructor(
previewUrlCacheEntity.title = data.title
previewUrlCacheEntity.description = data.description
previewUrlCacheEntity.mxcUrl = data.mxcUrl
previewUrlCacheEntity.imageHeight = data.imageHeight
previewUrlCacheEntity.imageWidth = data.imageWidth
previewUrlCacheEntity.lastUpdatedTimestamp = Date().time
}

View file

@ -27,5 +27,7 @@ internal fun PreviewUrlCacheEntity.toDomain() = PreviewUrlData(
siteName = siteName,
title = title,
description = description,
mxcUrl = mxcUrl
mxcUrl = mxcUrl,
imageWidth = imageWidth,
imageHeight = imageHeight
)

View file

@ -83,12 +83,7 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
safePreviewUrlRetriever.addListener(attributes.informationData.eventId, previewUrlViewUpdater)
}
holder.previewUrlView.delegate = previewUrlCallback
val urlPreviewBackgroundColor = if (attributes.informationData.messageLayout is TimelineMessageLayout.Bubble) {
Color.TRANSPARENT
} else {
ThemeUtils.getColor(holder.view.context, R.attr.vctr_system)
}
holder.previewUrlView.setCardBackgroundColor(urlPreviewBackgroundColor)
holder.previewUrlView.render(attributes.informationData.messageLayout)
if (useBigFont) {
holder.messageView.textSize = 44F

View file

@ -17,14 +17,20 @@
package im.vector.app.features.home.room.detail.timeline.url
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.util.AttributeSet
import android.view.View
import androidx.core.view.isVisible
import com.google.android.material.card.MaterialCardView
import im.vector.app.R
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.glide.GlideApp
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.databinding.ViewUrlPreviewBinding
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
import im.vector.app.features.home.room.detail.timeline.view.TimelineMessageLayoutRenderer
import im.vector.app.features.media.ImageContentRenderer
import im.vector.app.features.themes.ThemeUtils
import org.matrix.android.sdk.api.extensions.orFalse
@ -37,7 +43,7 @@ class PreviewUrlView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : MaterialCardView(context, attrs, defStyleAttr), View.OnClickListener {
) : MaterialCardView(context, attrs, defStyleAttr), View.OnClickListener, TimelineMessageLayoutRenderer {
private lateinit var views: ViewUrlPreviewBinding
@ -75,6 +81,22 @@ class PreviewUrlView @JvmOverloads constructor(
}
}
override fun render(messageLayout: TimelineMessageLayout) {
when (messageLayout) {
is TimelineMessageLayout.Default -> {
val backgroundColor = ThemeUtils.getColor(context, R.attr.vctr_system)
setCardBackgroundColor(backgroundColor)
val guidelineBegin = DimensionConverter(resources).dpToPx(8)
views.urlPreviewStartGuideline.setGuidelineBegin(guidelineBegin)
}
is TimelineMessageLayout.Bubble -> {
setCardBackgroundColor(Color.TRANSPARENT)
rippleColor = ColorStateList.valueOf(Color.TRANSPARENT)
views.urlPreviewStartGuideline.setGuidelineBegin(0)
}
}
}
override fun onClick(v: View?) {
when (val finalState = state) {
is PreviewUrlUiState.Data -> delegate?.onPreviewUrlClicked(finalState.url)
@ -126,7 +148,7 @@ class PreviewUrlView @JvmOverloads constructor(
isVisible = true
views.urlPreviewTitle.setTextOrHide(previewUrlData.title)
views.urlPreviewImage.isVisible = previewUrlData.mxcUrl?.let { imageContentRenderer.render(it, views.urlPreviewImage) }.orFalse()
views.urlPreviewImage.isVisible = imageContentRenderer.render(previewUrlData, views.urlPreviewImage)
views.urlPreviewDescription.setTextOrHide(previewUrlData.description)
views.urlPreviewDescription.maxLines = when {
previewUrlData.mxcUrl != null -> 2

View file

@ -44,6 +44,7 @@ import im.vector.app.core.utils.DimensionConverter
import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
import org.matrix.android.sdk.api.session.media.PreviewUrlData
import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt
import timber.log.Timber
import java.io.File
@ -61,6 +62,9 @@ interface AttachmentData : Parcelable {
val allowNonMxcUrls: Boolean
}
private const val URL_PREVIEW_IMAGE_MIN_FULL_WIDTH_PX = 600
private const val URL_PREVIEW_IMAGE_MIN_FULL_HEIGHT_PX = 315
class ImageContentRenderer @Inject constructor(private val localFilesHelper: LocalFilesHelper,
private val activeSessionHolder: ActiveSessionHolder,
private val dimensionConverter: DimensionConverter) {
@ -89,12 +93,20 @@ class ImageContentRenderer @Inject constructor(private val localFilesHelper: Loc
/**
* For url preview
*/
fun render(mxcUrl: String, imageView: ImageView): Boolean {
fun render(previewUrlData: PreviewUrlData, imageView: ImageView): Boolean {
val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
val imageUrl = contentUrlResolver.resolveFullSize(mxcUrl) ?: return false
val imageUrl = contentUrlResolver.resolveFullSize(previewUrlData.mxcUrl) ?: return false
val maxHeight = dimensionConverter.resources.getDimensionPixelSize(R.dimen.preview_url_view_image_max_height)
val height = previewUrlData.imageHeight ?: URL_PREVIEW_IMAGE_MIN_FULL_HEIGHT_PX
val width = previewUrlData.imageWidth ?: URL_PREVIEW_IMAGE_MIN_FULL_WIDTH_PX
if (height < URL_PREVIEW_IMAGE_MIN_FULL_HEIGHT_PX || width < URL_PREVIEW_IMAGE_MIN_FULL_WIDTH_PX) {
imageView.scaleType = ImageView.ScaleType.CENTER_INSIDE
} else {
imageView.scaleType = ImageView.ScaleType.CENTER_CROP
}
GlideApp.with(imageView)
.load(imageUrl)
.override(width, height.coerceAtMost(maxHeight))
.into(imageView)
return true
}

View file

@ -17,12 +17,11 @@
<im.vector.app.features.home.room.detail.timeline.url.PreviewUrlView
android:id="@+id/messageUrlPreview"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"
android:foreground="?attr/selectableItemBackground"
android:visibility="gone"
tools:visibility="gone" />
tools:visibility="visible" />
</LinearLayout>

View file

@ -1,79 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/informationUrlPreviewContainer"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:parentTag="com.google.android.material.card.MaterialCardView">
<LinearLayout
android:layout_width="wrap_content"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="@dimen/chat_bubble_fixed_size"
android:minWidth="208dp"
android:orientation="vertical">
<!--Image dimensions will be overrode by ImageContentRenderer -->
<ImageView
android:id="@+id/url_preview_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintHeight_max="208dp"
android:scaleType="centerCrop"
android:importantForAccessibility="no"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:src="@tools:sample/backgrounds/scenic" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/url_preview_start_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:importantForAccessibility="no"
android:layout_gravity="center"
android:maxHeight="200dp"
android:scaleType="fitXY"
tools:src="@tools:sample/backgrounds/scenic" />
android:orientation="vertical"
app:layout_constraintGuide_begin="8dp" />
<TextView
android:id="@+id/url_preview_site"
style="@style/Widget.Vector.TextView.Caption"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:singleLine="true"
app:layout_constraintEnd_toStartOf="@id/url_preview_close"
android:textColor="?vctr_content_secondary"
app:layout_constraintStart_toStartOf="@id/url_preview_start_guideline"
app:layout_constraintTop_toBottomOf="@id/url_preview_image"
app:layout_goneMarginTop="12dp"
tools:text="BBC News" />
<TextView
android:id="@+id/url_preview_title"
style="@style/Widget.Vector.TextView.Body.Medium"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="@dimen/layout_touch_size"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="2"
android:textColor="?vctr_content_primary"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@id/url_preview_close"
app:layout_constraintStart_toStartOf="@id/url_preview_start_guideline"
app:layout_constraintTop_toBottomOf="@id/url_preview_site"
app:layout_goneMarginTop="12dp"
tools:text="Jo Malone denounces her former brand's John Boyega decision" />
<TextView
android:id="@+id/url_preview_description"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:ellipsize="end"
android:textColor="?vctr_content_secondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/url_preview_start_guideline"
app:layout_constraintTop_toBottomOf="@id/url_preview_title"
tools:text="The British perfumer says removing actor John Boyega from his own advert was “utterly despicable”." />
</LinearLayout>
<ImageView
android:id="@+id/url_preview_close"
android:layout_width="@dimen/layout_touch_size"
android:layout_height="@dimen/layout_touch_size"
android:layout_gravity="top|end"
android:contentDescription="@string/action_close"
android:scaleType="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="@drawable/ic_close_with_circular_bg"
tools:ignore="MissingPrefix" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="@+id/url_preview_close"
android:layout_width="@dimen/layout_touch_size"
android:layout_height="@dimen/layout_touch_size"
android:layout_gravity="top|end"
android:contentDescription="@string/action_close"
android:scaleType="center"
android:src="@drawable/ic_close_with_circular_bg"
tools:ignore="MissingPrefix" />
</merge>