[merge] Bring back old url preview code

Already added back old layout in merge, but that's not enough

Change-Id: I0c262f1a33890959bde77a0d54916a88c4ff2cc9
This commit is contained in:
SpiritCroc 2021-12-23 16:12:00 +01:00
parent ca8d940c0e
commit f3874be337
4 changed files with 198 additions and 61 deletions

View File

@ -17,10 +17,8 @@
package im.vector.app.features.home.room.detail.timeline.item
import android.content.Context
import android.graphics.Color
import android.text.TextUtils
import android.text.method.MovementMethod
import android.widget.LinearLayout
import androidx.core.text.PrecomputedTextCompat
import androidx.core.view.isVisible
import androidx.core.widget.TextViewCompat
@ -36,7 +34,7 @@ import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlUiState
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlView
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlViewSc
import im.vector.app.features.media.ImageContentRenderer
import org.matrix.android.sdk.api.extensions.orFalse
@ -152,11 +150,11 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
class Holder : AbsMessageItem.Holder(STUB_ID) {
val messageView by bind<FooteredTextView>(R.id.messageTextView)
val previewUrlView by bind<PreviewUrlView>(R.id.messageUrlPreview)
val previewUrlView by bind<PreviewUrlViewSc>(R.id.messageUrlPreview)
}
inner class PreviewUrlViewUpdater : PreviewUrlRetriever.PreviewUrlRetrieverListener {
var previewUrlView: PreviewUrlView? = null
var previewUrlView: PreviewUrlViewSc? = null
var holder: Holder? = null
var imageContentRenderer: ImageContentRenderer? = null

View File

@ -23,7 +23,7 @@ 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.databinding.ViewUrlPreviewScBinding
import im.vector.app.databinding.ViewUrlPreviewBinding
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.media.ImageContentRenderer
import im.vector.app.features.themes.ThemeUtils
@ -39,10 +39,7 @@ class PreviewUrlView @JvmOverloads constructor(
defStyleAttr: Int = 0
) : MaterialCardView(context, attrs, defStyleAttr), View.OnClickListener {
private lateinit var views: ViewUrlPreviewScBinding
var footerWidth: Int = 0
var footerHeight: Int = 0
private lateinit var views: ViewUrlPreviewBinding
var delegate: TimelineEventController.PreviewUrlCallback? = null
@ -106,33 +103,11 @@ class PreviewUrlView @JvmOverloads constructor(
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// Get max available width - we're faking "wrap_content" here to use all available space,
// since match_parent doesn't work here as our parent does wrap_content as well
/*
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val widthLimit = if (widthMode == MeasureSpec.AT_MOST) {
widthSize.toFloat()
} else {
Float.MAX_VALUE
}
*/
//setMeasuredDimension(round(widthLimit).toInt(), measuredHeight)
// We extract the size from an AT_MOST spec, which is the width limit, but change the mode to EXACTLY
val newWidthSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY)
// We measure our children based on the now fixed width
super.onMeasure(newWidthSpec, heightMeasureSpec)
}
// PRIVATE METHODS ****************************************************************************************************************************************
private fun setupView() {
inflate(context, R.layout.view_url_preview_sc, this)
views = ViewUrlPreviewScBinding.bind(this)
inflate(context, R.layout.view_url_preview, this)
views = ViewUrlPreviewBinding.bind(this)
setOnClickListener(this)
views.urlPreviewImage.setOnClickListener { onImageClick() }
@ -149,21 +124,17 @@ class PreviewUrlView @JvmOverloads constructor(
}
private fun renderData(previewUrlData: PreviewUrlData, imageContentRenderer: ImageContentRenderer) {
// Set footer sizes before setText() calls so they are available onMeasure
val siteText = previewUrlData.siteName.takeIf { it != previewUrlData.title }
updateFooterSpaceInternal(siteText)
isVisible = true
views.urlPreviewTitle.setTextOrHide(previewUrlData.title)
views.urlPreviewImage.isVisible = previewUrlData.mxcUrl?.let { imageContentRenderer.render(it, views.urlPreviewImage, hideOnFail = true) }.orFalse()
views.urlPreviewImage.isVisible = previewUrlData.mxcUrl?.let { imageContentRenderer.render(it, views.urlPreviewImage) }.orFalse()
views.urlPreviewDescription.setTextOrHide(previewUrlData.description)
views.urlPreviewDescription.maxLines = when {
previewUrlData.mxcUrl != null -> 2
previewUrlData.title != null -> 3
else -> 5
}
views.urlPreviewSite.setTextOrHide(siteText)
views.urlPreviewSite.setTextOrHide(previewUrlData.siteName.takeIf { it != previewUrlData.title })
}
/**
@ -175,23 +146,4 @@ class PreviewUrlView @JvmOverloads constructor(
views.urlPreviewDescription.isVisible = false
views.urlPreviewSite.isVisible = false
}
public fun updateFooterSpace() {
val siteText = views.urlPreviewSite.text as String?
updateFooterSpaceInternal(siteText)
requestLayout()
}
private fun updateFooterSpaceInternal(siteText: String?) {
val siteViewHidden = siteText == null || siteText.isBlank() // identical to setTextOrHide
if (siteViewHidden) {
views.urlPreviewDescription.footerWidth = footerWidth
views.urlPreviewDescription.footerHeight = footerHeight
} else {
views.urlPreviewSite.footerWidth = footerWidth
views.urlPreviewSite.footerHeight = footerHeight
views.urlPreviewDescription.footerWidth = 0
views.urlPreviewDescription.footerHeight = 0
}
}
}

View File

@ -0,0 +1,187 @@
/*
* Copyright (c) 2020 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.url
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import im.vector.app.R
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.databinding.ViewUrlPreviewScBinding
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.media.ImageContentRenderer
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.media.PreviewUrlData
/**
* A View to display a PreviewUrl and some other state
*/
class PreviewUrlViewSc @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), View.OnClickListener {
private lateinit var views: ViewUrlPreviewScBinding
var footerWidth: Int = 0
var footerHeight: Int = 0
var delegate: TimelineEventController.PreviewUrlCallback? = null
init {
setupView()
}
private var state: PreviewUrlUiState = PreviewUrlUiState.Unknown
/**
* This methods is responsible for rendering the view according to the newState
*
* @param newState the newState representing the view
*/
fun render(newState: PreviewUrlUiState,
imageContentRenderer: ImageContentRenderer,
force: Boolean = false) {
if (newState == state && !force) {
return
}
state = newState
hideAll()
when (newState) {
PreviewUrlUiState.Unknown,
PreviewUrlUiState.NoUrl -> renderHidden()
PreviewUrlUiState.Loading -> renderLoading()
is PreviewUrlUiState.Error -> renderHidden()
is PreviewUrlUiState.Data -> renderData(newState.previewUrlData, imageContentRenderer)
}
}
override fun onClick(v: View?) {
when (val finalState = state) {
is PreviewUrlUiState.Data -> delegate?.onPreviewUrlClicked(finalState.url)
else -> Unit
}
}
private fun onImageClick() {
when (val finalState = state) {
is PreviewUrlUiState.Data -> {
delegate?.onPreviewUrlImageClicked(
sharedView = views.urlPreviewImage,
mxcUrl = finalState.previewUrlData.mxcUrl,
title = finalState.previewUrlData.title
)
}
else -> Unit
}
}
private fun onCloseClick() {
when (val finalState = state) {
is PreviewUrlUiState.Data -> delegate?.onPreviewUrlCloseClicked(finalState.eventId, finalState.url)
else -> Unit
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// Get max available width - we're faking "wrap_content" here to use all available space,
// since match_parent doesn't work here as our parent does wrap_content as well
/*
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val widthLimit = if (widthMode == MeasureSpec.AT_MOST) {
widthSize.toFloat()
} else {
Float.MAX_VALUE
}
*/
//setMeasuredDimension(round(widthLimit).toInt(), measuredHeight)
// We extract the size from an AT_MOST spec, which is the width limit, but change the mode to EXACTLY
val newWidthSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY)
// We measure our children based on the now fixed width
super.onMeasure(newWidthSpec, heightMeasureSpec)
}
// PRIVATE METHODS ****************************************************************************************************************************************
private fun setupView() {
inflate(context, R.layout.view_url_preview_sc, this)
views = ViewUrlPreviewScBinding.bind(this)
setOnClickListener(this)
views.urlPreviewImage.setOnClickListener { onImageClick() }
views.urlPreviewClose.setOnClickListener { onCloseClick() }
}
private fun renderHidden() {
isVisible = false
}
private fun renderLoading() {
// Just hide for the moment
isVisible = false
}
private fun renderData(previewUrlData: PreviewUrlData, imageContentRenderer: ImageContentRenderer) {
// Set footer sizes before setText() calls so they are available onMeasure
val siteText = previewUrlData.siteName.takeIf { it != previewUrlData.title }
updateFooterSpaceInternal(siteText)
isVisible = true
views.urlPreviewTitle.setTextOrHide(previewUrlData.title)
views.urlPreviewImage.isVisible = previewUrlData.mxcUrl?.let { imageContentRenderer.render(it, views.urlPreviewImage, hideOnFail = true) }.orFalse()
views.urlPreviewDescription.setTextOrHide(previewUrlData.description)
views.urlPreviewSite.setTextOrHide(siteText)
}
/**
* Hide all views that are not visible in all state
*/
private fun hideAll() {
views.urlPreviewTitle.isVisible = false
views.urlPreviewImage.isVisible = false
views.urlPreviewDescription.isVisible = false
views.urlPreviewSite.isVisible = false
}
public fun updateFooterSpace() {
val siteText = views.urlPreviewSite.text as String?
updateFooterSpaceInternal(siteText)
requestLayout()
}
private fun updateFooterSpaceInternal(siteText: String?) {
val siteViewHidden = siteText == null || siteText.isBlank() // identical to setTextOrHide
if (siteViewHidden) {
views.urlPreviewDescription.footerWidth = footerWidth
views.urlPreviewDescription.footerHeight = footerHeight
} else {
views.urlPreviewSite.footerWidth = footerWidth
views.urlPreviewSite.footerHeight = footerHeight
views.urlPreviewDescription.footerWidth = 0
views.urlPreviewDescription.footerHeight = 0
}
}
}

View File

@ -25,9 +25,9 @@
tools:text="@sample/messages.json/data/message"
tools:ignore="RtlHardcoded" />
<im.vector.app.features.home.room.detail.timeline.url.PreviewUrlView
<im.vector.app.features.home.room.detail.timeline.url.PreviewUrlViewSc
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"