mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
Merge branch 'dev' of https://github.com/loki-project/session-android into media-saving
This commit is contained in:
commit
76c253ee77
20
app/src/main/java/org/thoughtcrime/securesms/AppContext.kt
Normal file
20
app/src/main/java/org/thoughtcrime/securesms/AppContext.kt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package org.thoughtcrime.securesms
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import nl.komponents.kovenant.Kovenant
|
||||||
|
import nl.komponents.kovenant.jvm.asDispatcher
|
||||||
|
import org.session.libsignal.utilities.ThreadUtils
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
object AppContext {
|
||||||
|
|
||||||
|
fun configureKovenant() {
|
||||||
|
Kovenant.context {
|
||||||
|
callbackContext.dispatcher = Executors.newSingleThreadExecutor().asDispatcher()
|
||||||
|
workerContext.dispatcher = ThreadUtils.executorPool.asDispatcher()
|
||||||
|
multipleCompletion = { v1, v2 ->
|
||||||
|
Log.d("Loki", "Promise resolved more than once (first with $v1, then with $v2); ignoring $v2.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,15 +30,17 @@ import androidx.lifecycle.ProcessLifecycleOwner;
|
|||||||
import androidx.multidex.MultiDexApplication;
|
import androidx.multidex.MultiDexApplication;
|
||||||
|
|
||||||
import org.conscrypt.Conscrypt;
|
import org.conscrypt.Conscrypt;
|
||||||
import org.session.libsession.messaging.MessagingConfiguration;
|
import org.session.libsession.messaging.MessagingModuleConfiguration;
|
||||||
import org.session.libsession.messaging.avatars.AvatarHelper;
|
import org.session.libsession.messaging.avatars.AvatarHelper;
|
||||||
|
import org.session.libsession.messaging.file_server.FileServerAPI;
|
||||||
import org.session.libsession.messaging.jobs.JobQueue;
|
import org.session.libsession.messaging.jobs.JobQueue;
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI;
|
import org.session.libsession.messaging.mentions.MentionsManager;
|
||||||
|
import org.session.libsession.messaging.open_groups.OpenGroupAPI;
|
||||||
import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier;
|
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.ClosedGroupPoller;
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.Poller;
|
import org.session.libsession.messaging.sending_receiving.pollers.Poller;
|
||||||
import org.session.libsession.messaging.threads.Address;
|
import org.session.libsession.messaging.threads.Address;
|
||||||
import org.session.libsession.snode.SnodeConfiguration;
|
import org.session.libsession.snode.SnodeModule;
|
||||||
import org.session.libsession.utilities.IdentityKeyUtil;
|
import org.session.libsession.utilities.IdentityKeyUtil;
|
||||||
import org.session.libsession.utilities.SSKEnvironment;
|
import org.session.libsession.utilities.SSKEnvironment;
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
@ -47,12 +49,7 @@ import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageContextWr
|
|||||||
import org.session.libsession.utilities.dynamiclanguage.LocaleParser;
|
import org.session.libsession.utilities.dynamiclanguage.LocaleParser;
|
||||||
import org.session.libsession.utilities.preferences.ProfileKeyUtil;
|
import org.session.libsession.utilities.preferences.ProfileKeyUtil;
|
||||||
import org.session.libsignal.service.api.util.StreamDetails;
|
import org.session.libsignal.service.api.util.StreamDetails;
|
||||||
import org.session.libsignal.service.loki.api.PushNotificationAPI;
|
import org.session.libsignal.service.loki.LokiAPIDatabaseProtocol;
|
||||||
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.database.LokiAPIDatabaseProtocol;
|
|
||||||
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
import org.session.libsignal.utilities.logging.Log;
|
||||||
import org.signal.aesgcmprovider.AesGcmProvider;
|
import org.signal.aesgcmprovider.AesGcmProvider;
|
||||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||||
@ -106,6 +103,7 @@ import dagger.ObjectGraph;
|
|||||||
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 nl.komponents.kovenant.Kovenant;
|
||||||
|
|
||||||
import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant;
|
import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant;
|
||||||
import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
|
import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
|
||||||
@ -166,6 +164,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
||||||
// Loki
|
// Loki
|
||||||
// ========
|
// ========
|
||||||
|
AppContext.INSTANCE.configureKovenant();
|
||||||
messageNotifier = new OptimizedMessageNotifier(new DefaultMessageNotifier());
|
messageNotifier = new OptimizedMessageNotifier(new DefaultMessageNotifier());
|
||||||
broadcaster = new Broadcaster(this);
|
broadcaster = new Broadcaster(this);
|
||||||
threadNotificationHandler = new Handler(Looper.getMainLooper());
|
threadNotificationHandler = new Handler(Looper.getMainLooper());
|
||||||
@ -173,17 +172,14 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(this);
|
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(this);
|
||||||
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
|
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
|
||||||
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||||
MessagingConfiguration.Companion.configure(this,
|
MessagingModuleConfiguration.Companion.configure(this,
|
||||||
DatabaseFactory.getStorage(this),
|
DatabaseFactory.getStorage(this),
|
||||||
DatabaseFactory.getAttachmentProvider(this),
|
DatabaseFactory.getAttachmentProvider(this),
|
||||||
new SessionProtocolImpl(this));
|
new SessionProtocolImpl(this));
|
||||||
SnodeConfiguration.Companion.configure(apiDB, broadcaster);
|
SnodeModule.Companion.configure(apiDB, broadcaster);
|
||||||
if (userPublicKey != null) {
|
if (userPublicKey != null) {
|
||||||
SwarmAPI.Companion.configureIfNeeded(apiDB);
|
MentionsManager.Companion.configureIfNeeded(userPublicKey, userDB);
|
||||||
SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster);
|
|
||||||
MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB);
|
|
||||||
}
|
}
|
||||||
PushNotificationAPI.Companion.configureIfNeeded(BuildConfig.DEBUG);
|
|
||||||
setUpStorageAPIIfNeeded();
|
setUpStorageAPIIfNeeded();
|
||||||
resubmitProfilePictureIfNeeded();
|
resubmitProfilePictureIfNeeded();
|
||||||
publicChatManager = new PublicChatManager(this);
|
publicChatManager = new PublicChatManager(this);
|
||||||
@ -428,7 +424,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
|
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
|
||||||
LokiAPIDatabaseProtocol apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
LokiAPIDatabaseProtocol apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
||||||
FileServerAPI.Companion.configure(userPublicKey, userPrivateKey, apiDB);
|
FileServerAPI.Companion.configure(userPublicKey, userPrivateKey, apiDB);
|
||||||
org.session.libsession.messaging.fileserver.FileServerAPI.Companion.configure(userPublicKey, userPrivateKey, apiDB);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,13 +453,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||||
if (userPublicKey == null) return;
|
if (userPublicKey == null) return;
|
||||||
if (poller != null) {
|
if (poller != null) {
|
||||||
SnodeAPI.shared.setUserPublicKey(userPublicKey);
|
|
||||||
poller.setUserPublicKey(userPublicKey);
|
poller.setUserPublicKey(userPublicKey);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
||||||
SwarmAPI.Companion.configureIfNeeded(apiDB);
|
|
||||||
SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster);
|
|
||||||
poller = new Poller();
|
poller = new Poller();
|
||||||
closedGroupPoller = new ClosedGroupPoller();
|
closedGroupPoller = new ClosedGroupPoller();
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
|||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
import org.session.libsession.messaging.threads.Address;
|
import org.session.libsession.messaging.threads.Address;
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ import androidx.loader.content.Loader;
|
|||||||
import org.session.libsession.messaging.messages.visible.LinkPreview;
|
import org.session.libsession.messaging.messages.visible.LinkPreview;
|
||||||
import org.session.libsession.messaging.messages.visible.Quote;
|
import org.session.libsession.messaging.messages.visible.Quote;
|
||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||||
|
import org.session.libsession.messaging.open_groups.OpenGroup;
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||||
import org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDeliveryStatus;
|
import org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDeliveryStatus;
|
||||||
@ -64,7 +65,6 @@ import org.thoughtcrime.securesms.util.DateUtils;
|
|||||||
import org.session.libsession.utilities.ExpirationUtil;
|
import org.session.libsession.utilities.ExpirationUtil;
|
||||||
import org.session.libsession.utilities.Util;
|
import org.session.libsession.utilities.Util;
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.sql.Date;
|
import java.sql.Date;
|
||||||
@ -263,7 +263,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
|||||||
}
|
}
|
||||||
toFrom.setText(toFromRes);
|
toFrom.setText(toFromRes);
|
||||||
long threadID = messageRecord.getThreadId();
|
long threadID = messageRecord.getThreadId();
|
||||||
PublicChat openGroup = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadID);
|
OpenGroup openGroup = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadID);
|
||||||
if (openGroup != null && messageRecord.isOutgoing()) {
|
if (openGroup != null && messageRecord.isOutgoing()) {
|
||||||
toFrom.setVisibility(View.GONE);
|
toFrom.setVisibility(View.GONE);
|
||||||
separator.setVisibility(View.GONE);
|
separator.setVisibility(View.GONE);
|
||||||
|
@ -5,6 +5,7 @@ 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.open_groups.OpenGroup
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.*
|
import org.session.libsession.messaging.sending_receiving.attachments.*
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
import org.session.libsession.messaging.utilities.DotNetAPI
|
import org.session.libsession.messaging.utilities.DotNetAPI
|
||||||
@ -26,7 +27,6 @@ import org.thoughtcrime.securesms.util.MediaUtil
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
|
|
||||||
class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), MessageDataProvider {
|
class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), MessageDataProvider {
|
||||||
|
|
||||||
override fun getAttachmentStream(attachmentId: Long): SessionServiceAttachmentStream? {
|
override fun getAttachmentStream(attachmentId: Long): SessionServiceAttachmentStream? {
|
||||||
@ -104,6 +104,10 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
|
|||||||
return smsDatabase.isOutgoingMessage(timestamp) || mmsDatabase.isOutgoingMessage(timestamp)
|
return smsDatabase.isOutgoingMessage(timestamp) || mmsDatabase.isOutgoingMessage(timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getOpenGroup(threadID: Long): OpenGroup? {
|
||||||
|
return null // TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
override fun updateAttachmentAfterUploadSucceeded(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult) {
|
override fun updateAttachmentAfterUploadSucceeded(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult) {
|
||||||
val database = DatabaseFactory.getAttachmentDatabase(context)
|
val database = DatabaseFactory.getAttachmentDatabase(context)
|
||||||
val databaseAttachment = getDatabaseAttachment(attachmentId) ?: return
|
val databaseAttachment = getDatabaseAttachment(attachmentId) ?: return
|
||||||
|
@ -30,7 +30,7 @@ import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
|
|||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||||
|
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
|
@ -17,7 +17,7 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
|
|||||||
import org.thoughtcrime.securesms.mms.ImageSlide;
|
import org.thoughtcrime.securesms.mms.ImageSlide;
|
||||||
import org.thoughtcrime.securesms.mms.SlidesClickedListener;
|
import org.thoughtcrime.securesms.mms.SlidesClickedListener;
|
||||||
|
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
|
@ -21,6 +21,7 @@ import androidx.annotation.RequiresApi;
|
|||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
|
|
||||||
|
import org.session.libsession.messaging.open_groups.OpenGroup;
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
@ -35,7 +36,6 @@ import org.session.libsession.messaging.threads.recipients.RecipientModifiedList
|
|||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
import org.session.libsession.utilities.ThemeUtil;
|
import org.session.libsession.utilities.ThemeUtil;
|
||||||
import org.session.libsession.utilities.Util;
|
import org.session.libsession.utilities.Util;
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
|
|||||||
|
|
||||||
long threadID = DatabaseFactory.getThreadDatabase(getContext()).getOrCreateThreadIdFor(conversationRecipient);
|
long threadID = DatabaseFactory.getThreadDatabase(getContext()).getOrCreateThreadIdFor(conversationRecipient);
|
||||||
String senderHexEncodedPublicKey = author.getAddress().serialize();
|
String senderHexEncodedPublicKey = author.getAddress().serialize();
|
||||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadID);
|
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadID);
|
||||||
if (senderHexEncodedPublicKey.equalsIgnoreCase(TextSecurePreferences.getLocalNumber(getContext()))) {
|
if (senderHexEncodedPublicKey.equalsIgnoreCase(TextSecurePreferences.getLocalNumber(getContext()))) {
|
||||||
quoteeDisplayName = TextSecurePreferences.getProfileName(getContext());
|
quoteeDisplayName = TextSecurePreferences.getProfileName(getContext());
|
||||||
} else if (publicChat != null) {
|
} else if (publicChat != null) {
|
||||||
|
@ -85,17 +85,17 @@ import org.greenrobot.eventbus.Subscribe;
|
|||||||
import org.greenrobot.eventbus.ThreadMode;
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
|
|
||||||
|
|
||||||
|
import org.session.libsession.messaging.mentions.MentionsManager;
|
||||||
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate;
|
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate;
|
||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||||
|
import org.session.libsession.messaging.open_groups.OpenGroup;
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||||
import org.session.libsession.messaging.threads.DistributionTypes;
|
import org.session.libsession.messaging.threads.DistributionTypes;
|
||||||
import org.session.libsession.utilities.GroupUtil;
|
import org.session.libsession.utilities.GroupUtil;
|
||||||
import org.session.libsession.utilities.MediaTypes;
|
import org.session.libsession.utilities.MediaTypes;
|
||||||
import org.session.libsignal.libsignal.InvalidMessageException;
|
import org.session.libsignal.libsignal.InvalidMessageException;
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat;
|
import org.session.libsignal.service.loki.Mention;
|
||||||
import org.session.libsignal.service.loki.utilities.mentions.Mention;
|
|
||||||
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager;
|
|
||||||
import org.session.libsignal.service.loki.utilities.HexEncodingKt;
|
import org.session.libsignal.service.loki.utilities.HexEncodingKt;
|
||||||
import org.session.libsignal.service.loki.utilities.PublicKeyValidation;
|
import org.session.libsignal.service.loki.utilities.PublicKeyValidation;
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
@ -183,7 +183,7 @@ import org.session.libsession.utilities.ServiceUtil;
|
|||||||
import org.session.libsession.utilities.Util;
|
import org.session.libsession.utilities.Util;
|
||||||
|
|
||||||
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
||||||
import org.session.libsession.messaging.threads.GroupRecord;
|
import org.session.libsession.messaging.threads.GroupRecord;
|
||||||
import org.session.libsession.utilities.ExpirationUtil;
|
import org.session.libsession.utilities.ExpirationUtil;
|
||||||
@ -376,7 +376,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(threadId, this);
|
MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(threadId, this);
|
||||||
|
|
||||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||||
if (publicChat != null) {
|
if (publicChat != null) {
|
||||||
// Request open group info update and handle the successful result in #onOpenGroupInfoUpdated().
|
// Request open group info update and handle the successful result in #onOpenGroupInfoUpdated().
|
||||||
PublicChatInfoUpdateWorker.scheduleInstant(this, publicChat.getServer(), publicChat.getChannel());
|
PublicChatInfoUpdateWorker.scheduleInstant(this, publicChat.getServer(), publicChat.getChannel());
|
||||||
@ -1399,7 +1399,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
public void onOpenGroupInfoUpdated(OpenGroupUtilities.GroupInfoUpdatedEvent event) {
|
public void onOpenGroupInfoUpdated(OpenGroupUtilities.GroupInfoUpdatedEvent event) {
|
||||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||||
if (publicChat != null &&
|
if (publicChat != null &&
|
||||||
publicChat.getChannel() == event.getChannel() &&
|
publicChat.getChannel() == event.getChannel() &&
|
||||||
publicChat.getServer().equals(event.getUrl())) {
|
publicChat.getServer().equals(event.getUrl())) {
|
||||||
@ -2337,7 +2337,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
muteIndicatorImageView.setVisibility(View.VISIBLE);
|
muteIndicatorImageView.setVisibility(View.VISIBLE);
|
||||||
subtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault()));
|
subtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault()));
|
||||||
} else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) {
|
} else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) {
|
||||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||||
if (publicChat != null) {
|
if (publicChat != null) {
|
||||||
Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(publicChat.getChannel(), publicChat.getServer());
|
Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(publicChat.getChannel(), publicChat.getServer());
|
||||||
if (userCount == null) { userCount = 0; }
|
if (userCount == null) { userCount = 0; }
|
||||||
|
@ -60,7 +60,8 @@ import com.annimon.stream.Stream;
|
|||||||
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
|
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
|
||||||
import org.session.libsession.messaging.messages.visible.Quote;
|
import org.session.libsession.messaging.messages.visible.Quote;
|
||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI;
|
import org.session.libsession.messaging.open_groups.OpenGroup;
|
||||||
|
import org.session.libsession.messaging.open_groups.OpenGroupAPI;
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.MessageDetailsActivity;
|
import org.thoughtcrime.securesms.MessageDetailsActivity;
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
|
||||||
@ -88,13 +89,12 @@ import org.thoughtcrime.securesms.permissions.Permissions;
|
|||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||||
import org.session.libsession.utilities.task.ProgressDialogAsyncTask;
|
import org.session.libsession.utilities.task.ProgressDialogAsyncTask;
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat;
|
|
||||||
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
import org.session.libsession.utilities.Util;
|
import org.session.libsession.utilities.Util;
|
||||||
@ -396,7 +396,7 @@ public class ConversationFragment extends Fragment
|
|||||||
boolean isGroupChat = recipient.isGroupRecipient();
|
boolean isGroupChat = recipient.isGroupRecipient();
|
||||||
|
|
||||||
if (isGroupChat) {
|
if (isGroupChat) {
|
||||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
||||||
boolean isPublicChat = (publicChat != null);
|
boolean isPublicChat = (publicChat != null);
|
||||||
int selectedMessageCount = messageRecords.size();
|
int selectedMessageCount = messageRecords.size();
|
||||||
boolean areAllSentByUser = true;
|
boolean areAllSentByUser = true;
|
||||||
@ -508,7 +508,7 @@ public class ConversationFragment extends Fragment
|
|||||||
builder.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messagesCount, messagesCount));
|
builder.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messagesCount, messagesCount));
|
||||||
builder.setCancelable(true);
|
builder.setCancelable(true);
|
||||||
|
|
||||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
||||||
|
|
||||||
builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
|
builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -591,7 +591,7 @@ public class ConversationFragment extends Fragment
|
|||||||
builder.setTitle(R.string.ConversationFragment_ban_selected_user);
|
builder.setTitle(R.string.ConversationFragment_ban_selected_user);
|
||||||
builder.setCancelable(true);
|
builder.setCancelable(true);
|
||||||
|
|
||||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
||||||
|
|
||||||
builder.setPositiveButton(R.string.ban, (dialog, which) -> {
|
builder.setPositiveButton(R.string.ban, (dialog, which) -> {
|
||||||
ConversationAdapter chatAdapter = getListAdapter();
|
ConversationAdapter chatAdapter = getListAdapter();
|
||||||
|
@ -54,10 +54,11 @@ import com.annimon.stream.Stream;
|
|||||||
|
|
||||||
import org.session.libsession.messaging.jobs.AttachmentDownloadJob;
|
import org.session.libsession.messaging.jobs.AttachmentDownloadJob;
|
||||||
import org.session.libsession.messaging.jobs.JobQueue;
|
import org.session.libsession.messaging.jobs.JobQueue;
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI;
|
import org.session.libsession.messaging.open_groups.OpenGroup;
|
||||||
|
import org.session.libsession.messaging.open_groups.OpenGroupAPI;
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress;
|
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress;
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||||
import org.session.libsession.messaging.threads.recipients.RecipientModifiedListener;
|
import org.session.libsession.messaging.threads.recipients.RecipientModifiedListener;
|
||||||
import org.session.libsession.utilities.GroupUtil;
|
import org.session.libsession.utilities.GroupUtil;
|
||||||
@ -67,7 +68,6 @@ import org.session.libsession.utilities.Util;
|
|||||||
import org.session.libsession.utilities.ViewUtil;
|
import org.session.libsession.utilities.ViewUtil;
|
||||||
import org.session.libsession.utilities.views.Stub;
|
import org.session.libsession.utilities.views.Stub;
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
import org.session.libsignal.utilities.logging.Log;
|
||||||
import org.thoughtcrime.securesms.BindableConversationItem;
|
import org.thoughtcrime.securesms.BindableConversationItem;
|
||||||
import org.thoughtcrime.securesms.MediaPreviewActivity;
|
import org.thoughtcrime.securesms.MediaPreviewActivity;
|
||||||
@ -724,7 +724,7 @@ public class ConversationItem extends LinearLayout
|
|||||||
String publicKey = recipient.getAddress().toString();
|
String publicKey = recipient.getAddress().toString();
|
||||||
profilePictureView.setPublicKey(publicKey);
|
profilePictureView.setPublicKey(publicKey);
|
||||||
String displayName = recipient.getName();
|
String displayName = recipient.getName();
|
||||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID);
|
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID);
|
||||||
if (displayName == null && publicChat != null) {
|
if (displayName == null && publicChat != null) {
|
||||||
displayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.getId(), publicKey);
|
displayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.getId(), publicKey);
|
||||||
}
|
}
|
||||||
@ -911,7 +911,7 @@ public class ConversationItem extends LinearLayout
|
|||||||
profilePictureView.setVisibility(VISIBLE);
|
profilePictureView.setVisibility(VISIBLE);
|
||||||
int visibility = View.GONE;
|
int visibility = View.GONE;
|
||||||
|
|
||||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(messageRecord.getThreadId());
|
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(messageRecord.getThreadId());
|
||||||
if (publicChat != null) {
|
if (publicChat != null) {
|
||||||
boolean isModerator = OpenGroupAPI.isUserModerator(current.getRecipient().getAddress().toString(), publicChat.getChannel(), publicChat.getServer());
|
boolean isModerator = OpenGroupAPI.isUserModerator(current.getRecipient().getAddress().toString(), publicChat.getChannel(), publicChat.getServer());
|
||||||
visibility = isModerator ? View.VISIBLE : View.GONE;
|
visibility = isModerator ? View.VISIBLE : View.GONE;
|
||||||
|
@ -14,7 +14,7 @@ import androidx.annotation.ColorInt;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage;
|
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage;
|
||||||
import org.thoughtcrime.securesms.BindableConversationItem;
|
import org.thoughtcrime.securesms.BindableConversationItem;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt;
|
import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt;
|
||||||
|
@ -25,10 +25,9 @@ import org.session.libsession.utilities.Util;
|
|||||||
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer;
|
import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer;
|
||||||
import org.session.libsignal.service.loki.database.LokiOpenGroupDatabaseProtocol;
|
import org.session.libsignal.service.loki.LokiOpenGroupDatabaseProtocol;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@ -44,6 +43,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
|||||||
static final String GROUP_ID = "group_id";
|
static final String GROUP_ID = "group_id";
|
||||||
private static final String TITLE = "title";
|
private static final String TITLE = "title";
|
||||||
private static final String MEMBERS = "members";
|
private static final String MEMBERS = "members";
|
||||||
|
private static final String ZOMBIE_MEMBERS = "zombie_members";
|
||||||
private static final String AVATAR = "avatar";
|
private static final String AVATAR = "avatar";
|
||||||
private static final String AVATAR_ID = "avatar_id";
|
private static final String AVATAR_ID = "avatar_id";
|
||||||
private static final String AVATAR_KEY = "avatar_key";
|
private static final String AVATAR_KEY = "avatar_key";
|
||||||
@ -64,6 +64,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
|||||||
GROUP_ID + " TEXT, " +
|
GROUP_ID + " TEXT, " +
|
||||||
TITLE + " TEXT, " +
|
TITLE + " TEXT, " +
|
||||||
MEMBERS + " TEXT, " +
|
MEMBERS + " TEXT, " +
|
||||||
|
ZOMBIE_MEMBERS + " TEXT, " +
|
||||||
AVATAR + " BLOB, " +
|
AVATAR + " BLOB, " +
|
||||||
AVATAR_ID + " INTEGER, " +
|
AVATAR_ID + " INTEGER, " +
|
||||||
AVATAR_KEY + " BLOB, " +
|
AVATAR_KEY + " BLOB, " +
|
||||||
@ -81,7 +82,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
|||||||
};
|
};
|
||||||
|
|
||||||
private static final String[] GROUP_PROJECTION = {
|
private static final String[] GROUP_PROJECTION = {
|
||||||
GROUP_ID, TITLE, MEMBERS, AVATAR, AVATAR_ID, AVATAR_KEY, AVATAR_CONTENT_TYPE, AVATAR_RELAY, AVATAR_DIGEST,
|
GROUP_ID, TITLE, MEMBERS, ZOMBIE_MEMBERS, AVATAR, AVATAR_ID, AVATAR_KEY, AVATAR_CONTENT_TYPE, AVATAR_RELAY, AVATAR_DIGEST,
|
||||||
TIMESTAMP, ACTIVE, MMS, AVATAR_URL, ADMINS
|
TIMESTAMP, ACTIVE, MMS, AVATAR_URL, ADMINS
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -162,7 +163,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
|||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull List<Recipient> getGroupMembers(String groupId, boolean includeSelf) {
|
public @NonNull List<Recipient> getGroupMembers(String groupId, boolean includeSelf) {
|
||||||
List<Address> members = getCurrentMembers(groupId);
|
List<Address> members = getCurrentMembers(groupId, false);
|
||||||
List<Recipient> recipients = new LinkedList<>();
|
List<Recipient> recipients = new LinkedList<>();
|
||||||
|
|
||||||
for (Address member : members) {
|
for (Address member : members) {
|
||||||
@ -177,6 +178,17 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
|||||||
return recipients;
|
return recipients;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @NonNull List<Recipient> getGroupZombieMembers(String groupId) {
|
||||||
|
List<Address> members = getCurrentZombieMembers(groupId);
|
||||||
|
List<Recipient> recipients = new LinkedList<>();
|
||||||
|
|
||||||
|
for (Address member : members) {
|
||||||
|
recipients.add(Recipient.from(context, member, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
return recipients;
|
||||||
|
}
|
||||||
|
|
||||||
public long create(@NonNull String groupId, @Nullable String title, @NonNull List<Address> members,
|
public long create(@NonNull String groupId, @Nullable String title, @NonNull List<Address> members,
|
||||||
@Nullable SignalServiceAttachmentPointer avatar, @Nullable String relay, @Nullable List<Address> admins, @NonNull Long formationTimestamp)
|
@Nullable SignalServiceAttachmentPointer avatar, @Nullable String relay, @Nullable List<Address> admins, @NonNull Long formationTimestamp)
|
||||||
{
|
{
|
||||||
@ -300,6 +312,16 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateZombieMembers(String groupId, List<Address> members) {
|
||||||
|
Collections.sort(members);
|
||||||
|
|
||||||
|
ContentValues contents = new ContentValues();
|
||||||
|
contents.put(ZOMBIE_MEMBERS, Address.toSerializedList(members, ','));
|
||||||
|
contents.put(ACTIVE, 1);
|
||||||
|
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
|
||||||
|
new String[] {groupId});
|
||||||
|
}
|
||||||
|
|
||||||
public void updateAdmins(String groupId, List<Address> admins) {
|
public void updateAdmins(String groupId, List<Address> admins) {
|
||||||
Collections.sort(admins);
|
Collections.sort(admins);
|
||||||
|
|
||||||
@ -311,7 +333,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void removeMember(String groupId, Address source) {
|
public void removeMember(String groupId, Address source) {
|
||||||
List<Address> currentMembers = getCurrentMembers(groupId);
|
List<Address> currentMembers = getCurrentMembers(groupId, false);
|
||||||
currentMembers.remove(source);
|
currentMembers.remove(source);
|
||||||
|
|
||||||
ContentValues contents = new ContentValues();
|
ContentValues contents = new ContentValues();
|
||||||
@ -329,18 +351,22 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Address> getCurrentMembers(String groupId) {
|
private List<Address> getCurrentMembers(String groupId, boolean zombieMembers) {
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|
||||||
|
String membersColumn = MEMBERS;
|
||||||
|
if (zombieMembers) membersColumn = ZOMBIE_MEMBERS;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {MEMBERS},
|
cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {membersColumn},
|
||||||
GROUP_ID + " = ?",
|
GROUP_ID + " = ?",
|
||||||
new String[] {groupId},
|
new String[] {groupId},
|
||||||
null, null, null);
|
null, null, null);
|
||||||
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS));
|
String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(membersColumn));
|
||||||
return Address.fromSerializedList(serializedMembers, ',');
|
if (serializedMembers != null && !serializedMembers.isEmpty())
|
||||||
|
return Address.fromSerializedList(serializedMembers, ',');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LinkedList<>();
|
return new LinkedList<>();
|
||||||
@ -350,6 +376,10 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Address> getCurrentZombieMembers(String groupId) {
|
||||||
|
return getCurrentMembers(groupId, true);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isActive(String groupId) {
|
public boolean isActive(String groupId) {
|
||||||
Optional<GroupRecord> record = getGroup(groupId);
|
Optional<GroupRecord> record = getGroup(groupId);
|
||||||
return record.isPresent() && record.get().isActive();
|
return record.isPresent() && record.get().isActive();
|
||||||
|
@ -28,7 +28,6 @@ import com.annimon.stream.Collectors;
|
|||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
import com.google.android.mms.pdu_alt.NotificationInd;
|
import com.google.android.mms.pdu_alt.NotificationInd;
|
||||||
import com.google.android.mms.pdu_alt.PduHeaders;
|
import com.google.android.mms.pdu_alt.PduHeaders;
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
|
|
||||||
import net.sqlcipher.database.SQLiteDatabase;
|
import net.sqlcipher.database.SQLiteDatabase;
|
||||||
|
|
||||||
@ -60,7 +59,7 @@ 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.DatabaseAttachment;
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
|
||||||
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
||||||
import org.session.libsession.messaging.threads.Address;
|
import org.session.libsession.messaging.threads.Address;
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||||
|
@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.database
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.google.protobuf.ByteString
|
|
||||||
import org.session.libsession.messaging.StorageProtocol
|
import org.session.libsession.messaging.StorageProtocol
|
||||||
import org.session.libsession.messaging.jobs.AttachmentUploadJob
|
import org.session.libsession.messaging.jobs.AttachmentUploadJob
|
||||||
import org.session.libsession.messaging.jobs.Job
|
import org.session.libsession.messaging.jobs.Job
|
||||||
@ -13,18 +12,17 @@ import org.session.libsession.messaging.messages.signal.*
|
|||||||
import org.session.libsession.messaging.messages.signal.IncomingTextMessage
|
import org.session.libsession.messaging.messages.signal.IncomingTextMessage
|
||||||
import org.session.libsession.messaging.messages.visible.Attachment
|
import org.session.libsession.messaging.messages.visible.Attachment
|
||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroup
|
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||||
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.DatabaseAttachment
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||||
import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage
|
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
||||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
import org.session.libsession.messaging.threads.Address.Companion.fromSerialized
|
import org.session.libsession.messaging.threads.Address.Companion.fromSerialized
|
||||||
import org.session.libsession.messaging.threads.GroupRecord
|
import org.session.libsession.messaging.threads.GroupRecord
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||||
import org.session.libsession.messaging.utilities.UpdateMessageBuilder
|
import org.session.libsession.messaging.utilities.ClosedGroupUpdateMessageData
|
||||||
import org.session.libsession.messaging.utilities.UpdateMessageData
|
|
||||||
import org.session.libsession.utilities.GroupUtil
|
import org.session.libsession.utilities.GroupUtil
|
||||||
import org.session.libsession.utilities.IdentityKeyUtil
|
import org.session.libsession.utilities.IdentityKeyUtil
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
@ -34,7 +32,6 @@ import org.session.libsignal.libsignal.util.KeyHelper
|
|||||||
import org.session.libsignal.libsignal.util.guava.Optional
|
import org.session.libsignal.libsignal.util.guava.Optional
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer
|
import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceGroup
|
import org.session.libsignal.service.api.messages.SignalServiceGroup
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
|
||||||
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.jobs.RetrieveProfileAvatarJob
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
|
||||||
@ -44,7 +41,6 @@ import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
|||||||
import org.thoughtcrime.securesms.loki.utilities.get
|
import org.thoughtcrime.securesms.loki.utilities.get
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getString
|
import org.thoughtcrime.securesms.loki.utilities.getString
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority
|
import org.thoughtcrime.securesms.mms.PartAuthority
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol {
|
class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol {
|
||||||
override fun getUserPublicKey(): String? {
|
override fun getUserPublicKey(): String? {
|
||||||
@ -395,6 +391,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
|||||||
DatabaseFactory.getGroupDatabase(context).setActive(groupID, value)
|
DatabaseFactory.getGroupDatabase(context).setActive(groupID, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getZombieMember(groupID: String): Set<String> {
|
||||||
|
return DatabaseFactory.getGroupDatabase(context).getGroupZombieMembers(groupID).map { it.address.serialize() }.toHashSet()
|
||||||
|
}
|
||||||
|
|
||||||
override fun removeMember(groupID: String, member: Address) {
|
override fun removeMember(groupID: String, member: Address) {
|
||||||
DatabaseFactory.getGroupDatabase(context).removeMember(groupID, member)
|
DatabaseFactory.getGroupDatabase(context).removeMember(groupID, member)
|
||||||
}
|
}
|
||||||
@ -403,10 +403,14 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
|||||||
DatabaseFactory.getGroupDatabase(context).updateMembers(groupID, members)
|
DatabaseFactory.getGroupDatabase(context).updateMembers(groupID, members)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun updateZombieMembers(groupID: String, members: List<Address>) {
|
||||||
|
DatabaseFactory.getGroupDatabase(context).updateZombieMembers(groupID, members)
|
||||||
|
}
|
||||||
|
|
||||||
override fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection<String>, admins: Collection<String>, sentTimestamp: Long) {
|
override fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection<String>, admins: Collection<String>, sentTimestamp: Long) {
|
||||||
val group = SignalServiceGroup(type, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList())
|
val group = SignalServiceGroup(type, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList())
|
||||||
val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, sentTimestamp, "", Optional.of(group), 0, true)
|
val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, sentTimestamp, "", Optional.of(group), 0, true)
|
||||||
val updateData = UpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON()
|
val updateData = ClosedGroupUpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON()
|
||||||
val infoMessage = IncomingGroupMessage(m, groupID, updateData, true)
|
val infoMessage = IncomingGroupMessage(m, groupID, updateData, true)
|
||||||
val smsDB = DatabaseFactory.getSmsDatabase(context)
|
val smsDB = DatabaseFactory.getSmsDatabase(context)
|
||||||
smsDB.insertMessageInbox(infoMessage)
|
smsDB.insertMessageInbox(infoMessage)
|
||||||
@ -416,7 +420,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
|||||||
val userPublicKey = getUserPublicKey()
|
val userPublicKey = getUserPublicKey()
|
||||||
val recipient = Recipient.from(context, Address.fromSerialized(groupID), false)
|
val recipient = Recipient.from(context, Address.fromSerialized(groupID), false)
|
||||||
|
|
||||||
val updateData = UpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON() ?: ""
|
val updateData = ClosedGroupUpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON() ?: ""
|
||||||
val infoMessage = OutgoingGroupMediaMessage(recipient, updateData, groupID, null, sentTimestamp, 0, true, null, listOf(), listOf())
|
val infoMessage = OutgoingGroupMediaMessage(recipient, updateData, groupID, null, sentTimestamp, 0, true, null, listOf(), listOf())
|
||||||
val mmsDB = DatabaseFactory.getMmsDatabase(context)
|
val mmsDB = DatabaseFactory.getMmsDatabase(context)
|
||||||
val mmsSmsDB = DatabaseFactory.getMmsSmsDatabase(context)
|
val mmsSmsDB = DatabaseFactory.getMmsSmsDatabase(context)
|
||||||
|
@ -54,9 +54,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
private static final int lokiV20 = 41;
|
private static final int lokiV20 = 41;
|
||||||
private static final int lokiV21 = 42;
|
private static final int lokiV21 = 42;
|
||||||
private static final int lokiV22 = 43;
|
private static final int lokiV22 = 43;
|
||||||
|
private static final int lokiV23 = 44;
|
||||||
|
|
||||||
// Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
|
// Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
|
||||||
private static final int DATABASE_VERSION = lokiV22;
|
private static final int DATABASE_VERSION = lokiV23;
|
||||||
private static final String DATABASE_NAME = "signal.db";
|
private static final String DATABASE_NAME = "signal.db";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
@ -272,6 +273,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
"SendDeliveryReceiptJob");
|
"SendDeliveryReceiptJob");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < lokiV23) {
|
||||||
|
db.execSQL("ALTER TABLE groups ADD COLUMN zombie_members TEXT");
|
||||||
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
|
@ -23,7 +23,7 @@ import android.text.SpannableString;
|
|||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase.Status;
|
import org.thoughtcrime.securesms.database.SmsDatabase.Status;
|
||||||
import org.session.libsession.database.documents.IdentityKeyMismatch;
|
import org.session.libsession.database.documents.IdentityKeyMismatch;
|
||||||
|
@ -25,9 +25,9 @@ import android.text.style.StyleSpan;
|
|||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage;
|
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage;
|
||||||
import org.session.libsession.messaging.utilities.UpdateMessageBuilder;
|
import org.session.libsession.messaging.utilities.ClosedGroupUpdateMessageBuilder;
|
||||||
import org.session.libsession.messaging.utilities.UpdateMessageData;
|
import org.session.libsession.messaging.utilities.ClosedGroupUpdateMessageData;
|
||||||
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
import org.session.libsession.database.documents.IdentityKeyMismatch;
|
import org.session.libsession.database.documents.IdentityKeyMismatch;
|
||||||
@ -93,14 +93,14 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||||||
@Override
|
@Override
|
||||||
public SpannableString getDisplayBody(@NonNull Context context) {
|
public SpannableString getDisplayBody(@NonNull Context context) {
|
||||||
if(isGroupUpdateMessage()) {
|
if(isGroupUpdateMessage()) {
|
||||||
UpdateMessageData updateMessageData = UpdateMessageData.Companion.fromJSON(getBody());
|
ClosedGroupUpdateMessageData updateMessageData = ClosedGroupUpdateMessageData.Companion.fromJSON(getBody());
|
||||||
return new SpannableString(UpdateMessageBuilder.INSTANCE.buildGroupUpdateMessage(context, updateMessageData, getIndividualRecipient().getAddress().serialize(), isOutgoing()));
|
return new SpannableString(ClosedGroupUpdateMessageBuilder.INSTANCE.buildGroupUpdateMessage(context, updateMessageData, getIndividualRecipient().getAddress().serialize(), isOutgoing()));
|
||||||
} else if (isExpirationTimerUpdate()) {
|
} else if (isExpirationTimerUpdate()) {
|
||||||
int seconds = (int) (getExpiresIn() / 1000);
|
int seconds = (int) (getExpiresIn() / 1000);
|
||||||
return new SpannableString(UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, getIndividualRecipient().getAddress().serialize(), isOutgoing()));
|
return new SpannableString(ClosedGroupUpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, getIndividualRecipient().getAddress().serialize(), isOutgoing()));
|
||||||
} else if (isDataExtraction()) {
|
} else if (isDataExtraction()) {
|
||||||
if (isScreenshotExtraction()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT, getIndividualRecipient().getAddress().serialize())));
|
if (isScreenshotExtraction()) return new SpannableString((ClosedGroupUpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT, getIndividualRecipient().getAddress().serialize())));
|
||||||
else if (isMediaSavedExtraction()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED, getIndividualRecipient().getAddress().serialize())));
|
else if (isMediaSavedExtraction()) return new SpannableString((ClosedGroupUpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED, getIndividualRecipient().getAddress().serialize())));
|
||||||
}
|
}
|
||||||
// TODO below lines are left here for compatibility with older group update messages, it can be deleted later on
|
// TODO below lines are left here for compatibility with older group update messages, it can be deleted later on
|
||||||
else if (isGroupUpdate() && isOutgoing()) {
|
else if (isGroupUpdate() && isOutgoing()) {
|
||||||
|
@ -5,7 +5,7 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||||
|
|
||||||
import org.session.libsession.database.documents.IdentityKeyMismatch;
|
import org.session.libsession.database.documents.IdentityKeyMismatch;
|
||||||
|
@ -28,8 +28,6 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||||
import org.session.libsession.messaging.utilities.UpdateMessageBuilder;
|
|
||||||
import org.session.libsession.messaging.utilities.UpdateMessageData;
|
|
||||||
import org.session.libsession.utilities.ExpirationUtil;
|
import org.session.libsession.utilities.ExpirationUtil;
|
||||||
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
|
@ -4,6 +4,8 @@ import android.graphics.Bitmap;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.session.libsession.messaging.jobs.Data;
|
import org.session.libsession.messaging.jobs.Data;
|
||||||
|
import org.session.libsession.utilities.DownloadUtilities;
|
||||||
|
import org.session.libsignal.service.api.crypto.AttachmentCipherInputStream;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.session.libsession.messaging.threads.GroupRecord;
|
import org.session.libsession.messaging.threads.GroupRecord;
|
||||||
@ -22,6 +24,7 @@ import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer
|
|||||||
import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException;
|
import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@ -91,9 +94,20 @@ public class AvatarDownloadJob extends BaseJob implements InjectableType {
|
|||||||
attachment = File.createTempFile("avatar", "tmp", context.getCacheDir());
|
attachment = File.createTempFile("avatar", "tmp", context.getCacheDir());
|
||||||
attachment.deleteOnExit();
|
attachment.deleteOnExit();
|
||||||
|
|
||||||
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, Optional.of(0), Optional.absent(), 0, 0, digest, fileName, false, Optional.absent(), url);
|
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, Optional.of(0), Optional.absent(), 0, 0, digest, fileName, false, Optional.absent(), url);
|
||||||
InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, MAX_AVATAR_SIZE);
|
|
||||||
Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key, 0, digest), 500, 500);
|
if (pointer.getUrl().isEmpty()) throw new InvalidMessageException("Missing attachment URL.");
|
||||||
|
DownloadUtilities.downloadFile(attachment, pointer.getUrl(), MAX_AVATAR_SIZE, null);
|
||||||
|
|
||||||
|
// Assume we're retrieving an attachment for an open group server if the digest is not set
|
||||||
|
InputStream inputStream;
|
||||||
|
if (!pointer.getDigest().isPresent()) {
|
||||||
|
inputStream = new FileInputStream(attachment);
|
||||||
|
} else {
|
||||||
|
inputStream = AttachmentCipherInputStream.createForAttachment(attachment, pointer.getSize().or(0), pointer.getKey(), pointer.getDigest().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key, 0, digest), 500, 500);
|
||||||
|
|
||||||
database.updateProfilePicture(groupId, avatar);
|
database.updateProfilePicture(groupId, avatar);
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
|
@ -10,9 +10,11 @@ import org.session.libsession.messaging.avatars.AvatarHelper;
|
|||||||
import org.session.libsession.messaging.jobs.Data;
|
import org.session.libsession.messaging.jobs.Data;
|
||||||
import org.session.libsession.messaging.threads.Address;
|
import org.session.libsession.messaging.threads.Address;
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||||
|
import org.session.libsession.utilities.DownloadUtilities;
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
import org.session.libsession.utilities.Util;
|
import org.session.libsession.utilities.Util;
|
||||||
import org.session.libsignal.service.api.SignalServiceMessageReceiver;
|
import org.session.libsignal.service.api.SignalServiceMessageReceiver;
|
||||||
|
import org.session.libsignal.service.api.crypto.ProfileCipherInputStream;
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
import org.session.libsignal.utilities.logging.Log;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
@ -22,6 +24,7 @@ import org.thoughtcrime.securesms.jobmanager.Job;
|
|||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -102,7 +105,8 @@ public class RetrieveProfileAvatarJob extends BaseJob implements InjectableType
|
|||||||
File downloadDestination = File.createTempFile("avatar", ".jpg", context.getCacheDir());
|
File downloadDestination = File.createTempFile("avatar", ".jpg", context.getCacheDir());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
InputStream avatarStream = receiver.retrieveProfileAvatar(profileAvatar, downloadDestination, profileKey, MAX_PROFILE_SIZE_BYTES);
|
DownloadUtilities.downloadFile(downloadDestination, profileAvatar, MAX_PROFILE_SIZE_BYTES, null);
|
||||||
|
InputStream avatarStream = new ProfileCipherInputStream(new FileInputStream(downloadDestination), profileKey);
|
||||||
File decryptDestination = File.createTempFile("avatar", ".jpg", context.getCacheDir());
|
File decryptDestination = File.createTempFile("avatar", ".jpg", context.getCacheDir());
|
||||||
|
|
||||||
Util.copy(avatarStream, new FileOutputStream(decryptDestination));
|
Util.copy(avatarStream, new FileOutputStream(decryptDestination));
|
||||||
|
@ -25,7 +25,7 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil.OpenGraph;
|
|||||||
|
|
||||||
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.UriAttachment;
|
import org.session.libsession.messaging.sending_receiving.attachments.UriAttachment;
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
import org.session.libsession.utilities.concurrent.SignalExecutors;
|
import org.session.libsession.utilities.concurrent.SignalExecutors;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
@ -13,9 +13,8 @@ import org.session.libsession.utilities.Debouncer;
|
|||||||
import org.session.libsession.utilities.Util;
|
import org.session.libsession.utilities.Util;
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,8 +37,14 @@ import java.io.IOException
|
|||||||
|
|
||||||
class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||||
private val originalMembers = HashSet<String>()
|
private val originalMembers = HashSet<String>()
|
||||||
|
private val zombies = HashSet<String>()
|
||||||
private val members = HashSet<String>()
|
private val members = HashSet<String>()
|
||||||
|
private val allMembers: Set<String>
|
||||||
|
get() {
|
||||||
|
return members + zombies
|
||||||
|
}
|
||||||
private var hasNameChanged = false
|
private var hasNameChanged = false
|
||||||
|
private var isSelfAdmin = false
|
||||||
private var isLoading = false
|
private var isLoading = false
|
||||||
set(newValue) { field = newValue; invalidateOptionsMenu() }
|
set(newValue) { field = newValue; invalidateOptionsMenu() }
|
||||||
|
|
||||||
@ -54,7 +60,10 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val memberListAdapter by lazy {
|
private val memberListAdapter by lazy {
|
||||||
EditClosedGroupMembersAdapter(this, GlideApp.with(this), this::onMemberClick)
|
if (isSelfAdmin)
|
||||||
|
EditClosedGroupMembersAdapter(this, GlideApp.with(this), isSelfAdmin, this::onMemberClick)
|
||||||
|
else
|
||||||
|
EditClosedGroupMembersAdapter(this, GlideApp.with(this), isSelfAdmin)
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var mainContentContainer: LinearLayout
|
private lateinit var mainContentContainer: LinearLayout
|
||||||
@ -81,7 +90,10 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
ThemeUtil.getThemedDrawableResId(this, R.attr.actionModeCloseDrawable))
|
ThemeUtil.getThemedDrawableResId(this, R.attr.actionModeCloseDrawable))
|
||||||
|
|
||||||
groupID = intent.getStringExtra(groupIDKey)!!
|
groupID = intent.getStringExtra(groupIDKey)!!
|
||||||
originalName = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().title
|
val groupInfo = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get()
|
||||||
|
originalName = groupInfo.title
|
||||||
|
isSelfAdmin = groupInfo.admins.any{ it.serialize() == TextSecurePreferences.getLocalNumber(this) }
|
||||||
|
|
||||||
name = originalName
|
name = originalName
|
||||||
|
|
||||||
mainContentContainer = findViewById(R.id.mainContentContainer)
|
mainContentContainer = findViewById(R.id.mainContentContainer)
|
||||||
@ -116,31 +128,35 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LoaderManager.getInstance(this).initLoader(loaderID, null, object : LoaderManager.LoaderCallbacks<List<String>> {
|
LoaderManager.getInstance(this).initLoader(loaderID, null, object : LoaderManager.LoaderCallbacks<GroupMembers> {
|
||||||
|
|
||||||
override fun onCreateLoader(id: Int, bundle: Bundle?): Loader<List<String>> {
|
override fun onCreateLoader(id: Int, bundle: Bundle?): Loader<GroupMembers> {
|
||||||
return EditClosedGroupLoader(this@EditClosedGroupActivity, groupID)
|
return EditClosedGroupLoader(this@EditClosedGroupActivity, groupID)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoadFinished(loader: Loader<List<String>>, members: List<String>) {
|
override fun onLoadFinished(loader: Loader<GroupMembers>, groupMembers: GroupMembers) {
|
||||||
// We no longer need any subsequent loading events
|
// We no longer need any subsequent loading events
|
||||||
// (they will occur on every activity resume).
|
// (they will occur on every activity resume).
|
||||||
LoaderManager.getInstance(this@EditClosedGroupActivity).destroyLoader(loaderID)
|
LoaderManager.getInstance(this@EditClosedGroupActivity).destroyLoader(loaderID)
|
||||||
|
|
||||||
|
members.clear()
|
||||||
|
members.addAll(groupMembers.members.toHashSet())
|
||||||
|
zombies.clear()
|
||||||
|
zombies.addAll(groupMembers.zombieMembers.toHashSet())
|
||||||
originalMembers.clear()
|
originalMembers.clear()
|
||||||
originalMembers.addAll(members.toHashSet())
|
originalMembers.addAll(members + zombies)
|
||||||
updateMembers(originalMembers)
|
updateMembers()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoaderReset(loader: Loader<List<String>>) {
|
override fun onLoaderReset(loader: Loader<GroupMembers>) {
|
||||||
updateMembers(setOf())
|
updateMembers()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
menuInflater.inflate(R.menu.menu_edit_closed_group, menu)
|
menuInflater.inflate(R.menu.menu_edit_closed_group, menu)
|
||||||
return members.isNotEmpty() && !isLoading
|
return allMembers.isNotEmpty() && !isLoading
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
@ -153,8 +169,8 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
if (data == null || data.extras == null || !data.hasExtra(SelectContactsActivity.selectedContactsKey)) return
|
if (data == null || data.extras == null || !data.hasExtra(SelectContactsActivity.selectedContactsKey)) return
|
||||||
|
|
||||||
val selectedContacts = data.extras!!.getStringArray(SelectContactsActivity.selectedContactsKey)!!.toSet()
|
val selectedContacts = data.extras!!.getStringArray(SelectContactsActivity.selectedContactsKey)!!.toSet()
|
||||||
val changedMembers = members + selectedContacts
|
members.addAll(selectedContacts)
|
||||||
updateMembers(changedMembers)
|
updateMembers()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,17 +189,12 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateMembers(members: Set<String>) {
|
private fun updateMembers() {
|
||||||
this.members.clear()
|
memberListAdapter.setMembers(allMembers)
|
||||||
this.members.addAll(members)
|
memberListAdapter.setZombieMembers(zombies)
|
||||||
memberListAdapter.setMembers(members)
|
|
||||||
|
|
||||||
val admins = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().admins.map { it.toString() }.toMutableSet()
|
mainContentContainer.visibility = if (allMembers.isEmpty()) View.GONE else View.VISIBLE
|
||||||
admins.remove(TextSecurePreferences.getLocalNumber(this))
|
emptyStateContainer.visibility = if (allMembers.isEmpty()) View.VISIBLE else View.GONE
|
||||||
memberListAdapter.setLockedMembers(admins)
|
|
||||||
|
|
||||||
mainContentContainer.visibility = if (members.isEmpty()) View.GONE else View.VISIBLE
|
|
||||||
emptyStateContainer.visibility = if (members.isEmpty()) View.VISIBLE else View.GONE
|
|
||||||
|
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
@ -200,8 +211,9 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
private fun onMemberClick(member: String) {
|
private fun onMemberClick(member: String) {
|
||||||
val bottomSheet = ClosedGroupEditingOptionsBottomSheet()
|
val bottomSheet = ClosedGroupEditingOptionsBottomSheet()
|
||||||
bottomSheet.onRemoveTapped = {
|
bottomSheet.onRemoveTapped = {
|
||||||
val changedMembers = members - member
|
if (zombies.contains(member)) zombies.remove(member)
|
||||||
updateMembers(changedMembers)
|
else members.remove(member)
|
||||||
|
updateMembers()
|
||||||
bottomSheet.dismiss()
|
bottomSheet.dismiss()
|
||||||
}
|
}
|
||||||
bottomSheet.show(supportFragmentManager, "GroupEditingOptionsBottomSheet")
|
bottomSheet.show(supportFragmentManager, "GroupEditingOptionsBottomSheet")
|
||||||
@ -209,7 +221,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
|
|
||||||
private fun onAddMembersClick() {
|
private fun onAddMembersClick() {
|
||||||
val intent = Intent(this@EditClosedGroupActivity, SelectContactsActivity::class.java)
|
val intent = Intent(this@EditClosedGroupActivity, SelectContactsActivity::class.java)
|
||||||
intent.putExtra(SelectContactsActivity.usersToExcludeKey, members.toTypedArray())
|
intent.putExtra(SelectContactsActivity.usersToExcludeKey, allMembers.toTypedArray())
|
||||||
intent.putExtra(SelectContactsActivity.emptyStateTextKey, "No contacts to add")
|
intent.putExtra(SelectContactsActivity.emptyStateTextKey, "No contacts to add")
|
||||||
startActivityForResult(intent, addUsersRequestCode)
|
startActivityForResult(intent, addUsersRequestCode)
|
||||||
}
|
}
|
||||||
@ -229,7 +241,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun commitChanges() {
|
private fun commitChanges() {
|
||||||
val hasMemberListChanges = (members != originalMembers)
|
val hasMemberListChanges = (allMembers != originalMembers)
|
||||||
|
|
||||||
if (!hasNameChanged && !hasMemberListChanges) {
|
if (!hasNameChanged && !hasMemberListChanges) {
|
||||||
return finish()
|
return finish()
|
||||||
@ -237,15 +249,13 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
|
|
||||||
val name = if (hasNameChanged) this.name else originalName
|
val name = if (hasNameChanged) this.name else originalName
|
||||||
|
|
||||||
val members = this.members.map {
|
val members = this.allMembers.map {
|
||||||
Recipient.from(this, Address.fromSerialized(it), false)
|
Recipient.from(this, Address.fromSerialized(it), false)
|
||||||
}.toSet()
|
}.toSet()
|
||||||
val originalMembers = this.originalMembers.map {
|
val originalMembers = this.originalMembers.map {
|
||||||
Recipient.from(this, Address.fromSerialized(it), false)
|
Recipient.from(this, Address.fromSerialized(it), false)
|
||||||
}.toSet()
|
}.toSet()
|
||||||
|
|
||||||
val admins = members.toSet() //TODO For now, consider all the users to be admins.
|
|
||||||
|
|
||||||
var isClosedGroup: Boolean
|
var isClosedGroup: Boolean
|
||||||
var groupPublicKey: String?
|
var groupPublicKey: String?
|
||||||
try {
|
try {
|
||||||
@ -303,4 +313,6 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class GroupMembers(val members: List<String>, val zombieMembers: List<String>) { }
|
||||||
}
|
}
|
@ -4,12 +4,19 @@ import android.content.Context
|
|||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.util.AsyncLoader
|
import org.thoughtcrime.securesms.util.AsyncLoader
|
||||||
|
|
||||||
class EditClosedGroupLoader(context: Context, val groupID: String) : AsyncLoader<List<String>>(context) {
|
class EditClosedGroupLoader(context: Context, val groupID: String) : AsyncLoader<EditClosedGroupActivity.GroupMembers>(context) {
|
||||||
|
|
||||||
override fun loadInBackground(): List<String> {
|
override fun loadInBackground(): EditClosedGroupActivity.GroupMembers {
|
||||||
val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, true)
|
val groupDatabase = DatabaseFactory.getGroupDatabase(context)
|
||||||
return members.map {
|
val members = groupDatabase.getGroupMembers(groupID, true)
|
||||||
it.address.toString()
|
val zombieMembers = groupDatabase.getGroupZombieMembers(groupID)
|
||||||
}
|
return EditClosedGroupActivity.GroupMembers(
|
||||||
|
members.map {
|
||||||
|
it.address.toString()
|
||||||
|
},
|
||||||
|
zombieMembers.map {
|
||||||
|
it.address.toString()
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,15 +7,17 @@ import org.session.libsession.messaging.threads.Address
|
|||||||
import org.thoughtcrime.securesms.loki.views.UserView
|
import org.thoughtcrime.securesms.loki.views.UserView
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
|
||||||
class EditClosedGroupMembersAdapter(
|
class EditClosedGroupMembersAdapter(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val glide: GlideRequests,
|
private val glide: GlideRequests,
|
||||||
|
private val admin: Boolean,
|
||||||
private val memberClickListener: ((String) -> Unit)? = null
|
private val memberClickListener: ((String) -> Unit)? = null
|
||||||
) : RecyclerView.Adapter<EditClosedGroupMembersAdapter.ViewHolder>() {
|
) : RecyclerView.Adapter<EditClosedGroupMembersAdapter.ViewHolder>() {
|
||||||
|
|
||||||
private val members = ArrayList<String>()
|
private val members = ArrayList<String>()
|
||||||
private val lockedMembers = HashSet<String>()
|
private val zombieMembers = ArrayList<String>()
|
||||||
|
|
||||||
fun setMembers(members: Collection<String>) {
|
fun setMembers(members: Collection<String>) {
|
||||||
this.members.clear()
|
this.members.clear()
|
||||||
@ -23,9 +25,9 @@ class EditClosedGroupMembersAdapter(
|
|||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setLockedMembers(members: Collection<String>) {
|
fun setZombieMembers(members: Collection<String>) {
|
||||||
this.lockedMembers.clear()
|
this.zombieMembers.clear()
|
||||||
this.lockedMembers.addAll(members)
|
this.zombieMembers.addAll(members)
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,15 +41,20 @@ class EditClosedGroupMembersAdapter(
|
|||||||
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
|
||||||
val member = members[position]
|
val member = members[position]
|
||||||
|
|
||||||
val lockedMember = lockedMembers.contains(member)
|
val unlocked = admin && member != TextSecurePreferences.getLocalNumber(context)
|
||||||
|
|
||||||
viewHolder.view.bind(Recipient.from(
|
viewHolder.view.bind(Recipient.from(
|
||||||
context,
|
context,
|
||||||
Address.fromSerialized(member), false),
|
Address.fromSerialized(member), false),
|
||||||
glide,
|
glide,
|
||||||
if (lockedMember) UserView.ActionIndicator.None else UserView.ActionIndicator.Menu)
|
if (unlocked) UserView.ActionIndicator.Menu else UserView.ActionIndicator.None)
|
||||||
|
|
||||||
if (!lockedMember) {
|
if (zombieMembers.contains(member))
|
||||||
|
viewHolder.view.alpha = 0.5F
|
||||||
|
else
|
||||||
|
viewHolder.view.alpha = 1F
|
||||||
|
|
||||||
|
if (unlocked) {
|
||||||
viewHolder.view.setOnClickListener { this.memberClickListener?.invoke(member) }
|
viewHolder.view.setOnClickListener { this.memberClickListener?.invoke(member) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,10 +30,10 @@ import org.greenrobot.eventbus.EventBus
|
|||||||
import org.greenrobot.eventbus.Subscribe
|
import org.greenrobot.eventbus.Subscribe
|
||||||
import org.greenrobot.eventbus.ThreadMode
|
import org.greenrobot.eventbus.ThreadMode
|
||||||
import org.session.libsession.messaging.jobs.JobQueue
|
import org.session.libsession.messaging.jobs.JobQueue
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI
|
import org.session.libsession.messaging.mentions.MentionsManager
|
||||||
|
import org.session.libsession.messaging.open_groups.OpenGroupAPI
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
import org.session.libsession.utilities.*
|
import org.session.libsession.utilities.*
|
||||||
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager
|
|
||||||
import org.session.libsignal.service.loki.utilities.toHexString
|
import org.session.libsignal.service.loki.utilities.toHexString
|
||||||
import org.session.libsignal.utilities.ThreadUtils
|
import org.session.libsignal.utilities.ThreadUtils
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
@ -139,7 +139,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
|||||||
val userDB = DatabaseFactory.getLokiUserDatabase(this)
|
val userDB = DatabaseFactory.getLokiUserDatabase(this)
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
|
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
|
||||||
if (userPublicKey != null) {
|
if (userPublicKey != null) {
|
||||||
MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB)
|
MentionsManager.configureIfNeeded(userPublicKey, userDB)
|
||||||
application.publicChatManager.startPollersIfNeeded()
|
application.publicChatManager.startPollersIfNeeded()
|
||||||
JobQueue.shared.resumePendingJobs()
|
JobQueue.shared.resumePendingJobs()
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import kotlinx.coroutines.launch
|
|||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.libsignal.util.KeyHelper
|
import org.session.libsignal.libsignal.util.KeyHelper
|
||||||
import org.session.libsignal.service.loki.crypto.MnemonicCodec
|
import org.session.libsignal.service.loki.MnemonicCodec
|
||||||
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
||||||
import org.session.libsignal.utilities.Hex
|
import org.session.libsignal.utilities.Hex
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
@ -45,7 +45,7 @@ class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDel
|
|||||||
private var restoreJob: Job? = null
|
private var restoreJob: Job? = null
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
if (restoreJob?.isActive == true) return // don't allow going back with pending job
|
if (restoreJob?.isActive == true) return // Don't allow going back with a pending job
|
||||||
super.onBackPressed()
|
super.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,14 +53,12 @@ class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDel
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setUpActionBarSessionLogo()
|
setUpActionBarSessionLogo()
|
||||||
// Set the registration sync variables
|
|
||||||
TextSecurePreferences.apply {
|
TextSecurePreferences.apply {
|
||||||
setHasViewedSeed(this@LinkDeviceActivity, true)
|
setHasViewedSeed(this@LinkDeviceActivity, true)
|
||||||
setConfigurationMessageSynced(this@LinkDeviceActivity, false)
|
setConfigurationMessageSynced(this@LinkDeviceActivity, false)
|
||||||
setRestorationTime(this@LinkDeviceActivity, System.currentTimeMillis())
|
setRestorationTime(this@LinkDeviceActivity, System.currentTimeMillis())
|
||||||
setLastProfileUpdateTime(this@LinkDeviceActivity, 0)
|
setLastProfileUpdateTime(this@LinkDeviceActivity, 0)
|
||||||
}
|
}
|
||||||
// registration variables are synced
|
|
||||||
setContentView(R.layout.activity_link_device)
|
setContentView(R.layout.activity_link_device)
|
||||||
viewPager.adapter = adapter
|
viewPager.adapter = adapter
|
||||||
tabLayout.setupWithViewPager(viewPager)
|
tabLayout.setupWithViewPager(viewPager)
|
||||||
|
@ -19,12 +19,12 @@ import android.widget.Toast
|
|||||||
import androidx.annotation.ColorRes
|
import androidx.annotation.ColorRes
|
||||||
import kotlinx.android.synthetic.main.activity_path.*
|
import kotlinx.android.synthetic.main.activity_path.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.loki.utilities.*
|
import org.thoughtcrime.securesms.loki.utilities.*
|
||||||
import org.thoughtcrime.securesms.loki.views.GlowViewUtilities
|
import org.thoughtcrime.securesms.loki.views.GlowViewUtilities
|
||||||
import org.thoughtcrime.securesms.loki.views.PathDotView
|
import org.thoughtcrime.securesms.loki.views.PathDotView
|
||||||
import org.session.libsignal.service.loki.api.Snode
|
import org.session.libsignal.service.loki.Snode
|
||||||
import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI
|
|
||||||
|
|
||||||
class PathActivity : PassphraseRequiredActionBarActivity() {
|
class PathActivity : PassphraseRequiredActionBarActivity() {
|
||||||
private val broadcastReceivers = mutableListOf<BroadcastReceiver>()
|
private val broadcastReceivers = mutableListOf<BroadcastReceiver>()
|
||||||
|
@ -15,7 +15,7 @@ import kotlinx.android.synthetic.main.activity_recovery_phrase_restore.*
|
|||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.libsignal.util.KeyHelper
|
import org.session.libsignal.libsignal.util.KeyHelper
|
||||||
import org.session.libsignal.service.loki.crypto.MnemonicCodec
|
import org.session.libsignal.service.loki.MnemonicCodec
|
||||||
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
||||||
import org.session.libsignal.utilities.Hex
|
import org.session.libsignal.utilities.Hex
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
@ -30,14 +30,12 @@ class RecoveryPhraseRestoreActivity : BaseActionBarActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setUpActionBarSessionLogo()
|
setUpActionBarSessionLogo()
|
||||||
// Set the registration sync variables
|
|
||||||
TextSecurePreferences.apply {
|
TextSecurePreferences.apply {
|
||||||
setHasViewedSeed(this@RecoveryPhraseRestoreActivity, true)
|
setHasViewedSeed(this@RecoveryPhraseRestoreActivity, true)
|
||||||
setConfigurationMessageSynced(this@RecoveryPhraseRestoreActivity, false)
|
setConfigurationMessageSynced(this@RecoveryPhraseRestoreActivity, false)
|
||||||
setRestorationTime(this@RecoveryPhraseRestoreActivity, System.currentTimeMillis())
|
setRestorationTime(this@RecoveryPhraseRestoreActivity, System.currentTimeMillis())
|
||||||
setLastProfileUpdateTime(this@RecoveryPhraseRestoreActivity, System.currentTimeMillis())
|
setLastProfileUpdateTime(this@RecoveryPhraseRestoreActivity, System.currentTimeMillis())
|
||||||
}
|
}
|
||||||
// registration variables are synced
|
|
||||||
setContentView(R.layout.activity_recovery_phrase_restore)
|
setContentView(R.layout.activity_recovery_phrase_restore)
|
||||||
mnemonicEditText.imeOptions = mnemonicEditText.imeOptions or 16777216 // Always use incognito keyboard
|
mnemonicEditText.imeOptions = mnemonicEditText.imeOptions or 16777216 // Always use incognito keyboard
|
||||||
restoreButton.setOnClickListener { restore() }
|
restoreButton.setOnClickListener { restore() }
|
||||||
|
@ -39,14 +39,12 @@ class RegisterActivity : BaseActionBarActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_register)
|
setContentView(R.layout.activity_register)
|
||||||
setUpActionBarSessionLogo()
|
setUpActionBarSessionLogo()
|
||||||
// Set the registration sync variables
|
|
||||||
TextSecurePreferences.apply {
|
TextSecurePreferences.apply {
|
||||||
setHasViewedSeed(this@RegisterActivity, false)
|
setHasViewedSeed(this@RegisterActivity, false)
|
||||||
setConfigurationMessageSynced(this@RegisterActivity, true)
|
setConfigurationMessageSynced(this@RegisterActivity, true)
|
||||||
setRestorationTime(this@RegisterActivity, 0)
|
setRestorationTime(this@RegisterActivity, 0)
|
||||||
setLastProfileUpdateTime(this@RegisterActivity, System.currentTimeMillis())
|
setLastProfileUpdateTime(this@RegisterActivity, System.currentTimeMillis())
|
||||||
}
|
}
|
||||||
// registration variables are synced
|
|
||||||
registerButton.setOnClickListener { register() }
|
registerButton.setOnClickListener { register() }
|
||||||
copyButton.setOnClickListener { copyPublicKey() }
|
copyButton.setOnClickListener { copyPublicKey() }
|
||||||
val termsExplanation = SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy")
|
val termsExplanation = SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy")
|
||||||
|
@ -16,7 +16,7 @@ import org.session.libsession.utilities.IdentityKeyUtil
|
|||||||
import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities
|
import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.service.loki.crypto.MnemonicCodec
|
import org.session.libsignal.service.loki.MnemonicCodec
|
||||||
import org.session.libsignal.service.loki.utilities.hexEncodedPrivateKey
|
import org.session.libsignal.service.loki.utilities.hexEncodedPrivateKey
|
||||||
|
|
||||||
class SeedActivity : BaseActionBarActivity() {
|
class SeedActivity : BaseActionBarActivity() {
|
||||||
|
@ -28,13 +28,13 @@ import nl.komponents.kovenant.functional.bind
|
|||||||
import nl.komponents.kovenant.task
|
import nl.komponents.kovenant.task
|
||||||
import nl.komponents.kovenant.ui.alwaysUi
|
import nl.komponents.kovenant.ui.alwaysUi
|
||||||
import org.session.libsession.messaging.avatars.AvatarHelper
|
import org.session.libsession.messaging.avatars.AvatarHelper
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI
|
import org.session.libsession.messaging.file_server.FileServerAPI
|
||||||
|
import org.session.libsession.messaging.open_groups.OpenGroupAPI
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol
|
import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsession.utilities.preferences.ProfileKeyUtil
|
import org.session.libsession.utilities.preferences.ProfileKeyUtil
|
||||||
import org.session.libsignal.service.api.util.StreamDetails
|
import org.session.libsignal.service.api.util.StreamDetails
|
||||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.avatar.AvatarSelection
|
import org.thoughtcrime.securesms.avatar.AvatarSelection
|
||||||
|
@ -8,13 +8,14 @@ import nl.komponents.kovenant.Promise
|
|||||||
import nl.komponents.kovenant.all
|
import nl.komponents.kovenant.all
|
||||||
import nl.komponents.kovenant.functional.map
|
import nl.komponents.kovenant.functional.map
|
||||||
import org.session.libsession.messaging.jobs.MessageReceiveJob
|
import org.session.libsession.messaging.jobs.MessageReceiveJob
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroup
|
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||||
|
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPoller
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller
|
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller
|
||||||
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.service.loki.api.SnodeAPI
|
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
|
import java.io.IOException
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Worker(context, params) {
|
class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||||
@ -70,7 +71,7 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor
|
|||||||
|
|
||||||
// Private chats
|
// Private chats
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
||||||
val privateChatsPromise = SnodeAPI.shared.getMessages(userPublicKey).map { envelopes ->
|
val privateChatsPromise = SnodeAPI.getMessages(userPublicKey).map { envelopes ->
|
||||||
envelopes.map { envelope ->
|
envelopes.map { envelope ->
|
||||||
MessageReceiveJob(envelope.toByteArray(), false).executeAsync()
|
MessageReceiveJob(envelope.toByteArray(), false).executeAsync()
|
||||||
}
|
}
|
||||||
@ -78,7 +79,7 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor
|
|||||||
promises.addAll(privateChatsPromise.get())
|
promises.addAll(privateChatsPromise.get())
|
||||||
|
|
||||||
// Closed groups
|
// Closed groups
|
||||||
promises.addAll(ApplicationContext.getInstance(context).closedGroupPoller.pollOnce())
|
promises.addAll(ClosedGroupPoller().pollOnce())
|
||||||
|
|
||||||
// Open Groups
|
// Open Groups
|
||||||
val openGroups = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().map { (_,chat)->
|
val openGroups = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().map { (_,chat)->
|
||||||
|
@ -3,12 +3,12 @@ package org.thoughtcrime.securesms.loki.api
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import nl.komponents.kovenant.functional.map
|
import nl.komponents.kovenant.functional.map
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
|
import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI
|
||||||
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
import org.session.libsignal.utilities.JsonUtil
|
import org.session.libsignal.utilities.JsonUtil
|
||||||
import org.session.libsignal.service.loki.api.PushNotificationAPI
|
|
||||||
import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI
|
|
||||||
import org.session.libsignal.service.loki.utilities.retryIfNeeded
|
import org.session.libsignal.service.loki.utilities.retryIfNeeded
|
||||||
|
|
||||||
object LokiPushNotificationManager {
|
object LokiPushNotificationManager {
|
||||||
@ -16,10 +16,10 @@ object LokiPushNotificationManager {
|
|||||||
private val tokenExpirationInterval = 12 * 60 * 60 * 1000
|
private val tokenExpirationInterval = 12 * 60 * 60 * 1000
|
||||||
|
|
||||||
private val server by lazy {
|
private val server by lazy {
|
||||||
PushNotificationAPI.shared.server
|
PushNotificationAPI.server
|
||||||
}
|
}
|
||||||
private val pnServerPublicKey by lazy {
|
private val pnServerPublicKey by lazy {
|
||||||
PushNotificationAPI.pnServerPublicKey
|
PushNotificationAPI.serverPublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ClosedGroupOperation {
|
enum class ClosedGroupOperation {
|
||||||
|
@ -2,9 +2,9 @@ package org.thoughtcrime.securesms.loki.api
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
|
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegates the [OpenGroupUtilities.updateGroupInfo] call to the work manager.
|
* Delegates the [OpenGroupUtilities.updateGroupInfo] call to the work manager.
|
||||||
@ -40,7 +40,7 @@ class PublicChatInfoUpdateWorker(val context: Context, params: WorkerParameters)
|
|||||||
val serverUrl = inputData.getString(DATA_KEY_SERVER_URL)!!
|
val serverUrl = inputData.getString(DATA_KEY_SERVER_URL)!!
|
||||||
val channel = inputData.getLong(DATA_KEY_CHANNEL, -1)
|
val channel = inputData.getLong(DATA_KEY_CHANNEL, -1)
|
||||||
|
|
||||||
val publicChatId = PublicChat.getId(channel, serverUrl)
|
val publicChatId = OpenGroup.getId(channel, serverUrl)
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
Log.v(TAG, "Updating open group info for $publicChatId.")
|
Log.v(TAG, "Updating open group info for $publicChatId.")
|
||||||
|
@ -5,14 +5,13 @@ import android.database.ContentObserver
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroup
|
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI
|
import org.session.libsession.messaging.open_groups.OpenGroupAPI
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroupInfo
|
import org.session.libsession.messaging.open_groups.OpenGroupInfo
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller
|
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsession.utilities.Util
|
import org.session.libsession.utilities.Util
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders
|
import org.thoughtcrime.securesms.database.DatabaseContentProviders
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.groups.GroupManager
|
import org.thoughtcrime.securesms.groups.GroupManager
|
||||||
@ -75,7 +74,7 @@ class PublicChatManager(private val context: Context) {
|
|||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public fun addChat(server: String, channel: Long, info: OpenGroupInfo): OpenGroup {
|
public fun addChat(server: String, channel: Long, info: OpenGroupInfo): OpenGroup {
|
||||||
val chat = PublicChat(channel, server, info.displayName, true)
|
val chat = OpenGroup(channel, server, info.displayName, true)
|
||||||
var threadID = GroupManager.getOpenGroupThreadID(chat.id, context)
|
var threadID = GroupManager.getOpenGroupThreadID(chat.id, context)
|
||||||
var profilePicture: Bitmap? = null
|
var profilePicture: Bitmap? = null
|
||||||
// Create the group if we don't have one
|
// Create the group if we don't have one
|
||||||
@ -96,12 +95,12 @@ class PublicChatManager(private val context: Context) {
|
|||||||
// Start polling
|
// Start polling
|
||||||
Util.runOnMain { startPollersIfNeeded() }
|
Util.runOnMain { startPollersIfNeeded() }
|
||||||
|
|
||||||
return OpenGroup.from(chat)
|
return chat
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun removeChat(server: String, channel: Long) {
|
public fun removeChat(server: String, channel: Long) {
|
||||||
val threadDB = DatabaseFactory.getThreadDatabase(context)
|
val threadDB = DatabaseFactory.getThreadDatabase(context)
|
||||||
val groupId = PublicChat.getId(channel, server)
|
val groupId = OpenGroup.getId(channel, server)
|
||||||
val threadId = GroupManager.getOpenGroupThreadID(groupId, context)
|
val threadId = GroupManager.getOpenGroupThreadID(groupId, context)
|
||||||
val groupAddress = threadDB.getRecipientForThreadId(threadId)!!.address.serialize()
|
val groupAddress = threadDB.getRecipientForThreadId(threadId)!!.address.serialize()
|
||||||
GroupManager.deleteGroup(groupAddress, context)
|
GroupManager.deleteGroup(groupAddress, context)
|
||||||
@ -110,7 +109,7 @@ class PublicChatManager(private val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshChatsAndPollers() {
|
private fun refreshChatsAndPollers() {
|
||||||
val storage = MessagingConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val chatsInDB = storage.getAllOpenGroups()
|
val chatsInDB = storage.getAllOpenGroups()
|
||||||
val removedChatThreadIds = chats.keys.filter { !chatsInDB.keys.contains(it) }
|
val removedChatThreadIds = chats.keys.filter { !chatsInDB.keys.contains(it) }
|
||||||
removedChatThreadIds.forEach { pollers.remove(it)?.stop() }
|
removedChatThreadIds.forEach { pollers.remove(it)?.stop() }
|
||||||
|
@ -6,8 +6,8 @@ import com.google.firebase.messaging.FirebaseMessagingService
|
|||||||
import com.google.firebase.messaging.RemoteMessage
|
import com.google.firebase.messaging.RemoteMessage
|
||||||
import org.session.libsession.messaging.jobs.JobQueue
|
import org.session.libsession.messaging.jobs.JobQueue
|
||||||
import org.session.libsession.messaging.jobs.MessageReceiveJob
|
import org.session.libsession.messaging.jobs.MessageReceiveJob
|
||||||
|
import org.session.libsession.messaging.utilities.MessageWrapper
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.service.loki.api.MessageWrapper
|
|
||||||
import org.session.libsignal.utilities.Base64
|
import org.session.libsignal.utilities.Base64
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
||||||
|
@ -7,8 +7,8 @@ import org.session.libsession.utilities.TextSecurePreferences
|
|||||||
import org.session.libsignal.libsignal.ecc.DjbECPrivateKey
|
import org.session.libsignal.libsignal.ecc.DjbECPrivateKey
|
||||||
import org.session.libsignal.libsignal.ecc.DjbECPublicKey
|
import org.session.libsignal.libsignal.ecc.DjbECPublicKey
|
||||||
import org.session.libsignal.libsignal.ecc.ECKeyPair
|
import org.session.libsignal.libsignal.ecc.ECKeyPair
|
||||||
import org.session.libsignal.service.loki.api.Snode
|
import org.session.libsignal.service.loki.Snode
|
||||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
|
import org.session.libsignal.service.loki.LokiAPIDatabaseProtocol
|
||||||
import org.session.libsignal.service.loki.utilities.PublicKeyValidation
|
import org.session.libsignal.service.loki.utilities.PublicKeyValidation
|
||||||
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
||||||
import org.session.libsignal.service.loki.utilities.toHexString
|
import org.session.libsignal.service.loki.utilities.toHexString
|
||||||
|
@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.loki.database
|
|||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import org.session.libsession.messaging.threads.Address
|
|
||||||
import org.thoughtcrime.securesms.database.Database
|
import org.thoughtcrime.securesms.database.Database
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||||
@ -10,7 +9,7 @@ import org.thoughtcrime.securesms.loki.utilities.get
|
|||||||
import org.thoughtcrime.securesms.loki.utilities.getInt
|
import org.thoughtcrime.securesms.loki.utilities.getInt
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getString
|
import org.thoughtcrime.securesms.loki.utilities.getString
|
||||||
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
|
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
|
||||||
import org.session.libsignal.service.loki.database.LokiMessageDatabaseProtocol
|
import org.session.libsignal.service.loki.LokiMessageDatabaseProtocol
|
||||||
|
|
||||||
class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol {
|
class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol {
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.loki.database
|
|||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
|
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Database
|
import org.thoughtcrime.securesms.database.Database
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
@ -12,13 +13,11 @@ import org.thoughtcrime.securesms.loki.utilities.*
|
|||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat
|
|
||||||
|
|
||||||
import org.session.libsignal.utilities.JsonUtil
|
import org.session.libsignal.utilities.JsonUtil
|
||||||
import org.session.libsignal.service.loki.database.LokiThreadDatabaseProtocol
|
|
||||||
import org.session.libsignal.service.loki.utilities.PublicKeyValidation
|
import org.session.libsignal.service.loki.utilities.PublicKeyValidation
|
||||||
|
|
||||||
class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiThreadDatabaseProtocol {
|
class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val sessionResetTable = "loki_thread_session_reset_database"
|
private val sessionResetTable = "loki_thread_session_reset_database"
|
||||||
@ -31,22 +30,22 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
|||||||
@JvmStatic val createPublicChatTableCommand = "CREATE TABLE $publicChatTable ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);"
|
@JvmStatic val createPublicChatTableCommand = "CREATE TABLE $publicChatTable ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getThreadID(hexEncodedPublicKey: String): Long {
|
fun getThreadID(hexEncodedPublicKey: String): Long {
|
||||||
val address = Address.fromSerialized(hexEncodedPublicKey)
|
val address = Address.fromSerialized(hexEncodedPublicKey)
|
||||||
val recipient = Recipient.from(context, address, false)
|
val recipient = Recipient.from(context, address, false)
|
||||||
return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient)
|
return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAllPublicChats(): Map<Long, PublicChat> {
|
fun getAllPublicChats(): Map<Long, OpenGroup> {
|
||||||
val database = databaseHelper.readableDatabase
|
val database = databaseHelper.readableDatabase
|
||||||
var cursor: Cursor? = null
|
var cursor: Cursor? = null
|
||||||
val result = mutableMapOf<Long, PublicChat>()
|
val result = mutableMapOf<Long, OpenGroup>()
|
||||||
try {
|
try {
|
||||||
cursor = database.rawQuery("select * from $publicChatTable", null)
|
cursor = database.rawQuery("select * from $publicChatTable", null)
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
val threadID = cursor.getLong(threadID)
|
val threadID = cursor.getLong(threadID)
|
||||||
val string = cursor.getString(publicChat)
|
val string = cursor.getString(publicChat)
|
||||||
val publicChat = PublicChat.fromJSON(string)
|
val publicChat = OpenGroup.fromJSON(string)
|
||||||
if (publicChat != null) { result[threadID] = publicChat }
|
if (publicChat != null) { result[threadID] = publicChat }
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -61,16 +60,16 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
|||||||
return getAllPublicChats().values.fold(setOf()) { set, chat -> set.plus(chat.server) }
|
return getAllPublicChats().values.fold(setOf()) { set, chat -> set.plus(chat.server) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPublicChat(threadID: Long): PublicChat? {
|
fun getPublicChat(threadID: Long): OpenGroup? {
|
||||||
if (threadID < 0) { return null }
|
if (threadID < 0) { return null }
|
||||||
val database = databaseHelper.readableDatabase
|
val database = databaseHelper.readableDatabase
|
||||||
return database.get(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor ->
|
return database.get(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor ->
|
||||||
val publicChatAsJSON = cursor.getString(publicChat)
|
val publicChatAsJSON = cursor.getString(publicChat)
|
||||||
PublicChat.fromJSON(publicChatAsJSON)
|
OpenGroup.fromJSON(publicChatAsJSON)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setPublicChat(publicChat: PublicChat, threadID: Long) {
|
fun setPublicChat(publicChat: OpenGroup, threadID: Long) {
|
||||||
if (threadID < 0) { return }
|
if (threadID < 0) { return }
|
||||||
val database = databaseHelper.writableDatabase
|
val database = databaseHelper.writableDatabase
|
||||||
val contentValues = ContentValues(2)
|
val contentValues = ContentValues(2)
|
||||||
@ -79,7 +78,7 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
|||||||
database.insertOrUpdate(publicChatTable, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() ))
|
database.insertOrUpdate(publicChatTable, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() ))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removePublicChat(threadID: Long) {
|
fun removePublicChat(threadID: Long) {
|
||||||
databaseHelper.writableDatabase.delete(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() ))
|
databaseHelper.writableDatabase.delete(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() ))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,7 +11,7 @@ import org.thoughtcrime.securesms.loki.utilities.get
|
|||||||
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
|
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol
|
import org.session.libsignal.service.loki.LokiUserDatabaseProtocol
|
||||||
|
|
||||||
class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiUserDatabaseProtocol {
|
class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiUserDatabaseProtocol {
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import kotlinx.android.synthetic.main.dialog_seed.view.*
|
|||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.utilities.IdentityKeyUtil
|
import org.session.libsession.utilities.IdentityKeyUtil
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities
|
import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities
|
||||||
import org.session.libsignal.service.loki.crypto.MnemonicCodec
|
import org.session.libsignal.service.loki.MnemonicCodec
|
||||||
import org.session.libsignal.service.loki.utilities.hexEncodedPrivateKey
|
import org.session.libsignal.service.loki.utilities.hexEncodedPrivateKey
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ import org.session.libsession.utilities.TextSecurePreferences
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
object ClosedGroupsProtocolV2 {
|
object ClosedGroupsProtocolV2 {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun handleMessage(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
fun handleMessage(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
||||||
if (!isValid(context, closedGroupUpdate, senderPublicKey, sentTimestamp)) { return }
|
if (!isValid(context, closedGroupUpdate, senderPublicKey, sentTimestamp)) { return }
|
||||||
@ -40,7 +41,6 @@ object ClosedGroupsProtocolV2 {
|
|||||||
DataMessage.ClosedGroupControlMessage.Type.MEMBERS_ADDED -> handleClosedGroupMembersAdded(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
DataMessage.ClosedGroupControlMessage.Type.MEMBERS_ADDED -> handleClosedGroupMembersAdded(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
||||||
DataMessage.ClosedGroupControlMessage.Type.NAME_CHANGE -> handleClosedGroupNameChange(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
DataMessage.ClosedGroupControlMessage.Type.NAME_CHANGE -> handleClosedGroupNameChange(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
||||||
DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT -> handleClosedGroupMemberLeft(context, sentTimestamp, groupPublicKey, senderPublicKey)
|
DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT -> handleClosedGroupMemberLeft(context, sentTimestamp, groupPublicKey, senderPublicKey)
|
||||||
DataMessage.ClosedGroupControlMessage.Type.UPDATE -> handleClosedGroupUpdate(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR -> handleGroupEncryptionKeyPair(context, closedGroupUpdate, groupPublicKey, senderPublicKey)
|
DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR -> handleGroupEncryptionKeyPair(context, closedGroupUpdate, groupPublicKey, senderPublicKey)
|
||||||
else -> {
|
else -> {
|
||||||
Log.d("Loki","Can't handle closed group update of unknown type: ${closedGroupUpdate.type}")
|
Log.d("Loki","Can't handle closed group update of unknown type: ${closedGroupUpdate.type}")
|
||||||
@ -64,7 +64,6 @@ object ClosedGroupsProtocolV2 {
|
|||||||
DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT -> {
|
DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT -> {
|
||||||
senderPublicKey.isNotEmpty()
|
senderPublicKey.isNotEmpty()
|
||||||
}
|
}
|
||||||
DataMessage.ClosedGroupControlMessage.Type.UPDATE,
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.NAME_CHANGE -> {
|
DataMessage.ClosedGroupControlMessage.Type.NAME_CHANGE -> {
|
||||||
!closedGroupUpdate.name.isNullOrEmpty()
|
!closedGroupUpdate.name.isNullOrEmpty()
|
||||||
}
|
}
|
||||||
|
@ -2,24 +2,13 @@ package org.thoughtcrime.securesms.loki.protocol
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.google.protobuf.ByteString
|
import com.google.protobuf.ByteString
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.messages.Destination
|
import org.session.libsession.messaging.messages.Destination
|
||||||
import org.session.libsession.messaging.messages.control.ConfigurationMessage
|
import org.session.libsession.messaging.messages.control.ConfigurationMessage
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsession.utilities.preferences.ProfileKeyUtil
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage
|
|
||||||
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
|
||||||
import org.session.libsignal.utilities.Base64
|
|
||||||
import org.session.libsignal.utilities.Hex
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.ContactUtilities
|
import org.thoughtcrime.securesms.loki.utilities.ContactUtilities
|
||||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
|
||||||
|
|
||||||
object MultiDeviceProtocol {
|
object MultiDeviceProtocol {
|
||||||
|
|
||||||
@ -51,80 +40,4 @@ object MultiDeviceProtocol {
|
|||||||
TextSecurePreferences.setLastConfigurationSyncTime(context, System.currentTimeMillis())
|
TextSecurePreferences.setLastConfigurationSyncTime(context, System.currentTimeMillis())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove this after we migrate to new message receiving pipeline
|
|
||||||
@JvmStatic
|
|
||||||
fun handleConfigurationMessage(context: Context, content: SignalServiceProtos.Content, senderPublicKey: String, timestamp: Long) {
|
|
||||||
synchronized(this) {
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return
|
|
||||||
if (TextSecurePreferences.getConfigurationMessageSynced(context) && !TextSecurePreferences.shouldUpdateProfile(context, timestamp)) return
|
|
||||||
if (senderPublicKey != userPublicKey) return
|
|
||||||
TextSecurePreferences.setConfigurationMessageSynced(context, true)
|
|
||||||
TextSecurePreferences.setLastProfileUpdateTime(context, timestamp)
|
|
||||||
|
|
||||||
val configurationMessage = ConfigurationMessage.fromProto(content) ?: return
|
|
||||||
|
|
||||||
val storage = MessagingConfiguration.shared.storage
|
|
||||||
val allClosedGroupPublicKeys = storage.getAllClosedGroupPublicKeys()
|
|
||||||
|
|
||||||
val threadDatabase = DatabaseFactory.getThreadDatabase(context)
|
|
||||||
val recipientDatabase = DatabaseFactory.getRecipientDatabase(context)
|
|
||||||
|
|
||||||
val ourRecipient = Recipient.from(context, Address.fromSerialized(userPublicKey),false)
|
|
||||||
|
|
||||||
for (closedGroup in configurationMessage.closedGroups) {
|
|
||||||
if (allClosedGroupPublicKeys.contains(closedGroup.publicKey)) continue
|
|
||||||
|
|
||||||
val closedGroupUpdate = DataMessage.ClosedGroupControlMessage.newBuilder()
|
|
||||||
closedGroupUpdate.type = DataMessage.ClosedGroupControlMessage.Type.NEW
|
|
||||||
closedGroupUpdate.publicKey = ByteString.copyFrom(Hex.fromStringCondensed(closedGroup.publicKey))
|
|
||||||
closedGroupUpdate.name = closedGroup.name
|
|
||||||
val encryptionKeyPair = SignalServiceProtos.KeyPair.newBuilder()
|
|
||||||
encryptionKeyPair.publicKey = ByteString.copyFrom(closedGroup.encryptionKeyPair!!.publicKey.serialize().removing05PrefixIfNeeded())
|
|
||||||
encryptionKeyPair.privateKey = ByteString.copyFrom(closedGroup.encryptionKeyPair!!.privateKey.serialize())
|
|
||||||
closedGroupUpdate.encryptionKeyPair = encryptionKeyPair.build()
|
|
||||||
closedGroupUpdate.addAllMembers(closedGroup.members.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) })
|
|
||||||
closedGroupUpdate.addAllAdmins(closedGroup.admins.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) })
|
|
||||||
|
|
||||||
ClosedGroupsProtocolV2.handleNewClosedGroup(context, closedGroupUpdate.build(), userPublicKey, timestamp)
|
|
||||||
}
|
|
||||||
val allOpenGroups = storage.getAllOpenGroups().map { it.value.server }
|
|
||||||
for (openGroup in configurationMessage.openGroups) {
|
|
||||||
if (allOpenGroups.contains(openGroup)) continue
|
|
||||||
OpenGroupUtilities.addGroup(context, openGroup, 1)
|
|
||||||
}
|
|
||||||
if (configurationMessage.displayName.isNotEmpty()) {
|
|
||||||
TextSecurePreferences.setProfileName(context, configurationMessage.displayName)
|
|
||||||
recipientDatabase.setProfileName(ourRecipient, configurationMessage.displayName)
|
|
||||||
}
|
|
||||||
if (configurationMessage.profileKey.isNotEmpty()) {
|
|
||||||
val profileKey = Base64.encodeBytes(configurationMessage.profileKey)
|
|
||||||
ProfileKeyUtil.setEncodedProfileKey(context, profileKey)
|
|
||||||
recipientDatabase.setProfileKey(ourRecipient, configurationMessage.profileKey)
|
|
||||||
if (!configurationMessage.profilePicture.isNullOrEmpty() && TextSecurePreferences.getProfilePictureURL(context) != configurationMessage.profilePicture) {
|
|
||||||
TextSecurePreferences.setProfilePictureURL(context, configurationMessage.profilePicture)
|
|
||||||
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(ourRecipient, configurationMessage.profilePicture))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (contact in configurationMessage.contacts) {
|
|
||||||
val address = Address.fromSerialized(contact.publicKey)
|
|
||||||
val recipient = Recipient.from(context, address, true)
|
|
||||||
if (!contact.profilePicture.isNullOrEmpty()) {
|
|
||||||
recipientDatabase.setProfileAvatar(recipient, contact.profilePicture)
|
|
||||||
}
|
|
||||||
if (contact.profileKey?.isNotEmpty() == true) {
|
|
||||||
recipientDatabase.setProfileKey(recipient, contact.profileKey)
|
|
||||||
}
|
|
||||||
if (contact.name.isNotEmpty()) {
|
|
||||||
recipientDatabase.setProfileName(recipient, contact.name)
|
|
||||||
}
|
|
||||||
recipientDatabase.setProfileSharing(recipient, true)
|
|
||||||
recipientDatabase.setRegistered(recipient, Recipient.RegisteredState.REGISTERED)
|
|
||||||
// create Thread if needed
|
|
||||||
threadDatabase.getOrCreateThreadIdFor(recipient)
|
|
||||||
}
|
|
||||||
if (configurationMessage.contacts.isNotEmpty()) {
|
|
||||||
threadDatabase.notifyUpdatedFromConfig()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -3,8 +3,9 @@ package org.thoughtcrime.securesms.loki.utilities
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
|
import org.session.libsignal.service.loki.Broadcaster
|
||||||
|
|
||||||
class Broadcaster(private val context: Context) : org.session.libsignal.service.loki.utilities.Broadcaster {
|
class Broadcaster(private val context: Context) : Broadcaster {
|
||||||
|
|
||||||
override fun broadcast(event: String) {
|
override fun broadcast(event: String) {
|
||||||
val intent = Intent(event)
|
val intent = Intent(event)
|
||||||
|
@ -7,7 +7,7 @@ import android.content.IntentFilter
|
|||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
import com.opencsv.CSVReader
|
import com.opencsv.CSVReader
|
||||||
import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.session.libsignal.utilities.ThreadUtils
|
import org.session.libsignal.utilities.ThreadUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package org.thoughtcrime.securesms.loki.utilities
|
package org.thoughtcrime.securesms.loki.utilities
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import org.session.libsession.messaging.mentions.MentionsManager
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager
|
|
||||||
|
|
||||||
object MentionManagerUtilities {
|
object MentionManagerUtilities {
|
||||||
|
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
package org.thoughtcrime.securesms.loki.utilities
|
package org.thoughtcrime.securesms.loki.utilities
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import org.session.libsignal.service.loki.crypto.MnemonicCodec
|
|
||||||
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
|
|
||||||
object MnemonicUtilities {
|
object MnemonicUtilities {
|
||||||
|
|
||||||
|
@ -3,12 +3,11 @@ package org.thoughtcrime.securesms.loki.utilities
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroup
|
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI
|
import org.session.libsession.messaging.open_groups.OpenGroupAPI
|
||||||
import org.session.libsession.utilities.GroupUtil
|
import org.session.libsession.utilities.GroupUtil
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsession.utilities.preferences.ProfileKeyUtil
|
import org.session.libsession.utilities.preferences.ProfileKeyUtil
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.groups.GroupManager
|
import org.thoughtcrime.securesms.groups.GroupManager
|
||||||
@ -23,10 +22,10 @@ object OpenGroupUtilities {
|
|||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun addGroup(context: Context, url: String, channel: Long): OpenGroup {
|
fun addGroup(context: Context, url: String, channel: Long): OpenGroup {
|
||||||
// Check for an existing group.
|
// Check for an existing group.
|
||||||
val groupID = PublicChat.getId(channel, url)
|
val groupID = OpenGroup.getId(channel, url)
|
||||||
val threadID = GroupManager.getOpenGroupThreadID(groupID, context)
|
val threadID = GroupManager.getOpenGroupThreadID(groupID, context)
|
||||||
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
||||||
if (openGroup != null) { return OpenGroup.from(openGroup) }
|
if (openGroup != null) { return openGroup }
|
||||||
|
|
||||||
// Add the new group.
|
// Add the new group.
|
||||||
val application = ApplicationContext.getInstance(context)
|
val application = ApplicationContext.getInstance(context)
|
||||||
@ -56,7 +55,7 @@ object OpenGroupUtilities {
|
|||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun updateGroupInfo(context: Context, url: String, channel: Long) {
|
fun updateGroupInfo(context: Context, url: String, channel: Long) {
|
||||||
// Check if open group has a related DB record.
|
// Check if open group has a related DB record.
|
||||||
val groupId = GroupUtil.getEncodedOpenGroupID(PublicChat.getId(channel, url).toByteArray())
|
val groupId = GroupUtil.getEncodedOpenGroupID(OpenGroup.getId(channel, url).toByteArray())
|
||||||
if (!DatabaseFactory.getGroupDatabase(context).hasGroup(groupId)) {
|
if (!DatabaseFactory.getGroupDatabase(context).hasGroup(groupId)) {
|
||||||
throw IllegalStateException("Attempt to update open group info for non-existent DB record: $groupId")
|
throw IllegalStateException("Attempt to update open group info for non-existent DB record: $groupId")
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import android.widget.ListView
|
|||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
import org.thoughtcrime.securesms.loki.utilities.toPx
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.session.libsignal.service.loki.utilities.mentions.Mention
|
import org.session.libsignal.service.loki.Mention
|
||||||
|
|
||||||
class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : ListView(context, attrs, defStyleAttr) {
|
class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : ListView(context, attrs, defStyleAttr) {
|
||||||
private var mentionCandidates = listOf<Mention>()
|
private var mentionCandidates = listOf<Mention>()
|
||||||
|
@ -8,8 +8,8 @@ import android.view.ViewGroup
|
|||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import kotlinx.android.synthetic.main.view_mention_candidate.view.*
|
import kotlinx.android.synthetic.main.view_mention_candidate.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroupAPI
|
import org.session.libsession.messaging.open_groups.OpenGroupAPI
|
||||||
import org.session.libsignal.service.loki.utilities.mentions.Mention
|
import org.session.libsignal.service.loki.Mention
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
|
|
||||||
class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) {
|
class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) {
|
||||||
|
@ -11,9 +11,9 @@ import android.util.AttributeSet
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
import org.thoughtcrime.securesms.loki.utilities.toPx
|
||||||
import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI
|
|
||||||
|
|
||||||
class PathStatusView : View {
|
class PathStatusView : View {
|
||||||
private val broadcastReceivers = mutableListOf<BroadcastReceiver>()
|
private val broadcastReceivers = mutableListOf<BroadcastReceiver>()
|
||||||
|
@ -11,10 +11,10 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
|
|||||||
import kotlinx.android.synthetic.main.view_profile_picture.view.*
|
import kotlinx.android.synthetic.main.view_profile_picture.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.avatars.ProfileContactPhoto
|
import org.session.libsession.messaging.avatars.ProfileContactPhoto
|
||||||
|
import org.session.libsession.messaging.mentions.MentionsManager
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.loki.utilities.AvatarPlaceholderGenerator
|
import org.thoughtcrime.securesms.loki.utilities.AvatarPlaceholderGenerator
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
@ -146,13 +146,13 @@ class ProfilePictureView : RelativeLayout {
|
|||||||
private fun setProfilePictureIfNeeded(imageView: ImageView, publicKey: String, displayName: String?, @DimenRes sizeResId: Int) {
|
private fun setProfilePictureIfNeeded(imageView: ImageView, publicKey: String, displayName: String?, @DimenRes sizeResId: Int) {
|
||||||
if (publicKey.isNotEmpty()) {
|
if (publicKey.isNotEmpty()) {
|
||||||
val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false)
|
val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false)
|
||||||
if (imagesCached.contains(recipient.profileAvatar.orEmpty())) return
|
if (imagesCached.contains(publicKey)) return
|
||||||
val signalProfilePicture = recipient.contactPhoto
|
val signalProfilePicture = recipient.contactPhoto
|
||||||
if (signalProfilePicture != null && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "0"
|
if (signalProfilePicture != null && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "0"
|
||||||
&& (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "") {
|
&& (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "") {
|
||||||
glide.clear(imageView)
|
glide.clear(imageView)
|
||||||
glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.AUTOMATIC).circleCrop().into(imageView)
|
glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.AUTOMATIC).circleCrop().into(imageView)
|
||||||
imagesCached.add(recipient.profileAvatar.orEmpty())
|
imagesCached.add(publicKey)
|
||||||
} else {
|
} else {
|
||||||
val sizeInPX = resources.getDimensionPixelSize(sizeResId)
|
val sizeInPX = resources.getDimensionPixelSize(sizeResId)
|
||||||
glide.clear(imageView)
|
glide.clear(imageView)
|
||||||
@ -162,7 +162,7 @@ class ProfilePictureView : RelativeLayout {
|
|||||||
publicKey,
|
publicKey,
|
||||||
displayName
|
displayName
|
||||||
)).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView)
|
)).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView)
|
||||||
imagesCached.add(recipient.profileAvatar.orEmpty())
|
imagesCached.add(publicKey)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
imageView.setImageDrawable(null)
|
imageView.setImageDrawable(null)
|
||||||
|
@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.mms;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI;
|
import org.session.libsession.messaging.file_server.FileServerAPI;
|
||||||
|
|
||||||
public class PushMediaConstraints extends MediaConstraints {
|
public class PushMediaConstraints extends MediaConstraints {
|
||||||
|
|
||||||
|
@ -98,13 +98,6 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@drawable/home_activity_gradient" />
|
android:background="@drawable/home_activity_gradient" />
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.loki.views.NewConversationButtonSetView
|
|
||||||
android:id="@+id/newConversationButtonSet"
|
|
||||||
android:layout_width="276dp"
|
|
||||||
android:layout_height="236dp"
|
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:layout_alignParentBottom="true" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/emptyStateContainer"
|
android:id="@+id/emptyStateContainer"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -131,6 +124,13 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.loki.views.NewConversationButtonSetView
|
||||||
|
android:id="@+id/newConversationButtonSet"
|
||||||
|
android:layout_width="276dp"
|
||||||
|
android:layout_height="236dp"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_alignParentBottom="true" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -1,5 +1,6 @@
|
|||||||
package org.session.libsession.database
|
package org.session.libsession.database
|
||||||
|
|
||||||
|
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.*
|
import org.session.libsession.messaging.sending_receiving.attachments.*
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
import org.session.libsession.messaging.utilities.DotNetAPI
|
import org.session.libsession.messaging.utilities.DotNetAPI
|
||||||
@ -30,7 +31,6 @@ interface MessageDataProvider {
|
|||||||
fun updateAttachmentAfterUploadSucceeded(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult)
|
fun updateAttachmentAfterUploadSucceeded(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult)
|
||||||
fun updateAttachmentAfterUploadFailed(attachmentId: Long)
|
fun updateAttachmentAfterUploadFailed(attachmentId: Long)
|
||||||
|
|
||||||
// Quotes
|
|
||||||
fun getMessageForQuote(timestamp: Long, author: Address): Pair<Long, Boolean>?
|
fun getMessageForQuote(timestamp: Long, author: Address): Pair<Long, Boolean>?
|
||||||
fun getAttachmentsAndLinkPreviewFor(mmsId: Long): List<Attachment>
|
fun getAttachmentsAndLinkPreviewFor(mmsId: Long): List<Attachment>
|
||||||
fun getMessageBodyFor(timestamp: Long, author: String): String
|
fun getMessageBodyFor(timestamp: Long, author: String): String
|
||||||
@ -38,4 +38,5 @@ interface MessageDataProvider {
|
|||||||
fun getAttachmentIDsFor(messageID: Long): List<Long>
|
fun getAttachmentIDsFor(messageID: Long): List<Long>
|
||||||
fun getLinkPreviewAttachmentIDFor(messageID: Long): Long?
|
fun getLinkPreviewAttachmentIDFor(messageID: Long): Long?
|
||||||
|
|
||||||
|
fun getOpenGroup(threadID: Long): OpenGroup?
|
||||||
}
|
}
|
@ -1,25 +0,0 @@
|
|||||||
package org.session.libsession.messaging
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import org.session.libsession.database.MessageDataProvider
|
|
||||||
import org.session.libsignal.service.loki.api.crypto.SessionProtocol
|
|
||||||
|
|
||||||
class MessagingConfiguration(
|
|
||||||
val context: Context,
|
|
||||||
val storage: StorageProtocol,
|
|
||||||
val messageDataProvider: MessageDataProvider,
|
|
||||||
val sessionProtocol: SessionProtocol)
|
|
||||||
{
|
|
||||||
companion object {
|
|
||||||
lateinit var shared: MessagingConfiguration
|
|
||||||
|
|
||||||
fun configure(context: Context,
|
|
||||||
storage: StorageProtocol,
|
|
||||||
messageDataProvider: MessageDataProvider,
|
|
||||||
sessionProtocol: SessionProtocol
|
|
||||||
) {
|
|
||||||
if (Companion::shared.isInitialized) { return }
|
|
||||||
shared = MessagingConfiguration(context, storage, messageDataProvider, sessionProtocol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,26 @@
|
|||||||
|
package org.session.libsession.messaging
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import org.session.libsession.database.MessageDataProvider
|
||||||
|
import org.session.libsignal.service.loki.api.crypto.SessionProtocol
|
||||||
|
|
||||||
|
class MessagingModuleConfiguration(
|
||||||
|
val context: Context,
|
||||||
|
val storage: StorageProtocol,
|
||||||
|
val messageDataProvider: MessageDataProvider,
|
||||||
|
val sessionProtocol: SessionProtocol)
|
||||||
|
{
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
lateinit var shared: MessagingModuleConfiguration
|
||||||
|
|
||||||
|
fun configure(context: Context,
|
||||||
|
storage: StorageProtocol,
|
||||||
|
messageDataProvider: MessageDataProvider,
|
||||||
|
sessionProtocol: SessionProtocol
|
||||||
|
) {
|
||||||
|
if (Companion::shared.isInitialized) { return }
|
||||||
|
shared = MessagingModuleConfiguration(context, storage, messageDataProvider, sessionProtocol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,11 +9,11 @@ import org.session.libsession.messaging.jobs.MessageSendJob
|
|||||||
import org.session.libsession.messaging.messages.control.ConfigurationMessage
|
import org.session.libsession.messaging.messages.control.ConfigurationMessage
|
||||||
import org.session.libsession.messaging.messages.visible.Attachment
|
import org.session.libsession.messaging.messages.visible.Attachment
|
||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||||
import org.session.libsession.messaging.opengroups.OpenGroup
|
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||||
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.dataextraction.DataExtractionNotificationInfoMessage
|
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
||||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
import org.session.libsession.messaging.threads.GroupRecord
|
import org.session.libsession.messaging.threads.GroupRecord
|
||||||
@ -21,7 +21,6 @@ import org.session.libsession.messaging.threads.recipients.Recipient.RecipientSe
|
|||||||
import org.session.libsignal.libsignal.ecc.ECKeyPair
|
import org.session.libsignal.libsignal.ecc.ECKeyPair
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer
|
import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceGroup
|
import org.session.libsignal.service.api.messages.SignalServiceGroup
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
|
||||||
|
|
||||||
interface StorageProtocol {
|
interface StorageProtocol {
|
||||||
|
|
||||||
@ -109,8 +108,10 @@ interface StorageProtocol {
|
|||||||
fun createGroup(groupID: String, title: String?, members: List<Address>, avatar: SignalServiceAttachmentPointer?, relay: String?, admins: List<Address>, formationTimestamp: Long)
|
fun createGroup(groupID: String, title: String?, members: List<Address>, avatar: SignalServiceAttachmentPointer?, relay: String?, admins: List<Address>, formationTimestamp: Long)
|
||||||
fun isGroupActive(groupPublicKey: String): Boolean
|
fun isGroupActive(groupPublicKey: String): Boolean
|
||||||
fun setActive(groupID: String, value: Boolean)
|
fun setActive(groupID: String, value: Boolean)
|
||||||
|
fun getZombieMember(groupID: String): Set<String>
|
||||||
fun removeMember(groupID: String, member: Address)
|
fun removeMember(groupID: String, member: Address)
|
||||||
fun updateMembers(groupID: String, members: List<Address>)
|
fun updateMembers(groupID: String, members: List<Address>)
|
||||||
|
fun updateZombieMembers(groupID: String, members: List<Address>)
|
||||||
// Closed Group
|
// Closed Group
|
||||||
fun getAllClosedGroupPublicKeys(): Set<String>
|
fun getAllClosedGroupPublicKeys(): Set<String>
|
||||||
fun getAllActiveClosedGroupPublicKeys(): Set<String>
|
fun getAllActiveClosedGroupPublicKeys(): Set<String>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.session.libsession.messaging.avatars;
|
package org.session.libsession.messaging.avatars;
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.session.libsession.messaging.avatars;
|
package org.session.libsession.messaging.avatars;
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
@ -19,5 +18,4 @@ public interface ContactPhoto extends Key {
|
|||||||
@Nullable Uri getUri(@NonNull Context context);
|
@Nullable Uri getUri(@NonNull Context context);
|
||||||
|
|
||||||
boolean isProfilePhoto();
|
boolean isProfilePhoto();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,5 +7,4 @@ public interface FallbackContactPhoto {
|
|||||||
|
|
||||||
public Drawable asDrawable(Context context, int color);
|
public Drawable asDrawable(Context context, int color);
|
||||||
public Drawable asDrawable(Context context, int color, boolean inverted);
|
public Drawable asDrawable(Context context, int color, boolean inverted);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ import org.session.libsession.utilities.ViewUtil;
|
|||||||
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
||||||
public class GeneratedContactPhoto implements FallbackContactPhoto {
|
public class GeneratedContactPhoto implements FallbackContactPhoto {
|
||||||
|
|
||||||
private static final Pattern PATTERN = Pattern.compile("[^\\p{L}\\p{Nd}\\p{S}]+");
|
private static final Pattern PATTERN = Pattern.compile("[^\\p{L}\\p{Nd}\\p{S}]+");
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package org.session.libsession.messaging.avatars;
|
package org.session.libsession.messaging.avatars;
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.session.libsession.messaging.MessagingConfiguration;
|
import org.session.libsession.messaging.MessagingModuleConfiguration;
|
||||||
import org.session.libsession.messaging.StorageProtocol;
|
import org.session.libsession.messaging.StorageProtocol;
|
||||||
import org.session.libsession.messaging.threads.Address;
|
import org.session.libsession.messaging.threads.Address;
|
||||||
import org.session.libsession.messaging.threads.GroupRecord;
|
import org.session.libsession.messaging.threads.GroupRecord;
|
||||||
@ -32,7 +31,7 @@ public class GroupRecordContactPhoto implements ContactPhoto {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream openInputStream(Context context) throws IOException {
|
public InputStream openInputStream(Context context) throws IOException {
|
||||||
StorageProtocol groupDatabase = MessagingConfiguration.shared.getStorage();
|
StorageProtocol groupDatabase = MessagingModuleConfiguration.shared.getStorage();
|
||||||
Optional<GroupRecord> groupRecord = Optional.of(groupDatabase.getGroup(address.toGroupString()));
|
Optional<GroupRecord> groupRecord = Optional.of(groupDatabase.getGroup(address.toGroupString()));
|
||||||
|
|
||||||
if (groupRecord.isPresent() && groupRecord.get().getAvatar() != null) {
|
if (groupRecord.isPresent() && groupRecord.get().getAvatar() != null) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.session.libsession.messaging.avatars;
|
package org.session.libsession.messaging.avatars;
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.session.libsession.messaging.avatars;
|
package org.session.libsession.messaging.avatars;
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
package org.session.libsession.messaging.fileserver
|
package org.session.libsession.messaging.file_server
|
||||||
|
|
||||||
import nl.komponents.kovenant.Promise
|
import nl.komponents.kovenant.Promise
|
||||||
import nl.komponents.kovenant.functional.map
|
import nl.komponents.kovenant.functional.map
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import org.session.libsession.messaging.utilities.DotNetAPI
|
import org.session.libsession.messaging.utilities.DotNetAPI
|
||||||
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
import org.session.libsignal.utilities.Base64
|
import org.session.libsignal.utilities.Base64
|
||||||
import org.session.libsignal.utilities.JsonUtil
|
import org.session.libsignal.utilities.JsonUtil
|
||||||
import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI
|
import org.session.libsignal.service.loki.LokiAPIDatabaseProtocol
|
||||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
|
|
||||||
import org.session.libsignal.service.loki.utilities.*
|
import org.session.libsignal.service.loki.utilities.*
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
|
|
||||||
class FileServerAPI(public val server: String, userPublicKey: String, userPrivateKey: ByteArray, private val database: LokiAPIDatabaseProtocol) : DotNetAPI() {
|
class FileServerAPI(public val server: String, userPublicKey: String, userPrivateKey: ByteArray, private val database: LokiAPIDatabaseProtocol) : DotNetAPI() {
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
package org.session.libsession.messaging.jobs
|
package org.session.libsession.messaging.jobs
|
||||||
|
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.fileserver.FileServerAPI
|
import org.session.libsession.messaging.file_server.FileServerAPI
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentState
|
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentState
|
||||||
import org.session.libsession.messaging.utilities.DotNetAPI
|
import org.session.libsession.messaging.utilities.DotNetAPI
|
||||||
import org.session.libsignal.service.api.crypto.AttachmentCipherInputStream
|
import org.session.libsignal.service.api.crypto.AttachmentCipherInputStream
|
||||||
@ -11,13 +11,10 @@ import java.io.File
|
|||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
|
||||||
class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long): Job {
|
class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long): Job {
|
||||||
|
|
||||||
override var delegate: JobDelegate? = null
|
override var delegate: JobDelegate? = null
|
||||||
override var id: String? = null
|
override var id: String? = null
|
||||||
override var failureCount: Int = 0
|
override var failureCount: Int = 0
|
||||||
|
|
||||||
private val MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024
|
|
||||||
|
|
||||||
// Error
|
// Error
|
||||||
internal sealed class Error(val description: String) : Exception(description) {
|
internal sealed class Error(val description: String) : Exception(description) {
|
||||||
object NoAttachment : Error("No such attachment.")
|
object NoAttachment : Error("No such attachment.")
|
||||||
@ -28,34 +25,32 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
|
|||||||
companion object {
|
companion object {
|
||||||
val KEY: String = "AttachmentDownloadJob"
|
val KEY: String = "AttachmentDownloadJob"
|
||||||
|
|
||||||
//keys used for database storage purpose
|
// Keys used for database storage
|
||||||
private val KEY_ATTACHMENT_ID = "attachment_id"
|
private val KEY_ATTACHMENT_ID = "attachment_id"
|
||||||
private val KEY_TS_INCOMING_MESSAGE_ID = "tsIncoming_message_id"
|
private val KEY_TS_INCOMING_MESSAGE_ID = "tsIncoming_message_id"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val handleFailure: (java.lang.Exception) -> Unit = { exception ->
|
val handleFailure: (java.lang.Exception) -> Unit = { exception ->
|
||||||
if(exception is Error && exception == Error.NoAttachment) {
|
if (exception == Error.NoAttachment) {
|
||||||
MessagingConfiguration.shared.messageDataProvider.setAttachmentState(AttachmentState.FAILED, attachmentID, databaseMessageID)
|
MessagingModuleConfiguration.shared.messageDataProvider.setAttachmentState(AttachmentState.FAILED, attachmentID, databaseMessageID)
|
||||||
this.handlePermanentFailure(exception)
|
this.handlePermanentFailure(exception)
|
||||||
} else if (exception is DotNetAPI.Error && exception == DotNetAPI.Error.ParsingFailed) {
|
} else if (exception == DotNetAPI.Error.ParsingFailed) {
|
||||||
// No need to retry if the response is invalid. Most likely this means we (incorrectly)
|
// No need to retry if the response is invalid. Most likely this means we (incorrectly)
|
||||||
// got a "Cannot GET ..." error from the file server.
|
// got a "Cannot GET ..." error from the file server.
|
||||||
MessagingConfiguration.shared.messageDataProvider.setAttachmentState(AttachmentState.FAILED, attachmentID, databaseMessageID)
|
MessagingModuleConfiguration.shared.messageDataProvider.setAttachmentState(AttachmentState.FAILED, attachmentID, databaseMessageID)
|
||||||
this.handlePermanentFailure(exception)
|
this.handlePermanentFailure(exception)
|
||||||
} else {
|
} else {
|
||||||
this.handleFailure(exception)
|
this.handleFailure(exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
val messageDataProvider = MessagingConfiguration.shared.messageDataProvider
|
val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider
|
||||||
val attachment = messageDataProvider.getDatabaseAttachment(attachmentID) ?: return handleFailure(Error.NoAttachment)
|
val attachment = messageDataProvider.getDatabaseAttachment(attachmentID) ?: return handleFailure(Error.NoAttachment)
|
||||||
messageDataProvider.setAttachmentState(AttachmentState.STARTED, attachmentID, this.databaseMessageID)
|
messageDataProvider.setAttachmentState(AttachmentState.STARTED, attachmentID, this.databaseMessageID)
|
||||||
val tempFile = createTempFile()
|
val tempFile = createTempFile()
|
||||||
|
|
||||||
FileServerAPI.shared.downloadFile(tempFile, attachment.url, MAX_ATTACHMENT_SIZE, null)
|
FileServerAPI.shared.downloadFile(tempFile, attachment.url, null)
|
||||||
|
|
||||||
// DECRYPTION
|
|
||||||
|
|
||||||
// Assume we're retrieving an attachment for an open group server if the digest is not set
|
// Assume we're retrieving an attachment for an open group server if the digest is not set
|
||||||
val stream = if (attachment.digest?.size ?: 0 == 0 || attachment.key.isNullOrEmpty()) FileInputStream(tempFile)
|
val stream = if (attachment.digest?.size ?: 0 == 0 || attachment.key.isNullOrEmpty()) FileInputStream(tempFile)
|
||||||
@ -84,17 +79,15 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createTempFile(): File {
|
private fun createTempFile(): File {
|
||||||
val file = File.createTempFile("push-attachment", "tmp", MessagingConfiguration.shared.context.cacheDir)
|
val file = File.createTempFile("push-attachment", "tmp", MessagingModuleConfiguration.shared.context.cacheDir)
|
||||||
file.deleteOnExit()
|
file.deleteOnExit()
|
||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
//database functions
|
|
||||||
|
|
||||||
override fun serialize(): Data {
|
override fun serialize(): Data {
|
||||||
return Data.Builder().putLong(KEY_ATTACHMENT_ID, attachmentID)
|
return Data.Builder().putLong(KEY_ATTACHMENT_ID, attachmentID)
|
||||||
.putLong(KEY_TS_INCOMING_MESSAGE_ID, databaseMessageID)
|
.putLong(KEY_TS_INCOMING_MESSAGE_ID, databaseMessageID)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFactoryKey(): String {
|
override fun getFactoryKey(): String {
|
||||||
@ -102,6 +95,7 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Factory: Job.Factory<AttachmentDownloadJob> {
|
class Factory: Job.Factory<AttachmentDownloadJob> {
|
||||||
|
|
||||||
override fun create(data: Data): AttachmentDownloadJob {
|
override fun create(data: Data): AttachmentDownloadJob {
|
||||||
return AttachmentDownloadJob(data.getLong(KEY_ATTACHMENT_ID), data.getLong(KEY_TS_INCOMING_MESSAGE_ID))
|
return AttachmentDownloadJob(data.getLong(KEY_ATTACHMENT_ID), data.getLong(KEY_TS_INCOMING_MESSAGE_ID))
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ package org.session.libsession.messaging.jobs
|
|||||||
import com.esotericsoftware.kryo.Kryo
|
import com.esotericsoftware.kryo.Kryo
|
||||||
import com.esotericsoftware.kryo.io.Input
|
import com.esotericsoftware.kryo.io.Input
|
||||||
import com.esotericsoftware.kryo.io.Output
|
import com.esotericsoftware.kryo.io.Output
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.fileserver.FileServerAPI
|
import org.session.libsession.messaging.file_server.FileServerAPI
|
||||||
import org.session.libsession.messaging.messages.Message
|
import org.session.libsession.messaging.messages.Message
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
import org.session.libsession.messaging.utilities.DotNetAPI
|
import org.session.libsession.messaging.utilities.DotNetAPI
|
||||||
@ -14,11 +14,10 @@ import org.session.libsignal.service.internal.crypto.PaddingInputStream
|
|||||||
import org.session.libsignal.service.internal.push.PushAttachmentData
|
import org.session.libsignal.service.internal.push.PushAttachmentData
|
||||||
import org.session.libsignal.service.internal.push.http.AttachmentCipherOutputStreamFactory
|
import org.session.libsignal.service.internal.push.http.AttachmentCipherOutputStreamFactory
|
||||||
import org.session.libsignal.service.internal.util.Util
|
import org.session.libsignal.service.internal.util.Util
|
||||||
import org.session.libsignal.service.loki.utilities.PlaintextOutputStreamFactory
|
import org.session.libsignal.service.loki.PlaintextOutputStreamFactory
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
|
|
||||||
class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val message: Message, val messageSendJobID: String) : Job {
|
class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val message: Message, val messageSendJobID: String) : Job {
|
||||||
|
|
||||||
override var delegate: JobDelegate? = null
|
override var delegate: JobDelegate? = null
|
||||||
override var id: String? = null
|
override var id: String? = null
|
||||||
override var failureCount: Int = 0
|
override var failureCount: Int = 0
|
||||||
@ -34,9 +33,7 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
|
|||||||
val TAG = AttachmentUploadJob::class.simpleName
|
val TAG = AttachmentUploadJob::class.simpleName
|
||||||
val KEY: String = "AttachmentUploadJob"
|
val KEY: String = "AttachmentUploadJob"
|
||||||
|
|
||||||
val maxFailureCount: Int = 20
|
// Keys used for database storage
|
||||||
|
|
||||||
//keys used for database storage purpose
|
|
||||||
private val KEY_ATTACHMENT_ID = "attachment_id"
|
private val KEY_ATTACHMENT_ID = "attachment_id"
|
||||||
private val KEY_THREAD_ID = "thread_id"
|
private val KEY_THREAD_ID = "thread_id"
|
||||||
private val KEY_MESSAGE = "message"
|
private val KEY_MESSAGE = "message"
|
||||||
@ -45,17 +42,13 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
|
|||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
try {
|
try {
|
||||||
val attachment = MessagingConfiguration.shared.messageDataProvider.getScaledSignalAttachmentStream(attachmentID)
|
val attachment = MessagingModuleConfiguration.shared.messageDataProvider.getScaledSignalAttachmentStream(attachmentID)
|
||||||
?: return handleFailure(Error.NoAttachment)
|
?: return handleFailure(Error.NoAttachment)
|
||||||
|
|
||||||
var server = FileServerAPI.shared.server
|
|
||||||
var shouldEncrypt = true
|
|
||||||
val usePadding = false
|
val usePadding = false
|
||||||
val openGroup = MessagingConfiguration.shared.storage.getOpenGroup(threadID)
|
val openGroup = MessagingModuleConfiguration.shared.storage.getOpenGroup(threadID)
|
||||||
openGroup?.let {
|
val server = if (openGroup != null) openGroup.server else FileServerAPI.shared.server
|
||||||
server = it.server
|
val shouldEncrypt = (openGroup == null) // Encrypt if this isn't an open group
|
||||||
shouldEncrypt = false
|
|
||||||
}
|
|
||||||
|
|
||||||
val attachmentKey = Util.getSecretBytes(64)
|
val attachmentKey = Util.getSecretBytes(64)
|
||||||
val paddedLength = if (usePadding) PaddingInputStream.getPaddedSize(attachment.length) else attachment.length
|
val paddedLength = if (usePadding) PaddingInputStream.getPaddedSize(attachment.length) else attachment.length
|
||||||
@ -67,9 +60,8 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
|
|||||||
|
|
||||||
val uploadResult = FileServerAPI.shared.uploadAttachment(server, attachmentData)
|
val uploadResult = FileServerAPI.shared.uploadAttachment(server, attachmentData)
|
||||||
handleSuccess(attachment, attachmentKey, uploadResult)
|
handleSuccess(attachment, attachmentKey, uploadResult)
|
||||||
|
|
||||||
} catch (e: java.lang.Exception) {
|
} catch (e: java.lang.Exception) {
|
||||||
if (e is Error && e == Error.NoAttachment) {
|
if (e == Error.NoAttachment) {
|
||||||
this.handlePermanentFailure(e)
|
this.handlePermanentFailure(e)
|
||||||
} else if (e is DotNetAPI.Error && !e.isRetryable) {
|
} else if (e is DotNetAPI.Error && !e.isRetryable) {
|
||||||
this.handlePermanentFailure(e)
|
this.handlePermanentFailure(e)
|
||||||
@ -77,33 +69,32 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
|
|||||||
this.handleFailure(e)
|
this.handleFailure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSuccess(attachment: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult) {
|
private fun handleSuccess(attachment: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult) {
|
||||||
Log.w(TAG, "Attachment uploaded successfully.")
|
Log.w(TAG, "Attachment uploaded successfully.")
|
||||||
delegate?.handleJobSucceeded(this)
|
delegate?.handleJobSucceeded(this)
|
||||||
MessagingConfiguration.shared.messageDataProvider.updateAttachmentAfterUploadSucceeded(attachmentID, attachment, attachmentKey, uploadResult)
|
MessagingModuleConfiguration.shared.messageDataProvider.updateAttachmentAfterUploadSucceeded(attachmentID, attachment, attachmentKey, uploadResult)
|
||||||
MessagingConfiguration.shared.storage.resumeMessageSendJobIfNeeded(messageSendJobID)
|
MessagingModuleConfiguration.shared.storage.resumeMessageSendJobIfNeeded(messageSendJobID)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handlePermanentFailure(e: Exception) {
|
private fun handlePermanentFailure(e: Exception) {
|
||||||
Log.w(TAG, "Attachment upload failed permanently due to error: $this.")
|
Log.w(TAG, "Attachment upload failed permanently due to error: $this.")
|
||||||
delegate?.handleJobFailedPermanently(this, e)
|
delegate?.handleJobFailedPermanently(this, e)
|
||||||
MessagingConfiguration.shared.messageDataProvider.updateAttachmentAfterUploadFailed(attachmentID)
|
MessagingModuleConfiguration.shared.messageDataProvider.updateAttachmentAfterUploadFailed(attachmentID)
|
||||||
failAssociatedMessageSendJob(e)
|
failAssociatedMessageSendJob(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleFailure(e: Exception) {
|
private fun handleFailure(e: Exception) {
|
||||||
Log.w(TAG, "Attachment upload failed due to error: $this.")
|
Log.w(TAG, "Attachment upload failed due to error: $this.")
|
||||||
delegate?.handleJobFailed(this, e)
|
delegate?.handleJobFailed(this, e)
|
||||||
if (failureCount + 1 == AttachmentUploadJob.maxFailureCount) {
|
if (failureCount + 1 == maxFailureCount) {
|
||||||
failAssociatedMessageSendJob(e)
|
failAssociatedMessageSendJob(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun failAssociatedMessageSendJob(e: Exception) {
|
private fun failAssociatedMessageSendJob(e: Exception) {
|
||||||
val storage = MessagingConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val messageSendJob = storage.getMessageSendJob(messageSendJobID)
|
val messageSendJob = storage.getMessageSendJob(messageSendJobID)
|
||||||
MessageSender.handleFailedMessageSend(this.message, e)
|
MessageSender.handleFailedMessageSend(this.message, e)
|
||||||
if (messageSendJob != null) {
|
if (messageSendJob != null) {
|
||||||
@ -111,10 +102,7 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//database functions
|
|
||||||
|
|
||||||
override fun serialize(): Data {
|
override fun serialize(): Data {
|
||||||
//serialize Message property
|
|
||||||
val kryo = Kryo()
|
val kryo = Kryo()
|
||||||
kryo.isRegistrationRequired = false
|
kryo.isRegistrationRequired = false
|
||||||
val serializedMessage = ByteArray(4096)
|
val serializedMessage = ByteArray(4096)
|
||||||
@ -122,10 +110,10 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
|
|||||||
kryo.writeObject(output, message)
|
kryo.writeObject(output, message)
|
||||||
output.close()
|
output.close()
|
||||||
return Data.Builder().putLong(KEY_ATTACHMENT_ID, attachmentID)
|
return Data.Builder().putLong(KEY_ATTACHMENT_ID, attachmentID)
|
||||||
.putString(KEY_THREAD_ID, threadID)
|
.putString(KEY_THREAD_ID, threadID)
|
||||||
.putByteArray(KEY_MESSAGE, serializedMessage)
|
.putByteArray(KEY_MESSAGE, serializedMessage)
|
||||||
.putString(KEY_MESSAGE_SEND_JOB_ID, messageSendJobID)
|
.putString(KEY_MESSAGE_SEND_JOB_ID, messageSendJobID)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFactoryKey(): String {
|
override fun getFactoryKey(): String {
|
||||||
@ -133,9 +121,9 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Factory: Job.Factory<AttachmentUploadJob> {
|
class Factory: Job.Factory<AttachmentUploadJob> {
|
||||||
|
|
||||||
override fun create(data: Data): AttachmentUploadJob {
|
override fun create(data: Data): AttachmentUploadJob {
|
||||||
val serializedMessage = data.getByteArray(KEY_MESSAGE)
|
val serializedMessage = data.getByteArray(KEY_MESSAGE)
|
||||||
//deserialize Message property
|
|
||||||
val kryo = Kryo()
|
val kryo = Kryo()
|
||||||
val input = Input(serializedMessage)
|
val input = Input(serializedMessage)
|
||||||
val message: Message = kryo.readObject(input, Message::class.java)
|
val message: Message = kryo.readObject(input, Message::class.java)
|
||||||
|
@ -12,7 +12,6 @@ import org.session.libsession.utilities.ParcelableUtil;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
// TODO AC: For now parcelable objects utilize byteArrays field to store their data into.
|
|
||||||
// Introduce a dedicated Map<String, byte[]> field specifically for parcelable needs.
|
// Introduce a dedicated Map<String, byte[]> field specifically for parcelable needs.
|
||||||
public class Data {
|
public class Data {
|
||||||
|
|
||||||
|
@ -8,15 +8,13 @@ interface Job {
|
|||||||
val maxFailureCount: Int
|
val maxFailureCount: Int
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
//keys used for database storage purpose
|
// Keys used for database storage
|
||||||
private val KEY_ID = "id"
|
private val KEY_ID = "id"
|
||||||
private val KEY_FAILURE_COUNT = "failure_count"
|
private val KEY_FAILURE_COUNT = "failure_count"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun execute()
|
fun execute()
|
||||||
|
|
||||||
//database functions
|
|
||||||
|
|
||||||
fun serialize(): Data
|
fun serialize(): Data
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.session.libsession.messaging.jobs
|
package org.session.libsession.messaging.jobs
|
||||||
|
|
||||||
interface JobDelegate {
|
interface JobDelegate {
|
||||||
|
|
||||||
fun handleJobSucceeded(job: Job)
|
fun handleJobSucceeded(job: Job)
|
||||||
fun handleJobFailed(job: Job, error: Exception)
|
fun handleJobFailed(job: Job, error: Exception)
|
||||||
fun handleJobFailedPermanently(job: Job, error: Exception)
|
fun handleJobFailedPermanently(job: Job, error: Exception)
|
||||||
|
@ -3,7 +3,7 @@ package org.session.libsession.messaging.jobs
|
|||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
|
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
@ -14,19 +14,17 @@ import kotlin.math.min
|
|||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
import kotlin.math.roundToLong
|
import kotlin.math.roundToLong
|
||||||
|
|
||||||
|
|
||||||
class JobQueue : JobDelegate {
|
class JobQueue : JobDelegate {
|
||||||
private var hasResumedPendingJobs = false // Just for debugging
|
private var hasResumedPendingJobs = false // Just for debugging
|
||||||
|
|
||||||
private val jobTimestampMap = ConcurrentHashMap<Long, AtomicInteger>()
|
private val jobTimestampMap = ConcurrentHashMap<Long, AtomicInteger>()
|
||||||
|
|
||||||
private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||||
private val scope = GlobalScope + SupervisorJob()
|
private val scope = GlobalScope + SupervisorJob()
|
||||||
private val queue = Channel<Job>(UNLIMITED)
|
private val queue = Channel<Job>(UNLIMITED)
|
||||||
|
|
||||||
val timer = Timer()
|
val timer = Timer()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// process jobs
|
// Process jobs
|
||||||
scope.launch(dispatcher) {
|
scope.launch(dispatcher) {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
queue.receive().let { job ->
|
queue.receive().let { job ->
|
||||||
@ -49,14 +47,14 @@ class JobQueue : JobDelegate {
|
|||||||
|
|
||||||
private fun addWithoutExecuting(job: Job) {
|
private fun addWithoutExecuting(job: Job) {
|
||||||
// When adding multiple jobs in rapid succession, timestamps might not be good enough as a unique ID. To
|
// When adding multiple jobs in rapid succession, timestamps might not be good enough as a unique ID. To
|
||||||
// deal with this we keep track of the number of jobs with a given timestamp and that to the end of the
|
// deal with this we keep track of the number of jobs with a given timestamp and add that to the end of the
|
||||||
// timestamp to make it a unique ID. We can't use a random number because we do still want to keep track
|
// timestamp to make it a unique ID. We can't use a random number because we do still want to keep track
|
||||||
// of the order in which the jobs were added.
|
// of the order in which the jobs were added.
|
||||||
val currentTime = System.currentTimeMillis()
|
val currentTime = System.currentTimeMillis()
|
||||||
jobTimestampMap.putIfAbsent(currentTime, AtomicInteger())
|
jobTimestampMap.putIfAbsent(currentTime, AtomicInteger())
|
||||||
job.id = currentTime.toString() + jobTimestampMap[currentTime]!!.getAndIncrement().toString()
|
job.id = currentTime.toString() + jobTimestampMap[currentTime]!!.getAndIncrement().toString()
|
||||||
|
|
||||||
MessagingConfiguration.shared.storage.persistJob(job)
|
MessagingModuleConfiguration.shared.storage.persistJob(job)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resumePendingJobs() {
|
fun resumePendingJobs() {
|
||||||
@ -67,21 +65,21 @@ class JobQueue : JobDelegate {
|
|||||||
hasResumedPendingJobs = true
|
hasResumedPendingJobs = true
|
||||||
val allJobTypes = listOf(AttachmentDownloadJob.KEY, AttachmentDownloadJob.KEY, MessageReceiveJob.KEY, MessageSendJob.KEY, NotifyPNServerJob.KEY)
|
val allJobTypes = listOf(AttachmentDownloadJob.KEY, AttachmentDownloadJob.KEY, MessageReceiveJob.KEY, MessageSendJob.KEY, NotifyPNServerJob.KEY)
|
||||||
allJobTypes.forEach { type ->
|
allJobTypes.forEach { type ->
|
||||||
val allPendingJobs = MessagingConfiguration.shared.storage.getAllPendingJobs(type)
|
val allPendingJobs = MessagingModuleConfiguration.shared.storage.getAllPendingJobs(type)
|
||||||
allPendingJobs.sortedBy { it.id }.forEach { job ->
|
allPendingJobs.sortedBy { it.id }.forEach { job ->
|
||||||
Log.i("Jobs", "Resuming pending job of type: ${job::class.simpleName}.")
|
Log.i("Jobs", "Resuming pending job of type: ${job::class.simpleName}.")
|
||||||
queue.offer(job) // offer always called on unlimited capacity
|
queue.offer(job) // Offer always called on unlimited capacity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleJobSucceeded(job: Job) {
|
override fun handleJobSucceeded(job: Job) {
|
||||||
MessagingConfiguration.shared.storage.markJobAsSucceeded(job)
|
MessagingModuleConfiguration.shared.storage.markJobAsSucceeded(job)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleJobFailed(job: Job, error: Exception) {
|
override fun handleJobFailed(job: Job, error: Exception) {
|
||||||
job.failureCount += 1
|
job.failureCount += 1
|
||||||
val storage = MessagingConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
if (storage.isJobCanceled(job)) { return Log.i("Jobs", "${job::class.simpleName} canceled.")}
|
if (storage.isJobCanceled(job)) { return Log.i("Jobs", "${job::class.simpleName} canceled.")}
|
||||||
storage.persistJob(job)
|
storage.persistJob(job)
|
||||||
if (job.failureCount == job.maxFailureCount) {
|
if (job.failureCount == job.maxFailureCount) {
|
||||||
@ -98,7 +96,7 @@ class JobQueue : JobDelegate {
|
|||||||
|
|
||||||
override fun handleJobFailedPermanently(job: Job, error: Exception) {
|
override fun handleJobFailedPermanently(job: Job, error: Exception) {
|
||||||
job.failureCount += 1
|
job.failureCount += 1
|
||||||
val storage = MessagingConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
storage.persistJob(job)
|
storage.persistJob(job)
|
||||||
storage.markJobAsFailed(job)
|
storage.markJobAsFailed(job)
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import org.session.libsession.messaging.sending_receiving.handle
|
|||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
|
|
||||||
class MessageReceiveJob(val data: ByteArray, val isBackgroundPoll: Boolean, val openGroupMessageServerID: Long? = null, val openGroupID: String? = null) : Job {
|
class MessageReceiveJob(val data: ByteArray, val isBackgroundPoll: Boolean, val openGroupMessageServerID: Long? = null, val openGroupID: String? = null) : Job {
|
||||||
|
|
||||||
override var delegate: JobDelegate? = null
|
override var delegate: JobDelegate? = null
|
||||||
override var id: String? = null
|
override var id: String? = null
|
||||||
override var failureCount: Int = 0
|
override var failureCount: Int = 0
|
||||||
@ -20,7 +19,7 @@ class MessageReceiveJob(val data: ByteArray, val isBackgroundPoll: Boolean, val
|
|||||||
|
|
||||||
private val RECEIVE_LOCK = Object()
|
private val RECEIVE_LOCK = Object()
|
||||||
|
|
||||||
//keys used for database storage purpose
|
// Keys used for database storage
|
||||||
private val KEY_DATA = "data"
|
private val KEY_DATA = "data"
|
||||||
private val KEY_IS_BACKGROUND_POLL = "is_background_poll"
|
private val KEY_IS_BACKGROUND_POLL = "is_background_poll"
|
||||||
private val KEY_OPEN_GROUP_MESSAGE_SERVER_ID = "openGroupMessageServerID"
|
private val KEY_OPEN_GROUP_MESSAGE_SERVER_ID = "openGroupMessageServerID"
|
||||||
@ -68,8 +67,6 @@ class MessageReceiveJob(val data: ByteArray, val isBackgroundPoll: Boolean, val
|
|||||||
delegate?.handleJobFailed(this, e)
|
delegate?.handleJobFailed(this, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
//database functions
|
|
||||||
|
|
||||||
override fun serialize(): Data {
|
override fun serialize(): Data {
|
||||||
val builder = Data.Builder().putByteArray(KEY_DATA, data)
|
val builder = Data.Builder().putByteArray(KEY_DATA, data)
|
||||||
.putBoolean(KEY_IS_BACKGROUND_POLL, isBackgroundPoll)
|
.putBoolean(KEY_IS_BACKGROUND_POLL, isBackgroundPoll)
|
||||||
@ -83,6 +80,7 @@ class MessageReceiveJob(val data: ByteArray, val isBackgroundPoll: Boolean, val
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Factory: Job.Factory<MessageReceiveJob> {
|
class Factory: Job.Factory<MessageReceiveJob> {
|
||||||
|
|
||||||
override fun create(data: Data): MessageReceiveJob {
|
override fun create(data: Data): MessageReceiveJob {
|
||||||
return MessageReceiveJob(data.getByteArray(KEY_DATA), data.getBoolean(KEY_IS_BACKGROUND_POLL), data.getLong(KEY_OPEN_GROUP_MESSAGE_SERVER_ID), data.getString(KEY_OPEN_GROUP_ID))
|
return MessageReceiveJob(data.getByteArray(KEY_DATA), data.getBoolean(KEY_IS_BACKGROUND_POLL), data.getLong(KEY_OPEN_GROUP_MESSAGE_SERVER_ID), data.getString(KEY_OPEN_GROUP_ID))
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package org.session.libsession.messaging.jobs
|
|||||||
import com.esotericsoftware.kryo.Kryo
|
import com.esotericsoftware.kryo.Kryo
|
||||||
import com.esotericsoftware.kryo.io.Input
|
import com.esotericsoftware.kryo.io.Input
|
||||||
import com.esotericsoftware.kryo.io.Output
|
import com.esotericsoftware.kryo.io.Output
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.messages.Destination
|
import org.session.libsession.messaging.messages.Destination
|
||||||
import org.session.libsession.messaging.messages.Message
|
import org.session.libsession.messaging.messages.Message
|
||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||||
@ -11,7 +11,6 @@ import org.session.libsession.messaging.sending_receiving.MessageSender
|
|||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
|
|
||||||
class MessageSendJob(val message: Message, val destination: Destination) : Job {
|
class MessageSendJob(val message: Message, val destination: Destination) : Job {
|
||||||
|
|
||||||
override var delegate: JobDelegate? = null
|
override var delegate: JobDelegate? = null
|
||||||
override var id: String? = null
|
override var id: String? = null
|
||||||
override var failureCount: Int = 0
|
override var failureCount: Int = 0
|
||||||
@ -22,13 +21,13 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
|
|||||||
val TAG = MessageSendJob::class.simpleName
|
val TAG = MessageSendJob::class.simpleName
|
||||||
val KEY: String = "MessageSendJob"
|
val KEY: String = "MessageSendJob"
|
||||||
|
|
||||||
//keys used for database storage purpose
|
// Keys used for database storage
|
||||||
private val KEY_MESSAGE = "message"
|
private val KEY_MESSAGE = "message"
|
||||||
private val KEY_DESTINATION = "destination"
|
private val KEY_DESTINATION = "destination"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val messageDataProvider = MessagingConfiguration.shared.messageDataProvider
|
val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider
|
||||||
val message = message as? VisibleMessage
|
val message = message as? VisibleMessage
|
||||||
message?.let {
|
message?.let {
|
||||||
if(!messageDataProvider.isOutgoingMessage(message.sentTimestamp!!)) return // The message has been deleted
|
if(!messageDataProvider.isOutgoingMessage(message.sentTimestamp!!)) return // The message has been deleted
|
||||||
@ -39,7 +38,7 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
|
|||||||
val attachments = attachmentIDs.mapNotNull { messageDataProvider.getDatabaseAttachment(it) }
|
val attachments = attachmentIDs.mapNotNull { messageDataProvider.getDatabaseAttachment(it) }
|
||||||
val attachmentsToUpload = attachments.filter { it.url.isNullOrEmpty() }
|
val attachmentsToUpload = attachments.filter { it.url.isNullOrEmpty() }
|
||||||
attachmentsToUpload.forEach {
|
attachmentsToUpload.forEach {
|
||||||
if (MessagingConfiguration.shared.storage.getAttachmentUploadJob(it.attachmentId.rowId) != null) {
|
if (MessagingModuleConfiguration.shared.storage.getAttachmentUploadJob(it.attachmentId.rowId) != null) {
|
||||||
// Wait for it to finish
|
// Wait for it to finish
|
||||||
} else {
|
} else {
|
||||||
val job = AttachmentUploadJob(it.attachmentId.rowId, message.threadID!!.toString(), message, id!!)
|
val job = AttachmentUploadJob(it.attachmentId.rowId, message.threadID!!.toString(), message, id!!)
|
||||||
@ -72,15 +71,12 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
|
|||||||
Log.w(TAG, "Failed to send $message::class.simpleName.")
|
Log.w(TAG, "Failed to send $message::class.simpleName.")
|
||||||
val message = message as? VisibleMessage
|
val message = message as? VisibleMessage
|
||||||
message?.let {
|
message?.let {
|
||||||
if(!MessagingConfiguration.shared.messageDataProvider.isOutgoingMessage(message.sentTimestamp!!)) return // The message has been deleted
|
if(!MessagingModuleConfiguration.shared.messageDataProvider.isOutgoingMessage(message.sentTimestamp!!)) return // The message has been deleted
|
||||||
}
|
}
|
||||||
delegate?.handleJobFailed(this, error)
|
delegate?.handleJobFailed(this, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
//database functions
|
|
||||||
|
|
||||||
override fun serialize(): Data {
|
override fun serialize(): Data {
|
||||||
//serialize Message and Destination properties
|
|
||||||
val kryo = Kryo()
|
val kryo = Kryo()
|
||||||
kryo.isRegistrationRequired = false
|
kryo.isRegistrationRequired = false
|
||||||
val output = Output(ByteArray(4096), -1) // maxBufferSize '-1' will dynamically grow internally if we run out of room serializing the message
|
val output = Output(ByteArray(4096), -1) // maxBufferSize '-1' will dynamically grow internally if we run out of room serializing the message
|
||||||
@ -92,8 +88,8 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
|
|||||||
output.close()
|
output.close()
|
||||||
val serializedDestination = output.toBytes()
|
val serializedDestination = output.toBytes()
|
||||||
return Data.Builder().putByteArray(KEY_MESSAGE, serializedMessage)
|
return Data.Builder().putByteArray(KEY_MESSAGE, serializedMessage)
|
||||||
.putByteArray(KEY_DESTINATION, serializedDestination)
|
.putByteArray(KEY_DESTINATION, serializedDestination)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFactoryKey(): String {
|
override fun getFactoryKey(): String {
|
||||||
@ -101,10 +97,10 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Factory: Job.Factory<MessageSendJob> {
|
class Factory: Job.Factory<MessageSendJob> {
|
||||||
|
|
||||||
override fun create(data: Data): MessageSendJob {
|
override fun create(data: Data): MessageSendJob {
|
||||||
val serializedMessage = data.getByteArray(KEY_MESSAGE)
|
val serializedMessage = data.getByteArray(KEY_MESSAGE)
|
||||||
val serializedDestination = data.getByteArray(KEY_DESTINATION)
|
val serializedDestination = data.getByteArray(KEY_DESTINATION)
|
||||||
//deserialize Message and Destination properties
|
|
||||||
val kryo = Kryo()
|
val kryo = Kryo()
|
||||||
var input = Input(serializedMessage)
|
var input = Input(serializedMessage)
|
||||||
val message = kryo.readClassAndObject(input) as Message
|
val message = kryo.readClassAndObject(input) as Message
|
||||||
|
@ -26,7 +26,7 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job {
|
|||||||
companion object {
|
companion object {
|
||||||
val KEY: String = "NotifyPNServerJob"
|
val KEY: String = "NotifyPNServerJob"
|
||||||
|
|
||||||
//keys used for database storage purpose
|
// Keys used for database storage
|
||||||
private val KEY_MESSAGE = "message"
|
private val KEY_MESSAGE = "message"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,18 +61,14 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job {
|
|||||||
delegate?.handleJobFailed(this, error)
|
delegate?.handleJobFailed(this, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
//database functions
|
|
||||||
|
|
||||||
override fun serialize(): Data {
|
override fun serialize(): Data {
|
||||||
//serialize SnodeMessage property
|
|
||||||
val kryo = Kryo()
|
val kryo = Kryo()
|
||||||
kryo.isRegistrationRequired = false
|
kryo.isRegistrationRequired = false
|
||||||
val serializedMessage = ByteArray(4096)
|
val serializedMessage = ByteArray(4096)
|
||||||
val output = Output(serializedMessage)
|
val output = Output(serializedMessage)
|
||||||
kryo.writeObject(output, message)
|
kryo.writeObject(output, message)
|
||||||
output.close()
|
output.close()
|
||||||
return Data.Builder().putByteArray(KEY_MESSAGE, serializedMessage)
|
return Data.Builder().putByteArray(KEY_MESSAGE, serializedMessage).build();
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFactoryKey(): String {
|
override fun getFactoryKey(): String {
|
||||||
@ -80,9 +76,9 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Factory: Job.Factory<NotifyPNServerJob> {
|
class Factory: Job.Factory<NotifyPNServerJob> {
|
||||||
|
|
||||||
override fun create(data: Data): NotifyPNServerJob {
|
override fun create(data: Data): NotifyPNServerJob {
|
||||||
val serializedMessage = data.getByteArray(KEY_MESSAGE)
|
val serializedMessage = data.getByteArray(KEY_MESSAGE)
|
||||||
//deserialize SnodeMessage property
|
|
||||||
val kryo = Kryo()
|
val kryo = Kryo()
|
||||||
val input = Input(serializedMessage)
|
val input = Input(serializedMessage)
|
||||||
val message: SnodeMessage = kryo.readObject(input, SnodeMessage::class.java)
|
val message: SnodeMessage = kryo.readObject(input, SnodeMessage::class.java)
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.session.libsession.messaging.jobs
|
package org.session.libsession.messaging.jobs
|
||||||
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class SessionJobInstantiator(private val jobFactories: Map<String, Job.Factory<out Job>>) {
|
class SessionJobInstantiator(private val jobFactories: Map<String, Job.Factory<out Job>>) {
|
||||||
|
|
||||||
fun instantiate(jobFactoryKey: String, data: Data): Job {
|
fun instantiate(jobFactoryKey: String, data: Data): Job {
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.session.libsession.messaging.jobs
|
package org.session.libsession.messaging.jobs
|
||||||
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class SessionJobManagerFactories {
|
class SessionJobManagerFactories {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
package org.session.libsignal.service.loki.utilities.mentions
|
package org.session.libsession.messaging.mentions
|
||||||
|
|
||||||
import org.session.libsignal.service.loki.database.LokiThreadDatabaseProtocol
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol
|
import org.session.libsignal.service.loki.Mention
|
||||||
|
|
||||||
class MentionsManager(private val userPublicKey: String, private val threadDatabase: LokiThreadDatabaseProtocol,
|
import org.session.libsignal.service.loki.LokiUserDatabaseProtocol
|
||||||
private val userDatabase: LokiUserDatabaseProtocol) {
|
|
||||||
|
class MentionsManager(private val userPublicKey: String, private val userDatabase: LokiUserDatabaseProtocol) {
|
||||||
var userPublicKeyCache = mutableMapOf<Long, Set<String>>() // Thread ID to set of user hex encoded public keys
|
var userPublicKeyCache = mutableMapOf<Long, Set<String>>() // Thread ID to set of user hex encoded public keys
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
public lateinit var shared: MentionsManager
|
public lateinit var shared: MentionsManager
|
||||||
|
|
||||||
public fun configureIfNeeded(userPublicKey: String, threadDatabase: LokiThreadDatabaseProtocol, userDatabase: LokiUserDatabaseProtocol) {
|
public fun configureIfNeeded(userPublicKey: String, userDatabase: LokiUserDatabaseProtocol) {
|
||||||
if (::shared.isInitialized) { return; }
|
if (::shared.isInitialized) { return; }
|
||||||
shared = MentionsManager(userPublicKey, threadDatabase, userDatabase)
|
shared = MentionsManager(userPublicKey, userDatabase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ class MentionsManager(private val userPublicKey: String, private val threadDatab
|
|||||||
// Prepare
|
// Prepare
|
||||||
val cache = userPublicKeyCache[threadID] ?: return listOf()
|
val cache = userPublicKeyCache[threadID] ?: return listOf()
|
||||||
// Gather candidates
|
// Gather candidates
|
||||||
val publicChat = threadDatabase.getPublicChat(threadID)
|
val publicChat = MessagingModuleConfiguration.shared.messageDataProvider.getOpenGroup(threadID)
|
||||||
var candidates: List<Mention> = cache.mapNotNull { publicKey ->
|
var candidates: List<Mention> = cache.mapNotNull { publicKey ->
|
||||||
val displayName: String?
|
val displayName: String?
|
||||||
if (publicChat != null) {
|
if (publicChat != null) {
|
@ -1,6 +1,6 @@
|
|||||||
package org.session.libsession.messaging.messages
|
package org.session.libsession.messaging.messages
|
||||||
|
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
import org.session.libsession.utilities.GroupUtil
|
import org.session.libsession.utilities.GroupUtil
|
||||||
import org.session.libsignal.service.loki.utilities.toHexString
|
import org.session.libsignal.service.loki.utilities.toHexString
|
||||||
@ -29,8 +29,8 @@ sealed class Destination {
|
|||||||
ClosedGroup(groupPublicKey)
|
ClosedGroup(groupPublicKey)
|
||||||
}
|
}
|
||||||
address.isOpenGroup -> {
|
address.isOpenGroup -> {
|
||||||
val threadID = MessagingConfiguration.shared.storage.getThreadID(address.contactIdentifier())!!
|
val threadID = MessagingModuleConfiguration.shared.storage.getThreadID(address.contactIdentifier())!!
|
||||||
val openGroup = MessagingConfiguration.shared.storage.getOpenGroup(threadID)!!
|
val openGroup = MessagingModuleConfiguration.shared.storage.getOpenGroup(threadID)!!
|
||||||
OpenGroup(openGroup.channel, openGroup.server)
|
OpenGroup(openGroup.channel, openGroup.server)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -5,7 +5,6 @@ import org.session.libsession.utilities.GroupUtil
|
|||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
|
|
||||||
abstract class Message {
|
abstract class Message {
|
||||||
|
|
||||||
var id: Long? = null
|
var id: Long? = null
|
||||||
var threadID: Long? = null
|
var threadID: Long? = null
|
||||||
var sentTimestamp: Long? = null
|
var sentTimestamp: Long? = null
|
||||||
@ -15,10 +14,9 @@ abstract class Message {
|
|||||||
var groupPublicKey: String? = null
|
var groupPublicKey: String? = null
|
||||||
var openGroupServerMessageID: Long? = null
|
var openGroupServerMessageID: Long? = null
|
||||||
|
|
||||||
open val ttl: Long = 2 * 24 * 60 * 60 * 1000
|
open val ttl: Long = 14 * 24 * 60 * 60 * 1000
|
||||||
open val isSelfSendValid: Boolean = false
|
open val isSelfSendValid: Boolean = false
|
||||||
|
|
||||||
// validation
|
|
||||||
open fun isValid(): Boolean {
|
open fun isValid(): Boolean {
|
||||||
sentTimestamp?.let {
|
sentTimestamp?.let {
|
||||||
if (it <= 0) return false
|
if (it <= 0) return false
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.session.libsession.messaging.messages.control
|
package org.session.libsession.messaging.messages.control
|
||||||
|
|
||||||
import com.google.protobuf.ByteString
|
import com.google.protobuf.ByteString
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||||
import org.session.libsession.utilities.GroupUtil
|
import org.session.libsession.utilities.GroupUtil
|
||||||
@ -19,8 +19,8 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
|
|
||||||
override val ttl: Long = run {
|
override val ttl: Long = run {
|
||||||
when (kind) {
|
when (kind) {
|
||||||
is Kind.EncryptionKeyPair -> return@run 4 * 24 * 60 * 60 * 1000
|
is Kind.EncryptionKeyPair -> 14 * 24 * 60 * 60 * 1000
|
||||||
else -> return@run 2 * 24 * 60 * 60 * 1000
|
else -> 14 * 24 * 60 * 60 * 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,15 +28,10 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
|
|
||||||
var kind: Kind? = null
|
var kind: Kind? = null
|
||||||
|
|
||||||
// Kind enum
|
|
||||||
sealed class Kind {
|
sealed class Kind {
|
||||||
class New(var publicKey: ByteString, var name: String, var encryptionKeyPair: ECKeyPair?, var members: List<ByteString>, var admins: List<ByteString>) : Kind() {
|
class New(var publicKey: ByteString, var name: String, var encryptionKeyPair: ECKeyPair?, var members: List<ByteString>, var admins: List<ByteString>) : Kind() {
|
||||||
internal constructor(): this(ByteString.EMPTY, "", null, listOf(), listOf())
|
internal constructor(): this(ByteString.EMPTY, "", null, listOf(), listOf())
|
||||||
}
|
}
|
||||||
/// - Note: Deprecated in favor of more explicit group updates.
|
|
||||||
class Update(var name: String, var members: List<ByteString>) : Kind() {
|
|
||||||
internal constructor(): this("", listOf())
|
|
||||||
}
|
|
||||||
/// An encryption key pair encrypted for each member individually.
|
/// An encryption key pair encrypted for each member individually.
|
||||||
///
|
///
|
||||||
/// - Note: `publicKey` is only set when an encryption key pair is sent in a one-to-one context (i.e. not in a group).
|
/// - Note: `publicKey` is only set when an encryption key pair is sent in a one-to-one context (i.e. not in a group).
|
||||||
@ -53,18 +48,15 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
internal constructor(): this(listOf())
|
internal constructor(): this(listOf())
|
||||||
}
|
}
|
||||||
class MemberLeft() : Kind()
|
class MemberLeft() : Kind()
|
||||||
class EncryptionKeyPairRequest(): Kind()
|
|
||||||
|
|
||||||
val description: String =
|
val description: String =
|
||||||
when(this) {
|
when(this) {
|
||||||
is New -> "new"
|
is New -> "new"
|
||||||
is Update -> "update"
|
|
||||||
is EncryptionKeyPair -> "encryptionKeyPair"
|
is EncryptionKeyPair -> "encryptionKeyPair"
|
||||||
is NameChange -> "nameChange"
|
is NameChange -> "nameChange"
|
||||||
is MembersAdded -> "membersAdded"
|
is MembersAdded -> "membersAdded"
|
||||||
is MembersRemoved -> "membersRemoved"
|
is MembersRemoved -> "membersRemoved"
|
||||||
is MemberLeft -> "memberLeft"
|
is MemberLeft -> "memberLeft"
|
||||||
is EncryptionKeyPairRequest -> "encryptionKeyPairRequest"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,12 +67,11 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
if (!proto.hasDataMessage() || !proto.dataMessage.hasClosedGroupControlMessage()) return null
|
if (!proto.hasDataMessage() || !proto.dataMessage.hasClosedGroupControlMessage()) return null
|
||||||
val closedGroupControlMessageProto = proto.dataMessage?.closedGroupControlMessage!!
|
val closedGroupControlMessageProto = proto.dataMessage?.closedGroupControlMessage!!
|
||||||
val kind: Kind
|
val kind: Kind
|
||||||
when(closedGroupControlMessageProto.type) {
|
when (closedGroupControlMessageProto.type) {
|
||||||
DataMessage.ClosedGroupControlMessage.Type.NEW -> {
|
DataMessage.ClosedGroupControlMessage.Type.NEW -> {
|
||||||
val publicKey = closedGroupControlMessageProto.publicKey ?: return null
|
val publicKey = closedGroupControlMessageProto.publicKey ?: return null
|
||||||
val name = closedGroupControlMessageProto.name ?: return null
|
val name = closedGroupControlMessageProto.name ?: return null
|
||||||
val encryptionKeyPairAsProto = closedGroupControlMessageProto.encryptionKeyPair ?: return null
|
val encryptionKeyPairAsProto = closedGroupControlMessageProto.encryptionKeyPair ?: return null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val encryptionKeyPair = ECKeyPair(DjbECPublicKey(encryptionKeyPairAsProto.publicKey.toByteArray()), DjbECPrivateKey(encryptionKeyPairAsProto.privateKey.toByteArray()))
|
val encryptionKeyPair = ECKeyPair(DjbECPublicKey(encryptionKeyPairAsProto.publicKey.toByteArray()), DjbECPrivateKey(encryptionKeyPairAsProto.privateKey.toByteArray()))
|
||||||
kind = Kind.New(publicKey, name, encryptionKeyPair, closedGroupControlMessageProto.membersList, closedGroupControlMessageProto.adminsList)
|
kind = Kind.New(publicKey, name, encryptionKeyPair, closedGroupControlMessageProto.membersList, closedGroupControlMessageProto.adminsList)
|
||||||
@ -89,10 +80,6 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataMessage.ClosedGroupControlMessage.Type.UPDATE -> {
|
|
||||||
val name = closedGroupControlMessageProto.name ?: return null
|
|
||||||
kind = Kind.Update(name, closedGroupControlMessageProto.membersList)
|
|
||||||
}
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR -> {
|
DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR -> {
|
||||||
val publicKey = closedGroupControlMessageProto.publicKey
|
val publicKey = closedGroupControlMessageProto.publicKey
|
||||||
val wrappers = closedGroupControlMessageProto.wrappersList.mapNotNull { KeyPairWrapper.fromProto(it) }
|
val wrappers = closedGroupControlMessageProto.wrappersList.mapNotNull { KeyPairWrapper.fromProto(it) }
|
||||||
@ -111,35 +98,28 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT -> {
|
DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT -> {
|
||||||
kind = Kind.MemberLeft()
|
kind = Kind.MemberLeft()
|
||||||
}
|
}
|
||||||
DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR_REQUEST -> {
|
|
||||||
kind = Kind.EncryptionKeyPairRequest()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ClosedGroupControlMessage(kind)
|
return ClosedGroupControlMessage(kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// constructor
|
|
||||||
internal constructor(kind: Kind?) : this() {
|
internal constructor(kind: Kind?) : this() {
|
||||||
this.kind = kind
|
this.kind = kind
|
||||||
}
|
}
|
||||||
|
|
||||||
// validation
|
|
||||||
override fun isValid(): Boolean {
|
override fun isValid(): Boolean {
|
||||||
if (!super.isValid()) return false
|
if (!super.isValid()) return false
|
||||||
val kind = kind ?: return false
|
val kind = kind ?: return false
|
||||||
return when(kind) {
|
return when(kind) {
|
||||||
is Kind.New -> {
|
is Kind.New -> {
|
||||||
!kind.publicKey.isEmpty && kind.name.isNotEmpty() && kind.encryptionKeyPair!!.publicKey != null
|
!kind.publicKey.isEmpty && kind.name.isNotEmpty() && kind.encryptionKeyPair!!.publicKey != null
|
||||||
&& kind.encryptionKeyPair!!.privateKey != null && kind.members.isNotEmpty() && kind.admins.isNotEmpty()
|
&& kind.encryptionKeyPair!!.privateKey != null && kind.members.isNotEmpty() && kind.admins.isNotEmpty()
|
||||||
}
|
}
|
||||||
is Kind.Update -> kind.name.isNotEmpty()
|
|
||||||
is Kind.EncryptionKeyPair -> true
|
is Kind.EncryptionKeyPair -> true
|
||||||
is Kind.NameChange -> kind.name.isNotEmpty()
|
is Kind.NameChange -> kind.name.isNotEmpty()
|
||||||
is Kind.MembersAdded -> kind.members.isNotEmpty()
|
is Kind.MembersAdded -> kind.members.isNotEmpty()
|
||||||
is Kind.MembersRemoved -> kind.members.isNotEmpty()
|
is Kind.MembersRemoved -> kind.members.isNotEmpty()
|
||||||
is Kind.MemberLeft -> true
|
is Kind.MemberLeft -> true
|
||||||
is Kind.EncryptionKeyPairRequest -> true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,14 +143,9 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
closedGroupControlMessage.addAllMembers(kind.members)
|
closedGroupControlMessage.addAllMembers(kind.members)
|
||||||
closedGroupControlMessage.addAllAdmins(kind.admins)
|
closedGroupControlMessage.addAllAdmins(kind.admins)
|
||||||
}
|
}
|
||||||
is Kind.Update -> {
|
|
||||||
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.UPDATE
|
|
||||||
closedGroupControlMessage.name = kind.name
|
|
||||||
closedGroupControlMessage.addAllMembers(kind.members)
|
|
||||||
}
|
|
||||||
is Kind.EncryptionKeyPair -> {
|
is Kind.EncryptionKeyPair -> {
|
||||||
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR
|
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR
|
||||||
closedGroupControlMessage.publicKey = kind.publicKey
|
closedGroupControlMessage.publicKey = kind.publicKey ?: ByteString.EMPTY
|
||||||
closedGroupControlMessage.addAllWrappers(kind.wrappers.map { it.toProto() })
|
closedGroupControlMessage.addAllWrappers(kind.wrappers.map { it.toProto() })
|
||||||
}
|
}
|
||||||
is Kind.NameChange -> {
|
is Kind.NameChange -> {
|
||||||
@ -188,9 +163,6 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
is Kind.MemberLeft -> {
|
is Kind.MemberLeft -> {
|
||||||
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT
|
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT
|
||||||
}
|
}
|
||||||
is Kind.EncryptionKeyPairRequest -> {
|
|
||||||
// TODO: closedGroupControlMessage.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.ENCRYPTION_KEY_PAIR_REQUEST
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
val contentProto = SignalServiceProtos.Content.newBuilder()
|
val contentProto = SignalServiceProtos.Content.newBuilder()
|
||||||
val dataMessageProto = DataMessage.newBuilder()
|
val dataMessageProto = DataMessage.newBuilder()
|
||||||
@ -199,8 +171,8 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
setGroupContext(dataMessageProto)
|
setGroupContext(dataMessageProto)
|
||||||
// Expiration timer
|
// Expiration timer
|
||||||
// TODO: We * want * expiration timer updates to be explicit. But currently Android will disable the expiration timer for a conversation
|
// TODO: We * want * expiration timer updates to be explicit. But currently Android will disable the expiration timer for a conversation
|
||||||
// if it receives a message without the current expiration timer value attached to it...
|
// if it receives a message without the current expiration timer value attached to it...
|
||||||
dataMessageProto.expireTimer = Recipient.from(MessagingConfiguration.shared.context, Address.fromSerialized(GroupUtil.doubleEncodeGroupID(recipient!!)), false).expireMessages
|
dataMessageProto.expireTimer = Recipient.from(MessagingModuleConfiguration.shared.context, Address.fromSerialized(GroupUtil.doubleEncodeGroupID(recipient!!)), false).expireMessages
|
||||||
contentProto.dataMessage = dataMessageProto.build()
|
contentProto.dataMessage = dataMessageProto.build()
|
||||||
return contentProto.build()
|
return contentProto.build()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.session.libsession.messaging.messages.control
|
package org.session.libsession.messaging.messages.control
|
||||||
|
|
||||||
import com.google.protobuf.ByteString
|
import com.google.protobuf.ByteString
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
import org.session.libsession.utilities.GroupUtil
|
import org.session.libsession.utilities.GroupUtil
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
@ -26,13 +26,14 @@ class ConfigurationMessage(var closedGroups: List<ClosedGroup>, var openGroups:
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun fromProto(proto: SignalServiceProtos.ConfigurationMessage.ClosedGroup): ClosedGroup? {
|
fun fromProto(proto: SignalServiceProtos.ConfigurationMessage.ClosedGroup): ClosedGroup? {
|
||||||
if (!proto.hasPublicKey() || !proto.hasName() || !proto.hasEncryptionKeyPair()) return null
|
if (!proto.hasPublicKey() || !proto.hasName() || !proto.hasEncryptionKeyPair()) return null
|
||||||
val publicKey = proto.publicKey.toByteArray().toHexString()
|
val publicKey = proto.publicKey.toByteArray().toHexString()
|
||||||
val name = proto.name
|
val name = proto.name
|
||||||
val encryptionKeyPairAsProto = proto.encryptionKeyPair
|
val encryptionKeyPairAsProto = proto.encryptionKeyPair
|
||||||
val encryptionKeyPair = ECKeyPair(DjbECPublicKey(encryptionKeyPairAsProto.publicKey.toByteArray().removing05PrefixIfNeeded()),
|
val encryptionKeyPair = ECKeyPair(DjbECPublicKey(encryptionKeyPairAsProto.publicKey.toByteArray().removing05PrefixIfNeeded()),
|
||||||
DjbECPrivateKey(encryptionKeyPairAsProto.privateKey.toByteArray()))
|
DjbECPrivateKey(encryptionKeyPairAsProto.privateKey.toByteArray()))
|
||||||
val members = proto.membersList.map { it.toByteArray().toHexString() }
|
val members = proto.membersList.map { it.toByteArray().toHexString() }
|
||||||
val admins = proto.adminsList.map { it.toByteArray().toHexString() }
|
val admins = proto.adminsList.map { it.toByteArray().toHexString() }
|
||||||
return ClosedGroup(publicKey, name, encryptionKeyPair, members, admins)
|
return ClosedGroup(publicKey, name, encryptionKeyPair, members, admins)
|
||||||
@ -58,6 +59,7 @@ class ConfigurationMessage(var closedGroups: List<ClosedGroup>, var openGroups:
|
|||||||
internal constructor(): this("", "", null, null)
|
internal constructor(): this("", "", null, null)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun fromProto(proto: SignalServiceProtos.ConfigurationMessage.Contact): Contact? {
|
fun fromProto(proto: SignalServiceProtos.ConfigurationMessage.Contact): Contact? {
|
||||||
if (!proto.hasName() || !proto.hasProfileKey()) return null
|
if (!proto.hasName() || !proto.hasProfileKey()) return null
|
||||||
val publicKey = proto.publicKey.toByteArray().toHexString()
|
val publicKey = proto.publicKey.toByteArray().toHexString()
|
||||||
@ -87,7 +89,6 @@ class ConfigurationMessage(var closedGroups: List<ClosedGroup>, var openGroups:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override val ttl: Long = 4 * 24 * 60 * 60 * 1000
|
|
||||||
override val isSelfSendValid: Boolean = true
|
override val isSelfSendValid: Boolean = true
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -95,7 +96,7 @@ class ConfigurationMessage(var closedGroups: List<ClosedGroup>, var openGroups:
|
|||||||
fun getCurrent(contacts: List<Contact>): ConfigurationMessage? {
|
fun getCurrent(contacts: List<Contact>): ConfigurationMessage? {
|
||||||
val closedGroups = mutableListOf<ClosedGroup>()
|
val closedGroups = mutableListOf<ClosedGroup>()
|
||||||
val openGroups = mutableListOf<String>()
|
val openGroups = mutableListOf<String>()
|
||||||
val sharedConfig = MessagingConfiguration.shared
|
val sharedConfig = MessagingModuleConfiguration.shared
|
||||||
val storage = sharedConfig.storage
|
val storage = sharedConfig.storage
|
||||||
val context = sharedConfig.context
|
val context = sharedConfig.context
|
||||||
val displayName = TextSecurePreferences.getProfileName(context) ?: return null
|
val displayName = TextSecurePreferences.getProfileName(context) ?: return null
|
||||||
|
@ -6,13 +6,12 @@ import org.session.libsignal.utilities.logging.Log
|
|||||||
class DataExtractionNotification(): ControlMessage() {
|
class DataExtractionNotification(): ControlMessage() {
|
||||||
var kind: Kind? = null
|
var kind: Kind? = null
|
||||||
|
|
||||||
// Kind enum
|
|
||||||
sealed class Kind {
|
sealed class Kind {
|
||||||
class Screenshot() : Kind()
|
class Screenshot() : Kind()
|
||||||
class MediaSaved(val timestamp: Long) : Kind()
|
class MediaSaved(val timestamp: Long) : Kind()
|
||||||
|
|
||||||
val description: String =
|
val description: String =
|
||||||
when(this) {
|
when (this) {
|
||||||
is Screenshot -> "screenshot"
|
is Screenshot -> "screenshot"
|
||||||
is MediaSaved -> "mediaSaved"
|
is MediaSaved -> "mediaSaved"
|
||||||
}
|
}
|
||||||
@ -35,12 +34,10 @@ class DataExtractionNotification(): ControlMessage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//constructor
|
|
||||||
internal constructor(kind: Kind) : this() {
|
internal constructor(kind: Kind) : this() {
|
||||||
this.kind = kind
|
this.kind = kind
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Validation
|
|
||||||
override fun isValid(): Boolean {
|
override fun isValid(): Boolean {
|
||||||
if (!super.isValid()) return false
|
if (!super.isValid()) return false
|
||||||
val kind = kind ?: return false
|
val kind = kind ?: return false
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package org.session.libsession.messaging.messages.control
|
package org.session.libsession.messaging.messages.control
|
||||||
|
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
|
|
||||||
class ExpirationTimerUpdate() : ControlMessage() {
|
class ExpirationTimerUpdate() : ControlMessage() {
|
||||||
|
|
||||||
/// In the case of a sync message, the public key of the person the message was targeted at.
|
/// In the case of a sync message, the public key of the person the message was targeted at.
|
||||||
/// - Note: `nil` if this isn't a sync message.
|
/// - Note: `nil` if this isn't a sync message.
|
||||||
var syncTarget: String? = null
|
var syncTarget: String? = null
|
||||||
@ -27,7 +26,6 @@ class ExpirationTimerUpdate() : ControlMessage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//constructor
|
|
||||||
internal constructor(syncTarget: String?, duration: Int) : this() {
|
internal constructor(syncTarget: String?, duration: Int) : this() {
|
||||||
this.syncTarget = syncTarget
|
this.syncTarget = syncTarget
|
||||||
this.duration = duration
|
this.duration = duration
|
||||||
@ -38,7 +36,6 @@ class ExpirationTimerUpdate() : ControlMessage() {
|
|||||||
this.duration = duration
|
this.duration = duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// validation
|
|
||||||
override fun isValid(): Boolean {
|
override fun isValid(): Boolean {
|
||||||
if (!super.isValid()) return false
|
if (!super.isValid()) return false
|
||||||
return duration != null
|
return duration != null
|
||||||
@ -58,7 +55,7 @@ class ExpirationTimerUpdate() : ControlMessage() {
|
|||||||
dataMessageProto.syncTarget = syncTarget
|
dataMessageProto.syncTarget = syncTarget
|
||||||
}
|
}
|
||||||
// Group context
|
// Group context
|
||||||
if (MessagingConfiguration.shared.storage.isClosedGroup(recipient!!)) {
|
if (MessagingModuleConfiguration.shared.storage.isClosedGroup(recipient!!)) {
|
||||||
try {
|
try {
|
||||||
setGroupContext(dataMessageProto)
|
setGroupContext(dataMessageProto)
|
||||||
} catch(e: Exception) {
|
} catch(e: Exception) {
|
||||||
|
@ -4,7 +4,6 @@ import org.session.libsignal.service.internal.push.SignalServiceProtos
|
|||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
|
|
||||||
class ReadReceipt() : ControlMessage() {
|
class ReadReceipt() : ControlMessage() {
|
||||||
|
|
||||||
var timestamps: List<Long>? = null
|
var timestamps: List<Long>? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -19,12 +18,10 @@ class ReadReceipt() : ControlMessage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//constructor
|
|
||||||
internal constructor(timestamps: List<Long>?) : this() {
|
internal constructor(timestamps: List<Long>?) : this() {
|
||||||
this.timestamps = timestamps
|
this.timestamps = timestamps
|
||||||
}
|
}
|
||||||
|
|
||||||
// validation
|
|
||||||
override fun isValid(): Boolean {
|
override fun isValid(): Boolean {
|
||||||
if (!super.isValid()) return false
|
if (!super.isValid()) return false
|
||||||
val timestamps = timestamps ?: return false
|
val timestamps = timestamps ?: return false
|
||||||
|
@ -4,8 +4,8 @@ import org.session.libsignal.service.internal.push.SignalServiceProtos
|
|||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
|
|
||||||
class TypingIndicator() : ControlMessage() {
|
class TypingIndicator() : ControlMessage() {
|
||||||
|
|
||||||
override val ttl: Long = 30 * 1000
|
override val ttl: Long = 30 * 1000
|
||||||
|
var kind: Kind? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "TypingIndicator"
|
const val TAG = "TypingIndicator"
|
||||||
@ -17,11 +17,8 @@ class TypingIndicator() : ControlMessage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kind enum
|
|
||||||
enum class Kind {
|
enum class Kind {
|
||||||
STARTED,
|
STARTED, STOPPED;
|
||||||
STOPPED,
|
|
||||||
;
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@ -40,14 +37,10 @@ class TypingIndicator() : ControlMessage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var kind: Kind? = null
|
|
||||||
|
|
||||||
//constructor
|
|
||||||
internal constructor(kind: Kind) : this() {
|
internal constructor(kind: Kind) : this() {
|
||||||
this.kind = kind
|
this.kind = kind
|
||||||
}
|
}
|
||||||
|
|
||||||
// validation
|
|
||||||
override fun isValid(): Boolean {
|
override fun isValid(): Boolean {
|
||||||
if (!super.isValid()) return false
|
if (!super.isValid()) return false
|
||||||
return kind != null
|
return kind != null
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.session.libsession.messaging.messages.signal;
|
package org.session.libsession.messaging.messages.signal;
|
||||||
|
|
||||||
import static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext;
|
|
||||||
|
|
||||||
public class IncomingGroupMessage extends IncomingTextMessage {
|
public class IncomingGroupMessage extends IncomingTextMessage {
|
||||||
|
|
||||||
private final String groupID;
|
private final String groupID;
|
||||||
|
@ -3,10 +3,10 @@ package org.session.libsession.messaging.messages.signal;
|
|||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||||
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.PointerAttachment;
|
import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment;
|
||||||
import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage;
|
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage;
|
||||||
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
||||||
import org.session.libsession.messaging.threads.Address;
|
import org.session.libsession.messaging.threads.Address;
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
||||||
import org.session.libsession.utilities.GroupUtil;
|
import org.session.libsession.utilities.GroupUtil;
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||||
|
@ -6,14 +6,10 @@ import androidx.annotation.Nullable;
|
|||||||
import org.session.libsession.messaging.threads.DistributionTypes;
|
import org.session.libsession.messaging.threads.DistributionTypes;
|
||||||
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.sharecontacts.Contact;
|
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||||
import org.session.libsignal.utilities.Base64;
|
|
||||||
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import org.session.libsession.database.documents.IdentityKeyMismatch;
|
|||||||
import org.session.libsession.database.documents.NetworkFailure;
|
import org.session.libsession.database.documents.NetworkFailure;
|
||||||
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.sharecontacts.Contact;
|
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
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.sharecontacts.Contact;
|
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import org.session.libsignal.service.internal.push.SignalServiceProtos
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class Attachment {
|
class Attachment {
|
||||||
|
|
||||||
var fileName: String? = null
|
var fileName: String? = null
|
||||||
var contentType: String? = null
|
var contentType: String? = null
|
||||||
var key: ByteArray? = null
|
var key: ByteArray? = null
|
||||||
@ -23,6 +22,7 @@ class Attachment {
|
|||||||
var url: String? = null
|
var url: String? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun fromProto(proto: SignalServiceProtos.AttachmentPointer): Attachment {
|
fun fromProto(proto: SignalServiceProtos.AttachmentPointer): Attachment {
|
||||||
val result = Attachment()
|
val result = Attachment()
|
||||||
result.fileName = proto.fileName
|
result.fileName = proto.fileName
|
||||||
@ -88,7 +88,6 @@ class Attachment {
|
|||||||
GENERIC
|
GENERIC
|
||||||
}
|
}
|
||||||
|
|
||||||
// validation
|
|
||||||
fun isValid(): Boolean {
|
fun isValid(): Boolean {
|
||||||
// key and digest can be nil for open group attachments
|
// key and digest can be nil for open group attachments
|
||||||
return (contentType != null && kind != null && size != null && sizeInBytes != null && url != null)
|
return (contentType != null && kind != null && size != null && sizeInBytes != null && url != null)
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
package org.session.libsession.messaging.messages.visible
|
|
||||||
|
|
||||||
import org.session.libsession.database.MessageDataProvider
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
|
||||||
|
|
||||||
class Contact() {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromProto(proto: SignalServiceProtos.Content): Contact? {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toProto(): SignalServiceProtos.DataMessage.Contact? {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +1,11 @@
|
|||||||
package org.session.libsession.messaging.messages.visible
|
package org.session.libsession.messaging.messages.visible
|
||||||
|
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview as SignalLinkPreiview
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview as SignalLinkPreiview
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
|
|
||||||
class LinkPreview() {
|
class LinkPreview() {
|
||||||
|
|
||||||
var title: String? = null
|
var title: String? = null
|
||||||
var url: String? = null
|
var url: String? = null
|
||||||
var attachmentID: Long? = 0
|
var attachmentID: Long? = 0
|
||||||
@ -29,15 +28,12 @@ class LinkPreview() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//constructor
|
|
||||||
internal constructor(title: String?, url: String, attachmentID: Long?) : this() {
|
internal constructor(title: String?, url: String, attachmentID: Long?) : this() {
|
||||||
this.title = title
|
this.title = title
|
||||||
this.url = url
|
this.url = url
|
||||||
this.attachmentID = attachmentID
|
this.attachmentID = attachmentID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// validation
|
|
||||||
fun isValid(): Boolean {
|
fun isValid(): Boolean {
|
||||||
return (title != null && url != null && attachmentID != null)
|
return (title != null && url != null && attachmentID != null)
|
||||||
}
|
}
|
||||||
@ -53,7 +49,7 @@ class LinkPreview() {
|
|||||||
title?.let { linkPreviewProto.title = title }
|
title?.let { linkPreviewProto.title = title }
|
||||||
val attachmentID = attachmentID
|
val attachmentID = attachmentID
|
||||||
attachmentID?.let {
|
attachmentID?.let {
|
||||||
MessagingConfiguration.shared.messageDataProvider.getSignalAttachmentPointer(attachmentID)?.let {
|
MessagingModuleConfiguration.shared.messageDataProvider.getSignalAttachmentPointer(attachmentID)?.let {
|
||||||
val attachmentProto = Attachment.createAttachmentPointer(it)
|
val attachmentProto = Attachment.createAttachmentPointer(it)
|
||||||
linkPreviewProto.image = attachmentProto
|
linkPreviewProto.image = attachmentProto
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import org.session.libsignal.utilities.logging.Log
|
|||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
|
|
||||||
class Profile() {
|
class Profile() {
|
||||||
|
|
||||||
var displayName: String? = null
|
var displayName: String? = null
|
||||||
var profileKey: ByteArray? = null
|
var profileKey: ByteArray? = null
|
||||||
var profilePictureURL: String? = null
|
var profilePictureURL: String? = null
|
||||||
@ -27,7 +26,6 @@ class Profile() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//constructor
|
|
||||||
internal constructor(displayName: String, profileKey: ByteArray? = null, profilePictureURL: String? = null) : this() {
|
internal constructor(displayName: String, profileKey: ByteArray? = null, profilePictureURL: String? = null) : this() {
|
||||||
this.displayName = displayName
|
this.displayName = displayName
|
||||||
this.profileKey = profileKey
|
this.profileKey = profileKey
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
package org.session.libsession.messaging.messages.visible
|
package org.session.libsession.messaging.messages.visible
|
||||||
|
|
||||||
import com.goterl.lazycode.lazysodium.BuildConfig
|
import com.goterl.lazycode.lazysodium.BuildConfig
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel as SignalQuote
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel as SignalQuote
|
||||||
import org.session.libsignal.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
|
|
||||||
class Quote() {
|
class Quote() {
|
||||||
|
|
||||||
var timestamp: Long? = 0
|
var timestamp: Long? = 0
|
||||||
var publicKey: String? = null
|
var publicKey: String? = null
|
||||||
var text: String? = null
|
var text: String? = null
|
||||||
@ -34,7 +33,6 @@ class Quote() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//constructor
|
|
||||||
internal constructor(timestamp: Long, publicKey: String, text: String?, attachmentID: Long?) : this() {
|
internal constructor(timestamp: Long, publicKey: String, text: String?, attachmentID: Long?) : this() {
|
||||||
this.timestamp = timestamp
|
this.timestamp = timestamp
|
||||||
this.publicKey = publicKey
|
this.publicKey = publicKey
|
||||||
@ -42,7 +40,6 @@ class Quote() {
|
|||||||
this.attachmentID = attachmentID
|
this.attachmentID = attachmentID
|
||||||
}
|
}
|
||||||
|
|
||||||
// validation
|
|
||||||
fun isValid(): Boolean {
|
fun isValid(): Boolean {
|
||||||
return (timestamp != null && publicKey != null)
|
return (timestamp != null && publicKey != null)
|
||||||
}
|
}
|
||||||
@ -70,7 +67,7 @@ class Quote() {
|
|||||||
|
|
||||||
private fun addAttachmentsIfNeeded(quoteProto: SignalServiceProtos.DataMessage.Quote.Builder) {
|
private fun addAttachmentsIfNeeded(quoteProto: SignalServiceProtos.DataMessage.Quote.Builder) {
|
||||||
if (attachmentID == null) return
|
if (attachmentID == null) return
|
||||||
val attachment = MessagingConfiguration.shared.messageDataProvider.getSignalAttachmentPointer(attachmentID!!)
|
val attachment = MessagingModuleConfiguration.shared.messageDataProvider.getSignalAttachmentPointer(attachmentID!!)
|
||||||
if (attachment == null) {
|
if (attachment == null) {
|
||||||
Log.w(TAG, "Ignoring invalid attachment for quoted message.")
|
Log.w(TAG, "Ignoring invalid attachment for quoted message.")
|
||||||
return
|
return
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.session.libsession.messaging.messages.visible
|
package org.session.libsession.messaging.messages.visible
|
||||||
|
|
||||||
import com.goterl.lazycode.lazysodium.BuildConfig
|
import com.goterl.lazycode.lazysodium.BuildConfig
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.messages.Message
|
import org.session.libsession.messaging.messages.Message
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||||
import org.session.libsession.messaging.threads.Address
|
import org.session.libsession.messaging.threads.Address
|
||||||
@ -12,13 +12,11 @@ import org.session.libsignal.utilities.logging.Log
|
|||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment as SignalAttachment
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment as SignalAttachment
|
||||||
|
|
||||||
class VisibleMessage : Message() {
|
class VisibleMessage : Message() {
|
||||||
|
|
||||||
var syncTarget: String? = null
|
var syncTarget: String? = null
|
||||||
var text: String? = null
|
var text: String? = null
|
||||||
val attachmentIDs: MutableList<Long> = mutableListOf()
|
val attachmentIDs: MutableList<Long> = mutableListOf()
|
||||||
var quote: Quote? = null
|
var quote: Quote? = null
|
||||||
var linkPreview: LinkPreview? = null
|
var linkPreview: LinkPreview? = null
|
||||||
var contact: Contact? = null
|
|
||||||
var profile: Profile? = null
|
var profile: Profile? = null
|
||||||
|
|
||||||
override val isSelfSendValid: Boolean = true
|
override val isSelfSendValid: Boolean = true
|
||||||
@ -60,10 +58,9 @@ class VisibleMessage : Message() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isMediaMessage(): Boolean {
|
fun isMediaMessage(): Boolean {
|
||||||
return attachmentIDs.isNotEmpty() || quote != null || linkPreview != null || contact != null
|
return attachmentIDs.isNotEmpty() || quote != null || linkPreview != null
|
||||||
}
|
}
|
||||||
|
|
||||||
// validation
|
|
||||||
override fun isValid(): Boolean {
|
override fun isValid(): Boolean {
|
||||||
if (!super.isValid()) return false
|
if (!super.isValid()) return false
|
||||||
if (attachmentIDs.isNotEmpty()) return true
|
if (attachmentIDs.isNotEmpty()) return true
|
||||||
@ -98,7 +95,7 @@ class VisibleMessage : Message() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Attachments
|
//Attachments
|
||||||
val attachments = attachmentIDs.mapNotNull { MessagingConfiguration.shared.messageDataProvider.getSignalAttachmentPointer(it) }
|
val attachments = attachmentIDs.mapNotNull { MessagingModuleConfiguration.shared.messageDataProvider.getSignalAttachmentPointer(it) }
|
||||||
if (!attachments.all { !it.url.isNullOrEmpty() }) {
|
if (!attachments.all { !it.url.isNullOrEmpty() }) {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
//TODO equivalent to iOS's preconditionFailure
|
//TODO equivalent to iOS's preconditionFailure
|
||||||
@ -111,8 +108,8 @@ class VisibleMessage : Message() {
|
|||||||
// Expiration timer
|
// Expiration timer
|
||||||
// TODO: We * want * expiration timer updates to be explicit. But currently Android will disable the expiration timer for a conversation
|
// TODO: We * want * expiration timer updates to be explicit. But currently Android will disable the expiration timer for a conversation
|
||||||
// if it receives a message without the current expiration timer value attached to it...
|
// if it receives a message without the current expiration timer value attached to it...
|
||||||
val storage = MessagingConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val context = MessagingConfiguration.shared.context
|
val context = MessagingModuleConfiguration.shared.context
|
||||||
val expiration = if (storage.isClosedGroup(recipient!!)) Recipient.from(context, Address.fromSerialized(GroupUtil.doubleEncodeGroupID(recipient!!)), false).expireMessages
|
val expiration = if (storage.isClosedGroup(recipient!!)) Recipient.from(context, Address.fromSerialized(GroupUtil.doubleEncodeGroupID(recipient!!)), false).expireMessages
|
||||||
else Recipient.from(context, Address.fromSerialized(recipient!!), false).expireMessages
|
else Recipient.from(context, Address.fromSerialized(recipient!!), false).expireMessages
|
||||||
dataMessage.expireTimer = expiration
|
dataMessage.expireTimer = expiration
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user