mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-25 01:07:47 +00:00
Merge remote-tracking branch 'upstream/dev' into ses-1936-oom
This commit is contained in:
commit
35335480ca
@ -31,8 +31,8 @@ configurations.all {
|
|||||||
exclude module: "commons-logging"
|
exclude module: "commons-logging"
|
||||||
}
|
}
|
||||||
|
|
||||||
def canonicalVersionCode = 371
|
def canonicalVersionCode = 372
|
||||||
def canonicalVersionName = "1.18.2"
|
def canonicalVersionName = "1.18.3"
|
||||||
|
|
||||||
def postFixSize = 10
|
def postFixSize = 10
|
||||||
def abiPostFix = ['armeabi-v7a' : 1,
|
def abiPostFix = ['armeabi-v7a' : 1,
|
||||||
|
@ -57,6 +57,7 @@ import org.signal.aesgcmprovider.AesGcmProvider;
|
|||||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||||
import org.thoughtcrime.securesms.crypto.KeyPairUtilities;
|
import org.thoughtcrime.securesms.crypto.KeyPairUtilities;
|
||||||
import org.thoughtcrime.securesms.database.EmojiSearchDatabase;
|
import org.thoughtcrime.securesms.database.EmojiSearchDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.LastSentTimestampCache;
|
||||||
import org.thoughtcrime.securesms.database.LokiAPIDatabase;
|
import org.thoughtcrime.securesms.database.LokiAPIDatabase;
|
||||||
import org.thoughtcrime.securesms.database.Storage;
|
import org.thoughtcrime.securesms.database.Storage;
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||||
@ -149,6 +150,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
|||||||
@Inject TextSecurePreferences textSecurePreferences;
|
@Inject TextSecurePreferences textSecurePreferences;
|
||||||
@Inject PushRegistry pushRegistry;
|
@Inject PushRegistry pushRegistry;
|
||||||
@Inject ConfigFactory configFactory;
|
@Inject ConfigFactory configFactory;
|
||||||
|
@Inject LastSentTimestampCache lastSentTimestampCache;
|
||||||
CallMessageProcessor callMessageProcessor;
|
CallMessageProcessor callMessageProcessor;
|
||||||
MessagingModuleConfiguration messagingModuleConfiguration;
|
MessagingModuleConfiguration messagingModuleConfiguration;
|
||||||
|
|
||||||
@ -218,7 +220,8 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
|||||||
device,
|
device,
|
||||||
messageDataProvider,
|
messageDataProvider,
|
||||||
()-> KeyPairUtilities.INSTANCE.getUserED25519KeyPair(this),
|
()-> KeyPairUtilities.INSTANCE.getUserED25519KeyPair(this),
|
||||||
configFactory
|
configFactory,
|
||||||
|
lastSentTimestampCache
|
||||||
);
|
);
|
||||||
callMessageProcessor = new CallMessageProcessor(this, textSecurePreferences, ProcessLifecycleOwner.get().getLifecycle(), storage);
|
callMessageProcessor = new CallMessageProcessor(this, textSecurePreferences, ProcessLifecycleOwner.get().getLifecycle(), storage);
|
||||||
Log.i(TAG, "onCreate()");
|
Log.i(TAG, "onCreate()");
|
||||||
|
@ -5,6 +5,8 @@ import android.text.TextUtils
|
|||||||
import com.google.protobuf.ByteString
|
import com.google.protobuf.ByteString
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import org.session.libsession.database.MessageDataProvider
|
import org.session.libsession.database.MessageDataProvider
|
||||||
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
|
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.Attachment
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
|
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentState
|
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentState
|
||||||
@ -184,10 +186,15 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
|
|||||||
override fun deleteMessage(messageID: Long, isSms: Boolean) {
|
override fun deleteMessage(messageID: Long, isSms: Boolean) {
|
||||||
val messagingDatabase: MessagingDatabase = if (isSms) DatabaseComponent.get(context).smsDatabase()
|
val messagingDatabase: MessagingDatabase = if (isSms) DatabaseComponent.get(context).smsDatabase()
|
||||||
else DatabaseComponent.get(context).mmsDatabase()
|
else DatabaseComponent.get(context).mmsDatabase()
|
||||||
|
val (threadId, timestamp) = runCatching { messagingDatabase.getMessageRecord(messageID).run { threadId to timestamp } }.getOrNull() ?: (null to null)
|
||||||
|
|
||||||
messagingDatabase.deleteMessage(messageID)
|
messagingDatabase.deleteMessage(messageID)
|
||||||
DatabaseComponent.get(context).lokiMessageDatabase().deleteMessage(messageID, isSms)
|
DatabaseComponent.get(context).lokiMessageDatabase().deleteMessage(messageID, isSms)
|
||||||
DatabaseComponent.get(context).lokiMessageDatabase().deleteMessageServerHash(messageID, mms = !isSms)
|
DatabaseComponent.get(context).lokiMessageDatabase().deleteMessageServerHash(messageID, mms = !isSms)
|
||||||
|
|
||||||
|
threadId ?: return
|
||||||
|
timestamp ?: return
|
||||||
|
MessagingModuleConfiguration.shared.lastSentTimestampCache.delete(threadId, timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteMessages(messageIDs: List<Long>, threadId: Long, isSms: Boolean) {
|
override fun deleteMessages(messageIDs: List<Long>, threadId: Long, isSms: Boolean) {
|
||||||
@ -195,12 +202,17 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
|
|||||||
val messagingDatabase: MessagingDatabase = if (isSms) DatabaseComponent.get(context).smsDatabase()
|
val messagingDatabase: MessagingDatabase = if (isSms) DatabaseComponent.get(context).smsDatabase()
|
||||||
else DatabaseComponent.get(context).mmsDatabase()
|
else DatabaseComponent.get(context).mmsDatabase()
|
||||||
|
|
||||||
|
val messages = messageIDs.mapNotNull { runCatching { messagingDatabase.getMessageRecord(it) }.getOrNull() }
|
||||||
|
|
||||||
// Perform local delete
|
// Perform local delete
|
||||||
messagingDatabase.deleteMessages(messageIDs.toLongArray(), threadId)
|
messagingDatabase.deleteMessages(messageIDs.toLongArray(), threadId)
|
||||||
|
|
||||||
// Perform online delete
|
// Perform online delete
|
||||||
DatabaseComponent.get(context).lokiMessageDatabase().deleteMessages(messageIDs)
|
DatabaseComponent.get(context).lokiMessageDatabase().deleteMessages(messageIDs)
|
||||||
DatabaseComponent.get(context).lokiMessageDatabase().deleteMessageServerHashes(messageIDs, mms = !isSms)
|
DatabaseComponent.get(context).lokiMessageDatabase().deleteMessageServerHashes(messageIDs, mms = !isSms)
|
||||||
|
|
||||||
|
val threadId = messages.firstOrNull()?.threadId
|
||||||
|
threadId?.let{ MessagingModuleConfiguration.shared.lastSentTimestampCache.delete(it, messages.map { it.timestamp }) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateMessageAsDeleted(timestamp: Long, author: String): Long? {
|
override fun updateMessageAsDeleted(timestamp: Long, author: String): Long? {
|
||||||
|
@ -327,7 +327,6 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onAttachmentNeedsDownload = { attachmentId, mmsId ->
|
onAttachmentNeedsDownload = { attachmentId, mmsId ->
|
||||||
// Start download (on IO thread)
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
JobQueue.shared.add(AttachmentDownloadJob(attachmentId, mmsId))
|
JobQueue.shared.add(AttachmentDownloadJob(attachmentId, mmsId))
|
||||||
}
|
}
|
||||||
@ -337,8 +336,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
)
|
)
|
||||||
adapter.visibleMessageViewDelegate = this
|
adapter.visibleMessageViewDelegate = this
|
||||||
|
|
||||||
// Register an AdapterDataObserver to scroll us to the bottom of the RecyclerView if we're
|
// Register an AdapterDataObserver to scroll us to the bottom of the RecyclerView for if
|
||||||
// already near the the bottom and the data changes.
|
// we're already near the the bottom and the data changes.
|
||||||
adapter.registerAdapterDataObserver(ConversationAdapterDataObserver(binding?.conversationRecyclerView!!, adapter))
|
adapter.registerAdapterDataObserver(ConversationAdapterDataObserver(binding?.conversationRecyclerView!!, adapter))
|
||||||
|
|
||||||
adapter
|
adapter
|
||||||
@ -376,7 +375,6 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
const val PICK_GIF = 10
|
const val PICK_GIF = 10
|
||||||
const val PICK_FROM_LIBRARY = 12
|
const val PICK_FROM_LIBRARY = 12
|
||||||
const val INVITE_CONTACTS = 124
|
const val INVITE_CONTACTS = 124
|
||||||
|
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.conversation.v2
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
import org.thoughtcrime.securesms.util.AbstractCursorLoader
|
import org.thoughtcrime.securesms.util.AbstractCursorLoader
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ class ConversationLoader(
|
|||||||
) : AbstractCursorLoader(context) {
|
) : AbstractCursorLoader(context) {
|
||||||
|
|
||||||
override fun getCursor(): Cursor {
|
override fun getCursor(): Cursor {
|
||||||
|
MessagingModuleConfiguration.shared.lastSentTimestampCache.refresh(threadID)
|
||||||
return DatabaseComponent.get(context).mmsSmsDatabase().getConversation(threadID, reverse)
|
return DatabaseComponent.get(context).mmsSmsDatabase().getConversation(threadID, reverse)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,7 +22,6 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.view.isGone
|
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.marginBottom
|
import androidx.core.view.marginBottom
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
@ -39,6 +38,7 @@ import org.session.libsession.utilities.modifyLayoutParams
|
|||||||
import org.session.libsignal.utilities.IdPrefix
|
import org.session.libsignal.utilities.IdPrefix
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
|
import org.thoughtcrime.securesms.database.LastSentTimestampCache
|
||||||
import org.thoughtcrime.securesms.database.LokiAPIDatabase
|
import org.thoughtcrime.securesms.database.LokiAPIDatabase
|
||||||
import org.thoughtcrime.securesms.database.LokiThreadDatabase
|
import org.thoughtcrime.securesms.database.LokiThreadDatabase
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase
|
import org.thoughtcrime.securesms.database.MmsDatabase
|
||||||
@ -73,6 +73,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
@Inject lateinit var mmsSmsDb: MmsSmsDatabase
|
@Inject lateinit var mmsSmsDb: MmsSmsDatabase
|
||||||
@Inject lateinit var smsDb: SmsDatabase
|
@Inject lateinit var smsDb: SmsDatabase
|
||||||
@Inject lateinit var mmsDb: MmsDatabase
|
@Inject lateinit var mmsDb: MmsDatabase
|
||||||
|
@Inject lateinit var lastSentTimestampCache: LastSentTimestampCache
|
||||||
|
|
||||||
private val binding by lazy { ViewVisibleMessageBinding.bind(this) }
|
private val binding by lazy { ViewVisibleMessageBinding.bind(this) }
|
||||||
private val swipeToReplyIcon = ContextCompat.getDrawable(context, R.drawable.ic_baseline_reply_24)!!.mutate()
|
private val swipeToReplyIcon = ContextCompat.getDrawable(context, R.drawable.ic_baseline_reply_24)!!.mutate()
|
||||||
@ -303,10 +304,6 @@ class VisibleMessageView : LinearLayout {
|
|||||||
|
|
||||||
// --- If we got here then we know the message is outgoing ---
|
// --- If we got here then we know the message is outgoing ---
|
||||||
|
|
||||||
val thisUsersSessionId = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
val lastSentMessageId = mmsSmsDb.getLastSentMessageFromSender(message.threadId, thisUsersSessionId)
|
|
||||||
val isLastSentMessage = lastSentMessageId == message.id
|
|
||||||
|
|
||||||
// ----- Case ii.) Message is outgoing but NOT scheduled to disappear -----
|
// ----- Case ii.) Message is outgoing but NOT scheduled to disappear -----
|
||||||
if (!scheduledToDisappear) {
|
if (!scheduledToDisappear) {
|
||||||
// If this isn't a disappearing message then we never show the timer
|
// If this isn't a disappearing message then we never show the timer
|
||||||
@ -319,9 +316,11 @@ class VisibleMessageView : LinearLayout {
|
|||||||
} else {
|
} else {
|
||||||
// ..but if the message HAS been successfully sent or read then only display the delivery status
|
// ..but if the message HAS been successfully sent or read then only display the delivery status
|
||||||
// text and image if this is the last sent message.
|
// text and image if this is the last sent message.
|
||||||
binding.messageStatusTextView.isVisible = isLastSentMessage
|
val lastSentTimestamp = lastSentTimestampCache.getTimestamp(message.threadId)
|
||||||
binding.messageStatusImageView.isVisible = isLastSentMessage
|
val isLastSent = lastSentTimestamp == message.timestamp
|
||||||
if (isLastSentMessage) { binding.messageStatusImageView.bringToFront() }
|
binding.messageStatusTextView.isVisible = isLastSent
|
||||||
|
binding.messageStatusImageView.isVisible = isLastSent
|
||||||
|
if (isLastSent) { binding.messageStatusImageView.bringToFront() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // ----- Case iii.) Message is outgoing AND scheduled to disappear -----
|
else // ----- Case iii.) Message is outgoing AND scheduled to disappear -----
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package org.thoughtcrime.securesms.crypto
|
package org.thoughtcrime.securesms.crypto
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.goterl.lazysodium.LazySodiumAndroid
|
|
||||||
import com.goterl.lazysodium.SodiumAndroid
|
|
||||||
import com.goterl.lazysodium.utils.Key
|
import com.goterl.lazysodium.utils.Key
|
||||||
import com.goterl.lazysodium.utils.KeyPair
|
import com.goterl.lazysodium.utils.KeyPair
|
||||||
|
import org.session.libsession.messaging.utilities.SodiumUtilities.sodium
|
||||||
import org.session.libsignal.crypto.ecc.DjbECPrivateKey
|
import org.session.libsignal.crypto.ecc.DjbECPrivateKey
|
||||||
import org.session.libsignal.crypto.ecc.DjbECPublicKey
|
import org.session.libsignal.crypto.ecc.DjbECPublicKey
|
||||||
import org.session.libsignal.crypto.ecc.ECKeyPair
|
import org.session.libsignal.crypto.ecc.ECKeyPair
|
||||||
@ -13,8 +12,6 @@ import org.session.libsignal.utilities.Hex
|
|||||||
|
|
||||||
object KeyPairUtilities {
|
object KeyPairUtilities {
|
||||||
|
|
||||||
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
|
||||||
|
|
||||||
fun generate(): KeyPairGenerationResult {
|
fun generate(): KeyPairGenerationResult {
|
||||||
val seed = sodium.randomBytesBuf(16)
|
val seed = sodium.randomBytesBuf(16)
|
||||||
try {
|
try {
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package org.thoughtcrime.securesms.database
|
||||||
|
|
||||||
|
import org.session.libsession.messaging.LastSentTimestampCache
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class LastSentTimestampCache @Inject constructor(
|
||||||
|
val mmsSmsDatabase: MmsSmsDatabase
|
||||||
|
): LastSentTimestampCache {
|
||||||
|
|
||||||
|
private val map = mutableMapOf<Long, Long>()
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun getTimestamp(threadId: Long): Long? = map[threadId]
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun submitTimestamp(threadId: Long, timestamp: Long) {
|
||||||
|
if (map[threadId]?.let { timestamp <= it } == true) return
|
||||||
|
|
||||||
|
map[threadId] = timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun delete(threadId: Long, timestamps: List<Long>) {
|
||||||
|
if (map[threadId]?.let { it !in timestamps } == true) return
|
||||||
|
map.remove(threadId)
|
||||||
|
refresh(threadId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun refresh(threadId: Long) {
|
||||||
|
if (map[threadId]?.let { it > 0 } == true) return
|
||||||
|
val lastOutgoingTimestamp = mmsSmsDatabase.getLastOutgoingTimestamp(threadId)
|
||||||
|
if (lastOutgoingTimestamp <= 0) return
|
||||||
|
map[threadId] = lastOutgoingTimestamp
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,11 @@ public interface MmsSmsColumns {
|
|||||||
public static final String THREAD_ID = "thread_id";
|
public static final String THREAD_ID = "thread_id";
|
||||||
public static final String READ = "read";
|
public static final String READ = "read";
|
||||||
public static final String BODY = "body";
|
public static final String BODY = "body";
|
||||||
|
|
||||||
|
// This is the address of the message recipient, which may be a single user, a group, or a community!
|
||||||
|
// It is NOT the address of the sender of any given message!
|
||||||
public static final String ADDRESS = "address";
|
public static final String ADDRESS = "address";
|
||||||
|
|
||||||
public static final String ADDRESS_DEVICE_ID = "address_device_id";
|
public static final String ADDRESS_DEVICE_ID = "address_device_id";
|
||||||
public static final String DELIVERY_RECEIPT_COUNT = "delivery_receipt_count";
|
public static final String DELIVERY_RECEIPT_COUNT = "delivery_receipt_count";
|
||||||
public static final String READ_RECEIPT_COUNT = "read_receipt_count";
|
public static final String READ_RECEIPT_COUNT = "read_receipt_count";
|
||||||
|
@ -163,6 +163,53 @@ public class MmsSmsDatabase extends Database {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @Nullable MessageRecord getSentMessageFor(long timestamp, String serializedAuthor) {
|
||||||
|
// Early exit if the author is not us
|
||||||
|
boolean isOwnNumber = Util.isOwnNumber(context, serializedAuthor);
|
||||||
|
if (!isOwnNumber) {
|
||||||
|
Log.i(TAG, "Asked to find sent messages but provided author is not us - returning null.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Cursor cursor = queryTables(PROJECTION, MmsSmsColumns.NORMALIZED_DATE_SENT + " = " + timestamp, null, null)) {
|
||||||
|
MmsSmsDatabase.Reader reader = readerFor(cursor);
|
||||||
|
|
||||||
|
MessageRecord messageRecord;
|
||||||
|
while ((messageRecord = reader.getNext()) != null) {
|
||||||
|
if (messageRecord.isOutgoing())
|
||||||
|
{
|
||||||
|
return messageRecord;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.i(TAG, "Could not find any message sent from us at provided timestamp - returning null.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageRecord getLastSentMessageRecordFromSender(long threadId, String serializedAuthor) {
|
||||||
|
// Early exit if the author is not us
|
||||||
|
boolean isOwnNumber = Util.isOwnNumber(context, serializedAuthor);
|
||||||
|
if (!isOwnNumber) {
|
||||||
|
Log.i(TAG, "Asked to find last sent message but provided author is not us - returning null.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String order = MmsSmsColumns.NORMALIZED_DATE_SENT + " DESC";
|
||||||
|
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId;
|
||||||
|
|
||||||
|
// Try everything with resources so that they auto-close on end of scope
|
||||||
|
try (Cursor cursor = queryTables(PROJECTION, selection, order, null)) {
|
||||||
|
try (MmsSmsDatabase.Reader reader = readerFor(cursor)) {
|
||||||
|
MessageRecord messageRecord;
|
||||||
|
while ((messageRecord = reader.getNext()) != null) {
|
||||||
|
if (messageRecord.isOutgoing()) { return messageRecord; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.i(TAG, "Could not find last sent message from us in given thread - returning null.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public @Nullable MessageRecord getMessageFor(long timestamp, Address author) {
|
public @Nullable MessageRecord getMessageFor(long timestamp, Address author) {
|
||||||
return getMessageFor(timestamp, author.serialize());
|
return getMessageFor(timestamp, author.serialize());
|
||||||
}
|
}
|
||||||
@ -295,15 +342,7 @@ public class MmsSmsDatabase extends Database {
|
|||||||
return identifiedMessages;
|
return identifiedMessages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getLastSentMessageFromSender(long threadId, String serializedAuthor) {
|
public long getLastOutgoingTimestamp(long threadId) {
|
||||||
|
|
||||||
// Early exit
|
|
||||||
boolean isOwnNumber = Util.isOwnNumber(context, serializedAuthor);
|
|
||||||
if (!isOwnNumber) {
|
|
||||||
Log.i(TAG, "Asked to find last sent message but sender isn't us - returning null.");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
String order = MmsSmsColumns.NORMALIZED_DATE_SENT + " DESC";
|
String order = MmsSmsColumns.NORMALIZED_DATE_SENT + " DESC";
|
||||||
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId;
|
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId;
|
||||||
|
|
||||||
@ -311,8 +350,13 @@ public class MmsSmsDatabase extends Database {
|
|||||||
try (Cursor cursor = queryTables(PROJECTION, selection, order, null)) {
|
try (Cursor cursor = queryTables(PROJECTION, selection, order, null)) {
|
||||||
try (MmsSmsDatabase.Reader reader = readerFor(cursor)) {
|
try (MmsSmsDatabase.Reader reader = readerFor(cursor)) {
|
||||||
MessageRecord messageRecord;
|
MessageRecord messageRecord;
|
||||||
|
long attempts = 0;
|
||||||
|
long maxAttempts = 20;
|
||||||
while ((messageRecord = reader.getNext()) != null) {
|
while ((messageRecord = reader.getNext()) != null) {
|
||||||
if (messageRecord.isOutgoing()) { return messageRecord.id; }
|
// Note: We rely on the message order to get us the most recent outgoing message - so we
|
||||||
|
// take the first outgoing message we find as the last outgoing message.
|
||||||
|
if (messageRecord.isOutgoing()) return messageRecord.getTimestamp();
|
||||||
|
if (attempts++ > maxAttempts) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import org.session.libsession.messaging.jobs.MessageReceiveParameters
|
|||||||
import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationMetadata
|
import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationMetadata
|
||||||
import org.session.libsession.messaging.utilities.MessageWrapper
|
import org.session.libsession.messaging.utilities.MessageWrapper
|
||||||
import org.session.libsession.messaging.utilities.SodiumUtilities
|
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||||
|
import org.session.libsession.messaging.utilities.SodiumUtilities.sodium
|
||||||
import org.session.libsession.utilities.bencode.Bencode
|
import org.session.libsession.utilities.bencode.Bencode
|
||||||
import org.session.libsession.utilities.bencode.BencodeList
|
import org.session.libsession.utilities.bencode.BencodeList
|
||||||
import org.session.libsession.utilities.bencode.BencodeString
|
import org.session.libsession.utilities.bencode.BencodeString
|
||||||
@ -28,7 +29,6 @@ import javax.inject.Inject
|
|||||||
private const val TAG = "PushHandler"
|
private const val TAG = "PushHandler"
|
||||||
|
|
||||||
class PushReceiver @Inject constructor(@ApplicationContext val context: Context) {
|
class PushReceiver @Inject constructor(@ApplicationContext val context: Context) {
|
||||||
private val sodium = LazySodiumAndroid(SodiumAndroid())
|
|
||||||
private val json = Json { ignoreUnknownKeys = true }
|
private val json = Json { ignoreUnknownKeys = true }
|
||||||
|
|
||||||
fun onPush(dataMap: Map<String, String>?) {
|
fun onPush(dataMap: Map<String, String>?) {
|
||||||
|
@ -18,6 +18,8 @@ import org.session.libsession.messaging.sending_receiving.notifications.Subscrip
|
|||||||
import org.session.libsession.messaging.sending_receiving.notifications.SubscriptionResponse
|
import org.session.libsession.messaging.sending_receiving.notifications.SubscriptionResponse
|
||||||
import org.session.libsession.messaging.sending_receiving.notifications.UnsubscribeResponse
|
import org.session.libsession.messaging.sending_receiving.notifications.UnsubscribeResponse
|
||||||
import org.session.libsession.messaging.sending_receiving.notifications.UnsubscriptionRequest
|
import org.session.libsession.messaging.sending_receiving.notifications.UnsubscriptionRequest
|
||||||
|
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||||
|
import org.session.libsession.messaging.utilities.SodiumUtilities.sodium
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.session.libsession.snode.SnodeAPI
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.snode.Version
|
import org.session.libsession.snode.Version
|
||||||
@ -34,8 +36,6 @@ private const val maxRetryCount = 4
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class PushRegistryV2 @Inject constructor(private val pushReceiver: PushReceiver) {
|
class PushRegistryV2 @Inject constructor(private val pushReceiver: PushReceiver) {
|
||||||
private val sodium = LazySodiumAndroid(SodiumAndroid())
|
|
||||||
|
|
||||||
fun register(
|
fun register(
|
||||||
device: Device,
|
device: Device,
|
||||||
token: String,
|
token: String,
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
package org.session.libsession.messaging
|
||||||
|
|
||||||
|
interface LastSentTimestampCache {
|
||||||
|
fun getTimestamp(threadId: Long): Long?
|
||||||
|
fun submitTimestamp(threadId: Long, timestamp: Long)
|
||||||
|
fun delete(threadId: Long, timestamps: List<Long>)
|
||||||
|
fun delete(threadId: Long, timestamp: Long) = delete(threadId, listOf(timestamp))
|
||||||
|
fun refresh(threadId: Long)
|
||||||
|
}
|
@ -13,7 +13,8 @@ class MessagingModuleConfiguration(
|
|||||||
val device: Device,
|
val device: Device,
|
||||||
val messageDataProvider: MessageDataProvider,
|
val messageDataProvider: MessageDataProvider,
|
||||||
val getUserED25519KeyPair: () -> KeyPair?,
|
val getUserED25519KeyPair: () -> KeyPair?,
|
||||||
val configFactory: ConfigFactoryProtocol
|
val configFactory: ConfigFactoryProtocol,
|
||||||
|
val lastSentTimestampCache: LastSentTimestampCache
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -21,6 +21,7 @@ import org.session.libsession.messaging.MessagingModuleConfiguration
|
|||||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller.Companion.maxInactivityPeriod
|
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller.Companion.maxInactivityPeriod
|
||||||
import org.session.libsession.messaging.utilities.SessionId
|
import org.session.libsession.messaging.utilities.SessionId
|
||||||
import org.session.libsession.messaging.utilities.SodiumUtilities
|
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||||
|
import org.session.libsession.messaging.utilities.SodiumUtilities.sodium
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.session.libsession.snode.OnionResponse
|
import org.session.libsession.snode.OnionResponse
|
||||||
import org.session.libsession.snode.SnodeAPI
|
import org.session.libsession.snode.SnodeAPI
|
||||||
@ -48,7 +49,6 @@ object OpenGroupApi {
|
|||||||
val defaultRooms = MutableSharedFlow<List<DefaultGroup>>(replay = 1)
|
val defaultRooms = MutableSharedFlow<List<DefaultGroup>>(replay = 1)
|
||||||
private val hasPerformedInitialPoll = mutableMapOf<String, Boolean>()
|
private val hasPerformedInitialPoll = mutableMapOf<String, Boolean>()
|
||||||
private var hasUpdatedLastOpenDate = false
|
private var hasUpdatedLastOpenDate = false
|
||||||
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
|
||||||
private val timeSinceLastOpen by lazy {
|
private val timeSinceLastOpen by lazy {
|
||||||
val context = MessagingModuleConfiguration.shared.context
|
val context = MessagingModuleConfiguration.shared.context
|
||||||
val lastOpenDate = TextSecurePreferences.getLastOpenTimeDate(context)
|
val lastOpenDate = TextSecurePreferences.getLastOpenTimeDate(context)
|
||||||
|
@ -8,6 +8,7 @@ import org.session.libsession.messaging.MessagingModuleConfiguration
|
|||||||
import org.session.libsession.messaging.sending_receiving.MessageReceiver.Error
|
import org.session.libsession.messaging.sending_receiving.MessageReceiver.Error
|
||||||
import org.session.libsession.messaging.utilities.SessionId
|
import org.session.libsession.messaging.utilities.SessionId
|
||||||
import org.session.libsession.messaging.utilities.SodiumUtilities
|
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||||
|
import org.session.libsession.messaging.utilities.SodiumUtilities.sodium
|
||||||
import org.session.libsignal.crypto.ecc.ECKeyPair
|
import org.session.libsignal.crypto.ecc.ECKeyPair
|
||||||
import org.session.libsignal.utilities.Hex
|
import org.session.libsignal.utilities.Hex
|
||||||
import org.session.libsignal.utilities.IdPrefix
|
import org.session.libsignal.utilities.IdPrefix
|
||||||
@ -17,8 +18,6 @@ import org.session.libsignal.utilities.removingIdPrefixIfNeeded
|
|||||||
|
|
||||||
object MessageDecrypter {
|
object MessageDecrypter {
|
||||||
|
|
||||||
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrypts `ciphertext` using the Session protocol and `x25519KeyPair`.
|
* Decrypts `ciphertext` using the Session protocol and `x25519KeyPair`.
|
||||||
*
|
*
|
||||||
|
@ -7,6 +7,7 @@ import com.goterl.lazysodium.interfaces.Sign
|
|||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender.Error
|
import org.session.libsession.messaging.sending_receiving.MessageSender.Error
|
||||||
import org.session.libsession.messaging.utilities.SodiumUtilities
|
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||||
|
import org.session.libsession.messaging.utilities.SodiumUtilities.sodium
|
||||||
import org.session.libsignal.utilities.Hex
|
import org.session.libsignal.utilities.Hex
|
||||||
import org.session.libsignal.utilities.IdPrefix
|
import org.session.libsignal.utilities.IdPrefix
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
@ -14,8 +15,6 @@ import org.session.libsignal.utilities.removingIdPrefixIfNeeded
|
|||||||
|
|
||||||
object MessageEncrypter {
|
object MessageEncrypter {
|
||||||
|
|
||||||
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypts `plaintext` using the Session protocol for `hexEncodedX25519PublicKey`.
|
* Encrypts `plaintext` using the Session protocol for `hexEncodedX25519PublicKey`.
|
||||||
*
|
*
|
||||||
|
@ -73,6 +73,7 @@ object MessageSender {
|
|||||||
|
|
||||||
// Convenience
|
// Convenience
|
||||||
fun send(message: Message, destination: Destination, isSyncMessage: Boolean): Promise<Unit, Exception> {
|
fun send(message: Message, destination: Destination, isSyncMessage: Boolean): Promise<Unit, Exception> {
|
||||||
|
if (message is VisibleMessage) MessagingModuleConfiguration.shared.lastSentTimestampCache.submitTimestamp(message.threadID!!, message.sentTimestamp!!)
|
||||||
return if (destination is Destination.LegacyOpenGroup || destination is Destination.OpenGroup || destination is Destination.OpenGroupInbox) {
|
return if (destination is Destination.LegacyOpenGroup || destination is Destination.OpenGroup || destination is Destination.OpenGroupInbox) {
|
||||||
sendToOpenGroupDestination(destination, message)
|
sendToOpenGroupDestination(destination, message)
|
||||||
} else {
|
} else {
|
||||||
@ -372,6 +373,7 @@ object MessageSender {
|
|||||||
|
|
||||||
// Result Handling
|
// Result Handling
|
||||||
private fun handleSuccessfulMessageSend(message: Message, destination: Destination, isSyncMessage: Boolean = false, openGroupSentTimestamp: Long = -1) {
|
private fun handleSuccessfulMessageSend(message: Message, destination: Destination, isSyncMessage: Boolean = false, openGroupSentTimestamp: Long = -1) {
|
||||||
|
if (message is VisibleMessage) MessagingModuleConfiguration.shared.lastSentTimestampCache.submitTimestamp(message.threadID!!, openGroupSentTimestamp)
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val userPublicKey = storage.getUserPublicKey()!!
|
val userPublicKey = storage.getUserPublicKey()!!
|
||||||
val timestamp = message.sentTimestamp!!
|
val timestamp = message.sentTimestamp!!
|
||||||
|
@ -290,6 +290,7 @@ fun MessageReceiver.handleVisibleMessage(
|
|||||||
): Long? {
|
): Long? {
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val context = MessagingModuleConfiguration.shared.context
|
val context = MessagingModuleConfiguration.shared.context
|
||||||
|
message.takeIf { it.isSenderSelf }?.sentTimestamp?.let { MessagingModuleConfiguration.shared.lastSentTimestampCache.submitTimestamp(threadId, it) }
|
||||||
val userPublicKey = storage.getUserPublicKey()
|
val userPublicKey = storage.getUserPublicKey()
|
||||||
val messageSender: String? = message.sender
|
val messageSender: String? = message.sender
|
||||||
|
|
||||||
@ -410,12 +411,7 @@ fun MessageReceiver.handleVisibleMessage(
|
|||||||
message.hasMention = listOf(userPublicKey, userBlindedKey)
|
message.hasMention = listOf(userPublicKey, userBlindedKey)
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.any { key ->
|
.any { key ->
|
||||||
return@any (
|
messageText?.contains("@$key") == true || key == (quoteModel?.author?.serialize() ?: "")
|
||||||
messageText != null &&
|
|
||||||
messageText.contains("@$key")
|
|
||||||
) || (
|
|
||||||
(quoteModel?.author?.serialize() ?: "") == key
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Persist the message
|
// Persist the message
|
||||||
|
@ -14,7 +14,7 @@ import org.whispersystems.curve25519.Curve25519
|
|||||||
import kotlin.experimental.xor
|
import kotlin.experimental.xor
|
||||||
|
|
||||||
object SodiumUtilities {
|
object SodiumUtilities {
|
||||||
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
||||||
private val curve by lazy { Curve25519.getInstance(Curve25519.BEST) }
|
private val curve by lazy { Curve25519.getInstance(Curve25519.BEST) }
|
||||||
|
|
||||||
private const val SCALAR_LENGTH: Int = 32 // crypto_core_ed25519_scalarbytes
|
private const val SCALAR_LENGTH: Int = 32 // crypto_core_ed25519_scalarbytes
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
package org.session.libsession.snode
|
package org.session.libsession.snode
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import com.goterl.lazysodium.LazySodiumAndroid
|
|
||||||
import com.goterl.lazysodium.SodiumAndroid
|
|
||||||
import com.goterl.lazysodium.exceptions.SodiumException
|
import com.goterl.lazysodium.exceptions.SodiumException
|
||||||
import com.goterl.lazysodium.interfaces.GenericHash
|
import com.goterl.lazysodium.interfaces.GenericHash
|
||||||
import com.goterl.lazysodium.interfaces.PwHash
|
import com.goterl.lazysodium.interfaces.PwHash
|
||||||
@ -19,6 +17,7 @@ import nl.komponents.kovenant.functional.map
|
|||||||
import nl.komponents.kovenant.task
|
import nl.komponents.kovenant.task
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.utilities.MessageWrapper
|
import org.session.libsession.messaging.utilities.MessageWrapper
|
||||||
|
import org.session.libsession.messaging.utilities.SodiumUtilities.sodium
|
||||||
import org.session.libsignal.crypto.getRandomElement
|
import org.session.libsignal.crypto.getRandomElement
|
||||||
import org.session.libsignal.database.LokiAPIDatabaseProtocol
|
import org.session.libsignal.database.LokiAPIDatabaseProtocol
|
||||||
import org.session.libsignal.protos.SignalServiceProtos
|
import org.session.libsignal.protos.SignalServiceProtos
|
||||||
@ -41,7 +40,6 @@ import kotlin.collections.set
|
|||||||
import kotlin.properties.Delegates.observable
|
import kotlin.properties.Delegates.observable
|
||||||
|
|
||||||
object SnodeAPI {
|
object SnodeAPI {
|
||||||
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
|
||||||
internal val database: LokiAPIDatabaseProtocol
|
internal val database: LokiAPIDatabaseProtocol
|
||||||
get() = SnodeModule.shared.storage
|
get() = SnodeModule.shared.storage
|
||||||
private val broadcaster: Broadcaster
|
private val broadcaster: Broadcaster
|
||||||
|
@ -1,23 +1,60 @@
|
|||||||
package org.session.libsignal.utilities
|
package org.session.libsignal.utilities
|
||||||
|
|
||||||
import android.os.Process
|
import android.os.Process
|
||||||
import java.util.concurrent.*
|
import java.util.concurrent.ExecutorService
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue
|
||||||
|
import java.util.concurrent.SynchronousQueue
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
object ThreadUtils {
|
object ThreadUtils {
|
||||||
|
|
||||||
|
const val TAG = "ThreadUtils"
|
||||||
|
|
||||||
const val PRIORITY_IMPORTANT_BACKGROUND_THREAD = Process.THREAD_PRIORITY_DEFAULT + Process.THREAD_PRIORITY_LESS_FAVORABLE
|
const val PRIORITY_IMPORTANT_BACKGROUND_THREAD = Process.THREAD_PRIORITY_DEFAULT + Process.THREAD_PRIORITY_LESS_FAVORABLE
|
||||||
|
|
||||||
val executorPool: ExecutorService = Executors.newCachedThreadPool()
|
// Paraphrased from: https://www.baeldung.com/kotlin/create-thread-pool
|
||||||
|
// "A cached thread pool such as one created via:
|
||||||
|
// `val executorPool: ExecutorService = Executors.newCachedThreadPool()`
|
||||||
|
// will utilize resources according to the requirements of submitted tasks. It will try to reuse
|
||||||
|
// existing threads for submitted tasks but will create as many threads as it needs if new tasks
|
||||||
|
// keep pouring in (with a memory usage of at least 1MB per created thread). These threads will
|
||||||
|
// live for up to 60 seconds of idle time before terminating by default. As such, it presents a
|
||||||
|
// very sharp tool that doesn't include any backpressure mechanism - and a sudden peak in load
|
||||||
|
// can bring the system down with an OutOfMemory error. We can achieve a similar effect but with
|
||||||
|
// better control by creating a ThreadPoolExecutor manually."
|
||||||
|
|
||||||
|
private val corePoolSize = Runtime.getRuntime().availableProcessors() // Default thread pool size is our CPU core count
|
||||||
|
private val maxPoolSize = corePoolSize * 4 // Allow a maximum pool size of up to 4 threads per core
|
||||||
|
private val keepAliveTimeSecs = 100L // How long to keep idle threads in the pool before they are terminated
|
||||||
|
private val workQueue = SynchronousQueue<Runnable>()
|
||||||
|
val executorPool: ExecutorService = ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTimeSecs, TimeUnit.SECONDS, workQueue)
|
||||||
|
|
||||||
|
// Note: To see how many threads are running in our app at any given time we can use:
|
||||||
|
// val threadCount = getAllStackTraces().size
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun queue(target: Runnable) {
|
fun queue(target: Runnable) {
|
||||||
executorPool.execute(target)
|
executorPool.execute {
|
||||||
|
try {
|
||||||
|
target.run()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun queue(target: () -> Unit) {
|
fun queue(target: () -> Unit) {
|
||||||
executorPool.execute(target)
|
executorPool.execute {
|
||||||
|
try {
|
||||||
|
target()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Thread executor used by the audio recorder only
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun newDynamicSingleThreadedExecutor(): ExecutorService {
|
fun newDynamicSingleThreadedExecutor(): ExecutorService {
|
||||||
val executor = ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, LinkedBlockingQueue())
|
val executor = ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, LinkedBlockingQueue())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user