From 45eb3549f6ba66838ef80b56beb83058510adf72 Mon Sep 17 00:00:00 2001 From: andrew Date: Thu, 4 May 2023 14:50:51 +0930 Subject: [PATCH] Add sync status message --- .../conversation/v2/ConversationActivityV2.kt | 8 ++ .../v2/ConversationReactionOverlay.java | 5 ++ .../menus/ConversationActionModeCallback.kt | 4 + .../v2/messages/VisibleMessageView.kt | 73 ++++++++++--------- .../v2/utilities/ResendMessageUtilities.kt | 16 +++- .../securesms/database/MessagingDatabase.java | 8 +- .../securesms/database/MmsDatabase.kt | 10 +++ .../securesms/database/MmsSmsColumns.java | 17 +++++ .../securesms/database/SmsDatabase.java | 15 ++++ .../securesms/database/Storage.kt | 56 +++++++++++++- .../database/model/DisplayRecord.java | 12 +++ .../menu/menu_conversation_item_action.xml | 5 ++ app/src/main/res/values/strings.xml | 4 + .../libsession/database/StorageProtocol.kt | 5 +- .../messaging/jobs/MessageSendJob.kt | 2 + .../messaging/messages/Destination.kt | 6 +- .../messages/visible/VisibleMessage.kt | 28 +++---- .../sending_receiving/MessageSender.kt | 37 ++++++++-- 18 files changed, 248 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index cabb583085..18c62a74c9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -1755,6 +1755,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe endActionMode() } + override fun resyncMessage(messages: Set) { + messages.iterator().forEach { messageRecord -> + ResendMessageUtilities.resend(this, messageRecord, viewModel.blindedPublicKey, isResync = true) + } + endActionMode() + } + override fun resendMessage(messages: Set) { messages.iterator().forEach { messageRecord -> ResendMessageUtilities.resend(this, messageRecord, viewModel.blindedPublicKey) @@ -1915,6 +1922,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe val selectedItems = setOf(message) when (action) { ConversationReactionOverlay.Action.REPLY -> reply(selectedItems) + ConversationReactionOverlay.Action.RESYNC -> resyncMessage(selectedItems) ConversationReactionOverlay.Action.RESEND -> resendMessage(selectedItems) ConversationReactionOverlay.Action.DOWNLOAD -> saveAttachment(selectedItems) ConversationReactionOverlay.Action.COPY_MESSAGE -> copyMessages(selectedItems) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.java index 1d81325e03..8f1fb6a0e5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationReactionOverlay.java @@ -700,6 +700,10 @@ public final class ConversationReactionOverlay extends FrameLayout { if (message.isFailed()) { items.add(new ActionItem(R.attr.menu_reply_icon, getContext().getResources().getString(R.string.conversation_context__menu_resend_message), () -> handleActionItemClicked(Action.RESEND))); } + // Resync + if (message.isSyncFailed()) { + items.add(new ActionItem(R.attr.menu_reply_icon, getContext().getResources().getString(R.string.conversation_context__menu_resync_message), () -> handleActionItemClicked(Action.RESYNC))); + } // Save media if (message.isMms() && ((MediaMmsMessageRecord)message).containsMediaSlide()) { items.add(new ActionItem(R.attr.menu_save_icon, getContext().getResources().getString(R.string.conversation_context_image__save_attachment), () -> handleActionItemClicked(Action.DOWNLOAD), @@ -885,6 +889,7 @@ public final class ConversationReactionOverlay extends FrameLayout { public enum Action { REPLY, RESEND, + RESYNC, DOWNLOAD, COPY_MESSAGE, COPY_SESSION_ID, diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationActionModeCallback.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationActionModeCallback.kt index d475a64448..f86920f90f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationActionModeCallback.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationActionModeCallback.kt @@ -70,6 +70,8 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p menu.findItem(R.id.menu_message_details).isVisible = (selectedItems.size == 1 && firstMessage.isOutgoing) // Resend menu.findItem(R.id.menu_context_resend).isVisible = (selectedItems.size == 1 && firstMessage.isFailed) + // Resync + menu.findItem(R.id.menu_context_resync).isVisible = (selectedItems.size == 1 && firstMessage.isSyncFailed) // Save media menu.findItem(R.id.menu_context_save_attachment).isVisible = (selectedItems.size == 1 && firstMessage.isMms && (firstMessage as MediaMmsMessageRecord).containsMediaSlide()) @@ -90,6 +92,7 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p R.id.menu_context_ban_and_delete_all -> delegate?.banAndDeleteAll(selectedItems) R.id.menu_context_copy -> delegate?.copyMessages(selectedItems) R.id.menu_context_copy_public_key -> delegate?.copySessionID(selectedItems) + R.id.menu_context_resync -> delegate?.resyncMessage(selectedItems) R.id.menu_context_resend -> delegate?.resendMessage(selectedItems) R.id.menu_message_details -> delegate?.showMessageDetail(selectedItems) R.id.menu_context_save_attachment -> delegate?.saveAttachment(selectedItems) @@ -113,6 +116,7 @@ interface ConversationActionModeCallbackDelegate { fun banAndDeleteAll(messages: Set) fun copyMessages(messages: Set) fun copySessionID(messages: Set) + fun resyncMessage(messages: Set) fun resendMessage(messages: Set) fun showMessageDetail(messages: Set) fun saveAttachment(messages: Set) 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 2c04bcfce3..745c91e910 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 @@ -292,39 +292,46 @@ class VisibleMessageView : LinearLayout { @StringRes val messageText: Int?, val contentDescription: String?) - private fun getMessageStatusImage(message: MessageRecord): MessageStatusInfo { - return when { - !message.isOutgoing -> MessageStatusInfo(null, - null, - null, - null) - message.isFailed -> - MessageStatusInfo( - R.drawable.ic_delivery_status_failed, - resources.getColor(R.color.destructive, context.theme), - R.string.delivery_status_failed, - null - ) - message.isPending -> - MessageStatusInfo( - R.drawable.ic_delivery_status_sending, - context.getColorFromAttr(R.attr.message_status_color), R.string.delivery_status_sending, - context.getString(R.string.AccessibilityId_message_sent_status_pending) - ) - message.isRead -> - MessageStatusInfo( - R.drawable.ic_delivery_status_read, - context.getColorFromAttr(R.attr.message_status_color), R.string.delivery_status_read, - null - ) - else -> - MessageStatusInfo( - R.drawable.ic_delivery_status_sent, - context.getColorFromAttr(R.attr.message_status_color), - R.string.delivery_status_sent, - context.getString(R.string.AccessibilityId_message_sent_status_tick) - ) - } + private fun getMessageStatusImage(message: MessageRecord): MessageStatusInfo = when { + message.isFailed -> + MessageStatusInfo( + R.drawable.ic_delivery_status_failed, + resources.getColor(R.color.destructive, context.theme), + R.string.delivery_status_failed, + null + ) + message.isSyncFailed -> + MessageStatusInfo( + R.drawable.ic_delivery_status_failed, + resources.getColor(R.color.destructive, context.theme), + R.string.delivery_status_sync_failed, + null + ) + message.isPending -> + MessageStatusInfo( + R.drawable.ic_delivery_status_sending, + context.getColorFromAttr(R.attr.message_status_color), R.string.delivery_status_sending, + context.getString(R.string.AccessibilityId_message_sent_status_pending) + ) + message.isResyncing -> + MessageStatusInfo( + R.drawable.ic_delivery_status_sending, + context.getColorFromAttr(R.attr.message_status_color), R.string.delivery_status_syncing, + context.getString(R.string.AccessibilityId_message_sent_status_syncing) + ) + message.isRead -> + MessageStatusInfo( + R.drawable.ic_delivery_status_read, + context.getColorFromAttr(R.attr.message_status_color), R.string.delivery_status_read, + null + ) + else -> + MessageStatusInfo( + R.drawable.ic_delivery_status_sent, + context.getColorFromAttr(R.attr.message_status_color), + R.string.delivery_status_sent, + context.getString(R.string.AccessibilityId_message_sent_status_tick) + ) } private fun updateExpirationTimer(message: MessageRecord) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ResendMessageUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ResendMessageUtilities.kt index 80f4cc0bf8..5e6283e1ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ResendMessageUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ResendMessageUtilities.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.conversation.v2.utilities import android.content.Context import org.session.libsession.messaging.MessagingModuleConfiguration +import org.session.libsession.messaging.messages.Destination import org.session.libsession.messaging.messages.visible.LinkPreview import org.session.libsession.messaging.messages.visible.OpenGroupInvitation import org.session.libsession.messaging.messages.visible.Quote @@ -10,12 +11,16 @@ import org.session.libsession.messaging.sending_receiving.MessageSender import org.session.libsession.messaging.utilities.UpdateMessageData import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.recipients.Recipient +import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord +private val TAG = ResendMessageUtilities.javaClass.simpleName + object ResendMessageUtilities { - fun resend(context: Context, messageRecord: MessageRecord, userBlindedKey: String?) { + fun resend(context: Context, messageRecord: MessageRecord, userBlindedKey: String?, isResync: Boolean = false) { + Log.d(TAG, "resend() called with: context = $context, messageRecord = $messageRecord, userBlindedKey = $userBlindedKey, isResync = $isResync") val recipient: Recipient = messageRecord.recipient val message = VisibleMessage() message.id = messageRecord.getId() @@ -55,8 +60,13 @@ object ResendMessageUtilities { val sentTimestamp = message.sentTimestamp val sender = MessagingModuleConfiguration.shared.storage.getUserPublicKey() if (sentTimestamp != null && sender != null) { - MessagingModuleConfiguration.shared.storage.markAsSending(sentTimestamp, sender) + if (isResync) { + MessagingModuleConfiguration.shared.storage.markAsResyncing(sentTimestamp, sender) + MessageSender.send(message, Destination.from(recipient.address), isSyncMessage = true) + } else { + MessagingModuleConfiguration.shared.storage.markAsSending(sentTimestamp, sender) + MessageSender.send(message, recipient.address) + } } - MessageSender.send(message, recipient.address) } } \ No newline at end of file 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 d3ba31747d..edc6bc1a6f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java @@ -37,6 +37,13 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn public abstract void markExpireStarted(long messageId, long startTime); public abstract void markAsSent(long messageId, boolean secure); + + public abstract void markAsSyncing(long id); + + public abstract void markAsResyncing(long id); + + public abstract void markAsSyncFailed(long id); + public abstract void markUnidentified(long messageId, boolean unidentified); public abstract void markAsDeleted(long messageId, boolean read, boolean hasMention); @@ -199,7 +206,6 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn contentValues.put(THREAD_ID, newThreadId); db.update(getTableName(), contentValues, where, args); } - public static class SyncMessageId { private final Address address; 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 3a4b35ad17..9e854698f5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt @@ -276,6 +276,16 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa notifyConversationListeners(threadId) } + override fun markAsSyncing(messageId: Long) { + markAs(messageId, MmsSmsColumns.Types.BASE_SYNCING_TYPE) + } + override fun markAsResyncing(messageId: Long) { + markAs(messageId, MmsSmsColumns.Types.BASE_RESYNCING_TYPE) + } + override fun markAsSyncFailed(messageId: Long) { + markAs(messageId, MmsSmsColumns.Types.BASE_SYNC_FAILED_TYPE) + } + fun markAsSending(messageId: Long) { markAs(messageId, MmsSmsColumns.Types.BASE_SENDING_TYPE) } 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 f3110a5c79..1e1cc50896 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java @@ -47,8 +47,13 @@ public interface MmsSmsColumns { 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_SYNCING_TYPE = 29; + protected static final long BASE_RESYNCING_TYPE = 30; + protected static final long BASE_SYNC_FAILED_TYPE = 31; protected static final long[] OUTGOING_MESSAGE_TYPES = {BASE_OUTBOX_TYPE, BASE_SENT_TYPE, + BASE_SYNCING_TYPE, BASE_RESYNCING_TYPE, + BASE_SYNC_FAILED_TYPE, BASE_SENDING_TYPE, BASE_SENT_FAILED_TYPE, BASE_PENDING_SECURE_SMS_FALLBACK, BASE_PENDING_INSECURE_SMS_FALLBACK, @@ -109,6 +114,18 @@ public interface MmsSmsColumns { return (type & BASE_TYPE_MASK) == BASE_DRAFT_TYPE; } + public static boolean isResyncingType(long type) { + return (type & BASE_TYPE_MASK) == BASE_RESYNCING_TYPE; + } + + public static boolean isSyncingType(long type) { + return (type & BASE_TYPE_MASK) == BASE_SYNCING_TYPE; + } + + public static boolean isSyncFailedMessageType(long type) { + return (type & BASE_TYPE_MASK) == BASE_SYNC_FAILED_TYPE; + } + public static boolean isFailedMessageType(long type) { return (type & BASE_TYPE_MASK) == BASE_SENT_FAILED_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 0d61b26796..42a00ccbb2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -202,6 +202,21 @@ public class SmsDatabase extends MessagingDatabase { updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE); } + @Override + public void markAsSyncing(long id) { + updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SYNCING_TYPE); + } + + @Override + public void markAsResyncing(long id) { + updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_RESYNCING_TYPE); + } + + @Override + public void markAsSyncFailed(long id) { + updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SYNC_FAILED_TYPE); + } + @Override public void markUnidentified(long id, boolean unidentified) { ContentValues contentValues = new ContentValues(1); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 076fa57afc..77f616026c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -41,6 +41,7 @@ import org.session.libsignal.messages.SignalServiceAttachmentPointer import org.session.libsignal.messages.SignalServiceGroup import org.session.libsignal.utilities.IdPrefix import org.session.libsignal.utilities.KeyHelper +import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.guava.Optional import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper @@ -53,6 +54,8 @@ import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.util.SessionMetaProtocol import java.security.MessageDigest +private val TAG = Storage::class.java.simpleName + class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol { override fun getUserPublicKey(): String? { @@ -356,6 +359,8 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, openGroupSentTimestamp: Long, threadId: Long ) { + Log.d(TAG, "updateSentTimestamp() called with: messageID = $messageID, isMms = $isMms, openGroupSentTimestamp = $openGroupSentTimestamp, threadId = $threadId") + if (isMms) { val mmsDb = DatabaseComponent.get(context).mmsDatabase() mmsDb.updateSentTimestamp(messageID, openGroupSentTimestamp, threadId) @@ -366,6 +371,8 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, } override fun markAsSent(timestamp: Long, author: String) { + Log.d(TAG, "markAsSent() called with: timestamp = $timestamp, author = $author") + val database = DatabaseComponent.get(context).mmsSmsDatabase() val messageRecord = database.getMessageFor(timestamp, author) ?: return if (messageRecord.isMms) { @@ -377,7 +384,29 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, } } + override fun markAsSyncing(timestamp: Long, author: String) { + Log.d(TAG, "markAsSyncing() called with: timestamp = $timestamp, author = $author") + + DatabaseComponent.get(context).mmsSmsDatabase() + .getMessageFor(timestamp, author) + ?.run { getMmsDatabaseElseSms(isMms).markAsSyncing(id) } + } + + private fun getMmsDatabaseElseSms(isMms: Boolean) = + if (isMms) DatabaseComponent.get(context).mmsDatabase() + else DatabaseComponent.get(context).smsDatabase() + + override fun markAsResyncing(timestamp: Long, author: String) { + Log.d(TAG, "markAsResyncing() called with: timestamp = $timestamp, author = $author") + + DatabaseComponent.get(context).mmsSmsDatabase() + .getMessageFor(timestamp, author) + ?.run { getMmsDatabaseElseSms(isMms).markAsResyncing(id) } + } + override fun markAsSending(timestamp: Long, author: String) { + Log.d(TAG, "markAsSending() called with: timestamp = $timestamp, author = $author") + val database = DatabaseComponent.get(context).mmsSmsDatabase() val messageRecord = database.getMessageFor(timestamp, author) ?: return if (messageRecord.isMms) { @@ -402,7 +431,9 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, } } - override fun setErrorMessage(timestamp: Long, author: String, error: Exception) { + override fun markAsSentFailed(timestamp: Long, author: String, error: Exception) { + Log.d(TAG, "markAsSentFailed() called with: timestamp = $timestamp, author = $author, error = $error") + val database = DatabaseComponent.get(context).mmsSmsDatabase() val messageRecord = database.getMessageFor(timestamp, author) ?: return if (messageRecord.isMms) { @@ -425,6 +456,28 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, } } + override fun markAsSyncFailed(timestamp: Long, author: String, error: Exception) { + Log.d(TAG, "markAsSyncFailed() called with: timestamp = $timestamp, author = $author, error = $error") + + val database = DatabaseComponent.get(context).mmsSmsDatabase() + val messageRecord = database.getMessageFor(timestamp, author) ?: return + + database.getMessageFor(timestamp, author) + ?.run { getMmsDatabaseElseSms(isMms).markAsSyncFailed(id) } + + if (error.localizedMessage != null) { + val message: String + if (error is OnionRequestAPI.HTTPRequestFailedAtDestinationException && error.statusCode == 429) { + message = "429: Rate limited." + } else { + message = error.localizedMessage!! + } + DatabaseComponent.get(context).lokiMessageDatabase().setErrorMessage(messageRecord.getId(), message) + } else { + DatabaseComponent.get(context).lokiMessageDatabase().setErrorMessage(messageRecord.getId(), error.javaClass.simpleName) + } + } + override fun clearErrorMessage(messageID: Long) { val db = DatabaseComponent.get(context).lokiMessageDatabase() db.clearErrorMessage(messageID) @@ -983,5 +1036,4 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, val recipientDb = DatabaseComponent.get(context).recipientDatabase() return recipientDb.blockedContacts } - } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java index ef0f4b54f3..39fba182aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java @@ -80,6 +80,18 @@ public abstract class DisplayRecord { return !isFailed() && !isPending(); } + public boolean isSyncing() { + return MmsSmsColumns.Types.isSyncingType(type); + } + + public boolean isResyncing() { + return MmsSmsColumns.Types.isResyncingType(type); + } + + public boolean isSyncFailed() { + return MmsSmsColumns.Types.isSyncFailedMessageType(type); + } + public boolean isFailed() { return MmsSmsColumns.Types.isFailedMessageType(type) || MmsSmsColumns.Types.isPendingSecureSmsFallbackType(type) diff --git a/app/src/main/res/menu/menu_conversation_item_action.xml b/app/src/main/res/menu/menu_conversation_item_action.xml index 8e20939225..ea81685617 100644 --- a/app/src/main/res/menu/menu_conversation_item_action.xml +++ b/app/src/main/res/menu/menu_conversation_item_action.xml @@ -32,6 +32,11 @@ android:icon="?menu_copy_icon" app:showAsAction="always" /> + + Message sent status: Sent Message sent status pending + Message sent status syncing Message request has been accepted Message Body Voice message @@ -627,6 +628,7 @@ Delete message Ban user Ban and delete all + Resync message Resend message Reply Reply to message @@ -1007,9 +1009,11 @@ Close Dialog Database Upgrade Failed Please contact support to report the error. + Syncing Sending Read Sent + Failed to sync Failed to send Search GIFs? Session will connect to Giphy to provide search results. You will not have full metadata protection when sending GIFs. diff --git a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt index d75e209d11..48d15a18ac 100644 --- a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt @@ -106,10 +106,13 @@ interface StorageProtocol { fun getAttachmentsForMessage(messageID: Long): List fun getMessageIdInDatabase(timestamp: Long, author: String): Long? // TODO: This is a weird name fun updateSentTimestamp(messageID: Long, isMms: Boolean, openGroupSentTimestamp: Long, threadId: Long) + fun markAsResyncing(timestamp: Long, author: String) + fun markAsSyncing(timestamp: Long, author: String) fun markAsSending(timestamp: Long, author: String) fun markAsSent(timestamp: Long, author: String) fun markUnidentified(timestamp: Long, author: String) - fun setErrorMessage(timestamp: Long, author: String, error: Exception) + fun markAsSyncFailed(timestamp: Long, author: String, error: Exception) + fun markAsSentFailed(timestamp: Long, author: String, error: Exception) fun clearErrorMessage(messageID: Long) fun setMessageServerHash(messageID: Long, serverHash: String) diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt index 524338592c..4300fe4b61 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt @@ -34,6 +34,8 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job { } override fun execute(dispatcherName: String) { + Log.d(TAG, "MessageSendJob#execute() called with: dispatcherName = $dispatcherName") + val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider val message = message as? VisibleMessage val storage = MessagingModuleConfiguration.shared.storage diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt index 3abf0ed3e1..f30c1b9168 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt @@ -7,13 +7,13 @@ import org.session.libsignal.utilities.toHexString sealed class Destination { - class Contact(var publicKey: String) : Destination() { + data class Contact(var publicKey: String) : Destination() { internal constructor(): this("") } - class ClosedGroup(var groupPublicKey: String) : Destination() { + data class ClosedGroup(var groupPublicKey: String) : Destination() { internal constructor(): this("") } - class LegacyOpenGroup(var roomToken: String, var server: String) : Destination() { + data class LegacyOpenGroup(var roomToken: String, var server: String) : Destination() { internal constructor(): this("", "") } diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt index e66147da18..705fc7ce45 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt @@ -11,20 +11,22 @@ import org.session.libsignal.protos.SignalServiceProtos import org.session.libsignal.utilities.Log import org.session.libsession.messaging.sending_receiving.attachments.Attachment as SignalAttachment -class VisibleMessage : Message() { - /** In the case of a sync message, the public key of the person the message was targeted at. - * - * **Note:** `nil` if this isn't a sync message. - */ - var syncTarget: String? = null - var text: String? = null - val attachmentIDs: MutableList = mutableListOf() - var quote: Quote? = null - var linkPreview: LinkPreview? = null - var profile: Profile? = null - var openGroupInvitation: OpenGroupInvitation? = null - var reaction: Reaction? = null +/** + * @param syncTarget In the case of a sync message, the public key of the person the message was targeted at. + * + * **Note:** `nil` if this isn't a sync message. + */ +class VisibleMessage( + var syncTarget: String? = null, + var text: String? = null, + val attachmentIDs: MutableList = mutableListOf(), + var quote: Quote? = null, + var linkPreview: LinkPreview? = null, + var profile: Profile? = null, + var openGroupInvitation: OpenGroupInvitation? = null, + var reaction: Reaction? = null, var hasMention: Boolean = false +) : Message() { override val isSelfSendValid: Boolean = true diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt index eb0d15739b..9da6cd8b14 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt @@ -39,6 +39,8 @@ import org.session.libsession.messaging.sending_receiving.attachments.Attachment import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview as SignalLinkPreview import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel as SignalQuote +private val TAG = MessageSender::class.java.simpleName + object MessageSender { // Error @@ -61,16 +63,20 @@ object MessageSender { } // Convenience - fun send(message: Message, destination: Destination): Promise { + fun send(message: Message, destination: Destination, isSyncMessage: Boolean = false): Promise { + Log.d(TAG, "send() called with: message = $message, destination = $destination, isSyncMessage = $isSyncMessage") + return if (destination is Destination.LegacyOpenGroup || destination is Destination.OpenGroup || destination is Destination.OpenGroupInbox) { sendToOpenGroupDestination(destination, message) } else { - sendToSnodeDestination(destination, message) + sendToSnodeDestination(destination, message, isSyncMessage) } } // One-on-One Chats & Closed Groups private fun sendToSnodeDestination(destination: Destination, message: Message, isSyncMessage: Boolean = false): Promise { + Log.d(TAG, "sendToSnodeDestination() called with: destination = $destination, message = $message, isSyncMessage = $isSyncMessage") + val deferred = deferred() val promise = deferred.promise val storage = MessagingModuleConfiguration.shared.storage @@ -86,7 +92,7 @@ object MessageSender { val isSelfSend = (message.recipient == userPublicKey) // Set the failure handler (need it here already for precondition failure handling) fun handleFailure(error: Exception) { - handleFailedMessageSend(message, error) + handleFailedMessageSend(message, error, isSyncMessage) if (destination is Destination.Contact && message is VisibleMessage && !isSelfSend) { SnodeModule.shared.broadcaster.broadcast("messageFailed", message.sentTimestamp!!) } @@ -220,6 +226,8 @@ object MessageSender { // Open Groups private fun sendToOpenGroupDestination(destination: Destination, message: Message): Promise { + Log.d(TAG, "sendToOpenGroupDestination() called with: destination = $destination, message = $message") + val deferred = deferred() val storage = MessagingModuleConfiguration.shared.storage if (message.sentTimestamp == null) { @@ -318,6 +326,8 @@ object MessageSender { // Result Handling fun handleSuccessfulMessageSend(message: Message, destination: Destination, isSyncMessage: Boolean = false, openGroupSentTimestamp: Long = -1) { + Log.d(TAG, "handleSuccessfulMessageSend() called with: message = $message, destination = $destination, isSyncMessage = $isSyncMessage, openGroupSentTimestamp = $openGroupSentTimestamp") + val storage = MessagingModuleConfiguration.shared.storage val userPublicKey = storage.getUserPublicKey()!! // Ignore future self-sends @@ -374,21 +384,32 @@ object MessageSender { // • the destination was a contact // • we didn't sync it already if (destination is Destination.Contact && !isSyncMessage) { - if (message is VisibleMessage) { message.syncTarget = destination.publicKey } - if (message is ExpirationTimerUpdate) { message.syncTarget = destination.publicKey } + if (message is VisibleMessage) message.syncTarget = destination.publicKey + if (message is ExpirationTimerUpdate) message.syncTarget = destination.publicKey + + storage.markAsSyncing(message.sentTimestamp!!, userPublicKey) sendToSnodeDestination(Destination.Contact(userPublicKey), message, true) } } - fun handleFailedMessageSend(message: Message, error: Exception) { + fun handleFailedMessageSend(message: Message, error: Exception, isSyncMessage: Boolean = false) { + Log.d(TAG, "handleFailedMessageSend() called with: message = $message, error = $error, isSyncMessage = $isSyncMessage") + val storage = MessagingModuleConfiguration.shared.storage val userPublicKey = storage.getUserPublicKey()!! - storage.setErrorMessage(message.sentTimestamp!!, message.sender?:userPublicKey, error) + + val timestamp = message.sentTimestamp!! + val author = message.sender ?: userPublicKey + + if (isSyncMessage) storage.markAsSyncFailed(timestamp, author, error) + else storage.markAsSentFailed(timestamp, author, error) } // Convenience @JvmStatic fun send(message: VisibleMessage, address: Address, attachments: List, quote: SignalQuote?, linkPreview: SignalLinkPreview?) { + Log.d(TAG, "send() called with: message = $message, address = $address, attachments = $attachments, quote = $quote, linkPreview = $linkPreview") + val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider val attachmentIDs = messageDataProvider.getAttachmentIDsFor(message.id!!) message.attachmentIDs.addAll(attachmentIDs) @@ -407,6 +428,8 @@ object MessageSender { @JvmStatic fun send(message: Message, address: Address) { + Log.d(TAG, "send() called with: message = $message, address = $address") + val threadID = MessagingModuleConfiguration.shared.storage.getOrCreateThreadIdFor(address) message.threadID = threadID val destination = Destination.from(address)