PreviewUrl: Application part - WIP
This commit is contained in:
parent
fcd9fe7d5a
commit
48354c7793
@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class UrlsExtractor @Inject constructor() {
|
||||
@ -30,6 +31,7 @@ internal class UrlsExtractor @Inject constructor() {
|
||||
return event.takeIf { it.getClearType() == EventType.MESSAGE }
|
||||
?.getClearContent()
|
||||
?.toModel<MessageContent>()
|
||||
?.takeIf { it.msgType == MessageType.MSGTYPE_TEXT || it.msgType == MessageType.MSGTYPE_EMOTE }
|
||||
?.body
|
||||
?.let { urlRegex.findAll(it) }
|
||||
?.map { it.value }
|
||||
|
147
vector/src/main/java/im/vector/app/core/ui/views/PreviewUrlView.kt
Executable file
147
vector/src/main/java/im/vector/app/core/ui/views/PreviewUrlView.kt
Executable file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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.core.ui.views
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isVisible
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.setTextOrHide
|
||||
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlUiState
|
||||
import im.vector.app.features.media.ImageContentRenderer
|
||||
import org.matrix.android.sdk.api.session.media.PreviewUrlData
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* A View to display a PreviewUrl and some other state
|
||||
*/
|
||||
class PreviewUrlView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr), View.OnClickListener {
|
||||
|
||||
@BindView(R.id.url_preview_title)
|
||||
lateinit var titleView: TextView
|
||||
|
||||
@BindView(R.id.url_preview_image)
|
||||
lateinit var imageView: ImageView
|
||||
|
||||
@BindView(R.id.url_preview_description)
|
||||
lateinit var descriptionView: TextView
|
||||
|
||||
@BindView(R.id.url_preview_site)
|
||||
lateinit var siteView: TextView
|
||||
|
||||
var delegate: Delegate? = 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) {
|
||||
Timber.v("State unchanged")
|
||||
return
|
||||
}
|
||||
Timber.v("Rendering $newState")
|
||||
|
||||
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?.onUrlClicked(finalState.previewUrlData.url)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
// PRIVATE METHODS ****************************************************************************************************************************************
|
||||
|
||||
private fun setupView() {
|
||||
inflate(context, R.layout.url_preview, this)
|
||||
ButterKnife.bind(this)
|
||||
|
||||
setOnClickListener(this)
|
||||
}
|
||||
|
||||
private fun renderHidden() {
|
||||
isVisible = false
|
||||
}
|
||||
|
||||
private fun renderLoading() {
|
||||
// TODO
|
||||
isVisible = false
|
||||
}
|
||||
|
||||
private fun renderData(previewUrlData: PreviewUrlData, imageContentRenderer: ImageContentRenderer) {
|
||||
isVisible = true
|
||||
titleView.setTextOrHide(previewUrlData.title)
|
||||
val mxcUrl = previewUrlData.mxcUrl
|
||||
imageView.isVisible = mxcUrl != null
|
||||
if (mxcUrl != null) {
|
||||
imageContentRenderer.render(mxcUrl, imageView)
|
||||
}
|
||||
descriptionView.setTextOrHide(previewUrlData.description)
|
||||
siteView.setTextOrHide(previewUrlData.siteName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide all views that are not visible in all state
|
||||
*/
|
||||
private fun hideAll() {
|
||||
titleView.isVisible = false
|
||||
imageView.isVisible = false
|
||||
descriptionView.isVisible = false
|
||||
siteView.isVisible = false
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface to delegate some actions to another object
|
||||
*/
|
||||
interface Delegate {
|
||||
// TODO
|
||||
fun onUrlClicked(url: String)
|
||||
|
||||
// TODO
|
||||
// fun close()
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@ import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator
|
||||
import im.vector.app.features.home.room.detail.sticker.StickerPickerActionHandler
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.RoomSummaryHolder
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.TimelineSettingsFactory
|
||||
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
||||
import im.vector.app.features.home.room.typing.TypingHelper
|
||||
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
|
||||
import im.vector.app.features.raw.wellknown.getElementWellknown
|
||||
@ -112,6 +113,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
private val rainbowGenerator: RainbowGenerator,
|
||||
private val session: Session,
|
||||
private val rawService: RawService,
|
||||
private val previewUrlRetriever: PreviewUrlRetriever,
|
||||
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider,
|
||||
private val stickerPickerActionHandler: StickerPickerActionHandler,
|
||||
private val roomSummaryHolder: RoomSummaryHolder,
|
||||
@ -1350,6 +1352,12 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
|
||||
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
|
||||
timelineEvents.accept(snapshot)
|
||||
|
||||
// PreviewUrl
|
||||
// TODO Check if URL preview is enable, check if encrypted room, etc.
|
||||
snapshot.forEach {
|
||||
previewUrlRetriever.getPreviewUrl(it.root, viewModelScope)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTimelineFailure(throwable: Throwable) {
|
||||
|
@ -58,6 +58,7 @@ import im.vector.app.features.home.room.detail.timeline.item.VerificationRequest
|
||||
import im.vector.app.features.home.room.detail.timeline.item.VerificationRequestItem_
|
||||
import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovementMethod
|
||||
import im.vector.app.features.home.room.detail.timeline.tools.linkify
|
||||
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
||||
import im.vector.app.features.html.CodeVisitor
|
||||
import im.vector.app.features.html.EventHtmlRenderer
|
||||
import im.vector.app.features.html.PillsPostProcessor
|
||||
@ -107,6 +108,7 @@ class MessageItemFactory @Inject constructor(
|
||||
private val defaultItemFactory: DefaultItemFactory,
|
||||
private val noticeItemFactory: NoticeItemFactory,
|
||||
private val avatarSizeProvider: AvatarSizeProvider,
|
||||
private val previewUrlRetriever: PreviewUrlRetriever,
|
||||
private val pillsPostProcessorFactory: PillsPostProcessor.Factory,
|
||||
private val session: Session) {
|
||||
|
||||
@ -424,6 +426,8 @@ class MessageItemFactory @Inject constructor(
|
||||
}
|
||||
.useBigFont(linkifiedBody.length <= MAX_NUMBER_OF_EMOJI_FOR_BIG_FONT * 2 && containsOnlyEmojis(linkifiedBody.toString()))
|
||||
.searchForPills(isFormatted)
|
||||
.previewUrlRetriever(previewUrlRetriever)
|
||||
.imageContentRenderer(imageContentRenderer)
|
||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||
.attributes(attributes)
|
||||
.highlighted(highlight)
|
||||
@ -529,6 +533,8 @@ class MessageItemFactory @Inject constructor(
|
||||
}
|
||||
}
|
||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||
.previewUrlRetriever(previewUrlRetriever)
|
||||
.imageContentRenderer(imageContentRenderer)
|
||||
.attributes(attributes)
|
||||
.highlighted(highlight)
|
||||
.movementMethod(createLinkMovementMethod(callback))
|
||||
|
@ -23,7 +23,11 @@ import androidx.core.widget.TextViewCompat
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.ui.views.PreviewUrlView
|
||||
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.media.ImageContentRenderer
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||
abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
||||
@ -37,10 +41,22 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
||||
@EpoxyAttribute
|
||||
var useBigFont: Boolean = false
|
||||
|
||||
@EpoxyAttribute
|
||||
var previewUrlRetriever: PreviewUrlRetriever? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var imageContentRenderer: ImageContentRenderer? = null
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
var movementMethod: MovementMethod? = null
|
||||
|
||||
private val previewUrlViewUpdater = PreviewUrlViewUpdater()
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
previewUrlViewUpdater.previewUrlView = holder.previewUrlView
|
||||
previewUrlViewUpdater.imageContentRenderer = imageContentRenderer
|
||||
previewUrlRetriever?.addListener(attributes.informationData.eventId, previewUrlViewUpdater)
|
||||
|
||||
if (useBigFont) {
|
||||
holder.messageView.textSize = 44F
|
||||
} else {
|
||||
@ -65,12 +81,27 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
||||
holder.messageView.setTextFuture(textFuture)
|
||||
}
|
||||
|
||||
override fun unbind(holder: Holder) {
|
||||
super.unbind(holder)
|
||||
previewUrlRetriever?.removeListener(attributes.informationData.eventId, previewUrlViewUpdater)
|
||||
}
|
||||
|
||||
override fun getViewType() = STUB_ID
|
||||
|
||||
class Holder : AbsMessageItem.Holder(STUB_ID) {
|
||||
val messageView by bind<AppCompatTextView>(R.id.messageTextView)
|
||||
val previewUrlView by bind<PreviewUrlView>(R.id.messageUrlPreview)
|
||||
}
|
||||
|
||||
inner class PreviewUrlViewUpdater : PreviewUrlRetriever.PreviewUrlRetrieverListener {
|
||||
var previewUrlView: PreviewUrlView? = null
|
||||
var imageContentRenderer: ImageContentRenderer? = null
|
||||
|
||||
override fun onStateUpdated(state: PreviewUrlUiState) {
|
||||
val safeImageContentRenderer = imageContentRenderer ?: return
|
||||
previewUrlView?.render(state, safeImageContentRenderer)
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
private const val STUB_ID = R.id.messageContentTextStub
|
||||
}
|
||||
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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 im.vector.app.core.di.ScreenScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.cache.CacheStrategy
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import javax.inject.Inject
|
||||
|
||||
@ScreenScope
|
||||
class PreviewUrlRetriever @Inject constructor(
|
||||
private val session: Session
|
||||
) {
|
||||
private val data = mutableMapOf<String, PreviewUrlUiState>()
|
||||
private val listeners = mutableMapOf<String, MutableSet<PreviewUrlRetrieverListener>>()
|
||||
|
||||
fun getPreviewUrl(event: Event, coroutineScope: CoroutineScope) {
|
||||
val eventId = event.eventId ?: return
|
||||
|
||||
val urlToRetrieve = synchronized(data) {
|
||||
if (data[eventId] == null) {
|
||||
// Keep only the first URL for the moment
|
||||
val url = session.mediaService().extractUrls(event).firstOrNull()
|
||||
if (url == null) {
|
||||
updateState(eventId, PreviewUrlUiState.NoUrl)
|
||||
} else {
|
||||
updateState(eventId, PreviewUrlUiState.Loading)
|
||||
}
|
||||
url
|
||||
} else {
|
||||
// Already handled
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
urlToRetrieve?.let { urlToRetrieve ->
|
||||
coroutineScope.launch {
|
||||
runCatching {
|
||||
session.mediaService().getPreviewUrl(
|
||||
url = urlToRetrieve,
|
||||
timestamp = null,
|
||||
cacheStrategy = CacheStrategy.TtlCache(CACHE_VALIDITY, false)
|
||||
)
|
||||
}.fold(
|
||||
{
|
||||
synchronized(data) {
|
||||
updateState(eventId, PreviewUrlUiState.Data(it))
|
||||
}
|
||||
},
|
||||
{
|
||||
synchronized(data) {
|
||||
updateState(eventId, PreviewUrlUiState.Error(it))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateState(eventId: String, state: PreviewUrlUiState) {
|
||||
data[eventId] = state
|
||||
// Notify the listener
|
||||
listeners[eventId].orEmpty().forEach {
|
||||
it.onStateUpdated(state)
|
||||
}
|
||||
}
|
||||
|
||||
// Called by the Epoxy item during binding
|
||||
fun addListener(key: String, listener: PreviewUrlRetrieverListener) {
|
||||
listeners.getOrPut(key) { mutableSetOf() }.add(listener)
|
||||
|
||||
// Give the current state if any
|
||||
synchronized(data) {
|
||||
listener.onStateUpdated(data[key] ?: PreviewUrlUiState.Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
// Called by the Epoxy item during unbinding
|
||||
fun removeListener(key: String, listener: PreviewUrlRetrieverListener) {
|
||||
listeners.getOrPut(key) { mutableSetOf() }.remove(listener)
|
||||
}
|
||||
|
||||
interface PreviewUrlRetrieverListener {
|
||||
fun onStateUpdated(state: PreviewUrlUiState)
|
||||
}
|
||||
|
||||
companion object {
|
||||
// One week in millis
|
||||
private const val CACHE_VALIDITY: Long = 7 * 24 * 3_600 * 1_000
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 org.matrix.android.sdk.api.session.media.PreviewUrlData
|
||||
|
||||
/**
|
||||
* The state representing a preview url UI state for an Event
|
||||
*/
|
||||
sealed class PreviewUrlUiState {
|
||||
// No info
|
||||
object Unknown : PreviewUrlUiState()
|
||||
|
||||
// The event does not contain any URLs
|
||||
object NoUrl : PreviewUrlUiState()
|
||||
|
||||
// Loading
|
||||
object Loading : PreviewUrlUiState()
|
||||
|
||||
// Error
|
||||
data class Error(val throwable: Throwable) : PreviewUrlUiState()
|
||||
|
||||
// PreviewUrl data
|
||||
data class Data(val previewUrlData: PreviewUrlData) : PreviewUrlUiState()
|
||||
}
|
@ -83,6 +83,19 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
||||
STICKER
|
||||
}
|
||||
|
||||
/**
|
||||
* For url preview
|
||||
*/
|
||||
fun render(mxcUrl: String, imageView: ImageView) {
|
||||
val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
|
||||
val imageUrl = contentUrlResolver.resolveFullSize(mxcUrl) ?: return
|
||||
|
||||
GlideApp.with(imageView)
|
||||
.load(imageUrl)
|
||||
.placeholder(R.drawable.ic_image)
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
/**
|
||||
* For gallery
|
||||
*/
|
||||
@ -227,7 +240,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
||||
val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
|
||||
val resolvedUrl = when (mode) {
|
||||
Mode.FULL_SIZE,
|
||||
Mode.STICKER -> resolveUrl(data)
|
||||
Mode.STICKER -> resolveUrl(data)
|
||||
Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, size.width, size.height, ContentUrlResolver.ThumbnailMethod.SCALE)
|
||||
}
|
||||
// Fallback to base url
|
||||
@ -295,7 +308,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
||||
finalHeight = min(maxImageWidth * height / width, maxImageHeight)
|
||||
finalWidth = finalHeight * width / height
|
||||
}
|
||||
Mode.STICKER -> {
|
||||
Mode.STICKER -> {
|
||||
// limit on width
|
||||
val maxWidthDp = min(dimensionConverter.dpToPx(120), maxImageWidth / 2)
|
||||
finalWidth = min(dimensionConverter.dpToPx(width), maxWidthDp)
|
||||
|
@ -87,7 +87,6 @@
|
||||
android:id="@+id/messageContentTextStub"
|
||||
style="@style/TimelineContentStubBaseParams"
|
||||
android:layout_height="wrap_content"
|
||||
android:inflatedId="@id/messageTextView"
|
||||
android:layout="@layout/item_timeline_event_text_message_stub"
|
||||
tools:visibility="visible" />
|
||||
|
||||
@ -143,16 +142,6 @@
|
||||
android:addStatesFromChildren="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
android:id="@+id/informationUrlPreview"
|
||||
layout="@layout/url_preview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
android:id="@+id/reactionsContainer"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -1,9 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/messageTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="14sp"
|
||||
tools:text="@sample/matrix.json/data/message" />
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/messageTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="14sp"
|
||||
tools:text="@sample/matrix.json/data/message" />
|
||||
|
||||
<im.vector.app.core.ui.views.PreviewUrlView
|
||||
android:id="@+id/messageUrlPreview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -1,10 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<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="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="wrap_content"
|
||||
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||
|
||||
<View
|
||||
android:id="@+id/url_preview_left_border"
|
||||
@ -34,6 +35,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="157dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@+id/url_preview_title"
|
||||
@ -69,4 +71,4 @@
|
||||
app:layout_constraintTop_toBottomOf="@+id/url_preview_description"
|
||||
tools:text="BBC News" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</merge>
|
Loading…
x
Reference in New Issue
Block a user