diff --git a/res/menu/conversation_copy_session_id.xml b/res/menu/conversation_copy_session_id.xml new file mode 100644 index 0000000000..e74d0b4c24 --- /dev/null +++ b/res/menu/conversation_copy_session_id.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 41276b0c85..a289237629 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1860,4 +1860,6 @@ Night System default + Copy Session ID + diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java index d839a90827..4a06575a30 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -21,6 +21,8 @@ import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; +import android.content.ClipData; +import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -747,6 +749,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } else { inflater.inflate(R.menu.conversation_block, menu); } + inflater.inflate(R.menu.conversation_copy_session_id, menu); } else if (isGroupConversation() && !isOpenGroupOrRSSFeed) { // inflater.inflate(R.menu.conversation_group_options, menu); @@ -854,6 +857,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity // case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true; case R.id.menu_unblock: handleUnblock(); return true; case R.id.menu_block: handleBlock(); return true; + case R.id.menu_copy_session_id: handleCopySessionID(); return true; case R.id.menu_view_media: handleViewMedia(); return true; case R.id.menu_add_shortcut: handleAddShortcut(); return true; case R.id.menu_search: handleSearch(); return true; @@ -1083,6 +1087,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity }).show(); } + private void handleCopySessionID() { + if (recipient.isGroupRecipient()) { return; } + String sessionID = recipient.getAddress().toPhoneString(); + ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("Session ID", sessionID); + clipboard.setPrimaryClip(clip); + Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show(); + } + private void handleViewMedia() { Intent intent = new Intent(this, MediaOverviewActivity.class); intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, recipient.getAddress()); diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 4b97762c9b..69ce6a08a6 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -834,6 +834,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType { public long handleSynchronizeSentMediaMessage(@NonNull SentTranscriptMessage message) throws MmsException { + if (SessionMetaProtocol.shouldIgnoreMessage(message.getTimestamp())) { return -1; } + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); Recipient recipients = getSyncMessageMasterDestination(message); Optional quote = getValidatedQuote(message.getMessage().getQuote()); @@ -1002,6 +1004,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { public long handleSynchronizeSentTextMessage(@NonNull SentTranscriptMessage message) throws MmsException { + if (SessionMetaProtocol.shouldIgnoreMessage(message.getTimestamp())) { return -1; } Recipient recipient = getSyncMessageMasterDestination(message); String body = message.getMessage().getBody().or(""); @@ -1459,7 +1462,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { return true; } - if (SessionMetaProtocol.shouldIgnoreMessage(content)) { + if (SessionMetaProtocol.shouldIgnoreMessage(content.getTimestamp())) { Log.d("Loki", "Ignoring duplicate message."); return true; } diff --git a/src/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt b/src/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt index 6a5ee5dad0..80b52c533e 100644 --- a/src/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt +++ b/src/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt @@ -5,7 +5,6 @@ import android.os.Handler import android.util.Log import nl.komponents.kovenant.Promise import nl.komponents.kovenant.functional.bind -import nl.komponents.kovenant.then import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.database.Address @@ -34,6 +33,7 @@ import java.util.* class PublicChatPoller(private val context: Context, private val group: PublicChat) { private val handler = Handler() private var hasStarted = false + private var isPollOngoing = false public var isCaughtUp = false // region Convenience @@ -186,12 +186,10 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh } fun processOutgoingMessage(message: PublicChatMessage) { val messageServerID = message.serverID ?: return - val isDuplicate = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID) != null - if (isDuplicate) { return } if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return } val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) val dataMessage = getDataMessage(message) - SessionMetaProtocol.dropFromTimestampCacheIfNeeded(dataMessage.timestamp) + SessionMetaProtocol.dropFromTimestampCacheIfNeeded(message.serverTimestamp) val transcript = SentTranscriptMessage(userHexEncodedPublicKey, message.serverTimestamp, dataMessage, dataMessage.expiresInSeconds.toLong(), Collections.singletonMap(userHexEncodedPublicKey, false)) transcript.messageServerID = messageServerID if (dataMessage.quote.isPresent || (dataMessage.attachments.isPresent && dataMessage.attachments.get().size > 0) || dataMessage.previews.isPresent) { @@ -212,6 +210,8 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh } } } + if (isPollOngoing) { return } + isPollOngoing = true val userDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userHexEncodedPublicKey) var uniqueDevices = setOf() val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize() @@ -249,8 +249,10 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh } } isCaughtUp = true + isPollOngoing = false }.fail { Log.d("Loki", "Failed to get messages for group chat with ID: ${group.channel} on server: ${group.server}.") + isPollOngoing = false } } diff --git a/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt index b4a16ed505..c3322f9a08 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt @@ -23,8 +23,7 @@ object SessionMetaProtocol { } @JvmStatic - fun shouldIgnoreMessage(content: SignalServiceContent): Boolean { - val timestamp = content.timestamp + fun shouldIgnoreMessage(timestamp: Long): Boolean { val shouldIgnoreMessage = timestamps.contains(timestamp) timestamps.add(timestamp) return shouldIgnoreMessage