diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationMessage.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationMessage.java index cdd864e77d..99612df4e1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationMessage.java @@ -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); } /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java index 884ab620a5..dfc3b39991 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -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()); }; } 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 b8e779924e..9ec1ccc20e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -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) { if (ids.isEmpty()) { return diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8b97455cf8..9edbf4a9f2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3698,8 +3698,8 @@ %1$d group updates %1$d chat updates - - %1$d disappearing message timer changes + + %1$d disappearing message timer changes ยท %2$s %1$d call events