mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
Split image from replies (#779)
* refactor: VisibleMessageContentView.kt re-using layouts instead of instantiating every bind to fix alignment and constraint issues for splitting thumbnails and body * refactor: constraint works for sms only records, adjust other components of the visible message content accordingly * feat: link previews and quotes now wrap content or align according to media type * refactor: move back to emojitextview for body * fix: add some padding at the bottom of the quote * fix: voice message view not rendering properly * fix: set visibility to false for each message content view on recycle event * fix: untrusted attachments * fix: compile issues and small UI improvement
This commit is contained in:
parent
c2657bb785
commit
bd5a324ad8
@ -17,19 +17,14 @@ import org.session.libsession.messaging.jobs.AttachmentDownloadJob
|
||||
import org.session.libsession.messaging.jobs.JobQueue
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||
import org.session.libsession.utilities.ViewUtil
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.MediaPreviewActivity
|
||||
import org.thoughtcrime.securesms.components.CornerMask
|
||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageContentView
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.KThumbnailView
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||
import org.thoughtcrime.securesms.longmessage.LongMessageActivity
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import org.thoughtcrime.securesms.mms.Slide
|
||||
import org.thoughtcrime.securesms.util.ActivityDispatcher
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class AlbumThumbnailView : FrameLayout {
|
||||
|
||||
@ -72,24 +67,7 @@ class AlbumThumbnailView : FrameLayout {
|
||||
val rawXInt = event.rawX.toInt()
|
||||
val rawYInt = event.rawY.toInt()
|
||||
val eventRect = Rect(rawXInt, rawYInt, rawXInt, rawYInt)
|
||||
// Z-check in specific order
|
||||
val testRect = Rect()
|
||||
// test "Read More"
|
||||
binding.albumCellBodyTextReadMore.getGlobalVisibleRect(testRect)
|
||||
if (testRect.contains(eventRect)) {
|
||||
// dispatch to activity view
|
||||
ActivityDispatcher.get(context)?.dispatchIntent { context ->
|
||||
LongMessageActivity.getIntent(context, mms.recipient.address, mms.getId(), true)
|
||||
}
|
||||
return
|
||||
}
|
||||
val intersectedSpans = binding.albumCellBodyText.getIntersectedModalSpans(eventRect)
|
||||
if (intersectedSpans.isNotEmpty()) {
|
||||
intersectedSpans.forEach { span ->
|
||||
span.onClick(binding.albumCellBodyText)
|
||||
}
|
||||
return
|
||||
}
|
||||
// test each album child
|
||||
binding.albumCellContainer.findViewById<ViewGroup>(R.id.album_thumbnail_root)?.children?.forEachIndexed { index, child ->
|
||||
child.getGlobalVisibleRect(testRect)
|
||||
@ -113,6 +91,11 @@ class AlbumThumbnailView : FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
fun clearViews() {
|
||||
binding.albumCellContainer.removeAllViews()
|
||||
slideSize = -1
|
||||
}
|
||||
|
||||
fun bind(glideRequests: GlideRequests, message: MmsMessageRecord,
|
||||
isStart: Boolean, isEnd: Boolean) {
|
||||
slides = message.slideDeck.thumbnailSlides
|
||||
@ -139,19 +122,6 @@ class AlbumThumbnailView : FrameLayout {
|
||||
val thumbnailView = getThumbnailView(position)
|
||||
thumbnailView.setImageResource(glideRequests, slide, isPreview = false, mms = message)
|
||||
}
|
||||
binding.albumCellBodyParent.isVisible = message.body.isNotEmpty()
|
||||
val body = VisibleMessageContentView.getBodySpans(context, message, null)
|
||||
binding.albumCellBodyText.text = body
|
||||
post {
|
||||
// post to await layout of text
|
||||
binding.albumCellBodyText.layout?.let { layout ->
|
||||
val maxEllipsis = (0 until layout.lineCount).maxByOrNull { lineNum -> layout.getEllipsisCount(lineNum) }
|
||||
?: 0
|
||||
// show read more text if at least one line is ellipsized
|
||||
ViewUtil.setPaddingTop(binding.albumCellBodyTextParent, if (maxEllipsis > 0) resources.getDimension(R.dimen.small_spacing).roundToInt() else resources.getDimension(R.dimen.medium_spacing).roundToInt())
|
||||
binding.albumCellBodyTextReadMore.isVisible = maxEllipsis > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
@ -39,7 +39,12 @@ class LinkPreviewView : LinearLayout {
|
||||
// endregion
|
||||
|
||||
// region Updating
|
||||
fun bind(message: MmsMessageRecord, glide: GlideRequests, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean, searchQuery: String?) {
|
||||
fun bind(
|
||||
message: MmsMessageRecord,
|
||||
glide: GlideRequests,
|
||||
isStartOfMessageCluster: Boolean,
|
||||
isEndOfMessageCluster: Boolean
|
||||
) {
|
||||
val linkPreview = message.linkPreviews.first()
|
||||
url = linkPreview.url
|
||||
// Thumbnail
|
||||
@ -57,8 +62,7 @@ class LinkPreviewView : LinearLayout {
|
||||
}
|
||||
binding.titleTextView.setTextColor(ResourcesCompat.getColor(resources, textColorID, context.theme))
|
||||
// Body
|
||||
bodyTextView = VisibleMessageContentView.getBodyTextView(context, message, searchQuery)
|
||||
binding.mainLinkPreviewContainer.addView(bodyTextView)
|
||||
binding.titleTextView.setTextColor(ResourcesCompat.getColor(resources, textColorID, context.theme))
|
||||
// Corner radii
|
||||
val cornerRadii = MessageBubbleUtilities.calculateRadii(context, isStartOfMessageCluster, isEndOfMessageCluster, message.isOutgoing)
|
||||
cornerMask.setTopLeftRadius(cornerRadii[0])
|
||||
|
@ -47,11 +47,10 @@ class QuoteView : LinearLayout {
|
||||
enum class Mode { Regular, Draft }
|
||||
|
||||
// region Lifecycle
|
||||
constructor(context: Context) : super(context) { throw IllegalAccessError("Use QuoteView(context:mode:) instead.") }
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { throw IllegalAccessError("Use QuoteView(context:mode:) instead.") }
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { throw IllegalAccessError("Use QuoteView(context:mode:) instead.") }
|
||||
constructor(context: Context) : this(context, Mode.Regular)
|
||||
constructor(context: Context, attrs: AttributeSet) : this(context, Mode.Regular, attrs)
|
||||
|
||||
constructor(context: Context, mode: Mode) : super(context) {
|
||||
constructor(context: Context, mode: Mode, attrs: AttributeSet? = null) : super(context, attrs) {
|
||||
this.mode = mode
|
||||
binding = ViewQuoteBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
// Add padding here (not on binding.mainQuoteViewContainer) to get a bit of a top inset while avoiding
|
||||
@ -65,7 +64,7 @@ class QuoteView : LinearLayout {
|
||||
val quoteViewMainContentContainerLayoutParams = binding.quoteViewMainContentContainer.layoutParams as RelativeLayout.LayoutParams
|
||||
// Since we're not showing the cancel button we can shorten the end margin
|
||||
quoteViewMainContentContainerLayoutParams.marginEnd = resources.getDimension(R.dimen.medium_spacing).roundToInt()
|
||||
binding.quoteViewMainContentContainer.layoutParams = quoteViewMainContentContainerLayoutParams
|
||||
// binding.quoteViewMainContentContainer.layoutParams = quoteViewMainContentContainerLayoutParams
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -135,7 +134,7 @@ class QuoteView : LinearLayout {
|
||||
if (!hasAttachments) {
|
||||
val accentLineLayoutParams = binding.quoteViewAccentLine.layoutParams as RelativeLayout.LayoutParams
|
||||
accentLineLayoutParams.height = getIntrinsicContentHeight(maxContentWidth) // Match the intrinsic * content * height
|
||||
binding.quoteViewAccentLine.layoutParams = accentLineLayoutParams
|
||||
// binding.quoteViewAccentLine.layoutParams = accentLineLayoutParams
|
||||
binding.quoteViewAccentLine.setBackgroundColor(getLineColor(isOutgoingMessage))
|
||||
} else if (attachments != null) {
|
||||
binding.quoteViewAttachmentPreviewImageView.imageTintList = ColorStateList.valueOf(ResourcesCompat.getColor(resources, R.color.white, context.theme))
|
||||
@ -165,11 +164,11 @@ class QuoteView : LinearLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.mainQuoteViewContainer.layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, getIntrinsicHeight(maxContentWidth))
|
||||
val quoteViewMainContentContainerLayoutParams = binding.quoteViewMainContentContainer.layoutParams as RelativeLayout.LayoutParams
|
||||
//mainQuoteViewContainer.layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, getIntrinsicHeight(maxContentWidth))
|
||||
// val quoteViewMainContentContainerLayoutParams = quoteViewMainContentContainer.layoutParams as RelativeLayout.LayoutParams
|
||||
// The start margin is different if we just show the accent line vs if we show an attachment thumbnail
|
||||
quoteViewMainContentContainerLayoutParams.marginStart = if (!hasAttachments) toPx(16, resources) else toPx(48, resources)
|
||||
binding.quoteViewMainContentContainer.layoutParams = quoteViewMainContentContainerLayoutParams
|
||||
// quoteViewMainContentContainerLayoutParams.marginStart = if (!hasAttachments) toPx(16, resources) else toPx(48, resources)
|
||||
// quoteViewMainContentContainer.layoutParams = quoteViewMainContentContainerLayoutParams
|
||||
}
|
||||
// endregion
|
||||
|
||||
|
@ -10,11 +10,10 @@ import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.URLSpan
|
||||
import android.text.util.Linkify
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@ -23,22 +22,20 @@ import androidx.core.graphics.BlendModeColorFilterCompat
|
||||
import androidx.core.graphics.BlendModeCompat
|
||||
import androidx.core.text.getSpans
|
||||
import androidx.core.text.toSpannable
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.isVisible
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ViewVisibleMessageContentBinding
|
||||
import okhttp3.HttpUrl
|
||||
import org.session.libsession.utilities.ThemeUtil
|
||||
import org.session.libsession.utilities.ViewUtil
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||
import org.thoughtcrime.securesms.conversation.v2.ModalUrlBottomSheet
|
||||
import org.thoughtcrime.securesms.conversation.v2.components.AlbumThumbnailView
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.ModalURLSpan
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import org.thoughtcrime.securesms.util.SearchUtil
|
||||
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||
@ -49,7 +46,7 @@ import kotlin.math.roundToInt
|
||||
|
||||
class VisibleMessageContentView : LinearLayout {
|
||||
private lateinit var binding: ViewVisibleMessageContentBinding
|
||||
var onContentClick: ((event: MotionEvent) -> Unit)? = null
|
||||
var onContentClick: MutableList<((event: MotionEvent) -> Unit)> = mutableListOf()
|
||||
var onContentDoubleTap: (() -> Unit)? = null
|
||||
var delegate: VisibleMessageContentViewDelegate? = null
|
||||
var indexInAdapter: Int = -1
|
||||
@ -74,23 +71,45 @@ class VisibleMessageContentView : LinearLayout {
|
||||
val filter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_IN)
|
||||
background.colorFilter = filter
|
||||
setBackground(background)
|
||||
// Body
|
||||
binding.mainContainer.removeAllViews()
|
||||
onContentClick = null
|
||||
|
||||
val onlyBodyMessage = message is SmsMessageRecord
|
||||
val mediaThumbnailMessage = contactIsTrusted && message is MmsMessageRecord && message.slideDeck.thumbnailSlide != null
|
||||
|
||||
// reset visibilities / containers
|
||||
onContentClick.clear()
|
||||
binding.albumThumbnailView.clearViews()
|
||||
onContentDoubleTap = null
|
||||
|
||||
if (message.isDeleted) {
|
||||
val deletedMessageView = DeletedMessageView(context)
|
||||
deletedMessageView.bind(message, getTextColor(context,message))
|
||||
binding.mainContainer.addView(deletedMessageView)
|
||||
} else if (message is MmsMessageRecord && message.linkPreviews.isNotEmpty()) {
|
||||
val linkPreviewView = LinkPreviewView(context)
|
||||
linkPreviewView.bind(message, glide, isStartOfMessageCluster, isEndOfMessageCluster, searchQuery)
|
||||
binding.mainContainer.addView(linkPreviewView)
|
||||
onContentClick = { event -> linkPreviewView.calculateHit(event) }
|
||||
// Body text view is inside the link preview for layout convenience
|
||||
} else if (message is MmsMessageRecord && message.quote != null) {
|
||||
binding.deletedMessageView.isVisible = true
|
||||
binding.deletedMessageView.bind(message, VisibleMessageContentView.getTextColor(context,message))
|
||||
return
|
||||
} else {
|
||||
binding.deletedMessageView.isVisible = false
|
||||
}
|
||||
|
||||
binding.quoteView.isVisible = message is MmsMessageRecord && message.quote != null
|
||||
val quoteLayoutParams = binding.quoteView.layoutParams
|
||||
quoteLayoutParams.width = if (mediaThumbnailMessage) 0 else ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
binding.quoteView.layoutParams = quoteLayoutParams
|
||||
|
||||
binding.linkPreviewView.isVisible = message is MmsMessageRecord && message.linkPreviews.isNotEmpty()
|
||||
|
||||
val linkPreviewLayout = binding.linkPreviewView.layoutParams
|
||||
linkPreviewLayout.width = if (mediaThumbnailMessage) 0 else ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
binding.linkPreviewView.layoutParams = linkPreviewLayout
|
||||
|
||||
binding.untrustedView.isVisible = !contactIsTrusted && message is MmsMessageRecord && message.quote == null
|
||||
binding.voiceMessageView.isVisible = contactIsTrusted && message is MmsMessageRecord && message.slideDeck.audioSlide != null
|
||||
binding.documentView.isVisible = contactIsTrusted && message is MmsMessageRecord && message.slideDeck.documentSlide != null
|
||||
binding.albumThumbnailView.isVisible = mediaThumbnailMessage
|
||||
binding.openGroupInvitationView.isVisible = message.isOpenGroupInvitation
|
||||
|
||||
var hideBody = false
|
||||
|
||||
if (message is MmsMessageRecord && message.quote != null) {
|
||||
binding.quoteView.isVisible = true
|
||||
val quote = message.quote!!
|
||||
val quoteView = QuoteView(context, QuoteView.Mode.Regular)
|
||||
// The max content width is the max message bubble size - 2 times the horizontal padding - 2
|
||||
// times the horizontal margin. This unfortunately has to be calculated manually
|
||||
// here to get the layout right.
|
||||
@ -100,88 +119,91 @@ class VisibleMessageContentView : LinearLayout {
|
||||
} else {
|
||||
quote.text
|
||||
}
|
||||
quoteView.bind(quote.author.toString(), quoteText, quote.attachment, thread,
|
||||
binding.quoteView.bind(quote.author.toString(), quoteText, quote.attachment, thread,
|
||||
message.isOutgoing, maxContentWidth, message.isOpenGroupInvitation, message.threadId,
|
||||
quote.isOriginalMissing, glide)
|
||||
binding.mainContainer.addView(quoteView)
|
||||
val bodyTextView = getBodyTextView(context, message, searchQuery)
|
||||
ViewUtil.setPaddingTop(bodyTextView, 0)
|
||||
binding.mainContainer.addView(bodyTextView)
|
||||
onContentClick = { event ->
|
||||
onContentClick.add { event ->
|
||||
val r = Rect()
|
||||
quoteView.getGlobalVisibleRect(r)
|
||||
binding.quoteView.getGlobalVisibleRect(r)
|
||||
if (r.contains(event.rawX.roundToInt(), event.rawY.roundToInt())) {
|
||||
delegate?.scrollToMessageIfPossible(quote.id)
|
||||
} else {
|
||||
bodyTextView.getIntersectedModalSpans(event).forEach { span ->
|
||||
span.onClick(bodyTextView)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (message is MmsMessageRecord && message.linkPreviews.isNotEmpty()) {
|
||||
binding.linkPreviewView.bind(message, glide, isStartOfMessageCluster, isEndOfMessageCluster)
|
||||
onContentClick.add { event -> binding.linkPreviewView.calculateHit(event) }
|
||||
// Body text view is inside the link preview for layout convenience
|
||||
} else if (message is MmsMessageRecord && message.slideDeck.audioSlide != null) {
|
||||
hideBody = true
|
||||
// Audio attachment
|
||||
if (contactIsTrusted || message.isOutgoing) {
|
||||
val voiceMessageView = VoiceMessageView(context)
|
||||
voiceMessageView.indexInAdapter = indexInAdapter
|
||||
voiceMessageView.delegate = context as? ConversationActivityV2
|
||||
voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
|
||||
binding.mainContainer.addView(voiceMessageView)
|
||||
binding.voiceMessageView.indexInAdapter = indexInAdapter
|
||||
binding.voiceMessageView.delegate = context as? ConversationActivityV2
|
||||
binding.voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
|
||||
// We have to use onContentClick (rather than a click listener directly on the voice
|
||||
// message view) so as to not interfere with all the other gestures.
|
||||
onContentClick = { voiceMessageView.togglePlayback() }
|
||||
onContentDoubleTap = { voiceMessageView.handleDoubleTap() }
|
||||
onContentClick.add { binding.voiceMessageView.togglePlayback() }
|
||||
onContentDoubleTap = { binding.voiceMessageView.handleDoubleTap() }
|
||||
} else {
|
||||
val untrustedView = UntrustedAttachmentView(context)
|
||||
untrustedView.bind(UntrustedAttachmentView.AttachmentType.AUDIO, getTextColor(context,message))
|
||||
binding.mainContainer.addView(untrustedView)
|
||||
onContentClick = { untrustedView.showTrustDialog(message.individualRecipient) }
|
||||
// TODO: move this out to its own area
|
||||
binding.untrustedView.bind(UntrustedAttachmentView.AttachmentType.AUDIO, VisibleMessageContentView.getTextColor(context,message))
|
||||
onContentClick.add { binding.untrustedView.showTrustDialog(message.individualRecipient) }
|
||||
}
|
||||
} else if (message is MmsMessageRecord && message.slideDeck.documentSlide != null) {
|
||||
hideBody = true
|
||||
// Document attachment
|
||||
if (contactIsTrusted || message.isOutgoing) {
|
||||
val documentView = DocumentView(context)
|
||||
documentView.bind(message, getTextColor(context, message))
|
||||
binding.mainContainer.addView(documentView)
|
||||
binding.documentView.bind(message, VisibleMessageContentView.getTextColor(context, message))
|
||||
} else {
|
||||
val untrustedView = UntrustedAttachmentView(context)
|
||||
untrustedView.bind(UntrustedAttachmentView.AttachmentType.DOCUMENT, getTextColor(context,message))
|
||||
binding.mainContainer.addView(untrustedView)
|
||||
onContentClick = { untrustedView.showTrustDialog(message.individualRecipient) }
|
||||
binding.untrustedView.bind(UntrustedAttachmentView.AttachmentType.DOCUMENT, VisibleMessageContentView.getTextColor(context,message))
|
||||
onContentClick.add { binding.untrustedView.showTrustDialog(message.individualRecipient) }
|
||||
}
|
||||
} else if (message is MmsMessageRecord && message.slideDeck.asAttachments().isNotEmpty()) {
|
||||
// Images/Video attachment
|
||||
/*
|
||||
* Images / Video attachment
|
||||
*/
|
||||
if (contactIsTrusted || message.isOutgoing) {
|
||||
val albumThumbnailView = AlbumThumbnailView(context)
|
||||
binding.mainContainer.addView(albumThumbnailView)
|
||||
// isStart and isEnd of cluster needed for calculating the mask for full bubble image groups
|
||||
// bind after add view because views are inflated and calculated during bind
|
||||
albumThumbnailView.bind(
|
||||
binding.albumThumbnailView.bind(
|
||||
glideRequests = glide,
|
||||
message = message,
|
||||
isStart = isStartOfMessageCluster,
|
||||
isEnd = isEndOfMessageCluster
|
||||
)
|
||||
onContentClick = { event ->
|
||||
albumThumbnailView.calculateHitObject(event, message, thread)
|
||||
onContentClick.add { event ->
|
||||
binding.albumThumbnailView.calculateHitObject(event, message, thread)
|
||||
}
|
||||
} else {
|
||||
val untrustedView = UntrustedAttachmentView(context)
|
||||
untrustedView.bind(UntrustedAttachmentView.AttachmentType.MEDIA, getTextColor(context,message))
|
||||
binding.mainContainer.addView(untrustedView)
|
||||
onContentClick = { untrustedView.showTrustDialog(message.individualRecipient) }
|
||||
hideBody = true
|
||||
binding.albumThumbnailView.clearViews()
|
||||
binding.untrustedView.bind(UntrustedAttachmentView.AttachmentType.MEDIA, VisibleMessageContentView.getTextColor(context,message))
|
||||
onContentClick.add { binding.untrustedView.showTrustDialog(message.individualRecipient) }
|
||||
}
|
||||
} else if (message.isOpenGroupInvitation) {
|
||||
val openGroupInvitationView = OpenGroupInvitationView(context)
|
||||
openGroupInvitationView.bind(message, getTextColor(context, message))
|
||||
binding.mainContainer.addView(openGroupInvitationView)
|
||||
onContentClick = { openGroupInvitationView.joinOpenGroup() }
|
||||
} else {
|
||||
val bodyTextView = getBodyTextView(context, message, searchQuery)
|
||||
binding.mainContainer.addView(bodyTextView)
|
||||
onContentClick = { event ->
|
||||
// intersectedModalSpans should only be a list of one item
|
||||
bodyTextView.getIntersectedModalSpans(event).forEach { span ->
|
||||
span.onClick(bodyTextView)
|
||||
hideBody = true
|
||||
binding.openGroupInvitationView.bind(message, VisibleMessageContentView.getTextColor(context, message))
|
||||
onContentClick.add { binding.openGroupInvitationView.joinOpenGroup() }
|
||||
}
|
||||
|
||||
binding.bodyTextView.isVisible = message.body.isNotEmpty() && !hideBody
|
||||
|
||||
// set it to use constraints if not only a text message, otherwise wrap content to whatever width it wants
|
||||
val params = binding.bodyTextView.layoutParams
|
||||
params.width = if (onlyBodyMessage) ViewGroup.LayoutParams.WRAP_CONTENT else 0
|
||||
binding.bodyTextView.layoutParams = params
|
||||
|
||||
if (message.body.isNotEmpty() && !hideBody) {
|
||||
val color = getTextColor(context, message)
|
||||
binding.bodyTextView.setTextColor(color)
|
||||
binding.bodyTextView.setLinkTextColor(color)
|
||||
val body = getBodySpans(context, message, searchQuery)
|
||||
binding.bodyTextView.text = body
|
||||
onContentClick.add { e: MotionEvent ->
|
||||
binding.bodyTextView.getIntersectedModalSpans(e).forEach { span ->
|
||||
span.onClick(binding.bodyTextView)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,35 +229,27 @@ class VisibleMessageContentView : LinearLayout {
|
||||
}
|
||||
|
||||
fun recycle() {
|
||||
binding.mainContainer.removeAllViews()
|
||||
arrayOf(
|
||||
binding.deletedMessageView,
|
||||
binding.untrustedView,
|
||||
binding.voiceMessageView,
|
||||
binding.openGroupInvitationView,
|
||||
binding.documentView,
|
||||
binding.quoteView,
|
||||
binding.linkPreviewView,
|
||||
binding.albumThumbnailView,
|
||||
binding.bodyTextView
|
||||
).forEach { view -> view.isVisible = false }
|
||||
}
|
||||
|
||||
fun playVoiceMessage() {
|
||||
binding.mainContainer.children.forEach { view ->
|
||||
if (view is VoiceMessageView) {
|
||||
return@forEach view.togglePlayback()
|
||||
}
|
||||
}
|
||||
binding.voiceMessageView.togglePlayback()
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Convenience
|
||||
companion object {
|
||||
|
||||
fun getBodyTextView(context: Context, message: MessageRecord, searchQuery: String?): TextView {
|
||||
val result = EmojiTextView(context)
|
||||
val vPadding = context.resources.getDimension(R.dimen.small_spacing).toInt()
|
||||
val hPadding = toPx(12, context.resources)
|
||||
result.setPadding(hPadding, vPadding, hPadding, vPadding)
|
||||
result.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.resources.getDimension(R.dimen.small_font_size))
|
||||
val color = getTextColor(context, message)
|
||||
result.setTextColor(color)
|
||||
result.setLinkTextColor(color)
|
||||
val body = getBodySpans(context, message, searchQuery)
|
||||
result.text = body
|
||||
return result
|
||||
}
|
||||
|
||||
fun getBodySpans(context: Context, message: MessageRecord, searchQuery: String?): Spannable {
|
||||
var body = message.body.toSpannable()
|
||||
|
||||
|
@ -15,7 +15,6 @@ import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
@ -181,7 +180,7 @@ class VisibleMessageView : LinearLayout {
|
||||
if (binding.profilePictureContainer.visibility != View.GONE) { maxWidth -= binding.profilePictureContainer.width }
|
||||
// Populate content view
|
||||
binding.messageContentView.indexInAdapter = indexInAdapter
|
||||
binding.messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread, searchQuery, isGroupThread || (contact?.isTrusted ?: false))
|
||||
binding.messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread, searchQuery, message.isOutgoing || isGroupThread || (contact?.isTrusted ?: false))
|
||||
binding.messageContentView.delegate = contentViewDelegate
|
||||
onDoubleTap = { binding.messageContentView.onContentDoubleTap?.invoke() }
|
||||
}
|
||||
@ -224,11 +223,13 @@ class VisibleMessageView : LinearLayout {
|
||||
}
|
||||
|
||||
private fun updateExpirationTimer(message: MessageRecord) {
|
||||
val expirationTimerViewLayoutParams = binding.expirationTimerView.layoutParams as RelativeLayout.LayoutParams
|
||||
val ruleToAdd = if (message.isOutgoing) RelativeLayout.ALIGN_START else RelativeLayout.ALIGN_END
|
||||
val ruleToRemove = if (message.isOutgoing) RelativeLayout.ALIGN_END else RelativeLayout.ALIGN_START
|
||||
expirationTimerViewLayoutParams.removeRule(ruleToRemove)
|
||||
expirationTimerViewLayoutParams.addRule(ruleToAdd, R.id.messageContentView)
|
||||
val expirationTimerViewLayoutParams = binding.expirationTimerView.layoutParams as MarginLayoutParams
|
||||
val container = binding.expirationTimerViewContainer
|
||||
val content = binding.messageContentView
|
||||
val expiration = binding.expirationTimerView
|
||||
container.removeAllViewsInLayout()
|
||||
container.addView(if (message.isOutgoing) expiration else content)
|
||||
container.addView(if (message.isOutgoing) content else expiration)
|
||||
val expirationTimerViewSize = toPx(12, resources)
|
||||
val smallSpacing = resources.getDimension(R.dimen.small_spacing).roundToInt()
|
||||
expirationTimerViewLayoutParams.marginStart = if (message.isOutgoing) -(smallSpacing + expirationTimerViewSize) else 0
|
||||
@ -261,6 +262,7 @@ class VisibleMessageView : LinearLayout {
|
||||
} else {
|
||||
binding.expirationTimerView.isVisible = false
|
||||
}
|
||||
container.requestLayout()
|
||||
}
|
||||
|
||||
private fun handleIsSelectedChanged() {
|
||||
@ -388,7 +390,7 @@ class VisibleMessageView : LinearLayout {
|
||||
}
|
||||
|
||||
fun onContentClick(event: MotionEvent) {
|
||||
binding.messageContentView.onContentClick?.invoke(event)
|
||||
binding.messageContentView.onContentClick.forEach { clickHandler -> clickHandler.invoke(event) }
|
||||
}
|
||||
|
||||
private fun onPress(event: MotionEvent) {
|
||||
|
@ -1,19 +1,19 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.utilities;
|
||||
|
||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.UiThread;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.bumptech.glide.RequestBuilder;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
|
||||
@ -22,8 +22,13 @@ import com.bumptech.glide.load.resource.bitmap.FitCenter;
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress;
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.session.libsignal.utilities.ListenableFuture;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import org.session.libsignal.utilities.SettableFuture;
|
||||
import org.session.libsignal.utilities.guava.Optional;
|
||||
import org.thoughtcrime.securesms.components.GlideBitmapListeningTarget;
|
||||
import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget;
|
||||
import org.thoughtcrime.securesms.components.TransferControlView;
|
||||
@ -33,17 +38,11 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.mms.SlideClickListener;
|
||||
import org.thoughtcrime.securesms.mms.SlidesClickedListener;
|
||||
import org.session.libsignal.utilities.guava.Optional;
|
||||
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.session.libsignal.utilities.ListenableFuture;
|
||||
import org.session.libsignal.utilities.SettableFuture;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||
import network.loki.messenger.R;
|
||||
|
||||
public class ThumbnailView extends FrameLayout {
|
||||
|
||||
@ -287,7 +286,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
} else if (slide.hasPlaceholder()) {
|
||||
buildPlaceholderGlideRequest(glideRequests, slide).into(new GlideBitmapListeningTarget(image, result));
|
||||
} else {
|
||||
glideRequests.clear(image);
|
||||
glideRequests.load(R.drawable.ic_image_white_24dp).centerInside().into(image);
|
||||
result.set(false);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@ -22,58 +20,4 @@
|
||||
android:layout_gravity="center"
|
||||
android:layout="@layout/transfer_controls_stub" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_alignTop="@+id/albumCellContainer"
|
||||
android:layout_alignStart="@+id/albumCellContainer"
|
||||
android:layout_alignEnd="@+id/albumCellContainer"
|
||||
android:layout_alignBottom="@+id/albumCellContainer"
|
||||
tools:visibility="visible"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="bottom"
|
||||
android:id="@+id/albumCellBodyParent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content">
|
||||
<ImageView
|
||||
android:id="@+id/albumCellShade"
|
||||
android:src="@drawable/image_shade"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toTopOf="@+id/albumCellBodyTextParent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
/>
|
||||
<LinearLayout
|
||||
android:id="@+id/albumCellBodyTextParent"
|
||||
android:padding="@dimen/medium_spacing"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/albumCellBodyTextReadMore"
|
||||
android:orientation="horizontal">
|
||||
<View
|
||||
android:layout_width="@dimen/accent_line_thickness"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/accent"/>
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:maxLines="4"
|
||||
android:ellipsize="end"
|
||||
android:id="@+id/albumCellBodyText"
|
||||
android:textColor="@color/core_white"
|
||||
android:layout_marginStart="@dimen/small_spacing"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
tools:visibility="visible"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:id="@+id/albumCellBodyTextReadMore"
|
||||
android:textColor="@color/core_white"
|
||||
android:paddingHorizontal="@dimen/medium_spacing"
|
||||
android:paddingBottom="@dimen/medium_spacing"
|
||||
android:textStyle="bold"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/ConversationItem_read_more"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</RelativeLayout>
|
@ -41,7 +41,8 @@
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
android:textStyle="bold"
|
||||
tools:text="The Day The Dinosaurs Died - Minute by Minute"
|
||||
tools:text="Some Text here"
|
||||
android:minWidth="@dimen/media_bubble_min_width"
|
||||
android:maxLines="3"
|
||||
android:ellipsize="end"
|
||||
android:textColor="@color/text"
|
||||
|
@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
android:id="@+id/mainQuoteViewContainer"
|
||||
<RelativeLayout android:id="@+id/mainQuoteViewContainer"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="@color/input_bar_background"
|
||||
android:paddingHorizontal="@dimen/medium_spacing">
|
||||
android:paddingHorizontal="@dimen/medium_spacing"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<View
|
||||
android:id="@+id/quoteViewAccentLine"
|
||||
@ -21,6 +21,7 @@
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginVertical="@dimen/small_spacing"
|
||||
android:layout_centerVertical="true"
|
||||
android:background="@drawable/view_quote_attachment_preview_background">
|
||||
|
||||
@ -45,23 +46,25 @@
|
||||
android:id="@+id/quoteViewMainContentContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="30dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginVertical="@dimen/small_spacing"
|
||||
android:layout_marginStart="@dimen/medium_spacing"
|
||||
android:layout_marginEnd="@dimen/medium_spacing"
|
||||
android:layout_toEndOf="@+id/quoteViewAttachmentPreviewContainer"
|
||||
android:layout_toStartOf="@+id/quoteViewCancelButton"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/quoteViewAuthorTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Spiderman"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end" />
|
||||
tools:text="Spiderman"
|
||||
android:textColor="@color/text"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/quoteViewBodyTextView"
|
||||
@ -69,7 +72,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="3"
|
||||
android:text="Yo, I need your help here!"
|
||||
tools:text="Yo, I need your help here!"
|
||||
android:textColor="@color/text"
|
||||
android:textSize="@dimen/small_font_size" />
|
||||
|
||||
|
@ -75,8 +75,9 @@
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end" />
|
||||
|
||||
<RelativeLayout
|
||||
<LinearLayout
|
||||
android:id="@+id/expirationTimerViewContainer"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
@ -89,9 +90,9 @@
|
||||
android:id="@+id/expirationTimerView"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_centerVertical="true" />
|
||||
android:layout_gravity="center_vertical" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -100,12 +101,12 @@
|
||||
<TextView
|
||||
android:id="@+id/messageTimestampTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="2dp"
|
||||
android:maxLines="1"
|
||||
android:textSize="10dp" />
|
||||
android:textSize="11sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/messageStatusImageView"
|
||||
|
@ -1,7 +1,104 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/mainContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/mainContainerConstraint"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<!-- Content that will only show on its own -->
|
||||
<org.thoughtcrime.securesms.conversation.v2.messages.DeletedMessageView
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/deletedMessageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<org.thoughtcrime.securesms.conversation.v2.messages.UntrustedAttachmentView
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/untrustedView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<org.thoughtcrime.securesms.conversation.v2.messages.VoiceMessageView
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/voiceMessageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<org.thoughtcrime.securesms.conversation.v2.messages.OpenGroupInvitationView
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/openGroupInvitationView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<org.thoughtcrime.securesms.conversation.v2.messages.DocumentView
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/documentView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<!-- Content that will show with other elements -->
|
||||
|
||||
<org.thoughtcrime.securesms.conversation.v2.messages.QuoteView
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@+id/albumThumbnailView"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
tools:visibility="visible"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/quoteView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<org.thoughtcrime.securesms.conversation.v2.messages.LinkPreviewView
|
||||
app:layout_constraintTop_toBottomOf="@+id/quoteView"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:visibility="visible"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/linkPreviewView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/bodyBarrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="end"
|
||||
app:constraint_referenced_ids="albumThumbnailView,linkPreviewView,quoteView,voiceMessageView"/>
|
||||
|
||||
<org.thoughtcrime.securesms.conversation.v2.components.AlbumThumbnailView
|
||||
app:layout_constraintTop_toBottomOf="@+id/linkPreviewView"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/albumThumbnailView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
tools:visibility="visible"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@+id/albumThumbnailView"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@+id/bodyBarrier"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="@dimen/small_spacing"
|
||||
android:id="@+id/bodyTextView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue
Block a user