Show final disappearing timer value for collapsed events.

This commit is contained in:
Michelle Tang
2026-03-25 13:08:28 -04:00
committed by Cody Henthorne
parent 957f473e77
commit e68691c966
4 changed files with 68 additions and 20 deletions

View File

@@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectCollection;
import org.thoughtcrime.securesms.conversation.v2.computed.FormattedDate;
import org.thoughtcrime.securesms.database.BodyRangeUtil;
import org.thoughtcrime.securesms.database.CollapsedState;
import org.thoughtcrime.securesms.database.CollapsibleEvents;
import org.thoughtcrime.securesms.database.MentionUtil;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SignalDatabase;
@@ -56,6 +57,7 @@ public class ConversationMessage {
@Nullable private final MemberLabel quoteMemberLabel;
@Nullable private final Recipient deletedByRecipient;
private final int collapsedSize;
private final long collapsedExpirationInMs;
private ConversationMessage(@NonNull MessageRecord messageRecord,
@Nullable CharSequence body,
@@ -68,19 +70,21 @@ public class ConversationMessage {
@Nullable MemberLabel memberLabel,
@Nullable MemberLabel quoteMemberLabel,
@Nullable Recipient deletedByRecipient,
int collapsedSize)
int collapsedSize,
long collapsedExpirationInMs)
{
this.messageRecord = messageRecord;
this.hasBeenQuoted = hasBeenQuoted;
this.mentions = mentions != null ? mentions : Collections.emptyList();
this.styleResult = styleResult != null ? styleResult : MessageStyler.Result.none();
this.threadRecipient = threadRecipient;
this.originalMessage = originalMessage;
this.computedProperties = computedProperties;
this.memberLabel = memberLabel;
this.quoteMemberLabel = quoteMemberLabel;
this.deletedByRecipient = deletedByRecipient;
this.collapsedSize = collapsedSize;
this.messageRecord = messageRecord;
this.hasBeenQuoted = hasBeenQuoted;
this.mentions = mentions != null ? mentions : Collections.emptyList();
this.styleResult = styleResult != null ? styleResult : MessageStyler.Result.none();
this.threadRecipient = threadRecipient;
this.originalMessage = originalMessage;
this.computedProperties = computedProperties;
this.memberLabel = memberLabel;
this.quoteMemberLabel = quoteMemberLabel;
this.deletedByRecipient = deletedByRecipient;
this.collapsedSize = collapsedSize;
this.collapsedExpirationInMs = collapsedExpirationInMs;
if (body != null) {
this.body = SpannableString.valueOf(body);
@@ -109,6 +113,10 @@ public class ConversationMessage {
return multiselectCollection;
}
public long getCollapsedExpirationInMs() {
return collapsedExpirationInMs;
}
public boolean hasBeenQuoted() {
return hasBeenQuoted;
}
@@ -299,8 +307,12 @@ public class ConversationMessage {
Recipient deletedBy = messageRecord.getDeletedBy() != null ? Recipient.resolved(messageRecord.getDeletedBy()) : null;
int collapsedSize = 0;
long collapsedExpirationInMs = 0;
if (CollapsedState.isHead(messageRecord.getCollapsedState())) {
collapsedSize = SignalDatabase.messages().getCollapsedCount(messageRecord.getId());
if (CollapsibleEvents.getCollapsibleType(messageRecord.getType(), messageRecord.getMessageExtras()) == CollapsibleEvents.CollapsibleType.DISAPPEARING_TIMER) {
collapsedExpirationInMs = SignalDatabase.messages().getDisappearingTimerStateForCollapsedSet(messageRecord.getId());
}
}
return new ConversationMessage(messageRecord,
@@ -314,7 +326,8 @@ public class ConversationMessage {
memberLabel,
quoteMemberLabel,
deletedBy,
collapsedSize);
collapsedSize,
collapsedExpirationInMs);
}
/**

View File

@@ -56,6 +56,7 @@ import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DrawableUtil;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MessageRecordUtil;
import org.thoughtcrime.securesms.util.Projection;
@@ -867,7 +868,7 @@ public final class ConversationUpdateItem extends FrameLayout
SpannableStringBuilder text = new SpannableStringBuilder()
.append(SignalSymbols.getSpannedString(getContext(), SignalSymbols.Weight.BOLD, getCollapsibleSymbol(collapsibleType), org.signal.core.ui.R.color.signal_colorOnSurfaceVariant))
.append(" ")
.append(getContext().getString(getCollapsibleString(collapsibleType), conversationMessage.getCollapsedSize()))
.append(getCollapsibleString(collapsibleType))
.append(" ")
.append(SignalSymbols.getSpannedString(getContext(), SignalSymbols.Weight.BOLD, collapsedState == CollapsedState.HEAD_EXPANDED ? SignalSymbols.Glyph.CHEVRON_UP : SignalSymbols.Glyph.CHEVRON_DOWN, org.signal.core.ui.R.color.signal_colorOnSurfaceVariant));
collapsedButton.setText(text);
@@ -888,11 +889,14 @@ public final class ConversationUpdateItem extends FrameLayout
}
}
private @StringRes int getCollapsibleString(CollapsibleEvents.CollapsibleType type) {
private @NonNull String getCollapsibleString(CollapsibleEvents.CollapsibleType type) {
return switch (type) {
case CALL_EVENT -> R.string.CollapsedEvent__call_event;
case DISAPPEARING_TIMER -> R.string.CollapsedEvent__disappearing_timer;
case CHAT_UPDATE -> conversationRecipient.isGroup() ? R.string.CollapsedEvent__group_update : R.string.CollapsedEvent__chat_update;
case CALL_EVENT -> getContext().getString(R.string.CollapsedEvent__call_event, conversationMessage.getCollapsedSize());
case DISAPPEARING_TIMER -> {
String time = ExpirationUtil.getExpirationAbbreviatedDisplayValue(getContext(), (int) (conversationMessage.getCollapsedExpirationInMs() / 1000));
yield getContext().getString(R.string.CollapsedEvent__disappearing_timer, conversationMessage.getCollapsedSize(), time) ;
}
case CHAT_UPDATE -> getContext().getString(conversationRecipient.isGroup() ? R.string.CollapsedEvent__group_update : R.string.CollapsedEvent__chat_update, conversationMessage.getCollapsedSize());
};
}

View File

@@ -4724,6 +4724,37 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
}
/**
* Gets the final disappearing timer value for a collapsed set of timer changes
*
* This should only be called when you know the id belongs to a set of disappearing messages
* and will return 0 if no timer update is found.
*/
fun getDisappearingTimerStateForCollapsedSet(id: Long): Long {
readableDatabase
.select(DATE_RECEIVED, TYPE, EXPIRES_IN, MESSAGE_EXTRAS)
.from(TABLE_NAME)
.where("$COLLAPSED_HEAD_ID = ? AND $COLLAPSED_STATE != ?", id, CollapsedState.PENDING_COLLAPSED.id)
.orderBy("$DATE_RECEIVED DESC")
.limit(1)
.run()
.use { cursor ->
if (cursor.moveToFirst()) {
if (MessageTypes.isExpirationTimerUpdate(cursor.requireLong(TYPE))) {
return cursor.requireLong(EXPIRES_IN)
} else {
val messageExtras = cursor.requireBlob(MESSAGE_EXTRAS)?.let { MessageExtras.ADAPTER.decode(it) }
if (messageExtras?.gv2UpdateDescription?.groupChangeUpdate?.updates?.isNotEmpty() == true) {
return messageExtras.gv2UpdateDescription.groupChangeUpdate.updates.findLast { update ->
update.groupExpirationTimerUpdate != null
}?.groupExpirationTimerUpdate?.expiresInMs ?: 0L
}
}
}
}
return 0
}
fun setNotifiedTimestamp(timestamp: Long, ids: List<Long>) {
if (ids.isEmpty()) {
return