mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-12 07:17:45 +00:00
refactor: use OpenGroupAPI.kt instead of PublicChatAPI.kt for all open group operations.
Syncing open group notify conversation list listeners debounced so UI doesn't freeze on initial syncs
This commit is contained in:
@@ -21,9 +21,9 @@ import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
||||
@@ -33,6 +33,7 @@ import org.conscrypt.Conscrypt;
|
||||
import org.session.libsession.messaging.MessagingConfiguration;
|
||||
import org.session.libsession.messaging.avatars.AvatarHelper;
|
||||
import org.session.libsession.messaging.jobs.JobQueue;
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI;
|
||||
import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier;
|
||||
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPoller;
|
||||
import org.session.libsession.messaging.sending_receiving.pollers.Poller;
|
||||
@@ -50,14 +51,12 @@ import org.session.libsignal.service.loki.api.PushNotificationAPI;
|
||||
import org.session.libsignal.service.loki.api.SnodeAPI;
|
||||
import org.session.libsignal.service.loki.api.SwarmAPI;
|
||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI;
|
||||
import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI;
|
||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol;
|
||||
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager;
|
||||
import org.session.libsignal.utilities.logging.Log;
|
||||
import org.signal.aesgcmprovider.AesGcmProvider;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
|
||||
import org.thoughtcrime.securesms.jobmanager.DependencyInjector;
|
||||
@@ -140,10 +139,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
public Poller poller = null;
|
||||
public ClosedGroupPoller closedGroupPoller = null;
|
||||
public PublicChatManager publicChatManager = null;
|
||||
private PublicChatAPI publicChatAPI = null;
|
||||
public Broadcaster broadcaster = null;
|
||||
public SignalCommunicationModule communicationModule;
|
||||
private Job firebaseInstanceIdJob;
|
||||
private Handler threadNotificationHandler;
|
||||
|
||||
private volatile boolean isAppVisible;
|
||||
|
||||
@@ -151,7 +150,11 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
return (ApplicationContext) context.getApplicationContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Handler getThreadNotificationHandler() {
|
||||
return this.threadNotificationHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.i(TAG, "onCreate()");
|
||||
@@ -166,6 +169,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
// ========
|
||||
messageNotifier = new OptimizedMessageNotifier(new DefaultMessageNotifier());
|
||||
broadcaster = new Broadcaster(this);
|
||||
threadNotificationHandler = new Handler(Looper.getMainLooper());
|
||||
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
||||
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(this);
|
||||
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
|
||||
@@ -285,22 +289,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
}
|
||||
|
||||
// Loki
|
||||
public @Nullable
|
||||
PublicChatAPI getPublicChatAPI() {
|
||||
if (publicChatAPI != null || !IdentityKeyUtil.hasIdentityKey(this)) {
|
||||
return publicChatAPI;
|
||||
}
|
||||
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||
if (userPublicKey == null) {
|
||||
return publicChatAPI;
|
||||
}
|
||||
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
|
||||
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
||||
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
|
||||
GroupDatabase groupDB = DatabaseFactory.getGroupDatabase(this);
|
||||
publicChatAPI = new PublicChatAPI(userPublicKey, userPrivateKey, apiDB, userDB, groupDB);
|
||||
return publicChatAPI;
|
||||
}
|
||||
|
||||
private void initializeSecurityProvider() {
|
||||
try {
|
||||
@@ -531,21 +519,12 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
|
||||
public void updateOpenGroupProfilePicturesIfNeeded() {
|
||||
AsyncTask.execute(() -> {
|
||||
PublicChatAPI publicChatAPI = null;
|
||||
try {
|
||||
publicChatAPI = getPublicChatAPI();
|
||||
} catch (Exception e) {
|
||||
// Do nothing
|
||||
}
|
||||
if (publicChatAPI == null) {
|
||||
return;
|
||||
}
|
||||
byte[] profileKey = ProfileKeyUtil.getProfileKey(this);
|
||||
String url = TextSecurePreferences.getProfilePictureURL(this);
|
||||
Set<String> servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers();
|
||||
for (String server : servers) {
|
||||
if (profileKey != null) {
|
||||
publicChatAPI.setProfilePicture(server, profileKey, url);
|
||||
OpenGroupAPI.setProfilePicture(server, profileKey, url);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@@ -16,11 +16,15 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.session.libsession.utilities.Debouncer;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
|
||||
import java.util.Set;
|
||||
@@ -31,10 +35,13 @@ public abstract class Database {
|
||||
|
||||
protected SQLCipherOpenHelper databaseHelper;
|
||||
protected final Context context;
|
||||
private final Debouncer threadNotificationDebouncer;
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
public Database(Context context, SQLCipherOpenHelper databaseHelper) {
|
||||
this.context = context;
|
||||
this.databaseHelper = databaseHelper;
|
||||
this.threadNotificationDebouncer = new Debouncer(ApplicationContext.getInstance(context).getThreadNotificationHandler(), 1000);
|
||||
}
|
||||
|
||||
protected void notifyConversationListeners(Set<Long> threadIds) {
|
||||
@@ -47,7 +54,7 @@ public abstract class Database {
|
||||
}
|
||||
|
||||
protected void notifyConversationListListeners() {
|
||||
context.getContentResolver().notifyChange(DatabaseContentProviders.ConversationList.CONTENT_URI, null);
|
||||
threadNotificationDebouncer.publish(()->context.getContentResolver().notifyChange(DatabaseContentProviders.ConversationList.CONTENT_URI, null));
|
||||
}
|
||||
|
||||
protected void notifyStickerListeners() {
|
||||
|
@@ -26,11 +26,12 @@ import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import org.session.libsession.messaging.jobs.JobQueue
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||
import org.session.libsession.utilities.*
|
||||
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager
|
||||
import org.session.libsignal.service.loki.utilities.toHexString
|
||||
@@ -359,8 +360,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
||||
apiDB.removeLastDeletionServerID(publicChat.channel, publicChat.server)
|
||||
apiDB.clearOpenGroupProfilePictureURL(publicChat.channel, publicChat.server)
|
||||
|
||||
ApplicationContext.getInstance(context).publicChatAPI!!
|
||||
.leave(publicChat.channel, publicChat.server)
|
||||
OpenGroupAPI.leave(publicChat.channel, publicChat.server)
|
||||
|
||||
ApplicationContext.getInstance(context).publicChatManager
|
||||
.removeChat(publicChat.server, publicChat.channel)
|
||||
|
@@ -28,6 +28,7 @@ import nl.komponents.kovenant.functional.bind
|
||||
import nl.komponents.kovenant.task
|
||||
import nl.komponents.kovenant.ui.alwaysUi
|
||||
import org.session.libsession.messaging.avatars.AvatarHelper
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI
|
||||
import org.session.libsession.messaging.threads.Address
|
||||
import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
@@ -179,11 +180,8 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
val promises = mutableListOf<Promise<*, Exception>>()
|
||||
val displayName = displayNameToBeUploaded
|
||||
if (displayName != null) {
|
||||
val publicChatAPI = ApplicationContext.getInstance(this).publicChatAPI
|
||||
if (publicChatAPI != null) {
|
||||
val servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers()
|
||||
promises.addAll(servers.map { publicChatAPI.setDisplayName(displayName, it) })
|
||||
}
|
||||
val servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers()
|
||||
promises.addAll(servers.map { OpenGroupAPI.setDisplayName(displayName, it) })
|
||||
TextSecurePreferences.setProfileName(this, displayName)
|
||||
}
|
||||
val profilePicture = profilePictureToBeUploaded
|
||||
|
@@ -7,12 +7,12 @@ import android.text.TextUtils
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.session.libsession.messaging.MessagingConfiguration
|
||||
import org.session.libsession.messaging.opengroups.OpenGroup
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupInfo
|
||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.Util
|
||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat
|
||||
import org.session.libsignal.service.loki.api.opengroups.PublicChatInfo
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.groups.GroupManager
|
||||
@@ -65,27 +65,23 @@ class PublicChatManager(private val context: Context) {
|
||||
//TODO Declare a specific type of checked exception instead of "Exception".
|
||||
@WorkerThread
|
||||
@Throws(java.lang.Exception::class)
|
||||
public fun addChat(server: String, channel: Long): PublicChat {
|
||||
val groupChatAPI = ApplicationContext.getInstance(context).publicChatAPI
|
||||
?: throw IllegalStateException("LokiPublicChatAPI is not set!")
|
||||
|
||||
public fun addChat(server: String, channel: Long): OpenGroup {
|
||||
// Ensure the auth token is acquired.
|
||||
groupChatAPI.getAuthToken(server).get()
|
||||
OpenGroupAPI.getAuthToken(server).get()
|
||||
|
||||
val channelInfo = groupChatAPI.getChannelInfo(channel, server).get()
|
||||
val channelInfo = OpenGroupAPI.getChannelInfo(channel, server).get()
|
||||
return addChat(server, channel, channelInfo)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public fun addChat(server: String, channel: Long, info: PublicChatInfo): PublicChat {
|
||||
public fun addChat(server: String, channel: Long, info: OpenGroupInfo): OpenGroup {
|
||||
val chat = PublicChat(channel, server, info.displayName, true)
|
||||
var threadID = GroupManager.getOpenGroupThreadID(chat.id, context)
|
||||
var profilePicture: Bitmap? = null
|
||||
// Create the group if we don't have one
|
||||
if (threadID < 0) {
|
||||
if (info.profilePictureURL.isNotEmpty()) {
|
||||
val profilePictureAsByteArray = ApplicationContext.getInstance(context).publicChatAPI
|
||||
?.downloadOpenGroupProfilePicture(server, info.profilePictureURL)
|
||||
val profilePictureAsByteArray = OpenGroupAPI.downloadOpenGroupProfilePicture(server, info.profilePictureURL)
|
||||
profilePicture = BitmapUtil.fromByteArray(profilePictureAsByteArray)
|
||||
}
|
||||
val result = GroupManager.createOpenGroup(chat.id, context, profilePicture, chat.displayName)
|
||||
@@ -95,12 +91,12 @@ class PublicChatManager(private val context: Context) {
|
||||
// Set our name on the server
|
||||
val displayName = TextSecurePreferences.getProfileName(context)
|
||||
if (!TextUtils.isEmpty(displayName)) {
|
||||
ApplicationContext.getInstance(context).publicChatAPI?.setDisplayName(displayName, server)
|
||||
OpenGroupAPI.setDisplayName(displayName, server)
|
||||
}
|
||||
// Start polling
|
||||
Util.runOnMain { startPollersIfNeeded() }
|
||||
|
||||
return chat
|
||||
return OpenGroup.from(chat)
|
||||
}
|
||||
|
||||
public fun removeChat(server: String, channel: Long) {
|
||||
|
@@ -1,238 +0,0 @@
|
||||
package org.thoughtcrime.securesms.loki.api
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.functional.bind
|
||||
import nl.komponents.kovenant.functional.map
|
||||
import org.session.libsession.messaging.threads.Address
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||
import org.session.libsession.utilities.IdentityKeyUtil
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsignal.libsignal.util.guava.Optional
|
||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer
|
||||
import org.session.libsignal.service.api.messages.SignalServiceContent
|
||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage
|
||||
import org.session.libsignal.service.api.messages.SignalServiceGroup
|
||||
import org.session.libsignal.service.api.push.SignalServiceAddress
|
||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat
|
||||
import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI
|
||||
import org.session.libsignal.service.loki.api.opengroups.PublicChatMessage
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
import org.session.libsignal.utilities.successBackground
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.jobs.PushDecryptJob
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
|
||||
import java.security.MessageDigest
|
||||
import java.util.*
|
||||
|
||||
class PublicChatPoller(private val context: Context, private val group: PublicChat) {
|
||||
private val handler by lazy { Handler() }
|
||||
private var hasStarted = false
|
||||
private var isPollOngoing = false
|
||||
public var isCaughtUp = false
|
||||
|
||||
// region Convenience
|
||||
private val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
||||
private var displayNameUpdatees = setOf<String>()
|
||||
|
||||
private val api: PublicChatAPI
|
||||
get() = {
|
||||
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
|
||||
val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context)
|
||||
val lokiUserDatabase = DatabaseFactory.getLokiUserDatabase(context)
|
||||
val openGroupDatabase = DatabaseFactory.getGroupDatabase(context)
|
||||
PublicChatAPI(userHexEncodedPublicKey, userPrivateKey, lokiAPIDatabase, lokiUserDatabase, openGroupDatabase)
|
||||
}()
|
||||
// endregion
|
||||
|
||||
// region Tasks
|
||||
private val pollForNewMessagesTask = object : Runnable {
|
||||
|
||||
override fun run() {
|
||||
pollForNewMessages()
|
||||
handler.postDelayed(this, pollForNewMessagesInterval)
|
||||
}
|
||||
}
|
||||
|
||||
private val pollForDeletedMessagesTask = object : Runnable {
|
||||
|
||||
override fun run() {
|
||||
pollForDeletedMessages()
|
||||
handler.postDelayed(this, pollForDeletedMessagesInterval)
|
||||
}
|
||||
}
|
||||
|
||||
private val pollForModeratorsTask = object : Runnable {
|
||||
|
||||
override fun run() {
|
||||
pollForModerators()
|
||||
handler.postDelayed(this, pollForModeratorsInterval)
|
||||
}
|
||||
}
|
||||
|
||||
private val pollForDisplayNamesTask = object : Runnable {
|
||||
|
||||
override fun run() {
|
||||
pollForDisplayNames()
|
||||
handler.postDelayed(this, pollForDisplayNamesInterval)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Settings
|
||||
companion object {
|
||||
private val pollForNewMessagesInterval: Long = 4 * 1000
|
||||
private val pollForDeletedMessagesInterval: Long = 60 * 1000
|
||||
private val pollForModeratorsInterval: Long = 10 * 60 * 1000
|
||||
private val pollForDisplayNamesInterval: Long = 60 * 1000
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Lifecycle
|
||||
fun startIfNeeded() {
|
||||
if (hasStarted) return
|
||||
pollForNewMessagesTask.run()
|
||||
pollForDeletedMessagesTask.run()
|
||||
pollForModeratorsTask.run()
|
||||
pollForDisplayNamesTask.run()
|
||||
hasStarted = true
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
handler.removeCallbacks(pollForNewMessagesTask)
|
||||
handler.removeCallbacks(pollForDeletedMessagesTask)
|
||||
handler.removeCallbacks(pollForModeratorsTask)
|
||||
handler.removeCallbacks(pollForDisplayNamesTask)
|
||||
hasStarted = false
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Polling
|
||||
private fun getDataMessage(message: PublicChatMessage): SignalServiceDataMessage {
|
||||
val id = group.id.toByteArray()
|
||||
val serviceGroup = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, SignalServiceGroup.GroupType.PUBLIC_CHAT, null, null, null, null)
|
||||
val quote = if (message.quote != null) {
|
||||
SignalServiceDataMessage.Quote(message.quote!!.quotedMessageTimestamp, SignalServiceAddress(message.quote!!.quoteePublicKey), message.quote!!.quotedMessageBody, listOf())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val attachments = message.attachments.mapNotNull { attachment ->
|
||||
if (attachment.kind != PublicChatMessage.Attachment.Kind.Attachment) { return@mapNotNull null }
|
||||
SignalServiceAttachmentPointer(
|
||||
attachment.serverID,
|
||||
attachment.contentType,
|
||||
ByteArray(0),
|
||||
Optional.of(attachment.size),
|
||||
Optional.absent(),
|
||||
attachment.width, attachment.height,
|
||||
Optional.absent(),
|
||||
Optional.of(attachment.fileName),
|
||||
false,
|
||||
Optional.fromNullable(attachment.caption),
|
||||
attachment.url)
|
||||
}
|
||||
val linkPreview = message.attachments.firstOrNull { it.kind == PublicChatMessage.Attachment.Kind.LinkPreview }
|
||||
val signalLinkPreviews = mutableListOf<SignalServiceDataMessage.Preview>()
|
||||
if (linkPreview != null) {
|
||||
val attachment = SignalServiceAttachmentPointer(
|
||||
linkPreview.serverID,
|
||||
linkPreview.contentType,
|
||||
ByteArray(0),
|
||||
Optional.of(linkPreview.size),
|
||||
Optional.absent(),
|
||||
linkPreview.width, linkPreview.height,
|
||||
Optional.absent(),
|
||||
Optional.of(linkPreview.fileName),
|
||||
false,
|
||||
Optional.fromNullable(linkPreview.caption),
|
||||
linkPreview.url)
|
||||
signalLinkPreviews.add(SignalServiceDataMessage.Preview(linkPreview.linkPreviewURL!!, linkPreview.linkPreviewTitle!!, Optional.of(attachment)))
|
||||
}
|
||||
val body = if (message.body == message.timestamp.toString()) "" else message.body // Workaround for the fact that the back-end doesn't accept messages without a body
|
||||
val syncTarget = if (message.senderPublicKey == userHexEncodedPublicKey) group.id else null
|
||||
return SignalServiceDataMessage(message.timestamp, serviceGroup, attachments, body, 0, false, null, quote, null, signalLinkPreviews, null, syncTarget)
|
||||
}
|
||||
|
||||
fun pollForNewMessages(): Promise<Unit, Exception> {
|
||||
if (isPollOngoing) { return Promise.of(Unit) }
|
||||
isPollOngoing = true
|
||||
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
|
||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
||||
FileServerAPI.configure(userHexEncodedPublicKey, userPrivateKey, apiDB)
|
||||
// Kovenant propagates a context to chained promises, so LokiPublicChatAPI.sharedContext should be used for all of the below
|
||||
val promise = api.getMessages(group.channel, group.server).bind(PublicChatAPI.sharedContext) { messages ->
|
||||
Promise.of(messages)
|
||||
}
|
||||
promise.successBackground { messages ->
|
||||
// Process messages in the background
|
||||
messages.forEach { message ->
|
||||
// If the sender of the current message is not a slave device, set the display name in the database
|
||||
val senderDisplayName = "${message.displayName} (...${message.senderPublicKey.takeLast(8)})"
|
||||
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.senderPublicKey, senderDisplayName)
|
||||
val senderHexEncodedPublicKey = message.senderPublicKey
|
||||
val serviceDataMessage = getDataMessage(message)
|
||||
val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.serverTimestamp, false)
|
||||
if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) {
|
||||
PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
|
||||
} else {
|
||||
PushDecryptJob(context).handleTextMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
|
||||
}
|
||||
// Update profile picture if needed
|
||||
val senderAsRecipient = Recipient.from(context, Address.fromSerialized(senderHexEncodedPublicKey), false)
|
||||
if (message.profilePicture != null && message.profilePicture!!.url.isNotEmpty()) {
|
||||
val profileKey = message.profilePicture!!.profileKey
|
||||
val url = message.profilePicture!!.url
|
||||
if (senderAsRecipient.profileKey == null || !MessageDigest.isEqual(senderAsRecipient.profileKey, profileKey)) {
|
||||
val database = DatabaseFactory.getRecipientDatabase(context)
|
||||
database.setProfileKey(senderAsRecipient, profileKey)
|
||||
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderAsRecipient, url))
|
||||
}
|
||||
}
|
||||
}
|
||||
isCaughtUp = true
|
||||
isPollOngoing = false
|
||||
}
|
||||
promise.fail {
|
||||
Log.d("Loki", "Failed to get messages for group chat with ID: ${group.channel} on server: ${group.server}.")
|
||||
isPollOngoing = false
|
||||
}
|
||||
return promise.map { Unit }
|
||||
}
|
||||
|
||||
private fun pollForDisplayNames() {
|
||||
if (displayNameUpdatees.isEmpty()) { return }
|
||||
val hexEncodedPublicKeys = displayNameUpdatees
|
||||
displayNameUpdatees = setOf()
|
||||
api.getDisplayNames(hexEncodedPublicKeys, group.server).successBackground { mapping ->
|
||||
for (pair in mapping.entries) {
|
||||
val senderDisplayName = "${pair.value} (...${pair.key.takeLast(8)})"
|
||||
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, pair.key, senderDisplayName)
|
||||
}
|
||||
}.fail {
|
||||
displayNameUpdatees = displayNameUpdatees.union(hexEncodedPublicKeys)
|
||||
}
|
||||
}
|
||||
|
||||
private fun pollForDeletedMessages() {
|
||||
api.getDeletedMessageServerIDs(group.channel, group.server).success { deletedMessageServerIDs ->
|
||||
val lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context)
|
||||
val deletedMessageIDs = deletedMessageServerIDs.mapNotNull { lokiMessageDatabase.getMessageID(it) }
|
||||
val smsMessageDatabase = DatabaseFactory.getSmsDatabase(context)
|
||||
val mmsMessageDatabase = DatabaseFactory.getMmsDatabase(context)
|
||||
deletedMessageIDs.forEach {
|
||||
smsMessageDatabase.deleteMessage(it)
|
||||
mmsMessageDatabase.delete(it)
|
||||
}
|
||||
}.fail {
|
||||
Log.d("Loki", "Failed to get deleted messages for group chat with ID: ${group.channel} on server: ${group.server}.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun pollForModerators() {
|
||||
api.getModerators(group.channel, group.server)
|
||||
}
|
||||
// endregion
|
||||
}
|
@@ -3,16 +3,15 @@ package org.thoughtcrime.securesms.loki.utilities
|
||||
import android.content.Context
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.session.libsession.utilities.preferences.ProfileKeyUtil
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.groups.GroupManager
|
||||
import org.session.libsession.messaging.opengroups.OpenGroup
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.preferences.ProfileKeyUtil
|
||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat
|
||||
import java.lang.Exception
|
||||
import java.lang.IllegalStateException
|
||||
import kotlin.jvm.Throws
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.groups.GroupManager
|
||||
|
||||
//TODO Refactor so methods declare specific type of checked exceptions and not generalized Exception.
|
||||
object OpenGroupUtilities {
|
||||
@@ -22,29 +21,27 @@ object OpenGroupUtilities {
|
||||
@JvmStatic
|
||||
@WorkerThread
|
||||
@Throws(Exception::class)
|
||||
fun addGroup(context: Context, url: String, channel: Long): PublicChat {
|
||||
fun addGroup(context: Context, url: String, channel: Long): OpenGroup {
|
||||
// Check for an existing group.
|
||||
val groupID = PublicChat.getId(channel, url)
|
||||
val threadID = GroupManager.getOpenGroupThreadID(groupID, context)
|
||||
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
||||
if (openGroup != null) { return openGroup }
|
||||
if (openGroup != null) { return OpenGroup.from(openGroup) }
|
||||
|
||||
// Add the new group.
|
||||
val application = ApplicationContext.getInstance(context)
|
||||
val displayName = TextSecurePreferences.getProfileName(context)
|
||||
val lokiPublicChatAPI = application.publicChatAPI
|
||||
?: throw IllegalStateException("LokiPublicChatAPI is not initialized.")
|
||||
|
||||
val group = application.publicChatManager.addChat(url, channel)
|
||||
|
||||
DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url)
|
||||
DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url)
|
||||
lokiPublicChatAPI.getMessages(channel, url)
|
||||
lokiPublicChatAPI.setDisplayName(displayName, url)
|
||||
lokiPublicChatAPI.join(channel, url)
|
||||
OpenGroupAPI.getMessages(channel, url)
|
||||
OpenGroupAPI.setDisplayName(displayName, url)
|
||||
OpenGroupAPI.join(channel, url)
|
||||
val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(context)
|
||||
val profileUrl: String? = TextSecurePreferences.getProfilePictureURL(context)
|
||||
lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl)
|
||||
OpenGroupAPI.setProfilePicture(url, profileKey, profileUrl)
|
||||
return group
|
||||
}
|
||||
|
||||
@@ -58,18 +55,15 @@ object OpenGroupUtilities {
|
||||
@WorkerThread
|
||||
@Throws(Exception::class)
|
||||
fun updateGroupInfo(context: Context, url: String, channel: Long) {
|
||||
val publicChatAPI = ApplicationContext.getInstance(context).publicChatAPI
|
||||
?: throw IllegalStateException("Public chat API is not initialized!")
|
||||
|
||||
// Check if open group has a related DB record.
|
||||
val groupId = GroupUtil.getEncodedOpenGroupID(PublicChat.getId(channel, url).toByteArray())
|
||||
if (!DatabaseFactory.getGroupDatabase(context).hasGroup(groupId)) {
|
||||
throw IllegalStateException("Attempt to update open group info for non-existent DB record: $groupId")
|
||||
}
|
||||
|
||||
val info = publicChatAPI.getChannelInfo(channel, url).get()
|
||||
val info = OpenGroupAPI.getChannelInfo(channel, url).get()
|
||||
|
||||
publicChatAPI.updateProfileIfNeeded(channel, url, groupId, info, false)
|
||||
OpenGroupAPI.updateProfileIfNeeded(channel, url, groupId, info, false)
|
||||
|
||||
EventBus.getDefault().post(GroupInfoUpdatedEvent(url, channel))
|
||||
}
|
||||
|
@@ -8,9 +8,9 @@ import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import kotlinx.android.synthetic.main.view_mention_candidate.view.*
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI
|
||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI
|
||||
import org.session.libsignal.service.loki.utilities.mentions.Mention
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
|
||||
class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
var mentionCandidate = Mention("", "")
|
||||
@@ -38,7 +38,7 @@ class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr:
|
||||
profilePictureView.glide = glide!!
|
||||
profilePictureView.update()
|
||||
if (publicChatServer != null && publicChatChannel != null) {
|
||||
val isUserModerator = PublicChatAPI.isUserModerator(mentionCandidate.publicKey, publicChatChannel!!, publicChatServer!!)
|
||||
val isUserModerator = OpenGroupAPI.isUserModerator(mentionCandidate.publicKey, publicChatChannel!!, publicChatServer!!)
|
||||
moderatorIconImageView.visibility = if (isUserModerator) View.VISIBLE else View.GONE
|
||||
} else {
|
||||
moderatorIconImageView.visibility = View.GONE
|
||||
|
Reference in New Issue
Block a user