diff --git a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java index c59224d481..66a4943088 100644 --- a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java +++ b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java @@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.jobs.TypingSendJob; import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.loki.LokiSessionResetImplementation; +import org.thoughtcrime.securesms.loki.MultiDeviceOpenGroupUpdateJob; import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob; import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment; import org.thoughtcrime.securesms.push.MessageSenderEventListener; @@ -115,7 +116,8 @@ import network.loki.messenger.BuildConfig; MultiDeviceStickerPackOperationJob.class, MultiDeviceStickerPackSyncJob.class, LinkPreviewRepository.class, - PushMessageSyncSendJob.class}) + PushMessageSyncSendJob.class, + MultiDeviceOpenGroupUpdateJob.class}) public class SignalCommunicationModule { diff --git a/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index cfa9aa7beb..9911b53514 100644 --- a/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver; +import org.thoughtcrime.securesms.loki.MultiDeviceOpenGroupUpdateJob; import org.thoughtcrime.securesms.loki.PushBackgroundMessageSendJob; import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob; @@ -74,6 +75,7 @@ public final class JobManagerFactories { put(UpdateApkJob.KEY, new UpdateApkJob.Factory()); put(PushMessageSyncSendJob.KEY, new PushMessageSyncSendJob.Factory()); put(PushBackgroundMessageSendJob.KEY, new PushBackgroundMessageSendJob.Factory()); + put(MultiDeviceOpenGroupUpdateJob.KEY, new MultiDeviceOpenGroupUpdateJob.Factory()); }}; } diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 6d7595b9b8..efca0e5e3a 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -59,6 +59,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.StickerRecord; import org.thoughtcrime.securesms.dependencies.InjectableType; +import org.thoughtcrime.securesms.groups.GroupManager; import org.thoughtcrime.securesms.groups.GroupMessageProcessor; import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; @@ -72,6 +73,7 @@ import org.thoughtcrime.securesms.loki.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.LokiSessionResetImplementation; import org.thoughtcrime.securesms.loki.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.MultiDeviceUtilities; +import org.thoughtcrime.securesms.loki.OpenGroupUtilities; import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity; import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities; import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyBundleDatabase; @@ -138,6 +140,7 @@ import org.whispersystems.signalservice.loki.api.DeviceLink; import org.whispersystems.signalservice.loki.api.DeviceLinkingSession; import org.whispersystems.signalservice.loki.api.LokiAPI; import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities; +import org.whispersystems.signalservice.loki.api.LokiPublicChat; import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher; import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus; import org.whispersystems.signalservice.loki.messaging.LokiServiceMessage; @@ -393,6 +396,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get()); else if (syncMessage.getContacts().isPresent()) handleContactSyncMessage(syncMessage.getContacts().get()); else if (syncMessage.getGroups().isPresent()) handleGroupSyncMessage(content, syncMessage.getGroups().get()); + else if (syncMessage.getOpenGroups().isPresent()) handleOpenGroupSyncMessage(syncMessage.getOpenGroups().get()); else Log.w(TAG, "Contains no known sync types..."); } else if (content.getCallMessage().isPresent()) { Log.i(TAG, "Got call message..."); @@ -749,6 +753,24 @@ public class PushDecryptJob extends BaseJob implements InjectableType { } } + private void handleOpenGroupSyncMessage(@NonNull List openGroups) { + try { + for (LokiPublicChat openGroup : openGroups) { + long threadID = GroupManager.getPublicChatThreadId(openGroup.getId(), context); + if (threadID > -1) continue; + + String url = openGroup.getServer(); + long channel = openGroup.getChannel(); + OpenGroupUtilities.addGroup(context, url, channel).fail(e -> { + Log.d("Loki", "Failed to sync open group " + url + " due to error: " + e + "."); + return Unit.INSTANCE; + }); + } + } catch (Exception e) { + Log.d("Loki", "Failed to sync open groups due to error: " + e + "."); + } + } + private void handleSynchronizeSentMessage(@NonNull SignalServiceContent content, @NonNull SentTranscriptMessage message) throws StorageFailedException diff --git a/src/org/thoughtcrime/securesms/loki/MultiDeviceOpenGroupUpdateJob.kt b/src/org/thoughtcrime/securesms/loki/MultiDeviceOpenGroupUpdateJob.kt new file mode 100644 index 0000000000..38ee04790b --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/MultiDeviceOpenGroupUpdateJob.kt @@ -0,0 +1,83 @@ +package org.thoughtcrime.securesms.loki + +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.dependencies.InjectableType +import org.thoughtcrime.securesms.groups.GroupManager +import org.thoughtcrime.securesms.jobmanager.Data +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.BaseJob +import org.thoughtcrime.securesms.logging.Log +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.whispersystems.signalservice.api.SignalServiceMessageSender +import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage +import org.whispersystems.signalservice.loki.api.LokiPublicChat +import java.util.concurrent.TimeUnit +import javax.inject.Inject + +class MultiDeviceOpenGroupUpdateJob private constructor(parameters: Parameters) : BaseJob(parameters), InjectableType { + companion object { + const val KEY = "MultiDeviceGroupUpdateJob" + private val TAG = MultiDeviceOpenGroupUpdateJob::class.java.simpleName + } + + @Inject + lateinit var messageSender: SignalServiceMessageSender + + constructor() : this(Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue("MultiDeviceGroupUpdateJob") + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .build()) + + override fun getFactoryKey(): String { + return KEY + } + + override fun serialize(): Data { + return Data.EMPTY + } + + @Throws(Exception::class) + public override fun onRun() { + if (!TextSecurePreferences.isMultiDevice(context)) { + Log.i(TAG, "Not multi device, aborting...") + return + } + + val openGroups = mutableListOf() + DatabaseFactory.getGroupDatabase(context).groups.use { reader -> + while (true) { + val record = reader.next ?: return@use + if (!record.isPublicChat) { continue; } + + val threadID = GroupManager.getThreadIdFromGroupId(record.encodedId, context) + val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) + if (openGroup != null) { + openGroups.add(openGroup) + } + } + } + + if (openGroups.size > 0) { + messageSender.sendMessage(0, SignalServiceSyncMessage.forOpenGroups(openGroups), + UnidentifiedAccessUtil.getAccessForSync(context)) + } else { + Log.d(TAG, "No open groups to sync.") + } + } + + public override fun onShouldRetry(exception: Exception): Boolean { + return false + } + + override fun onCanceled() { } + + class Factory : Job.Factory { + override fun create(parameters: Parameters, data: Data): MultiDeviceOpenGroupUpdateJob { + return MultiDeviceOpenGroupUpdateJob(parameters) + } + } +} diff --git a/src/org/thoughtcrime/securesms/loki/OpenGroupUtilities.kt b/src/org/thoughtcrime/securesms/loki/OpenGroupUtilities.kt new file mode 100644 index 0000000000..ae47fb25da --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/OpenGroupUtilities.kt @@ -0,0 +1,39 @@ +package org.thoughtcrime.securesms.loki + +import android.content.Context +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.then +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.groups.GroupManager +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.whispersystems.signalservice.loki.api.LokiPublicChat + +object OpenGroupUtilities { + @JvmStatic fun addGroup(context: Context, url: String, channel: Long): Promise { + // Check if we have an existing group + val groupId = LokiPublicChat.getId(channel, url) + val threadID = GroupManager.getPublicChatThreadId(groupId, context) + val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) + if (openGroup != null) { + return Promise.of(openGroup) + } + + // Add the new group + val application = ApplicationContext.getInstance(context) + val displayName = TextSecurePreferences.getProfileName(context) + val lokiPublicChatAPI = application.lokiPublicChatAPI ?: throw Error("LokiPublicChatAPI is not initialized") + return application.lokiPublicChatManager.addChat(url, channel).then { group -> + DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url) + DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url) + lokiPublicChatAPI.getMessages(channel, url) + lokiPublicChatAPI.setDisplayName(displayName, url) + lokiPublicChatAPI.join(channel, url) + val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(context) + val profileUrl: String? = TextSecurePreferences.getProfileAvatarUrl(context) + lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl) + group + } + } +} \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/JoinPublicChatActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/JoinPublicChatActivity.kt index 6d3b940378..a7f4b69bc7 100644 --- a/src/org/thoughtcrime/securesms/loki/redesign/activities/JoinPublicChatActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/JoinPublicChatActivity.kt @@ -16,14 +16,12 @@ import kotlinx.android.synthetic.main.fragment_enter_chat_url.* import network.loki.messenger.R import nl.komponents.kovenant.ui.failUi import nl.komponents.kovenant.ui.successUi -import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil -import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.loki.OpenGroupUtilities import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragment import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragmentDelegate -import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.thoughtcrime.securesms.sms.MessageSender class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate { private val adapter = JoinPublicChatActivityAdapter(this) @@ -68,19 +66,11 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode return Toast.makeText(this, "Invalid URL", Toast.LENGTH_SHORT).show() } showLoader() - val application = ApplicationContext.getInstance(this) + val channel: Long = 1 - val displayName = TextSecurePreferences.getProfileName(this) - val lokiPublicChatAPI = application.lokiPublicChatAPI!! - application.lokiPublicChatManager.addChat(url, channel).successUi { - DatabaseFactory.getLokiAPIDatabase(this).removeLastMessageServerID(channel, url) - DatabaseFactory.getLokiAPIDatabase(this).removeLastDeletionServerID(channel, url) - lokiPublicChatAPI.getMessages(channel, url) - lokiPublicChatAPI.setDisplayName(displayName, url) - lokiPublicChatAPI.join(channel, url) - val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(this) - val profileUrl: String? = TextSecurePreferences.getProfileAvatarUrl(this) - lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl) + OpenGroupUtilities.addGroup(this, url, channel).success { + MessageSender.syncAllOpenGroups(this) + }.successUi { finish() }.failUi { hideLoader() diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt index 759362b91c..2fae3f0914 100644 --- a/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt @@ -151,6 +151,7 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager Timer().schedule(4000) { MessageSender.syncAllGroups(this@LinkedDevicesActivity) MessageSender.syncAllContacts(this@LinkedDevicesActivity, Address.fromSerialized(deviceLink.slaveHexEncodedPublicKey)) + MessageSender.syncAllOpenGroups(this@LinkedDevicesActivity) } }.failUi { Toast.makeText(this, "Couldn't link device", Toast.LENGTH_LONG).show() diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java index 80a4baa482..7a72ac68d2 100644 --- a/src/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java @@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.loki.BackgroundMessage; import org.thoughtcrime.securesms.loki.FriendRequestHandler; import org.thoughtcrime.securesms.loki.GeneralUtilitiesKt; +import org.thoughtcrime.securesms.loki.MultiDeviceOpenGroupUpdateJob; import org.thoughtcrime.securesms.loki.MultiDeviceUtilities; import org.thoughtcrime.securesms.loki.PushBackgroundMessageSendJob; import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob; @@ -86,6 +87,10 @@ public class MessageSender { ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceGroupUpdateJob()); } + public static void syncAllOpenGroups(Context context) { + ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceOpenGroupUpdateJob()); + } + /** * Send a contact sync message to all our devices telling them that we want to sync `contact` */ diff --git a/src/org/thoughtcrime/securesms/util/GroupUtil.java b/src/org/thoughtcrime/securesms/util/GroupUtil.java index d4bdffa291..0e869da787 100644 --- a/src/org/thoughtcrime/securesms/util/GroupUtil.java +++ b/src/org/thoughtcrime/securesms/util/GroupUtil.java @@ -4,15 +4,13 @@ import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; -import android.widget.Toast; import com.google.protobuf.ByteString; -import network.loki.messenger.R; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.GroupDatabase.*; +import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.recipients.Recipient; @@ -26,6 +24,8 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import network.loki.messenger.R; + import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; public class GroupUtil { @@ -114,6 +114,8 @@ public class GroupUtil { } public static boolean leaveGroup(@NonNull Context context, Recipient groupRecipient) { + if (!groupRecipient.getAddress().isSignalGroup()) { return true; } + long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient); Optional leaveMessage = GroupUtil.createGroupLeaveMessage(context, groupRecipient);