diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/CallPreference.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/CallPreference.kt index 6eda6d68a5..03da020a41 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/CallPreference.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/CallPreference.kt @@ -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) 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 4b05c6795b..27f564bbf3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -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 { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt index 45f220fb0b..ed8cddf2b4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt @@ -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) } 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 e17c1d9e7f..eebf6d467b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -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, 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 { val insertResult = insertMessageInbox(retrieved = mediaMessage, editedMessage = targetMessage, notifyObservers = false) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java index 978e6dcf77..3cfda3206d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java @@ -83,6 +83,7 @@ public final class UpdateDescription { stringFactory, null, glyph, + true, 0, 0); }