diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java index 2a13c0c9124..508e8771145 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java @@ -134,6 +134,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind private View uncheckedView; private View checkedView; private View unreadMentions; + private View unreadReactions; private View pinnedView; private int thumbSize; private GlideLiveDataTarget thumbTarget; @@ -171,6 +172,7 @@ protected void onFinishInflate() { this.uncheckedView = findViewById(R.id.conversation_list_item_unchecked); this.checkedView = findViewById(R.id.conversation_list_item_checked); this.unreadMentions = findViewById(R.id.conversation_list_item_unread_mentions_indicator); + this.unreadReactions = findViewById(R.id.conversation_list_item_unread_reactions_indicator); this.thumbSize = (int) DimensionUnit.SP.toPixels(16f); this.thumbTarget = new GlideLiveDataTarget(thumbSize, thumbSize); this.searchStyleFactory = () -> new CharacterStyle[] { new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.signal_colorOnSurface)), SpanUtil.getBoldSpan() }; @@ -324,6 +326,7 @@ public void bindMessage(@NonNull LifecycleOwner lifecycleOwner, archivedView.setVisibility(GONE); unreadIndicator.setVisibility(GONE); unreadMentions.setVisibility(GONE); + unreadReactions.setVisibility(GONE); deliveryStatusIndicator.setNone(); alertView.setNone(); @@ -362,6 +365,7 @@ public void bindGroupWithMembers(@NonNull LifecycleOwner lifecycleOwner, archivedView.setVisibility(GONE); unreadIndicator.setVisibility(GONE); unreadMentions.setVisibility(GONE); + unreadReactions.setVisibility(GONE); deliveryStatusIndicator.setNone(); alertView.setNone(); @@ -549,15 +553,30 @@ private void setUnreadIndicator(ThreadRecord thread) { if (thread.isRead()) { unreadIndicator.setVisibility(View.GONE); unreadMentions.setVisibility(View.GONE); + unreadReactions.setVisibility(View.GONE); return; } if (thread.getUnreadSelfMentionsCount() > 0) { + // we have mentions, show those and unread indicator if there are multiple unread messages unreadMentions.setVisibility(View.VISIBLE); unreadIndicator.setVisibility(thread.getUnreadCount() == 1 ? View.GONE : View.VISIBLE); + unreadReactions.setVisibility(View.GONE); } else { + // no mentions, hide the mention indicator unreadMentions.setVisibility(View.GONE); - unreadIndicator.setVisibility(View.VISIBLE); + // check for unread messages and reactions separately since either could cause + // the thread to be unread + if (thread.getUnreadCount() > 0) { + unreadIndicator.setVisibility(View.VISIBLE); + } else { + unreadIndicator.setVisibility(View.GONE); + } + if (thread.getUnreadReactionToSelfCount() > 0) { + unreadReactions.setVisibility(View.VISIBLE); + } else { + unreadReactions.setVisibility(View.GONE); + } } unreadIndicator.setText(unreadCount > 0 ? String.valueOf(unreadCount) : " "); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt index a58e6870d95..bcfbdb39337 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -45,6 +45,7 @@ import org.signal.core.util.insertInto import org.signal.core.util.logging.Log import org.signal.core.util.readToList import org.signal.core.util.readToSet +import org.signal.core.util.readToSingleBoolean import org.signal.core.util.readToSingleInt import org.signal.core.util.readToSingleLong import org.signal.core.util.readToSingleLongOrNull @@ -951,7 +952,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat ) val messageId = MessageId(db.insert(TABLE_NAME, null, values)) - threads.incrementUnread(threadId, 1, 0) + threads.incrementUnread(threadId, 1, 0, 0) threads.update(threadId, true) messageId @@ -2501,6 +2502,15 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat .readToSingleInt() } + fun getUnreadReactionsToSelfCount(threadId: Long): Int { + return readableDatabase + .count() + .from(TABLE_NAME) + .where("$THREAD_ID = ? AND $STORY_TYPE = 0 AND $PARENT_STORY_ID <= 0 AND $ORIGINAL_MESSAGE_ID IS NULL AND $SCHEDULED_DATE = -1 AND ($outgoingTypeClause) AND $REACTIONS_UNREAD = 1", threadId) + .run() + .readToSingleInt() + } + /** * Trims data related to expired messages. Only intended to be run after a backup restore. */ @@ -2888,7 +2898,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat editedMessage == null ) { val incrementUnreadMentions = retrieved.mentions.isNotEmpty() && retrieved.mentions.any { it.recipientId == Recipient.self().id } - threads.incrementUnread(threadId, 1, if (incrementUnreadMentions) 1 else 0) + threads.incrementUnread(threadId, 1, if (incrementUnreadMentions) 1 else 0, 0) ThreadUpdateJob.enqueue(threadId) } @@ -2934,7 +2944,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat ) .run() - threads.incrementUnread(threadId, 1, 0) + threads.incrementUnread(threadId, 1, 0, 0) threads.update(threadId, true) notifyConversationListeners(threadId) @@ -2963,7 +2973,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat ) .run() - threads.incrementUnread(threadId, 1, 0) + threads.incrementUnread(threadId, 1, 0, 0) threads.update(threadId, true) notifyConversationListeners(threadId) @@ -5237,12 +5247,24 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat fun updateReactionsUnread(db: SQLiteDatabase, messageId: Long, hasReactions: Boolean, isRemoval: Boolean) { try { - val isOutgoing = getMessageRecord(messageId).isOutgoing + val messageRecord = getMessageRecord(messageId) + val isOutgoing = messageRecord.isOutgoing + val threadId = messageRecord.threadId + val hasUnreadReactions = readableDatabase + .select(REACTIONS_UNREAD) + .from(TABLE_NAME) + .where("$ID = ?", messageId) + .run() + .readToSingleBoolean() val values = ContentValues() if (!hasReactions) { values.put(REACTIONS_UNREAD, 0) } else if (!isRemoval) { + // increment unread reactions to self only on first reaction + if (!hasUnreadReactions && isOutgoing) { + threads.incrementUnread(threadId, 0, 0, 1) + } values.put(REACTIONS_UNREAD, 1) } @@ -5256,6 +5278,10 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat .where("$ID = ?", messageId) .run() } + + ThreadUpdateJob.enqueue(threadId) + notifyConversationListeners(threadId) + notifyConversationListListeners() } catch (e: NoSuchMessageException) { Log.w(TAG, "Failed to find message $messageId") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt index ed1cf5cdc37..301eb6cbc9f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt @@ -119,6 +119,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa const val LAST_SCROLLED = "last_scrolled" const val PINNED_ORDER = "pinned_order" const val UNREAD_SELF_MENTION_COUNT = "unread_self_mention_count" + const val UNREAD_REACTION_TO_SELF_COUNT = "unread_reaction_to_self_count" const val ACTIVE = "active" const val MAX_CACHE_SIZE = 1000 @@ -149,6 +150,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa $LAST_SCROLLED INTEGER DEFAULT 0, $PINNED_ORDER INTEGER UNIQUE DEFAULT NULL, $UNREAD_SELF_MENTION_COUNT INTEGER DEFAULT 0, + $UNREAD_REACTION_TO_SELF_COUNT INTEGER DEFAULT 0, $ACTIVE INTEGER DEFAULT 0, $SNIPPET_MESSAGE_EXTRAS BLOB DEFAULT NULL, $SNIPPET_MESSAGE_ID INTEGER DEFAULT 0 @@ -187,7 +189,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa HAS_READ_RECEIPT, LAST_SCROLLED, PINNED_ORDER, - UNREAD_SELF_MENTION_COUNT + UNREAD_SELF_MENTION_COUNT, + UNREAD_REACTION_TO_SELF_COUNT ) private val TYPED_THREAD_PROJECTION: List = THREAD_PROJECTION @@ -240,6 +243,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa readReceiptCount: Int, unreadCount: Int, unreadMentionCount: Int, + unreadReactionToSelfCount: Int, messageExtras: MessageExtras? ) { var extraSerialized: String? = null @@ -267,6 +271,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa ACTIVE to 1, UNREAD_COUNT to unreadCount, UNREAD_SELF_MENTION_COUNT to unreadMentionCount, + UNREAD_REACTION_TO_SELF_COUNT to unreadReactionToSelfCount, SNIPPET_MESSAGE_EXTRAS to messageExtras?.encode(), SNIPPET_MESSAGE_ID to messageId ) @@ -473,7 +478,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa .values( READ to ReadStatus.READ.serialize(), UNREAD_COUNT to 0, - UNREAD_SELF_MENTION_COUNT to 0 + UNREAD_SELF_MENTION_COUNT to 0, + UNREAD_REACTION_TO_SELF_COUNT to 0 ) .run() @@ -563,12 +569,14 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa val unreadCount = messages.getUnreadCount(threadId) val unreadMentionsCount = messages.getUnreadMentionCount(threadId) + val unreadReactionToSelfCount = messages.getUnreadReactionsToSelfCount(threadId) val lastSeenTimestamp = messages.getMostRecentReadMessageDateReceived(threadId) ?: System.currentTimeMillis() val contentValues = contentValuesOf( READ to ReadStatus.READ.serialize(), UNREAD_COUNT to unreadCount, UNREAD_SELF_MENTION_COUNT to unreadMentionsCount, + UNREAD_REACTION_TO_SELF_COUNT to unreadReactionToSelfCount, LAST_SEEN to lastSeenTimestamp ) @@ -770,17 +778,18 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa .use(mapCursorToType) } - fun incrementUnread(threadId: Long, unreadAmount: Int, unreadSelfMentionAmount: Int) { + fun incrementUnread(threadId: Long, unreadAmount: Int, unreadSelfMentionAmount: Int, unreadReactionToSelfAmount: Int) { writableDatabase.execSQL( """ UPDATE $TABLE_NAME SET $READ = ${ReadStatus.UNREAD.serialize()}, $UNREAD_COUNT = $UNREAD_COUNT + ?, $UNREAD_SELF_MENTION_COUNT = $UNREAD_SELF_MENTION_COUNT + ?, + $UNREAD_REACTION_TO_SELF_COUNT = $UNREAD_REACTION_TO_SELF_COUNT + ?, $LAST_SCROLLED = ? WHERE $ID = ? """, - SqlUtil.buildArgs(unreadAmount, unreadSelfMentionAmount, 0, threadId) + SqlUtil.buildArgs(unreadAmount, unreadSelfMentionAmount, unreadReactionToSelfAmount, 0, threadId) ) } @@ -1562,13 +1571,15 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa val previous = getThreadRecord(threadId) val unreadCount = messages.getUnreadCount(threadId) val unreadMentionsCount = messages.getUnreadMentionCount(threadId) + val unreadReactionToSelfCount = messages.getUnreadReactionsToSelfCount(threadId) writableDatabase .update(TABLE_NAME) .values( READ to if (unreadCount == 0) ReadStatus.READ.serialize() else ReadStatus.UNREAD.serialize(), UNREAD_COUNT to unreadCount, - UNREAD_SELF_MENTION_COUNT to unreadMentionsCount + UNREAD_SELF_MENTION_COUNT to unreadMentionsCount, + UNREAD_REACTION_TO_SELF_COUNT to unreadReactionToSelfCount ) .where("$ID = ?", threadId) .run() @@ -1655,10 +1666,12 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa } else if (threadId != null) { val unreadCount = messages.getUnreadCount(threadId) val unreadMentionsCount = messages.getUnreadMentionCount(threadId) + val unreadReactionToSelfCount = messages.getUnreadReactionsToSelfCount(threadId) values.put(READ, if (unreadCount == 0) ReadStatus.READ.serialize() else ReadStatus.UNREAD.serialize()) values.put(UNREAD_COUNT, unreadCount) values.put(UNREAD_SELF_MENTION_COUNT, unreadMentionsCount) + values.put(UNREAD_REACTION_TO_SELF_COUNT, unreadReactionToSelfCount) } writableDatabase @@ -1800,6 +1813,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa readReceiptCount = 0, unreadCount = 0, unreadMentionCount = 0, + unreadReactionToSelfCount = 0, messageExtras = null ) } @@ -1813,6 +1827,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa val threadBody: ThreadBody = ThreadBodyUtil.getFormattedBodyFor(context, record) val unreadCount: Int = messages.getUnreadCount(threadId) val unreadMentionCount: Int = messages.getUnreadMentionCount(threadId) + val unreadReactionToSelfCount: Int = messages.getUnreadReactionsToSelfCount(threadId) updateThread( threadId = threadId, @@ -1831,6 +1846,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa readReceiptCount = record.hasReadReceipt().toInt(), unreadCount = unreadCount, unreadMentionCount = unreadMentionCount, + unreadReactionToSelfCount = unreadReactionToSelfCount, messageExtras = record.messageExtras ) @@ -2011,6 +2027,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa LAST_SCROLLED to 0, PINNED_ORDER to null, UNREAD_SELF_MENTION_COUNT to 0, + UNREAD_REACTION_TO_SELF_COUNT to 0, ACTIVE to 0 ) @@ -2314,6 +2331,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa .setForcedUnread(cursor.requireInt(READ) == ReadStatus.FORCED_UNREAD.serialize()) .setPinned(cursor.requireBoolean(PINNED_ORDER)) .setUnreadSelfMentionsCount(cursor.requireInt(UNREAD_SELF_MENTION_COUNT)) + .setUnreadReactionToSelfCount(cursor.requireInt(UNREAD_REACTION_TO_SELF_COUNT)) .setExtra(extra) .setSnippetMessageExtras(messageExtras) .build() diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index a5836fb25e7..ef255234e91 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -148,6 +148,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V290_AddArchiveThum import org.thoughtcrime.securesms.database.helpers.migration.V291_NullOutRemoteKeyIfEmpty import org.thoughtcrime.securesms.database.helpers.migration.V292_AddPollTables import org.thoughtcrime.securesms.database.helpers.migration.V293_LastResortKeyTupleTableMigration +import org.thoughtcrime.securesms.database.helpers.migration.V294_ThreadUnreadReactionToSelfCount import org.thoughtcrime.securesms.database.SQLiteDatabase as SignalSqliteDatabase /** @@ -301,10 +302,11 @@ object SignalDatabaseMigrations { 290 to V290_AddArchiveThumbnailTransferStateColumn, 291 to V291_NullOutRemoteKeyIfEmpty, 292 to V292_AddPollTables, - 293 to V293_LastResortKeyTupleTableMigration + 293 to V293_LastResortKeyTupleTableMigration, + 294 to V294_ThreadUnreadReactionToSelfCount ) - const val DATABASE_VERSION = 293 + const val DATABASE_VERSION = 294 @JvmStatic fun migrate(context: Application, db: SignalSqliteDatabase, oldVersion: Int, newVersion: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V294_ThreadUnreadReactionToSelfCount.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V294_ThreadUnreadReactionToSelfCount.kt new file mode 100644 index 00000000000..c0ca61a21f0 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V294_ThreadUnreadReactionToSelfCount.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import org.thoughtcrime.securesms.database.SQLiteDatabase + +@Suppress("ClassName") +object V294_ThreadUnreadReactionToSelfCount : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("ALTER TABLE thread ADD COLUMN unread_reaction_to_self_count INTEGER DEFAULT 0") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java index dff9ac6166b..93899b2ee32 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java @@ -58,30 +58,32 @@ public final class ThreadRecord { private final long lastSeen; private final boolean isPinned; private final int unreadSelfMentionsCount; + private final int unreadReactionToSelfCount; private final MessageExtras messageExtras; private ThreadRecord(@NonNull Builder builder) { - this.threadId = builder.threadId; - this.body = builder.body; - this.recipient = builder.recipient; - this.date = builder.date; - this.type = builder.type; - this.deliveryStatus = builder.deliveryStatus; - this.hasDeliveryReceipt = builder.hasDeliveryReceipt; - this.hasReadReceipt = builder.hasReadReceipt; - this.snippetUri = builder.snippetUri; - this.contentType = builder.contentType; - this.extra = builder.extra; - this.meaningfulMessages = builder.meaningfulMessages; - this.unreadCount = builder.unreadCount; - this.forcedUnread = builder.forcedUnread; - this.distributionType = builder.distributionType; - this.archived = builder.archived; - this.expiresIn = builder.expiresIn; - this.lastSeen = builder.lastSeen; - this.isPinned = builder.isPinned; - this.unreadSelfMentionsCount = builder.unreadSelfMentionsCount; - this.messageExtras = builder.messageExtras; + this.threadId = builder.threadId; + this.body = builder.body; + this.recipient = builder.recipient; + this.date = builder.date; + this.type = builder.type; + this.deliveryStatus = builder.deliveryStatus; + this.hasDeliveryReceipt = builder.hasDeliveryReceipt; + this.hasReadReceipt = builder.hasReadReceipt; + this.snippetUri = builder.snippetUri; + this.contentType = builder.contentType; + this.extra = builder.extra; + this.meaningfulMessages = builder.meaningfulMessages; + this.unreadCount = builder.unreadCount; + this.forcedUnread = builder.forcedUnread; + this.distributionType = builder.distributionType; + this.archived = builder.archived; + this.expiresIn = builder.expiresIn; + this.lastSeen = builder.lastSeen; + this.isPinned = builder.isPinned; + this.unreadSelfMentionsCount = builder.unreadSelfMentionsCount; + this.unreadReactionToSelfCount = builder.unreadReactionToSelfCount; + this.messageExtras = builder.messageExtras; } public long getThreadId() { @@ -125,7 +127,7 @@ public boolean isForcedUnread() { } public boolean isRead() { - return unreadCount == 0 && !forcedUnread; + return unreadCount == 0 && unreadReactionToSelfCount == 0 && !forcedUnread; } public long getDate() { @@ -243,6 +245,10 @@ public int getUnreadSelfMentionsCount() { return unreadSelfMentionsCount; } + public int getUnreadReactionToSelfCount() { + return unreadReactionToSelfCount; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -265,6 +271,7 @@ public boolean equals(Object o) { body.equals(that.body) && recipient.equals(that.recipient) && unreadSelfMentionsCount == that.unreadSelfMentionsCount && + unreadReactionToSelfCount == that.unreadReactionToSelfCount && Objects.equals(snippetUri, that.snippetUri) && Objects.equals(contentType, that.contentType) && Objects.equals(extra, that.extra); @@ -291,7 +298,8 @@ public int hashCode() { expiresIn, lastSeen, isPinned, - unreadSelfMentionsCount); + unreadSelfMentionsCount, + unreadReactionToSelfCount); } public static class Builder { @@ -315,6 +323,7 @@ public static class Builder { private long lastSeen; private boolean isPinned; private int unreadSelfMentionsCount; + private int unreadReactionToSelfCount; private MessageExtras messageExtras; public Builder(long threadId) { @@ -426,6 +435,11 @@ public Builder setUnreadSelfMentionsCount(int unreadSelfMentionsCount) { return this; } + public Builder setUnreadReactionToSelfCount(int unreadReactionToSelfCount) { + this.unreadReactionToSelfCount = unreadReactionToSelfCount; + return this; + } + public ThreadRecord build() { if (distributionType == ThreadTable.DistributionTypes.CONVERSATION) { Preconditions.checkArgument(threadId > 0); diff --git a/app/src/main/res/drawable/ic_heart_12.xml b/app/src/main/res/drawable/ic_heart_12.xml new file mode 100644 index 00000000000..5867077d32e --- /dev/null +++ b/app/src/main/res/drawable/ic_heart_12.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/layout/conversation_list_item_view.xml b/app/src/main/res/layout/conversation_list_item_view.xml index d017670cd70..1f3da7e7260 100644 --- a/app/src/main/res/layout/conversation_list_item_view.xml +++ b/app/src/main/res/layout/conversation_list_item_view.xml @@ -133,8 +133,8 @@ android:id="@+id/conversation_list_item_status_container" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="12dp" android:layout_marginTop="6dp" + android:layout_marginStart="12dp" android:gravity="center" android:orientation="horizontal" app:layout_constraintEnd_toEndOf="parent" @@ -162,6 +162,21 @@ app:tint="@color/signal_colorOnPrimary" tools:visibility="visible" /> + +