diff --git a/src/org/thoughtcrime/securesms/database/Address.java b/src/org/thoughtcrime/securesms/database/Address.java
index 0969e0e14d..d777b6e86b 100644
--- a/src/org/thoughtcrime/securesms/database/Address.java
+++ b/src/org/thoughtcrime/securesms/database/Address.java
@@ -105,7 +105,7 @@ public class Address implements Parcelable, Comparable
{
public boolean isGroup() { return GroupUtil.isEncodedGroup(address); }
- public boolean isSignalGroup() { return !isPublicChat() && !isRSSFeed(); }
+ public boolean isSignalGroup() { return GroupUtil.isSignalGroup(address); }
public boolean isPublicChat() { return GroupUtil.isPublicChat(address); }
diff --git a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
index 9e1bf481f1..1e7a3dfb4a 100644
--- a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
+++ b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
@@ -46,6 +46,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;
@@ -111,7 +112,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 266d6194df..3a375a6169 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;
@@ -73,6 +74,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 d85fe7b720..4fc2508de9 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.redesign.utilities.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.api.LokiFileServerAPI;
import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher;
import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus;
@@ -394,6 +397,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...");
@@ -751,6 +755,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..0ef7ca67ed
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/MultiDeviceOpenGroupUpdateJob.kt
@@ -0,0 +1,80 @@
+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"
+ }
+
+ @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.d("Loki", "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("Loki", "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/redesign/activities/HomeActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt
index 3993a93f10..44812f438d 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt
@@ -194,7 +194,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
private fun openConversation(thread: ThreadRecord) {
val intent = Intent(this, ConversationActivity::class.java)
- intent.putExtra(ConversationActivity.ADDRESS_EXTRA, thread.recipient.getAddress())
+ intent.putExtra(ConversationActivity.ADDRESS_EXTRA, thread.recipient.address)
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, thread.threadId)
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, thread.distributionType)
intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis())
@@ -255,9 +255,9 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
val dialog = AlertDialog.Builder(activity)
dialog.setMessage(dialogMessage)
dialog.setPositiveButton(R.string.yes) { _, _ ->
- val isGroup = recipient.isGroupRecipient
+ val isClosedGroup = recipient.address.isSignalGroup
// Send a leave group message if this is an active closed group
- if (isGroup && DatabaseFactory.getGroupDatabase(activity).isActive(recipient.address.toGroupString())) {
+ if (isClosedGroup && DatabaseFactory.getGroupDatabase(activity).isActive(recipient.address.toGroupString())) {
if (!GroupUtil.leaveGroup(activity, recipient)) {
Toast.makeText(activity, "Couldn't leave group", Toast.LENGTH_LONG).show()
clearView(activity.recyclerView, viewHolder)
@@ -267,10 +267,11 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
// Archive the conversation and then delete it after 10 seconds (the case where the
// app was closed before the conversation could be deleted is handled in onCreate)
threadDatabase.archiveConversation(threadID)
+ val delay = if (isClosedGroup) 10000L else 1000L
val handler = Handler()
- handler.postDelayed(deleteThread, 10000)
+ handler.postDelayed(deleteThread, delay)
// Notify the user
- val toastMessage = if (isGroup) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message
+ val toastMessage = if (recipient.isGroupRecipient) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message
Toast.makeText(activity, toastMessage, Toast.LENGTH_LONG).show()
}
dialog.setNegativeButton(R.string.no) { _, _ ->
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/JoinPublicChatActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/JoinPublicChatActivity.kt
index 6d3b940378..9364419d24 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.redesign.utilities.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 1def8899cd..d9147fbfdd 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt
@@ -154,6 +154,7 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager
Timer().schedule(4000) {
MessageSender.syncAllGroups(this@LinkedDevicesActivity)
MessageSender.syncAllContacts(this@LinkedDevicesActivity, Address.fromSerialized(deviceLink.slaveHexEncodedPublicKey))
+ MessageSender.syncAllOpenGroups(this@LinkedDevicesActivity)
}
}.fail {
LokiFileServerAPI.shared.removeDeviceLink(deviceLink) // If this fails we have a problem
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/utilities/OpenGroupUtilities.kt b/src/org/thoughtcrime/securesms/loki/redesign/utilities/OpenGroupUtilities.kt
new file mode 100644
index 0000000000..87f7afdf6b
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/redesign/utilities/OpenGroupUtilities.kt
@@ -0,0 +1,37 @@
+package org.thoughtcrime.securesms.loki.redesign.utilities
+
+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 for 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/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..907a21217e 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 {
@@ -87,6 +87,10 @@ public class GroupUtil {
return groupId.startsWith(ENCODED_RSS_FEED_GROUP_PREFIX);
}
+ public static boolean isSignalGroup(@NonNull String groupId) {
+ return groupId.startsWith(ENCODED_SIGNAL_GROUP_PREFIX);
+ }
+
@WorkerThread
public static Optional createGroupLeaveMessage(@NonNull Context context, @NonNull Recipient groupRecipient) {
String encodedGroupId = groupRecipient.getAddress().toGroupString();
@@ -114,6 +118,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);