Expire group calls.

This commit is contained in:
Michelle Tang
2026-06-05 11:46:41 -04:00
committed by Cody Henthorne
parent 27ddd62d7a
commit d586eff80b
5 changed files with 60 additions and 16 deletions
@@ -47,7 +47,7 @@ object CallPreference {
}
private fun presentTimer(messageRecord: MessageRecord) {
if (messageRecord.expiresIn > 0) {
if (messageRecord.expiresIn > 0 && messageRecord.expireStarted > 0) {
binding.callTimer.visible = true
binding.callTimer.setPercentComplete(0f)
@@ -934,7 +934,7 @@ public final class ConversationUpdateItem extends FrameLayout
}
private void presentTimer(UpdateDescription updateDescription) {
if (updateDescription.hasExpiration() && messageRecord.getExpiresIn() > 0) {
if (updateDescription.hasExpiration() && messageRecord.getExpiresIn() > 0 && messageRecord.getExpireStarted() > 0) {
timer = new ExpirationTimer(messageRecord.getExpireStarted(), messageRecord.getExpiresIn());
handler.post(timerUpdateRunnable);
} else {
@@ -703,7 +703,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
peekGroupCallEraId,
peekJoinedUuids,
isCallFull,
call.event == Event.RINGING
call.event
)
} else {
SignalDatabase.messages.insertGroupCall(
@@ -803,6 +803,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
val call = getCallById(callId, groupRecipientId)
if (call == null) {
val direction = if (sender == Recipient.self().id) Direction.OUTGOING else Direction.INCOMING
val isMissedIncoming = direction == Direction.INCOMING && !isGroupCallActive && !didLocalUserJoin
writableDatabase
.insertInto(TABLE_NAME)
@@ -816,7 +817,8 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
TIMESTAMP to timestamp,
RINGER to null,
LOCAL_JOINED to didLocalUserJoin,
GROUP_CALL_ACTIVE to isGroupCallActive
GROUP_CALL_ACTIVE to isGroupCallActive,
READ to ReadState.serialize(if (isMissedIncoming) ReadState.UNREAD else ReadState.READ)
)
.run(SQLiteDatabase.CONFLICT_ABORT)
@@ -839,6 +841,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
}
AppDependencies.databaseObserver.notifyCallUpdateObservers()
AppDependencies.databaseObserver.notifyConversationListListeners()
}
/**
@@ -933,7 +936,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
): Boolean {
val localJoined = call.didLocalUserJoin || hasLocalUserJoined
Log.d(TAG, "Updating group call state: localJoined: $localJoined, isGroupCallActive: $isGroupCallActive")
Log.d(TAG, "Updating group call state: localJoined: $localJoined, isGroupCallActive: $isGroupCallActive, call event: ${call.event}")
val changed = writableDatabase.update(TABLE_NAME)
.values(
@@ -957,6 +960,18 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
Log.d(TAG, "[updateGroupCallState] Transitioned group call ${call.callId} from RINGING to ACCEPTED on local join")
}
val unanswered = call.event == Event.GENERIC_GROUP_CALL || call.event == Event.MISSED || call.event == Event.MISSED_NOTIFICATION_PROFILE
if (!isGroupCallActive && !localJoined && unanswered) {
val updated = writableDatabase.update(TABLE_NAME)
.values(
READ to ReadState.serialize(ReadState.UNREAD),
EVENT to Event.serialize(Event.MISSED)
)
.where("$CALL_ID = ?", call.callId)
.run()
Log.d(TAG, "[updateGroupCallState] Marking call as missed: $updated")
}
return changed
}
@@ -1079,6 +1094,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
}
AppDependencies.databaseObserver.notifyCallUpdateObservers()
AppDependencies.databaseObserver.notifyConversationListListeners()
}
private fun updateEventFromRingState(
@@ -1146,7 +1162,8 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
TYPE to Type.serialize(Type.GROUP_CALL),
DIRECTION to Direction.serialize(direction),
TIMESTAMP to timestamp,
RINGER to ringerRecipient.toLong()
RINGER to ringerRecipient.toLong(),
READ to ReadState.serialize(if (direction == Direction.INCOMING) ReadState.UNREAD else ReadState.READ)
)
.run(SQLiteDatabase.CONFLICT_ABORT)
}
@@ -73,6 +73,7 @@ import org.thoughtcrime.securesms.attachments.DatabaseAttachment.DisplayOrderCom
import org.thoughtcrime.securesms.backup.v2.exporters.ChatItemArchiveExporter
import org.thoughtcrime.securesms.contactshare.Contact
import org.thoughtcrime.securesms.conversation.MessageStyler
import org.thoughtcrime.securesms.database.CallTable.Event
import org.thoughtcrime.securesms.database.EarlyDeliveryReceiptCache.Receipt
import org.thoughtcrime.securesms.database.MentionUtil.UpdatedBodyAndMentions
import org.thoughtcrime.securesms.database.SignalDatabase.Companion.attachments
@@ -985,6 +986,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
): MessageId {
val recipient = Recipient.resolved(groupRecipientId)
val threadId = threads.getOrCreateThreadIdFor(recipient)
val expiresIn = if (RemoteConfig.disappearMore) recipient.expiresInSeconds.seconds.inWholeMilliseconds else 0
val messageId: MessageId = writableDatabase.withinTransaction { db ->
val self = Recipient.self()
val markRead = joinedUuids.contains(self.requireServiceId().rawUuid) || self.id == sender
@@ -1004,11 +1006,12 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
TO_RECIPIENT_ID to groupRecipientId.serialize(),
DATE_RECEIVED to timestamp,
DATE_SENT to timestamp,
READ to if (markRead) 1 else 0,
NOTIFIED to if (markRead) 1 else 0,
READ to 1,
NOTIFIED to 1,
BODY to Base64.encodeWithPadding(updateDetails),
TYPE to MessageTypes.GROUP_CALL_TYPE,
THREAD_ID to threadId
THREAD_ID to threadId,
EXPIRES_IN to expiresIn
)
val messageId = MessageId(db.insert(TABLE_NAME, null, values))
@@ -1016,9 +1019,14 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val isActiveCall = joinedUuids.isNotEmpty() || isIncomingGroupCallRingingOnLocalDevice
if (!isActiveCall) {
maybeCollapseMessage(db = db, messageId = messageId.id, threadId = threadId, dateReceived = timestamp, messageExtras = null, messageType = MessageTypes.GROUP_CALL_TYPE)
if (markRead && expiresIn != 0L) {
Log.d(TAG, "[insertGroupCall] Starting expiration timer for group call.")
val now = System.currentTimeMillis()
markExpireStarted(messageId.id, now)
AppDependencies.expiringMessageManager.scheduleDeletion(messageId.id, true, now, expiresIn)
}
}
threads.incrementUnread(threadId, 1, 0)
threads.update(threadId, true)
messageId
@@ -1094,8 +1102,9 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
eraId: String,
joinedUuids: Collection<UUID>,
isCallFull: Boolean,
isRingingOnLocalDevice: Boolean
event: Event
): MessageId {
val isRingingOnLocalDevice = event == Event.RINGING
writableDatabase.withinTransaction { db ->
val message = try {
getMessageRecord(messageId = messageId)
@@ -1113,7 +1122,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
BODY to body
)
if (sameEraId && (containsSelf || updateDetail.localUserJoined)) {
val localJoined = sameEraId && (containsSelf || updateDetail.localUserJoined)
if (localJoined) {
contentValues.put(READ, 1)
contentValues.put(NOTIFIED, 1)
}
@@ -1121,8 +1131,9 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val query = buildTrueUpdateQuery(ID_WHERE, buildArgs(messageId), contentValues)
val updated = db.update(TABLE_NAME, contentValues, query.where, query.whereArgs) > 0
if (inCallUuids.isEmpty() && message.collapsedState == CollapsedState.NONE) {
maybeCollapseMessage(db = db, messageId = messageId, threadId = message.threadId, dateReceived = message.dateReceived, messageExtras = message.messageExtras, messageType = message.type)
if (inCallUuids.isEmpty()) {
val acknowledgedCall = localJoined || event == Event.DECLINED
finalizeEndedGroupCallMessage(db, message, acknowledgedCall, logPrefix = "[updateGroupCall]")
}
if (updated) {
@@ -1174,8 +1185,9 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val query = buildTrueUpdateQuery(ID_WHERE, buildArgs(record.id), contentValues)
val updated = db.update(TABLE_NAME, contentValues, query.where, query.whereArgs) > 0
if (inCallUuids.isEmpty() && record.collapsedState == CollapsedState.NONE) {
maybeCollapseMessage(db = db, messageId = record.id, threadId = record.threadId, dateReceived = record.dateReceived, messageExtras = record.messageExtras, messageType = record.type)
if (inCallUuids.isEmpty()) {
val acknowledgedCall = sameEraId && (containsSelf || groupCallUpdateDetails.localUserJoined)
finalizeEndedGroupCallMessage(db, record, acknowledgedCall, logPrefix = "[updatePreviousGroupCall]")
}
if (updated) {
@@ -1185,6 +1197,20 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
}
fun finalizeEndedGroupCallMessage(db: SQLiteDatabase, message: MessageRecord, acknowledgedCall: Boolean, logPrefix: String) {
if (message.collapsedState == CollapsedState.NONE) {
maybeCollapseMessage(db = db, messageId = message.id, threadId = message.threadId, dateReceived = message.dateReceived, messageExtras = message.messageExtras, messageType = message.type)
}
val unstartedExpiration = message.expireStarted == 0L && message.expiresIn != 0L
if (unstartedExpiration && acknowledgedCall) {
Log.d(TAG, "$logPrefix Starting expiration after call has ended.")
val now = System.currentTimeMillis()
markExpireStarted(message.id, now)
AppDependencies.expiringMessageManager.scheduleDeletion(message.id, message.isMms, now, message.expiresIn)
}
}
fun insertEditMessageInbox(mediaMessage: IncomingMessage, targetMessage: MmsMessageRecord): Optional<InsertResult> {
val insertResult = insertMessageInbox(retrieved = mediaMessage, editedMessage = targetMessage, notifyObservers = false)
@@ -83,6 +83,7 @@ public final class UpdateDescription {
stringFactory,
null,
glyph,
true,
0,
0);
}