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