mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-17 14:48:26 +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.jobs.JobQueue
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
|
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||||
import org.session.libsession.utilities.ViewUtil
|
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.MediaPreviewActivity
|
import org.thoughtcrime.securesms.MediaPreviewActivity
|
||||||
import org.thoughtcrime.securesms.components.CornerMask
|
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.KThumbnailView
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans
|
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.longmessage.LongMessageActivity
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.mms.Slide
|
import org.thoughtcrime.securesms.mms.Slide
|
||||||
import org.thoughtcrime.securesms.util.ActivityDispatcher
|
import org.thoughtcrime.securesms.util.ActivityDispatcher
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
class AlbumThumbnailView : FrameLayout {
|
class AlbumThumbnailView : FrameLayout {
|
||||||
|
|
||||||
@ -72,24 +67,7 @@ class AlbumThumbnailView : FrameLayout {
|
|||||||
val rawXInt = event.rawX.toInt()
|
val rawXInt = event.rawX.toInt()
|
||||||
val rawYInt = event.rawY.toInt()
|
val rawYInt = event.rawY.toInt()
|
||||||
val eventRect = Rect(rawXInt, rawYInt, rawXInt, rawYInt)
|
val eventRect = Rect(rawXInt, rawYInt, rawXInt, rawYInt)
|
||||||
// Z-check in specific order
|
|
||||||
val testRect = Rect()
|
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
|
// test each album child
|
||||||
binding.albumCellContainer.findViewById<ViewGroup>(R.id.album_thumbnail_root)?.children?.forEachIndexed { index, child ->
|
binding.albumCellContainer.findViewById<ViewGroup>(R.id.album_thumbnail_root)?.children?.forEachIndexed { index, child ->
|
||||||
child.getGlobalVisibleRect(testRect)
|
child.getGlobalVisibleRect(testRect)
|
||||||
@ -113,6 +91,11 @@ class AlbumThumbnailView : FrameLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clearViews() {
|
||||||
|
binding.albumCellContainer.removeAllViews()
|
||||||
|
slideSize = -1
|
||||||
|
}
|
||||||
|
|
||||||
fun bind(glideRequests: GlideRequests, message: MmsMessageRecord,
|
fun bind(glideRequests: GlideRequests, message: MmsMessageRecord,
|
||||||
isStart: Boolean, isEnd: Boolean) {
|
isStart: Boolean, isEnd: Boolean) {
|
||||||
slides = message.slideDeck.thumbnailSlides
|
slides = message.slideDeck.thumbnailSlides
|
||||||
@ -139,19 +122,6 @@ class AlbumThumbnailView : FrameLayout {
|
|||||||
val thumbnailView = getThumbnailView(position)
|
val thumbnailView = getThumbnailView(position)
|
||||||
thumbnailView.setImageResource(glideRequests, slide, isPreview = false, mms = message)
|
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
|
// endregion
|
||||||
|
@ -39,7 +39,12 @@ class LinkPreviewView : LinearLayout {
|
|||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Updating
|
// 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()
|
val linkPreview = message.linkPreviews.first()
|
||||||
url = linkPreview.url
|
url = linkPreview.url
|
||||||
// Thumbnail
|
// Thumbnail
|
||||||
@ -57,8 +62,7 @@ class LinkPreviewView : LinearLayout {
|
|||||||
}
|
}
|
||||||
binding.titleTextView.setTextColor(ResourcesCompat.getColor(resources, textColorID, context.theme))
|
binding.titleTextView.setTextColor(ResourcesCompat.getColor(resources, textColorID, context.theme))
|
||||||
// Body
|
// Body
|
||||||
bodyTextView = VisibleMessageContentView.getBodyTextView(context, message, searchQuery)
|
binding.titleTextView.setTextColor(ResourcesCompat.getColor(resources, textColorID, context.theme))
|
||||||
binding.mainLinkPreviewContainer.addView(bodyTextView)
|
|
||||||
// Corner radii
|
// Corner radii
|
||||||
val cornerRadii = MessageBubbleUtilities.calculateRadii(context, isStartOfMessageCluster, isEndOfMessageCluster, message.isOutgoing)
|
val cornerRadii = MessageBubbleUtilities.calculateRadii(context, isStartOfMessageCluster, isEndOfMessageCluster, message.isOutgoing)
|
||||||
cornerMask.setTopLeftRadius(cornerRadii[0])
|
cornerMask.setTopLeftRadius(cornerRadii[0])
|
||||||
|
@ -47,11 +47,10 @@ class QuoteView : LinearLayout {
|
|||||||
enum class Mode { Regular, Draft }
|
enum class Mode { Regular, Draft }
|
||||||
|
|
||||||
// region Lifecycle
|
// region Lifecycle
|
||||||
constructor(context: Context) : super(context) { throw IllegalAccessError("Use QuoteView(context:mode:) instead.") }
|
constructor(context: Context) : this(context, Mode.Regular)
|
||||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { throw IllegalAccessError("Use QuoteView(context:mode:) instead.") }
|
constructor(context: Context, attrs: AttributeSet) : this(context, Mode.Regular, attrs)
|
||||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { throw IllegalAccessError("Use QuoteView(context:mode:) instead.") }
|
|
||||||
|
|
||||||
constructor(context: Context, mode: Mode) : super(context) {
|
constructor(context: Context, mode: Mode, attrs: AttributeSet? = null) : super(context, attrs) {
|
||||||
this.mode = mode
|
this.mode = mode
|
||||||
binding = ViewQuoteBinding.inflate(LayoutInflater.from(context), this, true)
|
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
|
// 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
|
val quoteViewMainContentContainerLayoutParams = binding.quoteViewMainContentContainer.layoutParams as RelativeLayout.LayoutParams
|
||||||
// Since we're not showing the cancel button we can shorten the end margin
|
// Since we're not showing the cancel button we can shorten the end margin
|
||||||
quoteViewMainContentContainerLayoutParams.marginEnd = resources.getDimension(R.dimen.medium_spacing).roundToInt()
|
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) {
|
if (!hasAttachments) {
|
||||||
val accentLineLayoutParams = binding.quoteViewAccentLine.layoutParams as RelativeLayout.LayoutParams
|
val accentLineLayoutParams = binding.quoteViewAccentLine.layoutParams as RelativeLayout.LayoutParams
|
||||||
accentLineLayoutParams.height = getIntrinsicContentHeight(maxContentWidth) // Match the intrinsic * content * height
|
accentLineLayoutParams.height = getIntrinsicContentHeight(maxContentWidth) // Match the intrinsic * content * height
|
||||||
binding.quoteViewAccentLine.layoutParams = accentLineLayoutParams
|
// binding.quoteViewAccentLine.layoutParams = accentLineLayoutParams
|
||||||
binding.quoteViewAccentLine.setBackgroundColor(getLineColor(isOutgoingMessage))
|
binding.quoteViewAccentLine.setBackgroundColor(getLineColor(isOutgoingMessage))
|
||||||
} else if (attachments != null) {
|
} else if (attachments != null) {
|
||||||
binding.quoteViewAttachmentPreviewImageView.imageTintList = ColorStateList.valueOf(ResourcesCompat.getColor(resources, R.color.white, context.theme))
|
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))
|
//mainQuoteViewContainer.layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, getIntrinsicHeight(maxContentWidth))
|
||||||
val quoteViewMainContentContainerLayoutParams = binding.quoteViewMainContentContainer.layoutParams as RelativeLayout.LayoutParams
|
// 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
|
// 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)
|
// quoteViewMainContentContainerLayoutParams.marginStart = if (!hasAttachments) toPx(16, resources) else toPx(48, resources)
|
||||||
binding.quoteViewMainContentContainer.layoutParams = quoteViewMainContentContainerLayoutParams
|
// quoteViewMainContentContainer.layoutParams = quoteViewMainContentContainerLayoutParams
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
@ -10,11 +10,10 @@ import android.text.style.ForegroundColorSpan
|
|||||||
import android.text.style.URLSpan
|
import android.text.style.URLSpan
|
||||||
import android.text.util.Linkify
|
import android.text.util.Linkify
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
@ -23,22 +22,20 @@ import androidx.core.graphics.BlendModeColorFilterCompat
|
|||||||
import androidx.core.graphics.BlendModeCompat
|
import androidx.core.graphics.BlendModeCompat
|
||||||
import androidx.core.text.getSpans
|
import androidx.core.text.getSpans
|
||||||
import androidx.core.text.toSpannable
|
import androidx.core.text.toSpannable
|
||||||
import androidx.core.view.children
|
import androidx.core.view.isVisible
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ViewVisibleMessageContentBinding
|
import network.loki.messenger.databinding.ViewVisibleMessageContentBinding
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import org.session.libsession.utilities.ThemeUtil
|
import org.session.libsession.utilities.ThemeUtil
|
||||||
import org.session.libsession.utilities.ViewUtil
|
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
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.ConversationActivityV2
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ModalUrlBottomSheet
|
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.MentionUtilities
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.ModalURLSpan
|
import org.thoughtcrime.securesms.conversation.v2.utilities.ModalURLSpan
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans
|
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
|
import org.thoughtcrime.securesms.database.model.SmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.util.SearchUtil
|
import org.thoughtcrime.securesms.util.SearchUtil
|
||||||
import org.thoughtcrime.securesms.util.UiModeUtilities
|
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||||
@ -49,7 +46,7 @@ import kotlin.math.roundToInt
|
|||||||
|
|
||||||
class VisibleMessageContentView : LinearLayout {
|
class VisibleMessageContentView : LinearLayout {
|
||||||
private lateinit var binding: ViewVisibleMessageContentBinding
|
private lateinit var binding: ViewVisibleMessageContentBinding
|
||||||
var onContentClick: ((event: MotionEvent) -> Unit)? = null
|
var onContentClick: MutableList<((event: MotionEvent) -> Unit)> = mutableListOf()
|
||||||
var onContentDoubleTap: (() -> Unit)? = null
|
var onContentDoubleTap: (() -> Unit)? = null
|
||||||
var delegate: VisibleMessageContentViewDelegate? = null
|
var delegate: VisibleMessageContentViewDelegate? = null
|
||||||
var indexInAdapter: Int = -1
|
var indexInAdapter: Int = -1
|
||||||
@ -74,23 +71,45 @@ class VisibleMessageContentView : LinearLayout {
|
|||||||
val filter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_IN)
|
val filter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_IN)
|
||||||
background.colorFilter = filter
|
background.colorFilter = filter
|
||||||
setBackground(background)
|
setBackground(background)
|
||||||
// Body
|
|
||||||
binding.mainContainer.removeAllViews()
|
val onlyBodyMessage = message is SmsMessageRecord
|
||||||
onContentClick = null
|
val mediaThumbnailMessage = contactIsTrusted && message is MmsMessageRecord && message.slideDeck.thumbnailSlide != null
|
||||||
|
|
||||||
|
// reset visibilities / containers
|
||||||
|
onContentClick.clear()
|
||||||
|
binding.albumThumbnailView.clearViews()
|
||||||
onContentDoubleTap = null
|
onContentDoubleTap = null
|
||||||
|
|
||||||
if (message.isDeleted) {
|
if (message.isDeleted) {
|
||||||
val deletedMessageView = DeletedMessageView(context)
|
binding.deletedMessageView.isVisible = true
|
||||||
deletedMessageView.bind(message, getTextColor(context,message))
|
binding.deletedMessageView.bind(message, VisibleMessageContentView.getTextColor(context,message))
|
||||||
binding.mainContainer.addView(deletedMessageView)
|
return
|
||||||
} else if (message is MmsMessageRecord && message.linkPreviews.isNotEmpty()) {
|
} else {
|
||||||
val linkPreviewView = LinkPreviewView(context)
|
binding.deletedMessageView.isVisible = false
|
||||||
linkPreviewView.bind(message, glide, isStartOfMessageCluster, isEndOfMessageCluster, searchQuery)
|
}
|
||||||
binding.mainContainer.addView(linkPreviewView)
|
|
||||||
onContentClick = { event -> linkPreviewView.calculateHit(event) }
|
binding.quoteView.isVisible = message is MmsMessageRecord && message.quote != null
|
||||||
// Body text view is inside the link preview for layout convenience
|
val quoteLayoutParams = binding.quoteView.layoutParams
|
||||||
} else if (message is MmsMessageRecord && message.quote != null) {
|
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 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
|
// 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
|
// times the horizontal margin. This unfortunately has to be calculated manually
|
||||||
// here to get the layout right.
|
// here to get the layout right.
|
||||||
@ -100,88 +119,91 @@ class VisibleMessageContentView : LinearLayout {
|
|||||||
} else {
|
} else {
|
||||||
quote.text
|
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,
|
message.isOutgoing, maxContentWidth, message.isOpenGroupInvitation, message.threadId,
|
||||||
quote.isOriginalMissing, glide)
|
quote.isOriginalMissing, glide)
|
||||||
binding.mainContainer.addView(quoteView)
|
onContentClick.add { event ->
|
||||||
val bodyTextView = getBodyTextView(context, message, searchQuery)
|
|
||||||
ViewUtil.setPaddingTop(bodyTextView, 0)
|
|
||||||
binding.mainContainer.addView(bodyTextView)
|
|
||||||
onContentClick = { event ->
|
|
||||||
val r = Rect()
|
val r = Rect()
|
||||||
quoteView.getGlobalVisibleRect(r)
|
binding.quoteView.getGlobalVisibleRect(r)
|
||||||
if (r.contains(event.rawX.roundToInt(), event.rawY.roundToInt())) {
|
if (r.contains(event.rawX.roundToInt(), event.rawY.roundToInt())) {
|
||||||
delegate?.scrollToMessageIfPossible(quote.id)
|
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) {
|
} else if (message is MmsMessageRecord && message.slideDeck.audioSlide != null) {
|
||||||
|
hideBody = true
|
||||||
// Audio attachment
|
// Audio attachment
|
||||||
if (contactIsTrusted || message.isOutgoing) {
|
if (contactIsTrusted || message.isOutgoing) {
|
||||||
val voiceMessageView = VoiceMessageView(context)
|
binding.voiceMessageView.indexInAdapter = indexInAdapter
|
||||||
voiceMessageView.indexInAdapter = indexInAdapter
|
binding.voiceMessageView.delegate = context as? ConversationActivityV2
|
||||||
voiceMessageView.delegate = context as? ConversationActivityV2
|
binding.voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
|
||||||
voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
|
|
||||||
binding.mainContainer.addView(voiceMessageView)
|
|
||||||
// We have to use onContentClick (rather than a click listener directly on the voice
|
// 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.
|
// message view) so as to not interfere with all the other gestures.
|
||||||
onContentClick = { voiceMessageView.togglePlayback() }
|
onContentClick.add { binding.voiceMessageView.togglePlayback() }
|
||||||
onContentDoubleTap = { voiceMessageView.handleDoubleTap() }
|
onContentDoubleTap = { binding.voiceMessageView.handleDoubleTap() }
|
||||||
} else {
|
} else {
|
||||||
val untrustedView = UntrustedAttachmentView(context)
|
// TODO: move this out to its own area
|
||||||
untrustedView.bind(UntrustedAttachmentView.AttachmentType.AUDIO, getTextColor(context,message))
|
binding.untrustedView.bind(UntrustedAttachmentView.AttachmentType.AUDIO, VisibleMessageContentView.getTextColor(context,message))
|
||||||
binding.mainContainer.addView(untrustedView)
|
onContentClick.add { binding.untrustedView.showTrustDialog(message.individualRecipient) }
|
||||||
onContentClick = { untrustedView.showTrustDialog(message.individualRecipient) }
|
|
||||||
}
|
}
|
||||||
} else if (message is MmsMessageRecord && message.slideDeck.documentSlide != null) {
|
} else if (message is MmsMessageRecord && message.slideDeck.documentSlide != null) {
|
||||||
|
hideBody = true
|
||||||
// Document attachment
|
// Document attachment
|
||||||
if (contactIsTrusted || message.isOutgoing) {
|
if (contactIsTrusted || message.isOutgoing) {
|
||||||
val documentView = DocumentView(context)
|
binding.documentView.bind(message, VisibleMessageContentView.getTextColor(context, message))
|
||||||
documentView.bind(message, getTextColor(context, message))
|
|
||||||
binding.mainContainer.addView(documentView)
|
|
||||||
} else {
|
} else {
|
||||||
val untrustedView = UntrustedAttachmentView(context)
|
binding.untrustedView.bind(UntrustedAttachmentView.AttachmentType.DOCUMENT, VisibleMessageContentView.getTextColor(context,message))
|
||||||
untrustedView.bind(UntrustedAttachmentView.AttachmentType.DOCUMENT, getTextColor(context,message))
|
onContentClick.add { binding.untrustedView.showTrustDialog(message.individualRecipient) }
|
||||||
binding.mainContainer.addView(untrustedView)
|
|
||||||
onContentClick = { untrustedView.showTrustDialog(message.individualRecipient) }
|
|
||||||
}
|
}
|
||||||
} else if (message is MmsMessageRecord && message.slideDeck.asAttachments().isNotEmpty()) {
|
} else if (message is MmsMessageRecord && message.slideDeck.asAttachments().isNotEmpty()) {
|
||||||
// Images/Video attachment
|
/*
|
||||||
|
* Images / Video attachment
|
||||||
|
*/
|
||||||
if (contactIsTrusted || message.isOutgoing) {
|
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
|
// 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
|
// bind after add view because views are inflated and calculated during bind
|
||||||
albumThumbnailView.bind(
|
binding.albumThumbnailView.bind(
|
||||||
glideRequests = glide,
|
glideRequests = glide,
|
||||||
message = message,
|
message = message,
|
||||||
isStart = isStartOfMessageCluster,
|
isStart = isStartOfMessageCluster,
|
||||||
isEnd = isEndOfMessageCluster
|
isEnd = isEndOfMessageCluster
|
||||||
)
|
)
|
||||||
onContentClick = { event ->
|
onContentClick.add { event ->
|
||||||
albumThumbnailView.calculateHitObject(event, message, thread)
|
binding.albumThumbnailView.calculateHitObject(event, message, thread)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val untrustedView = UntrustedAttachmentView(context)
|
hideBody = true
|
||||||
untrustedView.bind(UntrustedAttachmentView.AttachmentType.MEDIA, getTextColor(context,message))
|
binding.albumThumbnailView.clearViews()
|
||||||
binding.mainContainer.addView(untrustedView)
|
binding.untrustedView.bind(UntrustedAttachmentView.AttachmentType.MEDIA, VisibleMessageContentView.getTextColor(context,message))
|
||||||
onContentClick = { untrustedView.showTrustDialog(message.individualRecipient) }
|
onContentClick.add { binding.untrustedView.showTrustDialog(message.individualRecipient) }
|
||||||
}
|
}
|
||||||
} else if (message.isOpenGroupInvitation) {
|
} else if (message.isOpenGroupInvitation) {
|
||||||
val openGroupInvitationView = OpenGroupInvitationView(context)
|
hideBody = true
|
||||||
openGroupInvitationView.bind(message, getTextColor(context, message))
|
binding.openGroupInvitationView.bind(message, VisibleMessageContentView.getTextColor(context, message))
|
||||||
binding.mainContainer.addView(openGroupInvitationView)
|
onContentClick.add { binding.openGroupInvitationView.joinOpenGroup() }
|
||||||
onContentClick = { openGroupInvitationView.joinOpenGroup() }
|
}
|
||||||
} else {
|
|
||||||
val bodyTextView = getBodyTextView(context, message, searchQuery)
|
binding.bodyTextView.isVisible = message.body.isNotEmpty() && !hideBody
|
||||||
binding.mainContainer.addView(bodyTextView)
|
|
||||||
onContentClick = { event ->
|
// set it to use constraints if not only a text message, otherwise wrap content to whatever width it wants
|
||||||
// intersectedModalSpans should only be a list of one item
|
val params = binding.bodyTextView.layoutParams
|
||||||
bodyTextView.getIntersectedModalSpans(event).forEach { span ->
|
params.width = if (onlyBodyMessage) ViewGroup.LayoutParams.WRAP_CONTENT else 0
|
||||||
span.onClick(bodyTextView)
|
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() {
|
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() {
|
fun playVoiceMessage() {
|
||||||
binding.mainContainer.children.forEach { view ->
|
binding.voiceMessageView.togglePlayback()
|
||||||
if (view is VoiceMessageView) {
|
|
||||||
return@forEach view.togglePlayback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Convenience
|
// region Convenience
|
||||||
companion object {
|
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 {
|
fun getBodySpans(context: Context, message: MessageRecord, searchQuery: String?): Spannable {
|
||||||
var body = message.body.toSpannable()
|
var body = message.body.toSpannable()
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ import android.view.MotionEvent
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.RelativeLayout
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
@ -181,7 +180,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
if (binding.profilePictureContainer.visibility != View.GONE) { maxWidth -= binding.profilePictureContainer.width }
|
if (binding.profilePictureContainer.visibility != View.GONE) { maxWidth -= binding.profilePictureContainer.width }
|
||||||
// Populate content view
|
// Populate content view
|
||||||
binding.messageContentView.indexInAdapter = indexInAdapter
|
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
|
binding.messageContentView.delegate = contentViewDelegate
|
||||||
onDoubleTap = { binding.messageContentView.onContentDoubleTap?.invoke() }
|
onDoubleTap = { binding.messageContentView.onContentDoubleTap?.invoke() }
|
||||||
}
|
}
|
||||||
@ -224,11 +223,13 @@ class VisibleMessageView : LinearLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateExpirationTimer(message: MessageRecord) {
|
private fun updateExpirationTimer(message: MessageRecord) {
|
||||||
val expirationTimerViewLayoutParams = binding.expirationTimerView.layoutParams as RelativeLayout.LayoutParams
|
val expirationTimerViewLayoutParams = binding.expirationTimerView.layoutParams as MarginLayoutParams
|
||||||
val ruleToAdd = if (message.isOutgoing) RelativeLayout.ALIGN_START else RelativeLayout.ALIGN_END
|
val container = binding.expirationTimerViewContainer
|
||||||
val ruleToRemove = if (message.isOutgoing) RelativeLayout.ALIGN_END else RelativeLayout.ALIGN_START
|
val content = binding.messageContentView
|
||||||
expirationTimerViewLayoutParams.removeRule(ruleToRemove)
|
val expiration = binding.expirationTimerView
|
||||||
expirationTimerViewLayoutParams.addRule(ruleToAdd, R.id.messageContentView)
|
container.removeAllViewsInLayout()
|
||||||
|
container.addView(if (message.isOutgoing) expiration else content)
|
||||||
|
container.addView(if (message.isOutgoing) content else expiration)
|
||||||
val expirationTimerViewSize = toPx(12, resources)
|
val expirationTimerViewSize = toPx(12, resources)
|
||||||
val smallSpacing = resources.getDimension(R.dimen.small_spacing).roundToInt()
|
val smallSpacing = resources.getDimension(R.dimen.small_spacing).roundToInt()
|
||||||
expirationTimerViewLayoutParams.marginStart = if (message.isOutgoing) -(smallSpacing + expirationTimerViewSize) else 0
|
expirationTimerViewLayoutParams.marginStart = if (message.isOutgoing) -(smallSpacing + expirationTimerViewSize) else 0
|
||||||
@ -261,6 +262,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
} else {
|
} else {
|
||||||
binding.expirationTimerView.isVisible = false
|
binding.expirationTimerView.isVisible = false
|
||||||
}
|
}
|
||||||
|
container.requestLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleIsSelectedChanged() {
|
private fun handleIsSelectedChanged() {
|
||||||
@ -388,7 +390,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onContentClick(event: MotionEvent) {
|
fun onContentClick(event: MotionEvent) {
|
||||||
binding.messageContentView.onContentClick?.invoke(event)
|
binding.messageContentView.onContentClick.forEach { clickHandler -> clickHandler.invoke(event) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onPress(event: MotionEvent) {
|
private fun onPress(event: MotionEvent) {
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
package org.thoughtcrime.securesms.conversation.v2.utilities;
|
package org.thoughtcrime.securesms.conversation.v2.utilities;
|
||||||
|
|
||||||
|
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.UiThread;
|
|
||||||
import android.util.AttributeSet;
|
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.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.UiThread;
|
||||||
|
|
||||||
import com.bumptech.glide.RequestBuilder;
|
import com.bumptech.glide.RequestBuilder;
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
|
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.load.resource.bitmap.RoundedCorners;
|
||||||
import com.bumptech.glide.request.RequestOptions;
|
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.GlideBitmapListeningTarget;
|
||||||
import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget;
|
import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget;
|
||||||
import org.thoughtcrime.securesms.components.TransferControlView;
|
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.Slide;
|
||||||
import org.thoughtcrime.securesms.mms.SlideClickListener;
|
import org.thoughtcrime.securesms.mms.SlideClickListener;
|
||||||
import org.thoughtcrime.securesms.mms.SlidesClickedListener;
|
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.Collections;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
public class ThumbnailView extends FrameLayout {
|
public class ThumbnailView extends FrameLayout {
|
||||||
|
|
||||||
@ -287,7 +286,7 @@ public class ThumbnailView extends FrameLayout {
|
|||||||
} else if (slide.hasPlaceholder()) {
|
} else if (slide.hasPlaceholder()) {
|
||||||
buildPlaceholderGlideRequest(glideRequests, slide).into(new GlideBitmapListeningTarget(image, result));
|
buildPlaceholderGlideRequest(glideRequests, slide).into(new GlideBitmapListeningTarget(image, result));
|
||||||
} else {
|
} else {
|
||||||
glideRequests.clear(image);
|
glideRequests.load(R.drawable.ic_image_white_24dp).centerInside().into(image);
|
||||||
result.set(false);
|
result.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<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_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
@ -22,58 +20,4 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout="@layout/transfer_controls_stub" />
|
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>
|
</RelativeLayout>
|
@ -41,7 +41,8 @@
|
|||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:textSize="@dimen/small_font_size"
|
android:textSize="@dimen/small_font_size"
|
||||||
android:textStyle="bold"
|
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:maxLines="3"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:textColor="@color/text"
|
android:textColor="@color/text"
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout
|
<RelativeLayout android:id="@+id/mainQuoteViewContainer"
|
||||||
android:id="@+id/mainQuoteViewContainer"
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:background="@color/input_bar_background"
|
android:background="@color/input_bar_background"
|
||||||
android:paddingHorizontal="@dimen/medium_spacing">
|
android:paddingHorizontal="@dimen/medium_spacing"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/quoteViewAccentLine"
|
android:id="@+id/quoteViewAccentLine"
|
||||||
@ -21,6 +21,7 @@
|
|||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_marginVertical="@dimen/small_spacing"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:background="@drawable/view_quote_attachment_preview_background">
|
android:background="@drawable/view_quote_attachment_preview_background">
|
||||||
|
|
||||||
@ -45,23 +46,25 @@
|
|||||||
android:id="@+id/quoteViewMainContentContainer"
|
android:id="@+id/quoteViewMainContentContainer"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="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_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">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/quoteViewAuthorTextView"
|
android:id="@+id/quoteViewAuthorTextView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Spiderman"
|
android:ellipsize="end"
|
||||||
android:textSize="@dimen/small_font_size"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:textColor="@color/text"
|
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:ellipsize="end" />
|
tools:text="Spiderman"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:textSize="@dimen/small_font_size"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/quoteViewBodyTextView"
|
android:id="@+id/quoteViewBodyTextView"
|
||||||
@ -69,7 +72,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="3"
|
android:maxLines="3"
|
||||||
android:text="Yo, I need your help here!"
|
tools:text="Yo, I need your help here!"
|
||||||
android:textColor="@color/text"
|
android:textColor="@color/text"
|
||||||
android:textSize="@dimen/small_font_size" />
|
android:textSize="@dimen/small_font_size" />
|
||||||
|
|
||||||
|
@ -75,8 +75,9 @@
|
|||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:ellipsize="end" />
|
android:ellipsize="end" />
|
||||||
|
|
||||||
<RelativeLayout
|
<LinearLayout
|
||||||
android:id="@+id/expirationTimerViewContainer"
|
android:id="@+id/expirationTimerViewContainer"
|
||||||
|
android:orientation="horizontal"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
@ -89,9 +90,9 @@
|
|||||||
android:id="@+id/expirationTimerView"
|
android:id="@+id/expirationTimerView"
|
||||||
android:layout_width="12dp"
|
android:layout_width="12dp"
|
||||||
android:layout_height="12dp"
|
android:layout_height="12dp"
|
||||||
android:layout_centerVertical="true" />
|
android:layout_gravity="center_vertical" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -100,12 +101,12 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/messageTimestampTextView"
|
android:id="@+id/messageTimestampTextView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:layout_marginStart="2dp"
|
android:layout_marginStart="2dp"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textSize="10dp" />
|
android:textSize="11sp" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/messageStatusImageView"
|
android:id="@+id/messageStatusImageView"
|
||||||
|
@ -1,7 +1,104 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/mainContainer"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
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…
x
Reference in New Issue
Block a user