mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
Show send status of text messages.
This commit is contained in:
parent
1fe8dfa810
commit
c178535cc8
@ -30,6 +30,7 @@ import im.vector.app.core.date.VectorDateFormatter
|
||||
import im.vector.app.core.epoxy.LoadingItem_
|
||||
import im.vector.app.core.extensions.localDateTime
|
||||
import im.vector.app.core.extensions.nextOrNull
|
||||
import im.vector.app.core.extensions.prevOrNull
|
||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||
import im.vector.app.features.home.room.detail.RoomDetailAction
|
||||
import im.vector.app.features.home.room.detail.RoomDetailViewState
|
||||
@ -336,11 +337,12 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
||||
private fun buildCacheItem(currentPosition: Int, items: List<TimelineEvent>): CacheItemData {
|
||||
val event = items[currentPosition]
|
||||
val nextEvent = items.nextOrNull(currentPosition)
|
||||
val prevEvent = items.prevOrNull(currentPosition)
|
||||
if (hasReachedInvite && hasUTD) {
|
||||
return CacheItemData(event.localId, event.root.eventId, null, null, null)
|
||||
}
|
||||
updateUTDStates(event, nextEvent)
|
||||
val eventModel = timelineItemFactory.create(event, nextEvent, eventIdToHighlight, callback).also {
|
||||
val eventModel = timelineItemFactory.create(event, prevEvent, nextEvent, eventIdToHighlight, callback).also {
|
||||
it.id(event.localId)
|
||||
it.setOnVisibilityStateChanged(TimelineEventVisibilityStateChangedListener(callback, event))
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ class CallItemFactory @Inject constructor(
|
||||
): VectorEpoxyModel<*>? {
|
||||
if (event.root.eventId == null) return null
|
||||
val roomId = event.roomId
|
||||
val informationData = messageInformationDataFactory.create(event, null)
|
||||
val informationData = messageInformationDataFactory.create(event, null, null)
|
||||
val callSignalingContent = event.getCallSignallingContent() ?: return null
|
||||
val callId = callSignalingContent.callId ?: return null
|
||||
val call = callManager.getCallById(callId)
|
||||
|
@ -61,7 +61,7 @@ class DefaultItemFactory @Inject constructor(private val avatarSizeProvider: Ava
|
||||
} else {
|
||||
stringProvider.getString(R.string.rendering_event_error_exception, event.root.eventId)
|
||||
}
|
||||
val informationData = informationDataFactory.create(event, null)
|
||||
val informationData = informationDataFactory.create(event, null, null)
|
||||
return create(text, informationData, highlight, callback)
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat
|
||||
private val vectorPreferences: VectorPreferences) {
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
prevEvent: TimelineEvent?,
|
||||
nextEvent: TimelineEvent?,
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?): VectorEpoxyModel<*>? {
|
||||
@ -108,7 +109,7 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat
|
||||
}
|
||||
}
|
||||
|
||||
val informationData = messageInformationDataFactory.create(event, nextEvent)
|
||||
val informationData = messageInformationDataFactory.create(event, prevEvent, nextEvent)
|
||||
val attributes = attributesFactory.create(event.root.content.toModel<EncryptedEventContent>(), informationData, callback)
|
||||
return MessageTextItem_()
|
||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||
|
@ -48,7 +48,7 @@ class EncryptionItemFactory @Inject constructor(
|
||||
return null
|
||||
}
|
||||
val algorithm = event.root.getClearContent().toModel<EncryptionEventContent>()?.algorithm
|
||||
val informationData = informationDataFactory.create(event, null)
|
||||
val informationData = informationDataFactory.create(event, null, null)
|
||||
val attributes = messageItemAttributesFactory.create(null, informationData, callback)
|
||||
|
||||
val isSafeAlgorithm = algorithm == MXCRYPTO_ALGORITHM_MEGOLM
|
||||
|
@ -119,13 +119,14 @@ class MessageItemFactory @Inject constructor(
|
||||
}
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
prevEvent: TimelineEvent?,
|
||||
nextEvent: TimelineEvent?,
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?
|
||||
): VectorEpoxyModel<*>? {
|
||||
event.root.eventId ?: return null
|
||||
roomId = event.roomId
|
||||
val informationData = messageInformationDataFactory.create(event, nextEvent)
|
||||
val informationData = messageInformationDataFactory.create(event, prevEvent, nextEvent)
|
||||
if (event.root.isRedacted()) {
|
||||
// message is redacted
|
||||
val attributes = messageItemAttributesFactory.create(null, informationData, callback)
|
||||
|
@ -35,7 +35,7 @@ class NoticeItemFactory @Inject constructor(private val eventFormatter: NoticeEv
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?): NoticeItem? {
|
||||
val formattedText = eventFormatter.format(event) ?: return null
|
||||
val informationData = informationDataFactory.create(event, null)
|
||||
val informationData = informationDataFactory.create(event, null, null)
|
||||
val attributes = NoticeItem.Attributes(
|
||||
avatarRenderer = avatarRenderer,
|
||||
informationData = informationData,
|
||||
|
@ -38,6 +38,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
||||
private val userPreferencesProvider: UserPreferencesProvider) {
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
prevEvent: TimelineEvent?,
|
||||
nextEvent: TimelineEvent?,
|
||||
eventIdToHighlight: String?,
|
||||
callback: TimelineEventController.Callback?): VectorEpoxyModel<*> {
|
||||
@ -46,7 +47,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
||||
val computedModel = try {
|
||||
when (event.root.getClearType()) {
|
||||
EventType.STICKER,
|
||||
EventType.MESSAGE -> messageItemFactory.create(event, nextEvent, highlight, callback)
|
||||
EventType.MESSAGE -> messageItemFactory.create(event, prevEvent, nextEvent, highlight, callback)
|
||||
// State and call
|
||||
EventType.STATE_ROOM_TOMBSTONE,
|
||||
EventType.STATE_ROOM_NAME,
|
||||
@ -76,9 +77,9 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
||||
EventType.ENCRYPTED -> {
|
||||
if (event.root.isRedacted()) {
|
||||
// Redacted event, let the MessageItemFactory handle it
|
||||
messageItemFactory.create(event, nextEvent, highlight, callback)
|
||||
messageItemFactory.create(event, prevEvent, nextEvent, highlight, callback)
|
||||
} else {
|
||||
encryptedItemFactory.create(event, nextEvent, highlight, callback)
|
||||
encryptedItemFactory.create(event, prevEvent, nextEvent, highlight, callback)
|
||||
}
|
||||
}
|
||||
EventType.STATE_ROOM_ALIASES,
|
||||
|
@ -75,9 +75,9 @@ class VerificationItemFactory @Inject constructor(
|
||||
// If it's not a request ignore this event
|
||||
// if (refEvent.root.getClearContent().toModel<MessageVerificationRequestContent>() == null) return ignoredConclusion(event, highlight, callback)
|
||||
|
||||
val referenceInformationData = messageInformationDataFactory.create(refEvent, null)
|
||||
val referenceInformationData = messageInformationDataFactory.create(refEvent, null, null)
|
||||
|
||||
val informationData = messageInformationDataFactory.create(event, null)
|
||||
val informationData = messageInformationDataFactory.create(event, null, null)
|
||||
val attributes = messageItemAttributesFactory.create(null, informationData, callback)
|
||||
|
||||
when (event.root.getClearType()) {
|
||||
|
@ -64,7 +64,7 @@ class WidgetItemFactory @Inject constructor(
|
||||
callback: TimelineEventController.Callback?,
|
||||
widgetContent: WidgetContent,
|
||||
previousWidgetContent: WidgetContent?): VectorEpoxyModel<*> {
|
||||
val informationData = informationDataFactory.create(timelineEvent, null)
|
||||
val informationData = informationDataFactory.create(timelineEvent, null, null)
|
||||
val attributes = messageItemAttributesFactory.create(null, informationData, callback)
|
||||
|
||||
val disambiguatedDisplayName = timelineEvent.senderInfo.disambiguatedDisplayName
|
||||
|
@ -25,6 +25,7 @@ import im.vector.app.features.home.room.detail.timeline.item.PollResponseData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReactionInfoData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReferencesInfoData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.SendStateDecoration
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import org.matrix.android.sdk.api.crypto.VerificationState
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
@ -49,7 +50,7 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
|
||||
private val dateFormatter: VectorDateFormatter,
|
||||
private val vectorPreferences: VectorPreferences) {
|
||||
|
||||
fun create(event: TimelineEvent, nextEvent: TimelineEvent?): MessageInformationData {
|
||||
fun create(event: TimelineEvent, prevEvent: TimelineEvent?, nextEvent: TimelineEvent?): MessageInformationData {
|
||||
// Non nullability has been tested before
|
||||
val eventId = event.root.eventId!!
|
||||
|
||||
@ -70,6 +71,13 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
|
||||
val time = dateFormatter.format(event.root.originServerTs, DateFormatKind.MESSAGE_SIMPLE)
|
||||
val e2eDecoration = getE2EDecoration(event)
|
||||
|
||||
val isSentByMe = event.root.senderId == session.myUserId
|
||||
val sendStateDecoration = if (isSentByMe) {
|
||||
getSendStateDecoration(event.root.sendState, prevEvent?.root?.sendState, event.readReceipts.any { it.user.userId != session.myUserId })
|
||||
} else {
|
||||
SendStateDecoration.NONE
|
||||
}
|
||||
|
||||
return MessageInformationData(
|
||||
eventId = eventId,
|
||||
senderId = event.root.senderId ?: "",
|
||||
@ -110,11 +118,24 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
|
||||
?: VerificationState.REQUEST
|
||||
ReferencesInfoData(verificationState)
|
||||
},
|
||||
sentByMe = event.root.senderId == session.myUserId,
|
||||
e2eDecoration = e2eDecoration
|
||||
sentByMe = isSentByMe,
|
||||
e2eDecoration = e2eDecoration,
|
||||
sendStateDecoration = sendStateDecoration
|
||||
)
|
||||
}
|
||||
|
||||
private fun getSendStateDecoration(eventSendState: SendState, prevEventSendState: SendState?, anyReadReceipts: Boolean): SendStateDecoration {
|
||||
return if (eventSendState.isSending()) {
|
||||
SendStateDecoration.SENDING
|
||||
} else if (eventSendState.hasFailed()) {
|
||||
SendStateDecoration.FAILED
|
||||
} else if (eventSendState.isSent() && !prevEventSendState?.isSent().orFalse() && !anyReadReceipts) {
|
||||
SendStateDecoration.SENT
|
||||
} else {
|
||||
SendStateDecoration.NONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun getE2EDecoration(event: TimelineEvent): E2EDecoration {
|
||||
val roomSummary = roomSummariesHolder.get(event.roomId)
|
||||
return if (
|
||||
|
@ -31,7 +31,7 @@ import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||
|
||||
/**
|
||||
* Base timeline item that adds an optional information bar with the sender avatar, name and time
|
||||
* Base timeline item that adds an optional information bar with the sender avatar, name, time, send state
|
||||
* Adds associated click listeners (on avatar, displayname)
|
||||
*/
|
||||
abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>() {
|
||||
@ -82,6 +82,27 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
|
||||
holder.avatarImageView.setOnLongClickListener(null)
|
||||
holder.memberNameView.setOnLongClickListener(null)
|
||||
}
|
||||
|
||||
// Render send state indicator
|
||||
holder.sendStateImageView.isVisible = true
|
||||
when (attributes.informationData.sendStateDecoration) {
|
||||
SendStateDecoration.SENDING -> {
|
||||
holder.sendStateImageView
|
||||
.apply { setImageResource(R.drawable.ic_sending_message) }
|
||||
.apply { contentDescription = context.getString(R.string.event_status_a11y_sending) }
|
||||
}
|
||||
SendStateDecoration.SENT -> {
|
||||
holder.sendStateImageView
|
||||
.apply { setImageResource(R.drawable.ic_message_sent) }
|
||||
.apply { contentDescription = context.getString(R.string.event_status_a11y_sent) }
|
||||
}
|
||||
SendStateDecoration.FAILED -> {
|
||||
holder.sendStateImageView
|
||||
.apply { setImageResource(R.drawable.ic_sending_message_failed) }
|
||||
.apply { contentDescription = context.getString(R.string.event_status_a11y_failed) }
|
||||
}
|
||||
SendStateDecoration.NONE -> holder.sendStateImageView.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun unbind(holder: H) {
|
||||
@ -99,6 +120,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
|
||||
val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
|
||||
val memberNameView by bind<TextView>(R.id.messageMemberNameView)
|
||||
val timeView by bind<TextView>(R.id.messageTimeView)
|
||||
val sendStateImageView by bind<ImageView>(R.id.messageSendStateImageView)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,7 +42,8 @@ data class MessageInformationData(
|
||||
val readReceipts: List<ReadReceiptData> = emptyList(),
|
||||
val referencesInfoData: ReferencesInfoData? = null,
|
||||
val sentByMe: Boolean,
|
||||
val e2eDecoration: E2EDecoration = E2EDecoration.NONE
|
||||
val e2eDecoration: E2EDecoration = E2EDecoration.NONE,
|
||||
val sendStateDecoration: SendStateDecoration = SendStateDecoration.NONE
|
||||
) : Parcelable {
|
||||
|
||||
val matrixItem: MatrixItem
|
||||
@ -84,4 +85,11 @@ enum class E2EDecoration {
|
||||
WARN_SENT_BY_UNKNOWN
|
||||
}
|
||||
|
||||
enum class SendStateDecoration {
|
||||
NONE,
|
||||
SENDING,
|
||||
SENT,
|
||||
FAILED
|
||||
}
|
||||
|
||||
fun ReadReceiptData.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)
|
||||
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="30dp"
|
||||
android:height="30dp"
|
||||
android:viewportWidth="30"
|
||||
android:viewportHeight="30">
|
||||
<path
|
||||
android:pathData="M8.5714,22.5C8.5714,23.6864 9.5357,25 10.7143,25H19.2857C20.4643,25 21.4286,23.4428 21.4286,22.2564V11.4711C21.4286,10.2848 20.4643,9.3141 19.2857,9.3141H10.7143C9.5357,9.3141 8.5714,10.2848 8.5714,11.4711V22.5ZM21.4286,6.0785H18.75L17.9893,5.3128C17.7964,5.1186 17.5179,5 17.2393,5H12.7607C12.4821,5 12.2036,5.1186 12.0107,5.3128L11.25,6.0785H8.5714C7.9821,6.0785 7.5,6.5639 7.5,7.1571C7.5,7.7502 7.9821,8.2356 8.5714,8.2356H21.4286C22.0179,8.2356 22.5,7.7502 22.5,7.1571C22.5,6.5639 22.0179,6.0785 21.4286,6.0785Z"
|
||||
android:fillColor="#FE2928"/>
|
||||
</vector>
|
13
vector/src/main/res/drawable/ic_message_sent.xml
Normal file
13
vector/src/main/res/drawable/ic_message_sent.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="18"
|
||||
android:viewportHeight="18">
|
||||
<path
|
||||
android:pathData="M9,16C12.866,16 16,12.866 16,9C16,5.134 12.866,2 9,2C5.134,2 2,5.134 2,9C2,12.866 5.134,16 9,16ZM9,17C13.4183,17 17,13.4183 17,9C17,4.5817 13.4183,1 9,1C4.5817,1 1,4.5817 1,9C1,13.4183 4.5817,17 9,17Z"
|
||||
android:fillColor="#8D99A5"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M12.8697,5.9531C12.6784,5.7576 12.3597,5.7473 12.1578,5.9325L7.6207,10.048L5.9524,8.9163C5.7293,8.7722 5.4212,8.7722 5.2087,8.9574C4.9536,9.1632 4.9324,9.5336 5.1449,9.7805L7.0681,11.9206C7.1,11.9515 7.1319,11.9926 7.1744,12.0132C7.5356,12.3013 8.0776,12.2498 8.3751,11.9L8.4069,11.8589L12.891,6.6013C13.0397,6.4161 13.0397,6.1383 12.8697,5.9531Z"
|
||||
android:fillColor="#8D99A5"/>
|
||||
</vector>
|
10
vector/src/main/res/drawable/ic_retry_sending_messages.xml
Normal file
10
vector/src/main/res/drawable/ic_retry_sending_messages.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M4.0227,2.9646C5.1159,2.1 6.4987,1.5835 8,1.5835C11.3187,1.5835 14.049,4.1029 14.3825,7.3335H15.6723C15.9336,7.3335 16.0894,7.625 15.9445,7.8426L13.9388,10.8543C13.8094,11.0488 13.524,11.0488 13.3945,10.8543L11.3888,7.8426C11.2439,7.625 11.3997,7.3335 11.661,7.3335H12.8719C12.5465,4.9334 10.4893,3.0835 8,3.0835C6.8483,3.0835 5.7909,3.4786 4.9531,4.1411C4.8969,4.1856 4.8485,4.2213 4.813,4.2467C4.7951,4.2595 4.7803,4.2698 4.7692,4.2774L4.7553,4.2869L4.7505,4.2901L4.7487,4.2913L4.7479,4.2918L4.7476,4.2921L4.7474,4.2922L4.7473,4.2922L4.3334,3.6669L4.7472,4.2923C4.4018,4.5209 3.9365,4.4262 3.7079,4.0807C3.4798,3.736 3.5736,3.2719 3.9173,3.0428L3.9202,3.0408L3.9401,3.0268C3.9591,3.0132 3.988,2.992 4.0227,2.9646ZM3.1281,8.6668H4.339C4.6003,8.6668 4.7561,8.3753 4.6112,8.1577L2.6055,5.146C2.476,4.9516 2.1906,4.9516 2.0612,5.146L0.0555,8.1577C-0.0894,8.3753 0.0664,8.6668 0.3277,8.6668H1.6176C1.951,11.8974 4.6813,14.4168 8,14.4168C9.5683,14.4168 11.0069,13.8532 12.1215,12.9184C12.4388,12.6522 12.4803,12.1791 12.2141,11.8617C11.9479,11.5444 11.4749,11.5029 11.1575,11.7691C10.303,12.4859 9.2028,12.9168 8,12.9168C5.5107,12.9168 3.4535,11.0669 3.1281,8.6668Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
10
vector/src/main/res/drawable/ic_sending_message.xml
Normal file
10
vector/src/main/res/drawable/ic_sending_message.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="18"
|
||||
android:viewportHeight="18">
|
||||
<path
|
||||
android:pathData="M9,16C12.866,16 16,12.866 16,9C16,5.134 12.866,2 9,2C5.134,2 2,5.134 2,9C2,12.866 5.134,16 9,16ZM9,17C13.4183,17 17,13.4183 17,9C17,4.5817 13.4183,1 9,1C4.5817,1 1,4.5817 1,9C1,13.4183 4.5817,17 9,17Z"
|
||||
android:fillColor="#8D99A5"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
10
vector/src/main/res/drawable/ic_sending_message_failed.xml
Normal file
10
vector/src/main/res/drawable/ic_sending_message_failed.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M8,16C12.4183,16 16,12.4183 16,8C16,3.5817 12.4183,0 8,0C3.5817,0 0,3.5817 0,8C0,12.4183 3.5817,16 8,16ZM6.9806,4.5101C6.9306,3.9401 7.3506,3.4401 7.9206,3.4001C8.4806,3.3601 8.9806,3.7801 9.0406,4.3501V4.5101L8.7206,8.5101C8.6906,8.8801 8.3806,9.1601 8.0106,9.1601H7.9506C7.6006,9.1301 7.3306,8.8601 7.3006,8.5101L6.9806,4.5101ZM8.8801,11.1202C8.8801,11.6062 8.4861,12.0002 8.0001,12.0002C7.5141,12.0002 7.1201,11.6062 7.1201,11.1202C7.1201,10.6342 7.5141,10.2402 8.0001,10.2402C8.4861,10.2402 8.8801,10.6342 8.8801,11.1202Z"
|
||||
android:fillColor="#FF4B55"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
@ -81,6 +81,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/messageMemberNameView"
|
||||
android:layout_toEndOf="@id/messageStartGuideline"
|
||||
android:layout_toStartOf="@id/messageSendStateImageView"
|
||||
android:addStatesFromChildren="true">
|
||||
|
||||
<ViewStub
|
||||
@ -133,6 +134,17 @@
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/messageSendStateImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_sending_message"
|
||||
android:layout_alignBottom="@+id/viewStubContainer"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:contentDescription="@string/event_status_a11y_sending" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/informationBottom"
|
||||
android:layout_width="match_parent"
|
||||
|
Loading…
Reference in New Issue
Block a user