diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt index 89dfe07fd6..b4768165f4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt @@ -6,6 +6,7 @@ import com.google.protobuf.ByteString import org.greenrobot.eventbus.EventBus import org.session.libsession.database.MessageDataProvider import org.session.libsession.messaging.MessagingModuleConfiguration +import org.session.libsession.messaging.messages.MarkAsDeletedMessage import org.session.libsession.messaging.messages.control.UnsendRequest import org.session.libsession.messaging.sending_receiving.attachments.Attachment import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId @@ -221,7 +222,8 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) val message = database.getMessageFor(timestamp, address) ?: return null val messagingDatabase: MessagingDatabase = if (message.isMms) DatabaseComponent.get(context).mmsDatabase() else DatabaseComponent.get(context).smsDatabase() - messagingDatabase.markAsDeleted(message.id) + + messagingDatabase.markAsDeleted(message.id, message.isOutgoing) if (message.isOutgoing) { messagingDatabase.deleteMessage(message.id) } @@ -230,7 +232,7 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) } override fun markMessagesAsDeleted( - messageIDs: List, + messages: List, threadId: Long, isSms: Boolean ) { @@ -240,8 +242,8 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) //todo DELETION can this be batched? //todo DELETION we need to have different types of "marked as deleted": incoming and outgoing //todo DELETION we need to have customisable text for "marked as deleted" message - messageIDs.forEach { messageID -> - messagingDatabase.markAsDeleted(messageID) + messages.forEach { message -> + messagingDatabase.markAsDeleted(message.messageId, message.isOutgoing) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index bef867f148..dcdb722880 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -262,13 +262,12 @@ class VisibleMessageView : LinearLayout { // Get details regarding how we should display the message (it's delivery icon, icon tint colour, and // the resource string for what text to display (R.string.delivery_status_sent etc.). - val (iconID, iconColor, textId) = getMessageStatusInfo(message) - // If we get any nulls then a message isn't one with a state that we care about (i.e., control messages + // If we get a null messageStatus then the message isn't one with a state that we care about (i.e., control messages // etc.) - so bail. See: `DisplayRecord.is` for the full suite of message state methods. // Also: We set all delivery status elements visibility to false just to make sure we don't display any // stale data. - if (textId == null) return + val messageStatus = getMessageStatusInfo(message) ?: return binding.messageInnerLayout.modifyLayoutParams { gravity = if (message.isOutgoing) Gravity.END else Gravity.START @@ -277,16 +276,17 @@ class VisibleMessageView : LinearLayout { horizontalBias = if (message.isOutgoing) 1f else 0f } - // If the message is incoming AND it is not scheduled to disappear then don't show any status or timer details + // If the message is incoming AND it is not scheduled to disappear + // OR it is a deleted message then don't show any status or timer details val scheduledToDisappear = message.expiresIn > 0 - if (message.isIncoming && !scheduledToDisappear) return + if (message.isDeleted || message.isIncoming && !scheduledToDisappear) return // Set text & icons as appropriate for the message state. Note: Possible message states we care // about are: isFailed, isSyncFailed, isPending, isSyncing, isResyncing, isRead, and isSent. - textId.let(binding.messageStatusTextView::setText) - iconColor?.let(binding.messageStatusTextView::setTextColor) - iconID?.let { ContextCompat.getDrawable(context, it) } - ?.run { iconColor?.let { mutate().apply { setTint(it) } } ?: this } + messageStatus.messageText?.let(binding.messageStatusTextView::setText) + messageStatus.iconTint?.let(binding.messageStatusTextView::setTextColor) + messageStatus.iconId?.let { ContextCompat.getDrawable(context, it) } + ?.run { messageStatus.iconTint?.let { mutate().apply { setTint(it) } } ?: this } ?.let(binding.messageStatusImageView::setImageDrawable) // Potential options at this point are that the message is: @@ -363,7 +363,7 @@ class VisibleMessageView : LinearLayout { @ColorInt val iconTint: Int?, @StringRes val messageText: Int?) - private fun getMessageStatusInfo(message: MessageRecord): MessageStatusInfo = when { + private fun getMessageStatusInfo(message: MessageRecord): MessageStatusInfo? = when { message.isFailed -> MessageStatusInfo(R.drawable.ic_delivery_status_failed, getThemedColor(context, R.attr.danger), @@ -399,10 +399,15 @@ class VisibleMessageView : LinearLayout { context.getColorFromAttr(R.attr.message_status_color), R.string.delivery_status_sent ) + + // deleted messages do not have a status but we care about styling them so they need to return something + message.isDeleted -> + MessageStatusInfo(null, null, null) + else -> { // The message isn't one we care about for message statuses we display to the user (i.e., // control messages etc. - see the `DisplayRecord.is` suite of methods for options). - MessageStatusInfo(null, null, null) + null } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java index 72d87d5e1a..debea45484 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java @@ -46,7 +46,7 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn public abstract void markUnidentified(long messageId, boolean unidentified); - public abstract void markAsDeleted(long messageId); + public abstract void markAsDeleted(long messageId, boolean isOutgoing); public abstract boolean deleteMessage(long messageId); public abstract boolean deleteMessages(long[] messageId, long threadId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt index e1a98da219..9fb36a2347 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt @@ -290,7 +290,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa db.update(TABLE_NAME, contentValues, ID_WHERE, arrayOf(messageId.toString())) } - override fun markAsDeleted(messageId: Long) { + override fun markAsDeleted(messageId: Long, isOutgoing: Boolean) { val database = databaseHelper.writableDatabase val contentValues = ContentValues() contentValues.put(READ, 1) @@ -301,7 +301,10 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa queue(Runnable { attachmentDatabase.deleteAttachmentsForMessage(messageId) }) val threadId = getThreadIdForMessage(messageId) - markAs(messageId, MmsSmsColumns.Types.BASE_DELETED_TYPE, threadId) + val deletedType = if (isOutgoing) { MmsSmsColumns.Types.BASE_DELETED_OUTGOING_TYPE} else { + MmsSmsColumns.Types.BASE_DELETED_INCOMING_TYPE + } + markAs(messageId, deletedType, threadId) } override fun markExpireStarted(messageId: Long, startedTimestamp: Long) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java index e6bc04e364..6bf00501cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java @@ -50,10 +50,11 @@ public interface MmsSmsColumns { protected static final long BASE_PENDING_SECURE_SMS_FALLBACK = 25; protected static final long BASE_PENDING_INSECURE_SMS_FALLBACK = 26; public static final long BASE_DRAFT_TYPE = 27; - protected static final long BASE_DELETED_TYPE = 28; + protected static final long BASE_DELETED_OUTGOING_TYPE = 28; protected static final long BASE_SYNCING_TYPE = 29; protected static final long BASE_RESYNCING_TYPE = 30; protected static final long BASE_SYNC_FAILED_TYPE = 31; + protected static final long BASE_DELETED_INCOMING_TYPE = 32; protected static final long[] OUTGOING_MESSAGE_TYPES = {BASE_OUTBOX_TYPE, BASE_SENT_TYPE, BASE_SYNCING_TYPE, BASE_RESYNCING_TYPE, @@ -61,6 +62,7 @@ public interface MmsSmsColumns { BASE_SENDING_TYPE, BASE_SENT_FAILED_TYPE, BASE_PENDING_SECURE_SMS_FALLBACK, BASE_PENDING_INSECURE_SMS_FALLBACK, + BASE_DELETED_OUTGOING_TYPE, OUTGOING_CALL_TYPE}; @@ -182,7 +184,10 @@ public interface MmsSmsColumns { return (type & BASE_TYPE_MASK) == BASE_INBOX_TYPE; } - public static boolean isDeletedMessage(long type) { return (type & BASE_TYPE_MASK) == BASE_DELETED_TYPE; } + public static boolean isDeletedMessage(long type) { + return (type & BASE_TYPE_MASK) == BASE_DELETED_OUTGOING_TYPE || + (type & BASE_TYPE_MASK) == BASE_DELETED_INCOMING_TYPE; + } public static boolean isJoinedType(long type) { return (type & BASE_TYPE_MASK) == JOINED_TYPE; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index 7ae0158f84..f12e041f40 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -236,14 +236,17 @@ public class SmsDatabase extends MessagingDatabase { } @Override - public void markAsDeleted(long messageId) { + public void markAsDeleted(long messageId, boolean isOutgoing) { SQLiteDatabase database = databaseHelper.getWritableDatabase(); ContentValues contentValues = new ContentValues(); contentValues.put(READ, 1); contentValues.put(BODY, ""); contentValues.put(HAS_MENTION, 0); database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(messageId)}); - updateTypeBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_DELETED_TYPE); + + updateTypeBitmask(messageId, Types.BASE_TYPE_MASK, + isOutgoing? MmsSmsColumns.Types.BASE_DELETED_OUTGOING_TYPE : MmsSmsColumns.Types.BASE_DELETED_INCOMING_TYPE + ); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt index f2ea334d42..30530a683c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import org.session.libsession.database.MessageDataProvider import org.session.libsession.messaging.messages.Destination +import org.session.libsession.messaging.messages.MarkAsDeletedMessage import org.session.libsession.messaging.messages.control.MessageRequestResponse import org.session.libsession.messaging.messages.control.UnsendRequest import org.session.libsession.messaging.messages.signal.OutgoingTextMessage @@ -186,11 +187,17 @@ class DefaultConversationRepository @Inject constructor( val (mms, sms) = messages.partition { it.isMms } if(mms.isNotEmpty()){ - messageDataProvider.markMessagesAsDeleted(mms.map { it.id }, threadId, isSms = false) + messageDataProvider.markMessagesAsDeleted(mms.map { MarkAsDeletedMessage( + messageId = it.id, + isOutgoing = it.isOutgoing + ) }, threadId, isSms = false) } if(sms.isNotEmpty()){ - messageDataProvider.markMessagesAsDeleted(sms.map { it.id }, threadId, isSms = true) + messageDataProvider.markMessagesAsDeleted(sms.map { MarkAsDeletedMessage( + messageId = it.id, + isOutgoing = it.isOutgoing + ) }, threadId, isSms = true) } //todo DELETION delete attachments and links diff --git a/app/src/main/res/layout/view_deleted_message.xml b/app/src/main/res/layout/view_deleted_message.xml index ec0e167652..cda59f8718 100644 --- a/app/src/main/res/layout/view_deleted_message.xml +++ b/app/src/main/res/layout/view_deleted_message.xml @@ -2,12 +2,11 @@ + android:padding="@dimen/small_spacing"> , threadId: Long, isSms: Boolean) fun updateMessageAsDeleted(timestamp: Long, author: String): Long? - fun markMessagesAsDeleted(messageIDs: List, threadId: Long, isSms: Boolean) + fun markMessagesAsDeleted(messages: List, threadId: Long, isSms: Boolean) fun getServerHashForMessage(messageID: Long, mms: Boolean): String? fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment? fun getAttachmentStream(attachmentId: Long): SessionServiceAttachmentStream? diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/MarkAsDeletedMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/MarkAsDeletedMessage.kt new file mode 100644 index 0000000000..4ed0e880c9 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/MarkAsDeletedMessage.kt @@ -0,0 +1,6 @@ +package org.session.libsession.messaging.messages + +data class MarkAsDeletedMessage( + val messageId: Long, + val isOutgoing: Boolean +)