mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-28 20:45:17 +00:00
feat: support thread deletion and refactoring a lot of getOrCreateThread references to go via storage or assume they are correctly set to hook into the contact and volatile creation during thread creation
This commit is contained in:
parent
d3c55fad60
commit
f111513211
@ -7,6 +7,7 @@ import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ViewUserBinding
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionManagerUtilities
|
||||
@ -51,7 +52,7 @@ class UserView : LinearLayout {
|
||||
val contact = DatabaseComponent.get(context).sessionContactDatabase().getContactWithSessionID(publicKey)
|
||||
return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey
|
||||
}
|
||||
val threadID = DatabaseComponent.get(context).threadDatabase().getOrCreateThreadIdFor(user)
|
||||
val threadID = MessagingModuleConfiguration.shared.storage.getOrCreateThreadIdFor(user.address)
|
||||
MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded(threadID, context) // FIXME: This is a bad place to do this
|
||||
val address = user.address.serialize()
|
||||
binding.profilePictureView.root.glide = glide
|
||||
|
@ -237,7 +237,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
it
|
||||
}
|
||||
val recipient = Recipient.from(this, address, false)
|
||||
threadId = threadDb.getOrCreateThreadIdFor(recipient)
|
||||
threadId = storage.getOrCreateThreadIdFor(recipient.address)
|
||||
}
|
||||
} ?: finish()
|
||||
}
|
||||
|
@ -4,11 +4,8 @@ import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||
import org.session.libsession.utilities.Address
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.session.libsignal.utilities.JsonUtil
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
|
||||
class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) {
|
||||
|
||||
@ -24,12 +21,6 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
||||
val createPublicChatTableCommand = "CREATE TABLE $publicChatTable ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);"
|
||||
}
|
||||
|
||||
fun getThreadID(hexEncodedPublicKey: String): Long {
|
||||
val address = Address.fromSerialized(hexEncodedPublicKey)
|
||||
val recipient = Recipient.from(context, address, false)
|
||||
return DatabaseComponent.get(context).threadDatabase().getOrCreateThreadIdFor(recipient)
|
||||
}
|
||||
|
||||
fun getAllOpenGroups(): Map<Long, OpenGroup> {
|
||||
val database = databaseHelper.readableDatabase
|
||||
var cursor: Cursor? = null
|
||||
|
@ -20,13 +20,11 @@ import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import com.annimon.stream.Stream
|
||||
import com.google.android.mms.pdu_alt.NotificationInd
|
||||
import com.google.android.mms.pdu_alt.PduHeaders
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import org.session.libsession.messaging.messages.signal.IncomingMediaMessage
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingExpirationUpdateMessage
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingGroupMediaMessage
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingSecureMediaMessage
|
||||
@ -40,16 +38,13 @@ import org.session.libsession.utilities.Address.Companion.UNKNOWN
|
||||
import org.session.libsession.utilities.Address.Companion.fromExternal
|
||||
import org.session.libsession.utilities.Address.Companion.fromSerialized
|
||||
import org.session.libsession.utilities.Contact
|
||||
import org.session.libsession.utilities.GroupUtil.doubleEncodeGroupID
|
||||
import org.session.libsession.utilities.IdentityKeyMismatch
|
||||
import org.session.libsession.utilities.IdentityKeyMismatchList
|
||||
import org.session.libsession.utilities.NetworkFailure
|
||||
import org.session.libsession.utilities.NetworkFailureList
|
||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.isReadReceiptsEnabled
|
||||
import org.session.libsession.utilities.Util.toIsoBytes
|
||||
import org.session.libsession.utilities.Util.toIsoString
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.session.libsession.utilities.recipients.RecipientFormattingException
|
||||
import org.session.libsignal.utilities.JsonUtil
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.session.libsignal.utilities.ThreadUtils.queue
|
||||
@ -234,34 +229,6 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(RecipientFormattingException::class, MmsException::class)
|
||||
private fun getThreadIdFor(retrieved: IncomingMediaMessage): Long {
|
||||
return if (retrieved.groupId != null) {
|
||||
val groupRecipients = Recipient.from(
|
||||
context,
|
||||
retrieved.groupId,
|
||||
true
|
||||
)
|
||||
get(context).threadDatabase().getOrCreateThreadIdFor(groupRecipients)
|
||||
} else {
|
||||
val sender = Recipient.from(
|
||||
context,
|
||||
retrieved.from,
|
||||
true
|
||||
)
|
||||
get(context).threadDatabase().getOrCreateThreadIdFor(sender)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getThreadIdFor(notification: NotificationInd): Long {
|
||||
val fromString =
|
||||
if (notification.from != null && notification.from.textString != null) toIsoString(
|
||||
notification.from.textString
|
||||
) else ""
|
||||
val recipient = Recipient.from(context, fromExternal(context, fromString), false)
|
||||
return get(context).threadDatabase().getOrCreateThreadIdFor(recipient)
|
||||
}
|
||||
|
||||
private fun rawQuery(where: String, arguments: Array<String>?): Cursor {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.rawQuery(
|
||||
@ -272,10 +239,6 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
||||
)
|
||||
}
|
||||
|
||||
fun getMessages(idsAsString: String): Cursor {
|
||||
return rawQuery(idsAsString, null)
|
||||
}
|
||||
|
||||
fun getMessage(messageId: Long): Cursor {
|
||||
val cursor = rawQuery(RAW_ID_WHERE, arrayOf(messageId.toString()))
|
||||
setNotifyConverationListeners(cursor, getThreadIdForMessage(messageId))
|
||||
@ -633,15 +596,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
||||
serverTimestamp: Long,
|
||||
runThreadUpdate: Boolean
|
||||
): Optional<InsertResult> {
|
||||
var threadId = threadId
|
||||
if (threadId == -1L || retrieved.isGroupMessage) {
|
||||
try {
|
||||
threadId = getThreadIdFor(retrieved)
|
||||
} catch (e: RecipientFormattingException) {
|
||||
Log.w("MmsDatabase", e)
|
||||
if (threadId == -1L) throw MmsException(e)
|
||||
}
|
||||
}
|
||||
if (threadId < 0 ) throw MmsException("No thread ID supplied!")
|
||||
val contentValues = ContentValues()
|
||||
contentValues.put(DATE_SENT, retrieved.sentTimeMillis)
|
||||
contentValues.put(ADDRESS, retrieved.from.serialize())
|
||||
@ -710,27 +665,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
||||
serverTimestamp: Long,
|
||||
runThreadUpdate: Boolean
|
||||
): Optional<InsertResult> {
|
||||
var threadId = threadId
|
||||
if (threadId == -1L) {
|
||||
if (retrieved.isGroup) {
|
||||
val decodedGroupId: String = if (retrieved is OutgoingExpirationUpdateMessage) {
|
||||
retrieved.groupId
|
||||
} else {
|
||||
(retrieved as OutgoingGroupMediaMessage).groupId
|
||||
}
|
||||
val groupId: String
|
||||
groupId = try {
|
||||
doubleEncodeGroupID(decodedGroupId)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Couldn't encrypt group ID")
|
||||
throw MmsException(e)
|
||||
}
|
||||
val group = Recipient.from(context, fromSerialized(groupId), false)
|
||||
threadId = get(context).threadDatabase().getOrCreateThreadIdFor(group)
|
||||
} else {
|
||||
threadId = get(context).threadDatabase().getOrCreateThreadIdFor(retrieved.recipient)
|
||||
}
|
||||
}
|
||||
if (threadId < 0 ) throw MmsException("No thread ID supplied!")
|
||||
val messageId = insertMessageOutbox(retrieved, threadId, false, null, serverTimestamp, runThreadUpdate)
|
||||
if (messageId == -1L) {
|
||||
return Optional.absent()
|
||||
|
@ -202,13 +202,16 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF
|
||||
}
|
||||
val targetRecipient = Recipient.from(context, targetAddress, false)
|
||||
if (!targetRecipient.isGroupRecipient) {
|
||||
val recipientDb = DatabaseComponent.get(context).recipientDatabase()
|
||||
if (isUserSender || isUserBlindedSender) {
|
||||
setRecipientApproved(targetRecipient, true)
|
||||
} else {
|
||||
setRecipientApprovedMe(targetRecipient, true)
|
||||
}
|
||||
}
|
||||
if (message.threadID == null && !targetRecipient.isOpenGroupRecipient) {
|
||||
// open group recipients should explicitly create threads
|
||||
message.threadID = getOrCreateThreadIdFor(targetAddress)
|
||||
}
|
||||
if (message.isMediaMessage() || attachments.isNotEmpty()) {
|
||||
val quote: Optional<QuoteModel> = if (quotes != null) Optional.of(quotes) else Optional.absent()
|
||||
val linkPreviews: Optional<List<LinkPreview>> = if (linkPreview.isEmpty()) Optional.absent() else Optional.of(linkPreview.mapNotNull { it!! })
|
||||
@ -222,7 +225,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF
|
||||
it.toSignalPointer()
|
||||
}
|
||||
val mediaMessage = IncomingMediaMessage.from(message, senderAddress, targetRecipient.expireMessages * 1000L, group, signalServiceAttachments, quote, linkPreviews)
|
||||
mmsDatabase.insertSecureDecryptedMessageInbox(mediaMessage, message.threadID ?: -1, message.receivedTimestamp ?: 0, runThreadUpdate)
|
||||
mmsDatabase.insertSecureDecryptedMessageInbox(mediaMessage, message.threadID!!, message.receivedTimestamp ?: 0, runThreadUpdate)
|
||||
}
|
||||
if (insertResult.isPresent) {
|
||||
messageID = insertResult.get().messageId
|
||||
@ -998,6 +1001,11 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF
|
||||
profileManager.setUnidentifiedAccessMode(context, recipient, Recipient.UnidentifiedAccessMode.UNKNOWN)
|
||||
profileManager.setProfilePictureURL(context, recipient, url)
|
||||
}
|
||||
if (contact.hidden) {
|
||||
getThreadId(fromSerialized(contact.id))?.let { conversationThreadId ->
|
||||
deleteConversation(conversationThreadId)
|
||||
}
|
||||
}
|
||||
Log.d("Loki-DBG", "Updated contact $contact")
|
||||
}
|
||||
}
|
||||
@ -1075,8 +1083,21 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF
|
||||
|
||||
override fun deleteConversation(threadID: Long) {
|
||||
// TODO: delete from either contacts / convo volatile or the closed groups
|
||||
val recipient = getRecipientForThread(threadID)
|
||||
val threadDB = DatabaseComponent.get(context).threadDatabase()
|
||||
threadDB.deleteConversation(threadID)
|
||||
if (recipient != null) {
|
||||
if (recipient.isContactRecipient) {
|
||||
// TODO: handle contact
|
||||
val contacts = configFactory.contacts ?: return
|
||||
contacts.upsertContact(recipient.address.serialize()) {
|
||||
this.hidden = true
|
||||
}
|
||||
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context)
|
||||
} else if (recipient.isClosedGroupRecipient) {
|
||||
// TODO: handle closed group
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAttachmentDataUri(attachmentId: AttachmentId): Uri {
|
||||
@ -1094,6 +1115,8 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF
|
||||
|
||||
if (recipient.isBlocked) return
|
||||
|
||||
val threadId = getThreadId(recipient) ?: return
|
||||
|
||||
val mediaMessage = IncomingMediaMessage(
|
||||
address,
|
||||
sentTimestamp,
|
||||
@ -1112,7 +1135,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF
|
||||
Optional.of(message)
|
||||
)
|
||||
|
||||
database.insertSecureDecryptedMessageInbox(mediaMessage, -1, runThreadUpdate = true)
|
||||
database.insertSecureDecryptedMessageInbox(mediaMessage, threadId, runThreadUpdate = true)
|
||||
}
|
||||
|
||||
override fun insertMessageRequestResponse(response: MessageRequestResponse) {
|
||||
@ -1131,7 +1154,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF
|
||||
val mmsDb = DatabaseComponent.get(context).mmsDatabase()
|
||||
val smsDb = DatabaseComponent.get(context).smsDatabase()
|
||||
val sender = Recipient.from(context, fromSerialized(senderPublicKey), false)
|
||||
val threadId = threadDB.getOrCreateThreadIdFor(sender)
|
||||
val threadId = getOrCreateThreadIdFor(sender.address)
|
||||
val profile = response.profile
|
||||
if (profile != null) {
|
||||
val profileManager = SSKEnvironment.shared.profileManager
|
||||
|
@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.service;
|
||||
import android.content.Context;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.session.libsession.database.StorageProtocol;
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration;
|
||||
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate;
|
||||
import org.session.libsession.messaging.messages.signal.IncomingMediaMessage;
|
||||
@ -107,6 +108,10 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
|
||||
Address groupAddress = Address.fromSerialized(groupID);
|
||||
recipient = Recipient.from(context, groupAddress, false);
|
||||
}
|
||||
Long threadId = MessagingModuleConfiguration.getShared().getStorage().getThreadId(recipient);
|
||||
if (threadId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(address, sentTimestamp, -1,
|
||||
duration * 1000L, true,
|
||||
@ -121,7 +126,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
|
||||
Optional.absent(),
|
||||
Optional.absent());
|
||||
//insert the timer update message
|
||||
database.insertSecureDecryptedMessageInbox(mediaMessage, -1, true);
|
||||
database.insertSecureDecryptedMessageInbox(mediaMessage, threadId, true);
|
||||
|
||||
//set the timer to the conversation
|
||||
MessagingModuleConfiguration.getShared().getStorage().setExpirationTimer(recipient.getAddress().serialize(), duration);
|
||||
@ -140,6 +145,10 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
|
||||
|
||||
Address address = Address.fromSerialized((message.getSyncTarget() != null && !message.getSyncTarget().isEmpty()) ? message.getSyncTarget() : message.getRecipient());
|
||||
Recipient recipient = Recipient.from(context, address, false);
|
||||
if (message.getThreadID() == null || message.getThreadID() < 0) {
|
||||
StorageProtocol storage = MessagingModuleConfiguration.getShared().getStorage();
|
||||
message.setThreadID(storage.getOrCreateThreadIdFor(address));
|
||||
}
|
||||
|
||||
try {
|
||||
OutgoingExpirationUpdateMessage timerUpdateMessage = new OutgoingExpirationUpdateMessage(recipient, sentTimestamp, duration * 1000L, groupId);
|
||||
|
@ -13,17 +13,18 @@ inline session::config::Contacts *ptrToContacts(JNIEnv *env, jobject obj) {
|
||||
|
||||
inline jobject serialize_contact(JNIEnv *env, session::config::contact_info info) {
|
||||
jclass contactClass = env->FindClass("network/loki/messenger/libsession_util/util/Contact");
|
||||
jmethodID constructor = env->GetMethodID(contactClass, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZLnetwork/loki/messenger/libsession_util/util/UserPic;ILnetwork/loki/messenger/libsession_util/util/ExpiryMode;)V");
|
||||
jmethodID constructor = env->GetMethodID(contactClass, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZZLnetwork/loki/messenger/libsession_util/util/UserPic;ILnetwork/loki/messenger/libsession_util/util/ExpiryMode;)V");
|
||||
jstring id = env->NewStringUTF(info.session_id.data());
|
||||
jstring name = env->NewStringUTF(info.name.data());
|
||||
jstring nickname = env->NewStringUTF(info.nickname.data());
|
||||
jboolean approved, approvedMe, blocked;
|
||||
jboolean approved, approvedMe, blocked, hidden;
|
||||
approved = info.approved;
|
||||
approvedMe = info.approved_me;
|
||||
blocked = info.blocked;
|
||||
hidden = info.hidden;
|
||||
jobject profilePic = util::serialize_user_pic(env, info.profile_picture);
|
||||
jobject returnObj = env->NewObject(contactClass, constructor, id, name, nickname, approved,
|
||||
approvedMe, blocked, profilePic, info.priority,
|
||||
approvedMe, blocked, hidden, profilePic, info.priority,
|
||||
util::serialize_expiry(env, info.exp_mode, info.exp_timer));
|
||||
return returnObj;
|
||||
}
|
||||
@ -32,13 +33,14 @@ inline session::config::contact_info
|
||||
deserialize_contact(JNIEnv *env, jobject info, session::config::Contacts *conf) {
|
||||
jclass contactClass = env->FindClass("network/loki/messenger/libsession_util/util/Contact");
|
||||
|
||||
jfieldID getId, getName, getNick, getApproved, getApprovedMe, getBlocked, getUserPic, getPriority, getExpiry;
|
||||
jfieldID getId, getName, getNick, getApproved, getApprovedMe, getBlocked, getUserPic, getPriority, getExpiry, getHidden;
|
||||
getId = env->GetFieldID(contactClass, "id", "Ljava/lang/String;");
|
||||
getName = env->GetFieldID(contactClass, "name", "Ljava/lang/String;");
|
||||
getNick = env->GetFieldID(contactClass, "nickname", "Ljava/lang/String;");
|
||||
getApproved = env->GetFieldID(contactClass, "approved", "Z");
|
||||
getApprovedMe = env->GetFieldID(contactClass, "approvedMe", "Z");
|
||||
getBlocked = env->GetFieldID(contactClass, "blocked", "Z");
|
||||
getHidden = env->GetFieldID(contactClass, "hidden", "Z");
|
||||
getUserPic = env->GetFieldID(contactClass, "profilePicture",
|
||||
"Lnetwork/loki/messenger/libsession_util/util/UserPic;");
|
||||
getPriority = env->GetFieldID(contactClass, "priority", "I");
|
||||
@ -47,11 +49,12 @@ deserialize_contact(JNIEnv *env, jobject info, session::config::Contacts *conf)
|
||||
session_id = static_cast<jstring>(env->GetObjectField(info, getId));
|
||||
name = static_cast<jstring>(env->GetObjectField(info, getName));
|
||||
nickname = static_cast<jstring>(env->GetObjectField(info, getNick));
|
||||
bool approved, approvedMe, blocked;
|
||||
bool approved, approvedMe, blocked, hidden;
|
||||
int priority = env->GetIntField(info, getPriority);
|
||||
approved = env->GetBooleanField(info, getApproved);
|
||||
approvedMe = env->GetBooleanField(info, getApprovedMe);
|
||||
blocked = env->GetBooleanField(info, getBlocked);
|
||||
hidden = env->GetBooleanField(info, getHidden);
|
||||
jobject user_pic = env->GetObjectField(info, getUserPic);
|
||||
jobject expiry_mode = env->GetObjectField(info, getExpiry);
|
||||
|
||||
@ -83,6 +86,7 @@ deserialize_contact(JNIEnv *env, jobject info, session::config::Contacts *conf)
|
||||
contact_info.approved = approved;
|
||||
contact_info.approved_me = approvedMe;
|
||||
contact_info.blocked = blocked;
|
||||
contact_info.hidden = hidden;
|
||||
if (!url.empty() && !key.empty()) {
|
||||
contact_info.profile_picture = session::config::profile_pic(url, key);
|
||||
} else {
|
||||
|
@ -7,6 +7,7 @@ data class Contact(
|
||||
var approved: Boolean = false,
|
||||
var approvedMe: Boolean = false,
|
||||
var blocked: Boolean = false,
|
||||
var hidden: Boolean = false,
|
||||
var profilePicture: UserPic = UserPic.DEFAULT,
|
||||
var priority: Int = 0,
|
||||
var expiryMode: ExpiryMode
|
||||
|
Loading…
Reference in New Issue
Block a user