Bubbles: change again url preview
This commit is contained in:
parent
2d9454c5b6
commit
8c4dff4db9
|
@ -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>
|
||||
|
|
|
@ -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?
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -27,5 +27,7 @@ internal fun PreviewUrlCacheEntity.toDomain() = PreviewUrlData(
|
|||
siteName = siteName,
|
||||
title = title,
|
||||
description = description,
|
||||
mxcUrl = mxcUrl
|
||||
mxcUrl = mxcUrl,
|
||||
imageWidth = imageWidth,
|
||||
imageHeight = imageHeight
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue