mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-20 13:48:27 +00:00
feat: add basic message read logic for synchronizing last reads, need to modify the query to use the last seen instead of the unread count in a subquery possibly for thread display record
This commit is contained in:
parent
03a343d832
commit
2f2ebe9451
@ -40,6 +40,7 @@ import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPol
|
|||||||
import org.session.libsession.messaging.sending_receiving.pollers.Poller;
|
import org.session.libsession.messaging.sending_receiving.pollers.Poller;
|
||||||
import org.session.libsession.snode.SnodeModule;
|
import org.session.libsession.snode.SnodeModule;
|
||||||
import org.session.libsession.utilities.Address;
|
import org.session.libsession.utilities.Address;
|
||||||
|
import org.session.libsession.utilities.ConfigFactoryUpdateListener;
|
||||||
import org.session.libsession.utilities.ProfilePictureUtilities;
|
import org.session.libsession.utilities.ProfilePictureUtilities;
|
||||||
import org.session.libsession.utilities.SSKEnvironment;
|
import org.session.libsession.utilities.SSKEnvironment;
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
@ -115,6 +116,7 @@ import dagger.hilt.android.HiltAndroidApp;
|
|||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import kotlinx.coroutines.Job;
|
import kotlinx.coroutines.Job;
|
||||||
import network.loki.messenger.BuildConfig;
|
import network.loki.messenger.BuildConfig;
|
||||||
|
import network.loki.messenger.libsession_util.ConfigBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will be called once when the TextSecure process is created.
|
* Will be called once when the TextSecure process is created.
|
||||||
@ -125,7 +127,7 @@ import network.loki.messenger.BuildConfig;
|
|||||||
* @author Moxie Marlinspike
|
* @author Moxie Marlinspike
|
||||||
*/
|
*/
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
public class ApplicationContext extends Application implements DefaultLifecycleObserver {
|
public class ApplicationContext extends Application implements DefaultLifecycleObserver, ConfigFactoryUpdateListener {
|
||||||
|
|
||||||
public static final String PREFERENCES_NAME = "SecureSMS-Preferences";
|
public static final String PREFERENCES_NAME = "SecureSMS-Preferences";
|
||||||
|
|
||||||
@ -199,6 +201,11 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
|||||||
return this.persistentLogger;
|
return this.persistentLogger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notifyUpdates(@NonNull ConfigBase forConfigObject) {
|
||||||
|
// forward to the config factory / storage ig
|
||||||
|
storage.notifyConfigUpdates(forConfigObject);
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
DatabaseModule.init(this);
|
DatabaseModule.init(this);
|
||||||
|
@ -399,6 +399,13 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|||||||
database.update(TABLE_NAME, contentValues, ID_WHERE, arrayOf(id.toString()))
|
database.update(TABLE_NAME, contentValues, ID_WHERE, arrayOf(id.toString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setMessagesRead(threadId: Long, beforeTime: Long): List<MarkedMessageInfo> {
|
||||||
|
return setMessagesRead(
|
||||||
|
THREAD_ID + " = ? AND (" + READ + " = 0 OR " + REACTIONS_UNREAD + " = 1) AND " + DATE_RECEIVED + " <= ?",
|
||||||
|
arrayOf(threadId.toString(), beforeTime.toString())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun setMessagesRead(threadId: Long): List<MarkedMessageInfo> {
|
fun setMessagesRead(threadId: Long): List<MarkedMessageInfo> {
|
||||||
return setMessagesRead(
|
return setMessagesRead(
|
||||||
THREAD_ID + " = ? AND (" + READ + " = 0 OR " + REACTIONS_UNREAD + " = 1)",
|
THREAD_ID + " = ? AND (" + READ + " = 0 OR " + REACTIONS_UNREAD + " = 1)",
|
||||||
|
@ -321,6 +321,9 @@ public class SmsDatabase extends MessagingDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<MarkedMessageInfo> setMessagesRead(long threadId, long beforeTime) {
|
||||||
|
return setMessagesRead(THREAD_ID + " = ? AND (" + READ + " = 0 OR " + REACTIONS_UNREAD + " = 1) AND " + DATE_RECEIVED + " <= ?", new String[]{threadId+"", beforeTime+""});
|
||||||
|
}
|
||||||
public List<MarkedMessageInfo> setMessagesRead(long threadId) {
|
public List<MarkedMessageInfo> setMessagesRead(long threadId) {
|
||||||
return setMessagesRead(THREAD_ID + " = ? AND (" + READ + " = 0 OR " + REACTIONS_UNREAD + " = 1)", new String[] {String.valueOf(threadId)});
|
return setMessagesRead(THREAD_ID + " = ? AND (" + READ + " = 0 OR " + REACTIONS_UNREAD + " = 1)", new String[] {String.valueOf(threadId)});
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,11 @@ package org.thoughtcrime.securesms.database
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import network.loki.messenger.libsession_util.ConfigBase
|
||||||
|
import network.loki.messenger.libsession_util.Contacts
|
||||||
|
import network.loki.messenger.libsession_util.ConversationVolatileConfig
|
||||||
|
import network.loki.messenger.libsession_util.UserProfile
|
||||||
|
import network.loki.messenger.libsession_util.util.Conversation
|
||||||
import org.session.libsession.avatars.AvatarHelper
|
import org.session.libsession.avatars.AvatarHelper
|
||||||
import org.session.libsession.database.StorageProtocol
|
import org.session.libsession.database.StorageProtocol
|
||||||
import org.session.libsession.messaging.BlindedIdMapping
|
import org.session.libsession.messaging.BlindedIdMapping
|
||||||
@ -52,21 +57,26 @@ import org.session.libsession.utilities.recipients.Recipient
|
|||||||
import org.session.libsignal.crypto.ecc.ECKeyPair
|
import org.session.libsignal.crypto.ecc.ECKeyPair
|
||||||
import org.session.libsignal.messages.SignalServiceAttachmentPointer
|
import org.session.libsignal.messages.SignalServiceAttachmentPointer
|
||||||
import org.session.libsignal.messages.SignalServiceGroup
|
import org.session.libsignal.messages.SignalServiceGroup
|
||||||
|
import org.session.libsignal.utilities.Base64
|
||||||
import org.session.libsignal.utilities.IdPrefix
|
import org.session.libsignal.utilities.IdPrefix
|
||||||
import org.session.libsignal.utilities.KeyHelper
|
import org.session.libsignal.utilities.KeyHelper
|
||||||
|
import org.session.libsignal.utilities.Log
|
||||||
import org.session.libsignal.utilities.guava.Optional
|
import org.session.libsignal.utilities.guava.Optional
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||||
import org.thoughtcrime.securesms.database.model.MessageId
|
import org.thoughtcrime.securesms.database.model.MessageId
|
||||||
import org.thoughtcrime.securesms.database.model.ReactionRecord
|
import org.thoughtcrime.securesms.database.model.ReactionRecord
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ConfigFactory
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority
|
import org.thoughtcrime.securesms.mms.PartAuthority
|
||||||
|
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||||
import org.thoughtcrime.securesms.util.SessionMetaProtocol
|
import org.thoughtcrime.securesms.util.SessionMetaProtocol
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
import network.loki.messenger.libsession_util.util.Contact as LibSessionContact
|
||||||
|
|
||||||
class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol {
|
class Storage(context: Context, helper: SQLCipherOpenHelper, private val configFactory: ConfigFactory) : Database(context, helper), StorageProtocol {
|
||||||
|
|
||||||
override fun getUserPublicKey(): String? {
|
override fun getUserPublicKey(): String? {
|
||||||
return TextSecurePreferences.getLocalNumber(context)
|
return TextSecurePreferences.getLocalNumber(context)
|
||||||
@ -262,6 +272,81 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
|||||||
return DatabaseComponent.get(context).lokiAPIDatabase().getAuthToken(id)
|
return DatabaseComponent.get(context).lokiAPIDatabase().getAuthToken(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun notifyConfigUpdates(forConfigObject: ConfigBase) {
|
||||||
|
notifyUpdates(forConfigObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyUpdates(forConfigObject: ConfigBase) {
|
||||||
|
when (forConfigObject) {
|
||||||
|
is UserProfile -> updateUser(forConfigObject)
|
||||||
|
is Contacts -> updateContacts(forConfigObject)
|
||||||
|
is ConversationVolatileConfig -> updateConvoVolatile(forConfigObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateUser(userProfile: UserProfile) {
|
||||||
|
val userPublicKey = getUserPublicKey() ?: return
|
||||||
|
// would love to get rid of recipient and context from this
|
||||||
|
val recipient = Recipient.from(context, fromSerialized(userPublicKey), false)
|
||||||
|
// update name
|
||||||
|
val name = userProfile.getName() ?: return
|
||||||
|
val userPic = userProfile.getPic()
|
||||||
|
val profileManager = SSKEnvironment.shared.profileManager
|
||||||
|
if (name.isNotEmpty()) {
|
||||||
|
TextSecurePreferences.setProfileName(context, name)
|
||||||
|
profileManager.setName(context, recipient, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// update pfp
|
||||||
|
if (userPic == null) {
|
||||||
|
// clear picture if userPic is null
|
||||||
|
TextSecurePreferences.setProfileKey(context, null)
|
||||||
|
ProfileKeyUtil.setEncodedProfileKey(context, null)
|
||||||
|
profileManager.setProfileKey(context, recipient, null)
|
||||||
|
setUserProfilePictureURL(null)
|
||||||
|
} else if (userPic.key.isNotEmpty() && userPic.url.isNotEmpty()
|
||||||
|
&& TextSecurePreferences.getProfilePictureURL(context) != userPic.url) {
|
||||||
|
val profileKey = Base64.encodeBytes(userPic.key)
|
||||||
|
ProfileKeyUtil.setEncodedProfileKey(context, profileKey)
|
||||||
|
profileManager.setProfileKey(context, recipient, userPic.key)
|
||||||
|
setUserProfilePictureURL(userPic.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateContacts(contacts: Contacts) {
|
||||||
|
val extracted = contacts.all().toList()
|
||||||
|
extracted.forEach { contact ->
|
||||||
|
val address = Address.fromSerialized(contact.id)
|
||||||
|
val settings = getRecipientSettings(address) ?: run {
|
||||||
|
// new contact, store it
|
||||||
|
}
|
||||||
|
contact.name
|
||||||
|
contact.approved
|
||||||
|
contact.approvedMe
|
||||||
|
contact.blocked
|
||||||
|
contact.nickname
|
||||||
|
contact.profilePicture
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateConvoVolatile(convos: ConversationVolatileConfig) {
|
||||||
|
val extracted = convos.all()
|
||||||
|
for (conversation in extracted) {
|
||||||
|
val threadId = when (conversation) {
|
||||||
|
is Conversation.OneToOne -> conversation.sessionId.let {
|
||||||
|
getOrCreateThreadIdFor(fromSerialized(it))
|
||||||
|
}
|
||||||
|
is Conversation.LegacyClosedGroup -> conversation.groupId.let {
|
||||||
|
getOrCreateThreadIdFor("", it,null)
|
||||||
|
}
|
||||||
|
is Conversation.OpenGroup -> conversation.baseUrl.let {
|
||||||
|
getOrCreateThreadIdFor("",null, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d("Loki-DBG", "Should update thread $threadId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun setAuthToken(room: String, server: String, newValue: String) {
|
override fun setAuthToken(room: String, server: String, newValue: String) {
|
||||||
val id = "$server.$room"
|
val id = "$server.$room"
|
||||||
DatabaseComponent.get(context).lokiAPIDatabase().setAuthToken(id, newValue)
|
DatabaseComponent.get(context).lokiAPIDatabase().setAuthToken(id, newValue)
|
||||||
@ -455,6 +540,12 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
|||||||
|
|
||||||
override fun createGroup(groupId: String, title: String?, members: List<Address>, avatar: SignalServiceAttachmentPointer?, relay: String?, admins: List<Address>, formationTimestamp: Long) {
|
override fun createGroup(groupId: String, title: String?, members: List<Address>, avatar: SignalServiceAttachmentPointer?, relay: String?, admins: List<Address>, formationTimestamp: Long) {
|
||||||
DatabaseComponent.get(context).groupDatabase().create(groupId, title, members, avatar, relay, admins, formationTimestamp)
|
DatabaseComponent.get(context).groupDatabase().create(groupId, title, members, avatar, relay, admins, formationTimestamp)
|
||||||
|
val volatiles = configFactory.convoVolatile ?: return
|
||||||
|
val groupPublicKey = GroupUtil.doubleDecodeGroupId(groupId)
|
||||||
|
val groupVolatileConfig = volatiles.getOrConstructLegacyClosedGroup(groupPublicKey)
|
||||||
|
groupVolatileConfig.lastRead = formationTimestamp
|
||||||
|
volatiles.set(groupVolatileConfig)
|
||||||
|
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isGroupActive(groupPublicKey: String): Boolean {
|
override fun isGroupActive(groupPublicKey: String): Boolean {
|
||||||
@ -660,6 +751,25 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
|||||||
return if (recipientSettings.isPresent) { recipientSettings.get() } else null
|
return if (recipientSettings.isPresent) { recipientSettings.get() } else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun addLibSessionContacts(contacts: List<LibSessionContact>) {
|
||||||
|
val recipientDatabase = DatabaseComponent.get(context).recipientDatabase()
|
||||||
|
val threadDatabase = DatabaseComponent.get(context).threadDatabase()
|
||||||
|
val mappingDb = DatabaseComponent.get(context).blindedIdMappingDatabase()
|
||||||
|
val moreContacts = contacts.filter { contact ->
|
||||||
|
val id = SessionId(contact.id)
|
||||||
|
id.prefix != IdPrefix.BLINDED || mappingDb.getBlindedIdMapping(contact.id).none { it.sessionId != null }
|
||||||
|
}
|
||||||
|
for (contact in moreContacts) {
|
||||||
|
val address = fromSerialized(contact.id)
|
||||||
|
val recipient = Recipient.from(context, address, true)
|
||||||
|
val (url, key) = contact.profilePicture?.let { it.url to it.key } ?: (null to null)
|
||||||
|
// set or clear the avatar
|
||||||
|
recipientDatabase.setProfileAvatar(recipient, url)
|
||||||
|
recipientDatabase.setProfileKey(recipient, key)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun addContacts(contacts: List<ConfigurationMessage.Contact>) {
|
override fun addContacts(contacts: List<ConfigurationMessage.Contact>) {
|
||||||
val recipientDatabase = DatabaseComponent.get(context).recipientDatabase()
|
val recipientDatabase = DatabaseComponent.get(context).recipientDatabase()
|
||||||
val threadDatabase = DatabaseComponent.get(context).threadDatabase()
|
val threadDatabase = DatabaseComponent.get(context).threadDatabase()
|
||||||
|
@ -295,6 +295,27 @@ public class ThreadDatabase extends Database {
|
|||||||
notifyConversationListeners(threadId);
|
notifyConversationListeners(threadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<MarkedMessageInfo> setRead(long threadId, long lastReadTime) {
|
||||||
|
ContentValues contentValues = new ContentValues(1);
|
||||||
|
contentValues.put(READ, 1);
|
||||||
|
contentValues.put(UNREAD_COUNT, 0);
|
||||||
|
contentValues.put(UNREAD_MENTION_COUNT, 0);
|
||||||
|
contentValues.put(LAST_SEEN, lastReadTime);
|
||||||
|
|
||||||
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
|
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId+""});
|
||||||
|
|
||||||
|
final List<MarkedMessageInfo> smsRecords = DatabaseComponent.get(context).smsDatabase().setMessagesRead(threadId, lastReadTime);
|
||||||
|
final List<MarkedMessageInfo> mmsRecords = DatabaseComponent.get(context).mmsDatabase().setMessagesRead(threadId, lastReadTime);
|
||||||
|
|
||||||
|
notifyConversationListListeners();
|
||||||
|
|
||||||
|
return new LinkedList<MarkedMessageInfo>() {{
|
||||||
|
addAll(smsRecords);
|
||||||
|
addAll(mmsRecords);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
public List<MarkedMessageInfo> setRead(long threadId, boolean lastSeen) {
|
public List<MarkedMessageInfo> setRead(long threadId, boolean lastSeen) {
|
||||||
ContentValues contentValues = new ContentValues(1);
|
ContentValues contentValues = new ContentValues(1);
|
||||||
contentValues.put(READ, 1);
|
contentValues.put(READ, 1);
|
||||||
|
@ -5,21 +5,14 @@ import network.loki.messenger.libsession_util.ConfigBase
|
|||||||
import network.loki.messenger.libsession_util.Contacts
|
import network.loki.messenger.libsession_util.Contacts
|
||||||
import network.loki.messenger.libsession_util.ConversationVolatileConfig
|
import network.loki.messenger.libsession_util.ConversationVolatileConfig
|
||||||
import network.loki.messenger.libsession_util.UserProfile
|
import network.loki.messenger.libsession_util.UserProfile
|
||||||
import org.session.libsession.database.StorageProtocol
|
|
||||||
import org.session.libsession.utilities.Address
|
|
||||||
import org.session.libsession.utilities.ConfigFactoryProtocol
|
import org.session.libsession.utilities.ConfigFactoryProtocol
|
||||||
import org.session.libsession.utilities.ProfileKeyUtil
|
import org.session.libsession.utilities.ConfigFactoryUpdateListener
|
||||||
import org.session.libsession.utilities.SSKEnvironment
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
|
||||||
import org.session.libsignal.protos.SignalServiceProtos.SharedConfigMessage
|
import org.session.libsignal.protos.SignalServiceProtos.SharedConfigMessage
|
||||||
import org.session.libsignal.utilities.Base64
|
|
||||||
import org.thoughtcrime.securesms.database.ConfigDatabase
|
import org.thoughtcrime.securesms.database.ConfigDatabase
|
||||||
import java.util.concurrent.ConcurrentSkipListSet
|
import java.util.concurrent.ConcurrentSkipListSet
|
||||||
|
|
||||||
class ConfigFactory(private val context: Context,
|
class ConfigFactory(private val context: Context,
|
||||||
private val configDatabase: ConfigDatabase,
|
private val configDatabase: ConfigDatabase,
|
||||||
private val storage: StorageProtocol,
|
|
||||||
private val maybeGetUserInfo: ()->Pair<ByteArray, String>?):
|
private val maybeGetUserInfo: ()->Pair<ByteArray, String>?):
|
||||||
ConfigFactoryProtocol {
|
ConfigFactoryProtocol {
|
||||||
|
|
||||||
@ -42,6 +35,10 @@ class ConfigFactory(private val context: Context,
|
|||||||
private var _convoVolatileConfig: ConversationVolatileConfig? = null
|
private var _convoVolatileConfig: ConversationVolatileConfig? = null
|
||||||
private val convoHashes = ConcurrentSkipListSet<String>()
|
private val convoHashes = ConcurrentSkipListSet<String>()
|
||||||
|
|
||||||
|
private val listeners: MutableList<ConfigFactoryUpdateListener> = mutableListOf()
|
||||||
|
fun registerListener(listener: ConfigFactoryUpdateListener) { listeners += listener }
|
||||||
|
fun unregisterListener(listener: ConfigFactoryUpdateListener) { listeners -= listener }
|
||||||
|
|
||||||
override val user: UserProfile? = synchronized(userLock) {
|
override val user: UserProfile? = synchronized(userLock) {
|
||||||
if (_userConfig == null) {
|
if (_userConfig == null) {
|
||||||
val (secretKey, publicKey) = maybeGetUserInfo() ?: return@synchronized null
|
val (secretKey, publicKey) = maybeGetUserInfo() ?: return@synchronized null
|
||||||
@ -115,6 +112,9 @@ class ConfigFactory(private val context: Context,
|
|||||||
is Contacts -> persistContactsConfigDump()
|
is Contacts -> persistContactsConfigDump()
|
||||||
is ConversationVolatileConfig -> persistConvoVolatileConfigDump()
|
is ConversationVolatileConfig -> persistConvoVolatileConfigDump()
|
||||||
}
|
}
|
||||||
|
listeners.forEach { listener ->
|
||||||
|
listener.notifyUpdates(forConfigObject)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun appendHash(configObject: ConfigBase, hash: String) {
|
override fun appendHash(configObject: ConfigBase, hash: String) {
|
||||||
@ -125,14 +125,6 @@ class ConfigFactory(private val context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyUpdates(forConfigObject: ConfigBase) {
|
|
||||||
when (forConfigObject) {
|
|
||||||
is UserProfile -> updateUser(forConfigObject)
|
|
||||||
is Contacts -> updateContacts(forConfigObject)
|
|
||||||
is ConversationVolatileConfig -> updateConvoVolatile(forConfigObject)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getHashesFor(forConfigObject: ConfigBase): List<String> =
|
override fun getHashesFor(forConfigObject: ConfigBase): List<String> =
|
||||||
when (forConfigObject) {
|
when (forConfigObject) {
|
||||||
is UserProfile -> userHashes.toList()
|
is UserProfile -> userHashes.toList()
|
||||||
@ -147,42 +139,4 @@ class ConfigFactory(private val context: Context,
|
|||||||
is ConversationVolatileConfig -> convoHashes.removeAll(deletedHashes)
|
is ConversationVolatileConfig -> convoHashes.removeAll(deletedHashes)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateUser(userProfile: UserProfile) {
|
|
||||||
val (_, userPublicKey) = maybeGetUserInfo() ?: return
|
|
||||||
// would love to get rid of recipient and context from this
|
|
||||||
val recipient = Recipient.from(context, Address.fromSerialized(userPublicKey), false)
|
|
||||||
// update name
|
|
||||||
val name = userProfile.getName() ?: return
|
|
||||||
val userPic = userProfile.getPic()
|
|
||||||
val profileManager = SSKEnvironment.shared.profileManager
|
|
||||||
if (name.isNotEmpty()) {
|
|
||||||
TextSecurePreferences.setProfileName(context, name)
|
|
||||||
profileManager.setName(context, recipient, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// update pfp
|
|
||||||
if (userPic == null) {
|
|
||||||
// clear picture if userPic is null
|
|
||||||
TextSecurePreferences.setProfileKey(context, null)
|
|
||||||
ProfileKeyUtil.setEncodedProfileKey(context, null)
|
|
||||||
profileManager.setProfileKey(context, recipient, null)
|
|
||||||
storage.setUserProfilePictureURL(null)
|
|
||||||
} else if (userPic.key.isNotEmpty() && userPic.url.isNotEmpty()
|
|
||||||
&& TextSecurePreferences.getProfilePictureURL(context) != userPic.url) {
|
|
||||||
val profileKey = Base64.encodeBytes(userPic.key)
|
|
||||||
ProfileKeyUtil.setEncodedProfileKey(context, profileKey)
|
|
||||||
profileManager.setProfileKey(context, recipient, userPic.key)
|
|
||||||
storage.setUserProfilePictureURL(userPic.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateContacts(contacts: Contacts) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateConvoVolatile(convos: ConversationVolatileConfig) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -6,7 +6,6 @@ import dagger.Provides
|
|||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import net.zetetic.database.sqlcipher.SQLiteDatabase
|
|
||||||
import org.session.libsession.database.MessageDataProvider
|
import org.session.libsession.database.MessageDataProvider
|
||||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachmentProvider
|
import org.thoughtcrime.securesms.attachments.DatabaseAttachmentProvider
|
||||||
import org.thoughtcrime.securesms.crypto.AttachmentSecret
|
import org.thoughtcrime.securesms.crypto.AttachmentSecret
|
||||||
@ -136,7 +135,7 @@ object DatabaseModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideStorage(@ApplicationContext context: Context, openHelper: SQLCipherOpenHelper) = Storage(context,openHelper)
|
fun provideStorage(@ApplicationContext context: Context, openHelper: SQLCipherOpenHelper, configFactory: ConfigFactory) = Storage(context,openHelper, configFactory)
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
|
@ -6,10 +6,10 @@ import dagger.Provides
|
|||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import org.session.libsession.utilities.ConfigFactoryUpdateListener
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.crypto.KeyPairUtilities
|
import org.thoughtcrime.securesms.crypto.KeyPairUtilities
|
||||||
import org.thoughtcrime.securesms.database.ConfigDatabase
|
import org.thoughtcrime.securesms.database.ConfigDatabase
|
||||||
import org.thoughtcrime.securesms.database.Storage
|
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@ -23,12 +23,14 @@ object SessionUtilModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideConfigFactory(@ApplicationContext context: Context, configDatabase: ConfigDatabase, storage: Storage): ConfigFactory =
|
fun provideConfigFactory(@ApplicationContext context: Context, configDatabase: ConfigDatabase): ConfigFactory =
|
||||||
ConfigFactory(context, configDatabase, storage) {
|
ConfigFactory(context, configDatabase) {
|
||||||
val localUserPublicKey = TextSecurePreferences.getLocalNumber(context)
|
val localUserPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
val secretKey = maybeUserEdSecretKey(context)
|
val secretKey = maybeUserEdSecretKey(context)
|
||||||
if (localUserPublicKey == null || secretKey == null) null
|
if (localUserPublicKey == null || secretKey == null) null
|
||||||
else secretKey to localUserPublicKey
|
else secretKey to localUserPublicKey
|
||||||
|
}.apply {
|
||||||
|
registerListener(context as ConfigFactoryUpdateListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -213,6 +213,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
}
|
}
|
||||||
val compoundPromise = all(promises)
|
val compoundPromise = all(promises)
|
||||||
compoundPromise.successUi { // Do this on the UI thread so that it happens before the alwaysUi clause below
|
compoundPromise.successUi { // Do this on the UI thread so that it happens before the alwaysUi clause below
|
||||||
|
val userConfig = configFactory.user
|
||||||
if (isUpdatingProfilePicture && profilePicture != null) {
|
if (isUpdatingProfilePicture && profilePicture != null) {
|
||||||
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture)
|
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture)
|
||||||
TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt())
|
TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt())
|
||||||
@ -220,11 +221,14 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
// new config
|
// new config
|
||||||
val url = TextSecurePreferences.getProfilePictureURL(this)
|
val url = TextSecurePreferences.getProfilePictureURL(this)
|
||||||
val profileKey = ProfileKeyUtil.getProfileKey(this)
|
val profileKey = ProfileKeyUtil.getProfileKey(this)
|
||||||
if (!url.isNullOrEmpty() && !profileKey.isEmpty()) {
|
if (!url.isNullOrEmpty() && profileKey.isNotEmpty()) {
|
||||||
configFactory.user?.setPic(UserPic(url, profileKey))
|
userConfig?.setPic(UserPic(url, profileKey))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (profilePicture != null || displayName != null) {
|
if (profilePicture != null || displayName != null) {
|
||||||
|
if (userConfig != null && userConfig.needsDump()) {
|
||||||
|
configFactory.persist(userConfig)
|
||||||
|
}
|
||||||
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@SettingsActivity)
|
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@SettingsActivity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.database.SmsDatabase
|
|||||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ConfigFactory
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.resumeWithException
|
import kotlin.coroutines.resumeWithException
|
||||||
@ -83,7 +84,8 @@ class DefaultConversationRepository @Inject constructor(
|
|||||||
private val mmsSmsDb: MmsSmsDatabase,
|
private val mmsSmsDb: MmsSmsDatabase,
|
||||||
private val recipientDb: RecipientDatabase,
|
private val recipientDb: RecipientDatabase,
|
||||||
private val lokiMessageDb: LokiMessageDatabase,
|
private val lokiMessageDb: LokiMessageDatabase,
|
||||||
private val sessionJobDb: SessionJobDatabase
|
private val sessionJobDb: SessionJobDatabase,
|
||||||
|
private val configFactory: ConfigFactory
|
||||||
) : ConversationRepository {
|
) : ConversationRepository {
|
||||||
|
|
||||||
override fun maybeGetRecipientForThreadId(threadId: Long): Recipient? {
|
override fun maybeGetRecipientForThreadId(threadId: Long): Recipient? {
|
||||||
|
@ -2,6 +2,7 @@ package org.session.libsession.database
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import network.loki.messenger.libsession_util.ConfigBase
|
||||||
import org.session.libsession.messaging.BlindedIdMapping
|
import org.session.libsession.messaging.BlindedIdMapping
|
||||||
import org.session.libsession.messaging.calls.CallMessageType
|
import org.session.libsession.messaging.calls.CallMessageType
|
||||||
import org.session.libsession.messaging.contacts.Contact
|
import org.session.libsession.messaging.contacts.Contact
|
||||||
@ -31,6 +32,7 @@ import org.session.libsession.utilities.recipients.Recipient.RecipientSettings
|
|||||||
import org.session.libsignal.crypto.ecc.ECKeyPair
|
import org.session.libsignal.crypto.ecc.ECKeyPair
|
||||||
import org.session.libsignal.messages.SignalServiceAttachmentPointer
|
import org.session.libsignal.messages.SignalServiceAttachmentPointer
|
||||||
import org.session.libsignal.messages.SignalServiceGroup
|
import org.session.libsignal.messages.SignalServiceGroup
|
||||||
|
import network.loki.messenger.libsession_util.util.Contact as LibSessionContact
|
||||||
|
|
||||||
interface StorageProtocol {
|
interface StorageProtocol {
|
||||||
|
|
||||||
@ -164,6 +166,7 @@ interface StorageProtocol {
|
|||||||
fun setContact(contact: Contact)
|
fun setContact(contact: Contact)
|
||||||
fun getRecipientForThread(threadId: Long): Recipient?
|
fun getRecipientForThread(threadId: Long): Recipient?
|
||||||
fun getRecipientSettings(address: Address): RecipientSettings?
|
fun getRecipientSettings(address: Address): RecipientSettings?
|
||||||
|
fun addLibSessionContacts(contacts: List<LibSessionContact>)
|
||||||
fun addContacts(contacts: List<ConfigurationMessage.Contact>)
|
fun addContacts(contacts: List<ConfigurationMessage.Contact>)
|
||||||
|
|
||||||
// Attachments
|
// Attachments
|
||||||
@ -202,4 +205,7 @@ interface StorageProtocol {
|
|||||||
fun deleteReactions(messageId: Long, mms: Boolean)
|
fun deleteReactions(messageId: Long, mms: Boolean)
|
||||||
fun unblock(toUnblock: List<Recipient>)
|
fun unblock(toUnblock: List<Recipient>)
|
||||||
fun blockedContacts(): List<Recipient>
|
fun blockedContacts(): List<Recipient>
|
||||||
|
|
||||||
|
// Shared configs
|
||||||
|
fun notifyConfigUpdates(forConfigObject: ConfigBase)
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,6 @@ class Poller(private val configFactory: ConfigFactoryProtocol) {
|
|||||||
}
|
}
|
||||||
// process new results
|
// process new results
|
||||||
configFactory.persist(forConfigObject)
|
configFactory.persist(forConfigObject)
|
||||||
configFactory.notifyUpdates(forConfigObject)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun poll(snode: Snode, deferred: Deferred<Unit, Exception>): Promise<Unit, Exception> {
|
private fun poll(snode: Snode, deferred: Deferred<Unit, Exception>): Promise<Unit, Exception> {
|
||||||
|
@ -11,7 +11,10 @@ interface ConfigFactoryProtocol {
|
|||||||
val convoVolatile: ConversationVolatileConfig?
|
val convoVolatile: ConversationVolatileConfig?
|
||||||
fun persist(forConfigObject: ConfigBase)
|
fun persist(forConfigObject: ConfigBase)
|
||||||
fun appendHash(configObject: ConfigBase, hash: String)
|
fun appendHash(configObject: ConfigBase, hash: String)
|
||||||
fun notifyUpdates(forConfigObject: ConfigBase)
|
|
||||||
fun getHashesFor(forConfigObject: ConfigBase): List<String>
|
fun getHashesFor(forConfigObject: ConfigBase): List<String>
|
||||||
fun removeHashesFor(config: ConfigBase, deletedHashes: Set<String>): Boolean
|
fun removeHashesFor(config: ConfigBase, deletedHashes: Set<String>): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ConfigFactoryUpdateListener {
|
||||||
|
fun notifyUpdates(forConfigObject: ConfigBase)
|
||||||
}
|
}
|
@ -3,7 +3,6 @@ package org.session.libsession.utilities
|
|||||||
import org.session.libsignal.messages.SignalServiceGroup
|
import org.session.libsignal.messages.SignalServiceGroup
|
||||||
import org.session.libsignal.utilities.Hex
|
import org.session.libsignal.utilities.Hex
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import kotlin.jvm.Throws
|
|
||||||
|
|
||||||
object GroupUtil {
|
object GroupUtil {
|
||||||
const val CLOSED_GROUP_PREFIX = "__textsecure_group__!"
|
const val CLOSED_GROUP_PREFIX = "__textsecure_group__!"
|
||||||
@ -97,4 +96,10 @@ object GroupUtil {
|
|||||||
fun doubleDecodeGroupID(groupID: String): ByteArray {
|
fun doubleDecodeGroupID(groupID: String): ByteArray {
|
||||||
return getDecodedGroupIDAsData(getDecodedGroupID(groupID))
|
return getDecodedGroupIDAsData(getDecodedGroupID(groupID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun doubleDecodeGroupId(groupID: String): String {
|
||||||
|
return Hex.toStringCondensed(getDecodedGroupIDAsData(getDecodedGroupID(groupID)))
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user