mirror of
https://github.com/oxen-io/session-android.git
synced 2025-01-11 17:23:38 +00:00
Added public chat manager.
Replace hard coded public chat server with dynamic one.
This commit is contained in:
parent
8b92932b6d
commit
13d42f542c
@ -25,6 +25,7 @@ import android.database.ContentObserver;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.multidex.MultiDexApplication;
|
||||
|
||||
import com.crashlytics.android.Crashlytics;
|
||||
@ -61,8 +62,9 @@ import org.thoughtcrime.securesms.logging.PersistentLogger;
|
||||
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
||||
import org.thoughtcrime.securesms.loki.BackgroundPollWorker;
|
||||
import org.thoughtcrime.securesms.loki.LokiAPIDatabase;
|
||||
import org.thoughtcrime.securesms.loki.LokiGroupChatPoller;
|
||||
import org.thoughtcrime.securesms.loki.LokiPublicChatManager;
|
||||
import org.thoughtcrime.securesms.loki.LokiRSSFeedPoller;
|
||||
import org.thoughtcrime.securesms.loki.LokiUserDatabase;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
@ -98,6 +100,7 @@ import java.security.Security;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -132,9 +135,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
|
||||
// Loki
|
||||
private LokiLongPoller lokiLongPoller = null;
|
||||
private LokiGroupChatPoller lokiPublicChatPoller = null;
|
||||
private LokiRSSFeedPoller lokiNewsFeedPoller = null;
|
||||
private LokiRSSFeedPoller lokiMessengerUpdatesFeedPoller = null;
|
||||
private LokiPublicChatManager lokiPublicChatManager = null;
|
||||
private LokiGroupChatAPI lokiGroupChatAPI = null;
|
||||
public SignalCommunicationModule communicationModule;
|
||||
public MixpanelAPI mixpanel;
|
||||
|
||||
@ -147,7 +151,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
LokiGroupChatAPI.Companion.setDebugMode(BuildConfig.DEBUG); // Loki - Set debug mode if needed
|
||||
startKovenant();
|
||||
Log.i(TAG, "onCreate()");
|
||||
initializeSecurityProvider();
|
||||
@ -183,6 +186,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
mixpanel.trackMap(event, properties);
|
||||
return Unit.INSTANCE;
|
||||
};
|
||||
|
||||
lokiPublicChatManager = new LokiPublicChatManager(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -204,6 +209,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
MessageNotifier.setVisibleThread(-1);
|
||||
// Loki - Stop long polling if needed
|
||||
if (lokiLongPoller != null) { lokiLongPoller.stopIfNeeded(); }
|
||||
if (lokiPublicChatManager != null) { lokiPublicChatManager.stopPollers(); }
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -243,6 +249,22 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
return persistentLogger;
|
||||
}
|
||||
|
||||
public LokiPublicChatManager getLokiPublicChatManager() {
|
||||
return lokiPublicChatManager;
|
||||
}
|
||||
|
||||
public @Nullable LokiGroupChatAPI getLokiGroupChatAPI() {
|
||||
if (lokiGroupChatAPI == null && TextSecurePreferences.isPushRegistered(this)) {
|
||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
|
||||
LokiAPIDatabase apiDatabase = DatabaseFactory.getLokiAPIDatabase(this);
|
||||
LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(this);
|
||||
lokiGroupChatAPI = new LokiGroupChatAPI(userHexEncodedPublicKey, userPrivateKey, apiDatabase, userDatabase);
|
||||
}
|
||||
|
||||
return lokiGroupChatAPI;
|
||||
}
|
||||
|
||||
private void initializeSecurityProvider() {
|
||||
try {
|
||||
Class.forName("org.signal.aesgcmprovider.AesGcmCipher");
|
||||
@ -471,10 +493,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
if (lokiLongPoller != null) { lokiLongPoller.startIfNeeded(); }
|
||||
}
|
||||
|
||||
private LokiGroupChat lokiPublicChat() {
|
||||
return new LokiGroupChat(LokiGroupChatAPI.getPublicChatServerID(), LokiGroupChatAPI.getPublicChatServer(), "Loki Public Chat", true);
|
||||
}
|
||||
|
||||
private LokiRSSFeed lokiNewsFeed() {
|
||||
return new LokiRSSFeed("loki.network.feed", "https://loki.network/feed/", "Loki News", true);
|
||||
}
|
||||
@ -484,11 +502,21 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
}
|
||||
|
||||
public void createGroupChatsIfNeeded() {
|
||||
LokiGroupChat publicChat = lokiPublicChat();
|
||||
boolean isChatSetUp = TextSecurePreferences.isChatSetUp(this, publicChat.getId());
|
||||
if (!isChatSetUp || !publicChat.isDeletable()) {
|
||||
GroupManager.GroupActionResult result = GroupManager.createGroup(publicChat.getId(), this, new HashSet<>(), null, publicChat.getDisplayName(), false);
|
||||
TextSecurePreferences.markChatSetUp(this, publicChat.getId());
|
||||
List<LokiGroupChat> defaultChats = LokiGroupChat.Companion.defaultChats(BuildConfig.DEBUG);
|
||||
for (LokiGroupChat chat : defaultChats) {
|
||||
long threadID = GroupManager.getThreadId(chat.getId(), this);
|
||||
String migrationKey = chat.getId() + "_migrated";
|
||||
boolean isChatMigrated = TextSecurePreferences.getBooleanPreference(this, migrationKey, false);
|
||||
boolean isChatSetUp = TextSecurePreferences.isChatSetUp(this, chat.getId());
|
||||
if (!isChatSetUp || !chat.isDeletable()) {
|
||||
lokiPublicChatManager.addChat(chat.getServer(), chat.getChannel(), chat.getDisplayName());
|
||||
TextSecurePreferences.markChatSetUp(this, chat.getId());
|
||||
TextSecurePreferences.setBooleanPreference(this, migrationKey, true);
|
||||
} else if (threadID > -1 && !isChatMigrated) {
|
||||
// Migrate the old public chats.
|
||||
DatabaseFactory.getLokiThreadDatabase(this).setGroupChat(chat, threadID);
|
||||
TextSecurePreferences.setBooleanPreference(this, migrationKey, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -505,20 +533,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
}
|
||||
}
|
||||
|
||||
private void createGroupChatPollersIfNeeded() {
|
||||
// Only create the group chat pollers if their threads aren't deleted
|
||||
LokiGroupChat publicChat = lokiPublicChat();
|
||||
long threadID = GroupManager.getThreadId(publicChat.getId(), this);
|
||||
if (threadID >= 0 && lokiPublicChatPoller == null) {
|
||||
lokiPublicChatPoller = new LokiGroupChatPoller(this, publicChat);
|
||||
// Set up deletion listeners if needed
|
||||
setUpThreadDeletionListeners(threadID, () -> {
|
||||
if (lokiPublicChatPoller != null) lokiPublicChatPoller.stop();
|
||||
lokiPublicChatPoller = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void createRSSFeedPollersIfNeeded() {
|
||||
// Only create the RSS feed pollers if their threads aren't deleted
|
||||
LokiRSSFeed lokiNewsFeed = lokiNewsFeed();
|
||||
@ -559,8 +573,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
}
|
||||
|
||||
public void startGroupChatPollersIfNeeded() {
|
||||
createGroupChatPollersIfNeeded();
|
||||
if (lokiPublicChatPoller != null) lokiPublicChatPoller.startIfNeeded();
|
||||
lokiPublicChatManager.startPollersIfNeeded();
|
||||
}
|
||||
|
||||
public void startRSSFeedPollersIfNeeded() {
|
||||
|
@ -61,6 +61,7 @@ import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
|
||||
import org.whispersystems.signalservice.api.util.StreamDetails;
|
||||
import org.whispersystems.signalservice.loki.api.LokiGroupChat;
|
||||
import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI;
|
||||
import org.whispersystems.signalservice.loki.utilities.Analytics;
|
||||
|
||||
@ -68,6 +69,8 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@ -377,12 +380,13 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
|
||||
Analytics.Companion.getShared().track("Display Name Updated");
|
||||
|
||||
TextSecurePreferences.setProfileName(context, name);
|
||||
|
||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
|
||||
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).getPrivateKey().serialize();
|
||||
LokiAPIDatabase apiDatabase = DatabaseFactory.getLokiAPIDatabase(context);
|
||||
LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(context);
|
||||
new LokiGroupChatAPI(userHexEncodedPublicKey, userPrivateKey, apiDatabase, userDatabase).setDisplayName(name, LokiGroupChatAPI.getPublicChatServer());
|
||||
LokiGroupChatAPI chatAPI = ApplicationContext.getInstance(context).getLokiGroupChatAPI();
|
||||
if (chatAPI != null) {
|
||||
Set<String> groupChatServers = DatabaseFactory.getLokiThreadDatabase(context).getAllGroupChatServers();
|
||||
for (String server : groupChatServers) {
|
||||
chatAPI.setDisplayName(name, server);
|
||||
}
|
||||
}
|
||||
|
||||
// Loki - Original code
|
||||
// ========
|
||||
|
@ -21,6 +21,7 @@ import com.annimon.stream.Stream;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.database.Database;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
@ -31,6 +32,7 @@ import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.signalservice.loki.api.LokiGroupChat;
|
||||
import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI;
|
||||
|
||||
import java.util.List;
|
||||
@ -194,18 +196,14 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
|
||||
boolean isOwnNumber = Util.isOwnNumber(getContext(), author.getAddress());
|
||||
|
||||
String quoteeDisplayName = author.toShortString();
|
||||
if (quoteeDisplayName.equals(author.getAddress().toString())) {
|
||||
quoteeDisplayName = DatabaseFactory.getLokiUserDatabase(getContext()).getServerDisplayName(LokiGroupChatAPI.getPublicChatServer() + "." + LokiGroupChatAPI.getPublicChatServerID(), author.getAddress().toString());
|
||||
}
|
||||
|
||||
// If we're in a group then try and use the display name in the group
|
||||
if (conversationRecipient.isGroupRecipient()) {
|
||||
try {
|
||||
String serverId = GroupUtil.getDecodedStringId(conversationRecipient.getAddress().serialize());
|
||||
String senderDisplayName = DatabaseFactory.getLokiUserDatabase(getContext()).getServerDisplayName(serverId, author.getAddress().serialize());
|
||||
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(conversationRecipient);
|
||||
LokiGroupChat chat = DatabaseFactory.getLokiThreadDatabase(getContext()).getGroupChat(threadId);
|
||||
if (chat != null) {
|
||||
String senderDisplayName = DatabaseFactory.getLokiUserDatabase(getContext()).getServerDisplayName(chat.getId(), author.getAddress().serialize());
|
||||
if (senderDisplayName != null) { quoteeDisplayName = senderDisplayName; }
|
||||
} catch (Exception e) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,7 @@ import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHol
|
||||
import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickListener;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.Database;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
@ -105,6 +106,7 @@ import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.internal.util.concurrent.SettableFuture;
|
||||
import org.whispersystems.signalservice.loki.api.LokiGroupChat;
|
||||
import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -408,14 +410,15 @@ public class ConversationFragment extends Fragment
|
||||
boolean isGroupChat = recipient.isGroupRecipient();
|
||||
|
||||
if (isGroupChat) {
|
||||
boolean isLokiPublicChat = recipient.getName() != null && recipient.getName().equals("Loki Public Chat");
|
||||
LokiGroupChat groupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getGroupChat(threadId);
|
||||
boolean isPublicChat = groupChat != null;
|
||||
int selectedMessageCount = messageRecords.size();
|
||||
boolean isSentByUser = ((MessageRecord)messageRecords.toArray()[0]).isOutgoing();
|
||||
menu.findItem(R.id.menu_context_copy_public_key).setVisible(isLokiPublicChat && selectedMessageCount == 1 && !isSentByUser);
|
||||
menu.findItem(R.id.menu_context_reply).setVisible(isLokiPublicChat && selectedMessageCount == 1);
|
||||
menu.findItem(R.id.menu_context_copy_public_key).setVisible(isPublicChat && selectedMessageCount == 1 && !isSentByUser);
|
||||
menu.findItem(R.id.menu_context_reply).setVisible(isPublicChat && selectedMessageCount == 1);
|
||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext());
|
||||
boolean userCanModerate = LokiGroupChatAPI.Companion.isUserModerator(userHexEncodedPublicKey, LokiGroupChatAPI.getPublicChatServerID(), LokiGroupChatAPI.getPublicChatServer());
|
||||
boolean isDeleteOptionVisible = isLokiPublicChat && selectedMessageCount == 1 && (isSentByUser || userCanModerate);
|
||||
boolean userCanModerate = groupChat != null && LokiGroupChatAPI.Companion.isUserModerator(userHexEncodedPublicKey, groupChat.getChannel(), groupChat.getServer());
|
||||
boolean isDeleteOptionVisible = isPublicChat && selectedMessageCount == 1 && (isSentByUser || userCanModerate);
|
||||
menu.findItem(R.id.menu_context_delete_message).setVisible(isDeleteOptionVisible);
|
||||
} else {
|
||||
menu.findItem(R.id.menu_context_copy_public_key).setVisible(false);
|
||||
@ -509,8 +512,8 @@ public class ConversationFragment extends Fragment
|
||||
builder.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messagesCount, messagesCount));
|
||||
builder.setCancelable(true);
|
||||
|
||||
// Loki - The delete option is only visible to the user in a group chat if it's the Loki Public Chat
|
||||
boolean isLokiPublicChat = this.recipient.isGroupRecipient();
|
||||
// Loki - The delete option is only visible to the user in a group chat
|
||||
LokiGroupChat groupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getGroupChat(threadId);
|
||||
|
||||
builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
@ -524,19 +527,16 @@ public class ConversationFragment extends Fragment
|
||||
for (MessageRecord messageRecord : messageRecords) {
|
||||
boolean isThreadDeleted;
|
||||
|
||||
if (isLokiPublicChat) {
|
||||
if (groupChat != null) {
|
||||
final SettableFuture<?>[] future = { new SettableFuture<Unit>() };
|
||||
|
||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext());
|
||||
LokiAPIDatabase lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(getContext());
|
||||
LokiUserDatabase lokiUserDatabase = DatabaseFactory.getLokiUserDatabase(getContext());
|
||||
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(getContext()).getPrivateKey().serialize();
|
||||
LokiGroupChatAPI chatAPI = ApplicationContext.getInstance(getContext()).getLokiGroupChatAPI();
|
||||
boolean isSentByUser = messageRecord.isOutgoing();
|
||||
Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id);
|
||||
|
||||
if (serverID != null) {
|
||||
new LokiGroupChatAPI(userHexEncodedPublicKey, userPrivateKey, lokiAPIDatabase, lokiUserDatabase)
|
||||
.deleteMessage(serverID, LokiGroupChatAPI.getPublicChatServerID(), LokiGroupChatAPI.getPublicChatServer(), isSentByUser)
|
||||
if (chatAPI != null && serverID != null) {
|
||||
chatAPI
|
||||
.deleteMessage(serverID, groupChat.getChannel(), groupChat.getServer(), isSentByUser)
|
||||
.success(l -> {
|
||||
@SuppressWarnings("unchecked") SettableFuture<Unit> f = (SettableFuture<Unit>) future[0];
|
||||
f.set(Unit.INSTANCE);
|
||||
|
@ -71,6 +71,7 @@ import org.thoughtcrime.securesms.components.StickerView;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.Database;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
@ -112,6 +113,7 @@ import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.views.Stub;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.loki.api.LokiGroupChat;
|
||||
import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI;
|
||||
|
||||
import java.util.Collections;
|
||||
@ -934,13 +936,15 @@ public class ConversationItem extends LinearLayout
|
||||
|
||||
if (!next.isPresent() || next.get().isUpdate() || !current.getRecipient().getAddress().equals(next.get().getRecipient().getAddress())) {
|
||||
contactPhoto.setVisibility(VISIBLE);
|
||||
int visibility;
|
||||
if (conversationRecipient.getName() != null && conversationRecipient.getName().equals("Loki Public Chat")) {
|
||||
boolean isModerator = LokiGroupChatAPI.Companion.isUserModerator(current.getRecipient().getAddress().toString(), LokiGroupChatAPI.getPublicChatServerID(), LokiGroupChatAPI.getPublicChatServer());
|
||||
int visibility = View.GONE;
|
||||
|
||||
// If we have a chat then use that to determine mod status
|
||||
LokiGroupChat groupChat = DatabaseFactory.getLokiThreadDatabase(context).getGroupChat(messageRecord.getThreadId());
|
||||
if (groupChat != null) {
|
||||
boolean isModerator = LokiGroupChatAPI.Companion.isUserModerator(current.getRecipient().getAddress().toString(), groupChat.getChannel(), groupChat.getServer());
|
||||
visibility = isModerator ? View.VISIBLE : View.GONE;
|
||||
} else {
|
||||
visibility = View.GONE;
|
||||
}
|
||||
|
||||
moderatorIconImageView.setVisibility(visibility);
|
||||
} else {
|
||||
contactPhoto.setVisibility(GONE);
|
||||
|
@ -71,7 +71,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
private static final int lokiV2 = 23;
|
||||
private static final int lokiV3 = 24;
|
||||
|
||||
private static final int DATABASE_VERSION = lokiV2; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
|
||||
private static final int DATABASE_VERSION = lokiV3; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
|
||||
private static final String DATABASE_NAME = "signal.db";
|
||||
|
||||
private final Context context;
|
||||
@ -499,8 +499,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
if (oldVersion < lokiV3) {
|
||||
db.execSQL(LokiAPIDatabase.getCreatePairingAuthorisationTableCommand());
|
||||
db.execSQL(LokiThreadDatabase.getCreateGroupChatMappingTableCommand());
|
||||
|
||||
// TODO: Map old public chat threads to new manager format
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
|
@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
||||
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
||||
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.JobManager;
|
||||
@ -44,6 +45,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
||||
import org.whispersystems.signalservice.loki.api.LokiGroupChat;
|
||||
import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -285,7 +287,14 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
||||
|
||||
private @NonNull List<Address> getGroupMessageRecipients(String groupId, long messageId) {
|
||||
ArrayList<Address> result = new ArrayList<>();
|
||||
result.add(Address.fromSerialized(LokiGroupChatAPI.getPublicChatServer())); // Loki - All group messages should be directed to the Loki Public Chat for now
|
||||
|
||||
// Loki - All group messages should be directed to their servers
|
||||
long threadID = GroupManager.getThreadIdFromGroupId(groupId, context);
|
||||
LokiGroupChat chat = DatabaseFactory.getLokiThreadDatabase(context).getGroupChat(threadID);
|
||||
if (chat != null) {
|
||||
result.add(Address.fromSerialized(chat.getServer()));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
/*
|
||||
|
@ -50,6 +50,10 @@ fun Cursor.getString(columnName: String): String {
|
||||
return getString(getColumnIndexOrThrow(columnName))
|
||||
}
|
||||
|
||||
fun Cursor.getLong(columnName: String): Long {
|
||||
return getLong(getColumnIndexOrThrow(columnName))
|
||||
}
|
||||
|
||||
fun Cursor.getBase64EncodedData(columnName: String): ByteArray {
|
||||
return Base64.decode(getString(columnName))
|
||||
}
|
@ -45,12 +45,11 @@ class DisplayNameActivity : BaseActionBarActivity() {
|
||||
application.setUpStorageAPIIfNeeded()
|
||||
startActivity(Intent(this, ConversationListActivity::class.java))
|
||||
finish()
|
||||
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this)
|
||||
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).privateKey.serialize()
|
||||
val apiDatabase = DatabaseFactory.getLokiAPIDatabase(this)
|
||||
val userDatabase = DatabaseFactory.getLokiUserDatabase(this)
|
||||
if (name != null) {
|
||||
LokiGroupChatAPI(userHexEncodedPublicKey, userPrivateKey, apiDatabase, userDatabase).setDisplayName(name, LokiGroupChatAPI.publicChatServer)
|
||||
|
||||
val chatAPI = ApplicationContext.getInstance(this).lokiGroupChatAPI
|
||||
if (chatAPI != null && name != null) {
|
||||
val servers = DatabaseFactory.getLokiThreadDatabase(this).getAllGroupChatServers()
|
||||
servers.forEach { chatAPI.setDisplayName(name, it) }
|
||||
}
|
||||
}
|
||||
}
|
@ -24,8 +24,8 @@ fun toPx(dp: Int, resources: Resources): Int {
|
||||
return (dp * scale).roundToInt()
|
||||
}
|
||||
|
||||
fun isGroupRecipient(recipient: String): Boolean {
|
||||
return (LokiGroupChatAPI.publicChatServer == recipient)
|
||||
fun isGroupRecipient(context: Context, recipient: String): Boolean {
|
||||
return DatabaseFactory.getLokiThreadDatabase(context).getAllGroupChats().values.map { it.server }.contains(recipient)
|
||||
}
|
||||
|
||||
fun getFriendPublicKeys(context: Context, devicePublicKeys: Set<String>): Set<String> {
|
||||
|
@ -137,6 +137,12 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
database.insertOrUpdate(lastMessageServerIDCache, row, "$lastMessageServerIDCacheIndex = ?", wrap(index))
|
||||
}
|
||||
|
||||
fun removeLastMessageServerID(group: Long, server: String) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$group"
|
||||
database.delete(lastMessageServerIDCache,"$lastMessageServerIDCacheIndex = ?", wrap(index))
|
||||
}
|
||||
|
||||
override fun getLastDeletionServerID(group: Long, server: String): Long? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
val index = "$server.$group"
|
||||
@ -152,6 +158,12 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
database.insertOrUpdate(lastDeletionServerIDCache, row, "$lastDeletionServerIDCacheIndex = ?", wrap(index))
|
||||
}
|
||||
|
||||
fun removeLastDeletionServerID(group: Long, server: String) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$group"
|
||||
database.delete(lastDeletionServerIDCache,"$lastDeletionServerIDCacheIndex = ?", wrap(index))
|
||||
}
|
||||
|
||||
override fun getPairingAuthorisations(hexEncodedPublicKey: String): List<PairingAuthorisation> {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.getAll(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) { cursor ->
|
||||
|
115
src/org/thoughtcrime/securesms/loki/LokiPublicChatManager.kt
Normal file
115
src/org/thoughtcrime/securesms/loki/LokiPublicChatManager.kt
Normal file
@ -0,0 +1,115 @@
|
||||
package org.thoughtcrime.securesms.loki
|
||||
|
||||
import android.content.Context
|
||||
import android.database.ContentObserver
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.functional.bind
|
||||
import nl.komponents.kovenant.functional.map
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.groups.GroupManager
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.loki.api.LokiGroupChat
|
||||
import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI
|
||||
import java.util.HashSet
|
||||
|
||||
class LokiPublicChatManager(private val context: Context) {
|
||||
private var chats = mutableMapOf<Long, LokiGroupChat>()
|
||||
private val pollers = mutableMapOf<Long, LokiGroupChatPoller>()
|
||||
private val observers = mutableMapOf<Long, ContentObserver>()
|
||||
private var isPolling = false
|
||||
|
||||
public fun startPollersIfNeeded() {
|
||||
refreshChatsAndPollers()
|
||||
|
||||
for ((threadId, chat) in chats) {
|
||||
val poller = pollers[threadId] ?: LokiGroupChatPoller(context, chat)
|
||||
poller.startIfNeeded()
|
||||
listenToThreadDeletion(threadId)
|
||||
if (!pollers.containsKey(threadId)) { pollers[threadId] = poller }
|
||||
}
|
||||
isPolling = true
|
||||
}
|
||||
|
||||
public fun stopPollers() {
|
||||
pollers.values.forEach { it.stop() }
|
||||
isPolling = false
|
||||
}
|
||||
|
||||
public fun addChat(server: String, channel: Long): Promise<LokiGroupChat, Exception> {
|
||||
val groupChatAPI = ApplicationContext.getInstance(context).lokiGroupChatAPI ?: return Promise.ofFail(IllegalStateException())
|
||||
return groupChatAPI.getAuthToken(server).bind {
|
||||
groupChatAPI.getChannelInfo(channel, server)
|
||||
}.map {
|
||||
addChat(server, channel, it)
|
||||
}
|
||||
}
|
||||
|
||||
public fun addChat(server: String, channel: Long, name: String): LokiGroupChat {
|
||||
val chat = LokiGroupChat(channel, server, name, true)
|
||||
var threadID = GroupManager.getThreadId(chat.id, context)
|
||||
// Create the group if we don't have one
|
||||
if (threadID < 0) {
|
||||
val result = GroupManager.createGroup(chat.id, context, HashSet(), null, chat.displayName, false)
|
||||
threadID = result.threadId
|
||||
}
|
||||
DatabaseFactory.getLokiThreadDatabase(context).setGroupChat(chat, threadID)
|
||||
startPollersIfNeeded()
|
||||
|
||||
// Set our name on the server
|
||||
ApplicationContext.getInstance(context).lokiGroupChatAPI?.setDisplayName(server, TextSecurePreferences.getProfileName(context))
|
||||
|
||||
return chat
|
||||
}
|
||||
|
||||
private fun refreshChatsAndPollers() {
|
||||
val chatsInDB = DatabaseFactory.getLokiThreadDatabase(context).getAllGroupChats()
|
||||
val removedChatThreadIds = chats.keys.filter { !chatsInDB.keys.contains(it) }
|
||||
removedChatThreadIds.forEach { pollers.remove(it)?.stop() }
|
||||
|
||||
// Only append to chats if we have a thread for the chat
|
||||
chats = chatsInDB.filter { GroupManager.getThreadId(it.value.id, context) > -1 }.toMutableMap()
|
||||
}
|
||||
|
||||
private fun listenToThreadDeletion(threadID: Long) {
|
||||
if (threadID < 0 || observers[threadID] != null) { return }
|
||||
val observer = createDeletionObserver(threadID, Runnable {
|
||||
val chat = chats[threadID]
|
||||
|
||||
// Reset last message cache
|
||||
if (chat != null) {
|
||||
val apiDatabase = DatabaseFactory.getLokiAPIDatabase(context)
|
||||
apiDatabase.removeLastDeletionServerID(chat.channel, chat.server)
|
||||
apiDatabase.removeLastMessageServerID(chat.channel, chat.server)
|
||||
}
|
||||
|
||||
DatabaseFactory.getLokiThreadDatabase(context).removeGroupChat(threadID)
|
||||
pollers.remove(threadID)?.stop()
|
||||
observers.remove(threadID)
|
||||
startPollersIfNeeded()
|
||||
})
|
||||
observers[threadID] = observer
|
||||
|
||||
context.applicationContext.contentResolver.registerContentObserver(DatabaseContentProviders.Conversation.getUriForThread(threadID), true, observer)
|
||||
}
|
||||
|
||||
private fun createDeletionObserver(threadID: Long, onDelete: Runnable): ContentObserver {
|
||||
return object : ContentObserver(null) {
|
||||
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
super.onChange(selfChange)
|
||||
// Stop the poller if thread is deleted
|
||||
try {
|
||||
if (!DatabaseFactory.getThreadDatabase(context).hasThread(threadID)) {
|
||||
onDelete.run()
|
||||
context.applicationContext.contentResolver.unregisterContentObserver(this)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// TODO: Handle
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.loki
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import org.thoughtcrime.securesms.database.Address
|
||||
import org.thoughtcrime.securesms.database.Database
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
@ -12,6 +13,7 @@ import org.whispersystems.signalservice.loki.api.LokiGroupChat
|
||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadDatabaseProtocol
|
||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
|
||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadSessionResetStatus
|
||||
import java.lang.Exception
|
||||
|
||||
class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiThreadDatabaseProtocol {
|
||||
var delegate: LokiThreadDatabaseDelegate? = null
|
||||
@ -19,11 +21,11 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
||||
companion object {
|
||||
private val friendRequestTableName = "loki_thread_friend_request_database"
|
||||
private val sessionResetTableName = "loki_thread_session_reset_database"
|
||||
private val groupChatMappingTableName = "loki_group_chat_mapping_database"
|
||||
private val threadID = "thread_id"
|
||||
public val groupChatMappingTableName = "loki_group_chat_mapping_database"
|
||||
public val threadID = "thread_id"
|
||||
private val friendRequestStatus = "friend_request_status"
|
||||
private val sessionResetStatus = "session_reset_status"
|
||||
private val groupChatJSON = "group_chat_json"
|
||||
public val groupChatJSON = "group_chat_json"
|
||||
@JvmStatic val createFriendRequestTableCommand = "CREATE TABLE $friendRequestTableName ($threadID INTEGER PRIMARY KEY, $friendRequestStatus INTEGER DEFAULT 0);"
|
||||
@JvmStatic val createSessionResetTableCommand = "CREATE TABLE $sessionResetTableName ($threadID INTEGER PRIMARY KEY, $sessionResetStatus INTEGER DEFAULT 0);"
|
||||
@JvmStatic val createGroupChatMappingTableCommand = "CREATE TABLE $groupChatMappingTableName ($threadID INTEGER PRIMARY KEY, $groupChatJSON TEXT);"
|
||||
@ -90,7 +92,35 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
||||
notifyConversationListeners(threadID)
|
||||
}
|
||||
|
||||
fun getAllGroupChats(): Map<Long, LokiGroupChat> {
|
||||
val database = databaseHelper.readableDatabase
|
||||
var cursor: Cursor? = null
|
||||
|
||||
try {
|
||||
val map = mutableMapOf<Long, LokiGroupChat>()
|
||||
cursor = database.rawQuery("select * from $groupChatMappingTableName", null)
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
val threadID = cursor.getLong(Companion.threadID)
|
||||
val string = cursor.getString(groupChatJSON)
|
||||
val chat = LokiGroupChat.fromJSON(string)
|
||||
if (chat != null) { map[threadID] = chat }
|
||||
}
|
||||
return map
|
||||
} catch (e: Exception) {
|
||||
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
|
||||
return mapOf()
|
||||
}
|
||||
|
||||
fun getAllGroupChatServers(): Set<String> {
|
||||
return getAllGroupChats().values.fold(setOf<String>()) { set, chat -> set.plus(chat.server) }
|
||||
}
|
||||
|
||||
override fun getGroupChat(threadID: Long): LokiGroupChat? {
|
||||
if (threadID < 0) { return null }
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(groupChatMappingTableName, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor ->
|
||||
val string = cursor.getString(groupChatJSON)
|
||||
@ -99,6 +129,8 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
||||
}
|
||||
|
||||
override fun setGroupChat(groupChat: LokiGroupChat, threadID: Long) {
|
||||
if (threadID < 0) { return }
|
||||
|
||||
val database = databaseHelper.writableDatabase
|
||||
val contentValues = ContentValues(2)
|
||||
contentValues.put(Companion.threadID, threadID)
|
||||
|
@ -213,7 +213,7 @@ public class MessageSender {
|
||||
|
||||
// Just send the message normally if it's a group message
|
||||
String recipientPublicKey = recipient.getAddress().serialize();
|
||||
if (GeneralUtilitiesKt.isGroupRecipient(recipientPublicKey)) {
|
||||
if (GeneralUtilitiesKt.isGroupRecipient(context, recipientPublicKey)) {
|
||||
jobManager.add(new PushTextSendJob(messageId, recipient.getAddress()));
|
||||
return;
|
||||
}
|
||||
@ -243,7 +243,7 @@ public class MessageSender {
|
||||
|
||||
// Just send the message normally if it's a group message
|
||||
String recipientPublicKey = recipient.getAddress().serialize();
|
||||
if (GeneralUtilitiesKt.isGroupRecipient(recipientPublicKey)) {
|
||||
if (GeneralUtilitiesKt.isGroupRecipient(context, recipientPublicKey)) {
|
||||
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress());
|
||||
return;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user