mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-21 15:05:19 +00:00
Conversation activity no longer pulls the public chat info directly.
Public chat API partially refactored to avoid thread branching and to use Kotlin coroutines instead of kovenant futures.
This commit is contained in:
parent
55da7056dc
commit
4307140e5c
@ -89,7 +89,9 @@ dependencies {
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-common-java8:2.2.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
|
||||
implementation "androidx.work:work-runtime-ktx:2.4.0"
|
||||
implementation "androidx.core:core-ktx:1.3.2"
|
||||
|
||||
implementation ("com.google.firebase:firebase-messaging:18.0.0") {
|
||||
exclude group: 'com.google.firebase', module: 'firebase-core'
|
||||
|
@ -348,7 +348,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
String groupId = DatabaseFactory.getGroupDatabase(activity).getOrCreateGroupForMembers(memberAddresses, true, Collections.singletonList(local));
|
||||
Recipient groupRecipient = Recipient.from(activity, Address.fromSerialized(groupId), true);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(activity).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(activity).getOrCreateThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
|
||||
return new GroupActionResult(groupRecipient, threadId);
|
||||
}
|
||||
|
@ -767,7 +767,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
.setBlocked(recipient, blocked);
|
||||
|
||||
if (recipient.isGroupRecipient() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.getAddress().toGroupString())) {
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, recipient);
|
||||
|
||||
if (threadId != -1 && leaveMessage.isPresent()) {
|
||||
@ -776,7 +776,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
String groupId = recipient.getAddress().toGroupString();
|
||||
groupDatabase.setActive(groupId, false);
|
||||
groupDatabase.remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
|
||||
groupDatabase.removeMember(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
|
||||
} else {
|
||||
Log.w(TAG, "Failed to leave group. Can't block.");
|
||||
Toast.makeText(context, R.string.RecipientPreferenceActivity_error_leaving_group, Toast.LENGTH_LONG).show();
|
||||
|
@ -197,7 +197,7 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
|
||||
|
||||
String quoteeDisplayName = author.toShortString();
|
||||
|
||||
long threadID = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(conversationRecipient);
|
||||
long threadID = DatabaseFactory.getThreadDatabase(getContext()).getOrCreateThreadIdFor(conversationRecipient);
|
||||
String senderHexEncodedPublicKey = author.getAddress().serialize();
|
||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadID);
|
||||
if (senderHexEncodedPublicKey.equalsIgnoreCase(TextSecurePreferences.getLocalNumber(getContext()))) {
|
||||
|
@ -90,7 +90,7 @@ public class TypingStatusSender {
|
||||
Set<String> linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.getAddress().serialize());
|
||||
for (String device : linkedDevices) {
|
||||
Recipient deviceAsRecipient = Recipient.from(context, Address.fromSerialized(device), false);
|
||||
long deviceThreadID = threadDatabase.getThreadIdFor(deviceAsRecipient);
|
||||
long deviceThreadID = threadDatabase.getOrCreateThreadIdFor(deviceAsRecipient);
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(deviceThreadID, typingStarted));
|
||||
}
|
||||
}
|
||||
|
@ -156,6 +156,7 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity;
|
||||
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
||||
import org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabaseDelegate;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
||||
@ -163,6 +164,7 @@ import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
|
||||
import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt;
|
||||
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
|
||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities;
|
||||
import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView;
|
||||
import org.thoughtcrime.securesms.loki.views.ProfilePictureView;
|
||||
import org.thoughtcrime.securesms.loki.views.SessionRestoreBannerView;
|
||||
@ -462,20 +464,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||
if (publicChat != null) {
|
||||
PublicChatAPI publicChatAPI = ApplicationContext.getInstance(this).getPublicChatAPI();
|
||||
publicChatAPI.getChannelInfo(publicChat.getChannel(), publicChat.getServer()).success(info -> {
|
||||
String groupId = GroupUtil.getEncodedOpenGroupId(publicChat.getId().getBytes());
|
||||
|
||||
publicChatAPI.updateProfileIfNeeded(
|
||||
publicChat.getChannel(),
|
||||
publicChat.getServer(),
|
||||
groupId,
|
||||
info,
|
||||
false);
|
||||
|
||||
runOnUiThread(ConversationActivity.this::updateSubtitleTextView);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
// Request open group info update and handle the successful result in #onOpenGroupInfoUpdated().
|
||||
PublicChatInfoUpdateWorker.scheduleInstant(this, publicChat.getServer(), publicChat.getChannel());
|
||||
}
|
||||
|
||||
View rootView = findViewById(R.id.rootView);
|
||||
@ -1940,6 +1930,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
.show(TooltipPopup.POSITION_ABOVE);
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onOpenGroupInfoUpdated(OpenGroupUtilities.GroupInfoUpdatedEvent event) {
|
||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||
if (publicChat != null &&
|
||||
publicChat.getChannel() == event.getChannel() &&
|
||||
publicChat.getServer().equals(event.getUrl())) {
|
||||
this.updateSubtitleTextView();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeReceivers() {
|
||||
securityUpdateReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
@ -2095,7 +2095,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
long threadId = params[0];
|
||||
|
||||
if (drafts.size() > 0) {
|
||||
if (threadId == -1) threadId = threadDatabase.getThreadIdFor(getRecipient(), thisDistributionType);
|
||||
if (threadId == -1) threadId = threadDatabase.getOrCreateThreadIdFor(getRecipient(), thisDistributionType);
|
||||
|
||||
draftDatabase.insertDrafts(threadId, drafts);
|
||||
threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this),
|
||||
|
@ -218,6 +218,18 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
notifyConversationListListeners();
|
||||
}
|
||||
|
||||
public boolean delete(@NonNull String groupId) {
|
||||
int result = databaseHelper.getWritableDatabase().delete(TABLE_NAME, GROUP_ID + " = ?", new String[]{groupId});
|
||||
|
||||
if (result > 0) {
|
||||
Recipient.removeCached(Address.fromSerialized(groupId));
|
||||
notifyConversationListListeners();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void update(String groupId, String title, SignalServiceAttachmentPointer avatar) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
if (title != null) contentValues.put(TITLE, title);
|
||||
@ -262,7 +274,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
long avatarId;
|
||||
|
||||
if (newValue != null) avatarId = Math.abs(new SecureRandom().nextLong());
|
||||
else avatarId = 0;
|
||||
else avatarId = 0;
|
||||
|
||||
|
||||
ContentValues contentValues = new ContentValues(2);
|
||||
@ -300,7 +312,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", new String[] {groupId});
|
||||
}
|
||||
|
||||
public void remove(String groupId, Address source) {
|
||||
public void removeMember(String groupId, Address source) {
|
||||
List<Address> currentMembers = getCurrentMembers(groupId);
|
||||
currentMembers.remove(source);
|
||||
|
||||
@ -352,13 +364,21 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
database.update(TABLE_NAME, values, GROUP_ID + " = ?", new String[] {groupId});
|
||||
}
|
||||
|
||||
|
||||
public byte[] allocateGroupId() {
|
||||
byte[] groupId = new byte[16];
|
||||
new SecureRandom().nextBytes(groupId);
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public boolean hasGroup(@NonNull String groupId) {
|
||||
try (Cursor cursor = databaseHelper.getReadableDatabase().rawQuery(
|
||||
"SELECT 1 FROM " + TABLE_NAME + " WHERE " + GROUP_ID + " = ? LIMIT 1",
|
||||
new String[]{groupId}
|
||||
)) {
|
||||
return cursor.getCount() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Reader implements Closeable {
|
||||
|
||||
private final Cursor cursor;
|
||||
|
@ -321,10 +321,10 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
private long getThreadIdFor(IncomingMediaMessage retrieved) throws RecipientFormattingException, MmsException {
|
||||
if (retrieved.getGroupId() != null) {
|
||||
Recipient groupRecipients = Recipient.from(context, retrieved.getGroupId(), true);
|
||||
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipients);
|
||||
return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipients);
|
||||
} else {
|
||||
Recipient sender = Recipient.from(context, retrieved.getFrom(), true);
|
||||
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(sender);
|
||||
return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(sender);
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,7 +333,7 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
? Util.toIsoString(notification.getFrom().getTextString())
|
||||
: "";
|
||||
Recipient recipient = Recipient.from(context, Address.fromExternal(context, fromString), false);
|
||||
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
}
|
||||
|
||||
private Cursor rawQuery(@NonNull String where, @Nullable String[] arguments) {
|
||||
|
@ -556,7 +556,7 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
|
||||
private @NonNull Pair<Long, Long> insertCallLog(@NonNull Address address, long type, boolean unread) {
|
||||
Recipient recipient = Recipient.from(context, address, true);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
|
||||
ContentValues values = new ContentValues(6);
|
||||
values.put(ADDRESS, address.serialize());
|
||||
@ -620,8 +620,8 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
|
||||
long threadId;
|
||||
|
||||
if (groupRecipient == null) threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
else threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
|
||||
if (groupRecipient == null) threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
else threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipient);
|
||||
|
||||
ContentValues values = new ContentValues(6);
|
||||
values.put(ADDRESS, message.getSender().serialize());
|
||||
|
@ -209,7 +209,7 @@ public class SmsMigrator {
|
||||
|
||||
if (ourRecipients != null) {
|
||||
if (ourRecipients.size() == 1) {
|
||||
long ourThreadId = threadDatabase.getThreadIdFor(ourRecipients.iterator().next());
|
||||
long ourThreadId = threadDatabase.getOrCreateThreadIdFor(ourRecipients.iterator().next());
|
||||
migrateConversation(context, listener, progress, theirThreadId, ourThreadId);
|
||||
} else if (ourRecipients.size() > 1) {
|
||||
ourRecipients.add(Recipient.from(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), true));
|
||||
@ -222,7 +222,7 @@ public class SmsMigrator {
|
||||
|
||||
String ourGroupId = DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(memberAddresses, true, null);
|
||||
Recipient ourGroupRecipient = Recipient.from(context, Address.fromSerialized(ourGroupId), true);
|
||||
long ourThreadId = threadDatabase.getThreadIdFor(ourGroupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
|
||||
long ourThreadId = threadDatabase.getOrCreateThreadIdFor(ourGroupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
|
||||
|
||||
migrateConversation(context, listener, progress, theirThreadId, ourThreadId);
|
||||
}
|
||||
|
@ -512,11 +512,11 @@ public class ThreadDatabase extends Database {
|
||||
}
|
||||
}
|
||||
|
||||
public long getThreadIdFor(Recipient recipient) {
|
||||
return getThreadIdFor(recipient, DistributionTypes.DEFAULT);
|
||||
public long getOrCreateThreadIdFor(Recipient recipient) {
|
||||
return getOrCreateThreadIdFor(recipient, DistributionTypes.DEFAULT);
|
||||
}
|
||||
|
||||
public long getThreadIdFor(Recipient recipient, int distributionType) {
|
||||
public long getOrCreateThreadIdFor(Recipient recipient, int distributionType) {
|
||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||
String where = ADDRESS + " = ?";
|
||||
String[] recipientsArg = new String[]{recipient.getAddress().serialize()};
|
||||
|
@ -62,7 +62,7 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
|
||||
@Override
|
||||
public BucketedThreadMedia loadInBackground() {
|
||||
BucketedThreadMedia result = new BucketedThreadMedia(getContext());
|
||||
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(Recipient.from(getContext(), address, true));
|
||||
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getOrCreateThreadIdFor(Recipient.from(getContext(), address, true));
|
||||
|
||||
DatabaseFactory.getMediaDatabase(getContext()).subscribeToMediaChanges(observer);
|
||||
try (Cursor cursor = DatabaseFactory.getMediaDatabase(getContext()).getGalleryMediaForThread(threadId)) {
|
||||
|
@ -34,7 +34,7 @@ public class PagingMediaLoader extends AsyncLoader<Pair<Cursor, Integer>> {
|
||||
@Nullable
|
||||
@Override
|
||||
public Pair<Cursor, Integer> loadInBackground() {
|
||||
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(recipient);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getOrCreateThreadIdFor(recipient);
|
||||
Cursor cursor = DatabaseFactory.getMediaDatabase(getContext()).getGalleryMediaForThread(threadId);
|
||||
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
|
@ -23,7 +23,7 @@ public class ThreadMediaLoader extends AbstractCursorLoader {
|
||||
|
||||
@Override
|
||||
public Cursor getCursor() {
|
||||
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(Recipient.from(getContext(), address, true));
|
||||
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getOrCreateThreadIdFor(Recipient.from(getContext(), address, true));
|
||||
|
||||
if (gallery) return DatabaseFactory.getMediaDatabase(getContext()).getGalleryMediaForThread(threadId);
|
||||
else return DatabaseFactory.getMediaDatabase(getContext()).getDocumentMediaForThread(threadId);
|
||||
|
@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
||||
|
||||
@ -89,7 +90,8 @@ public class GroupManager {
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient, true);
|
||||
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses);
|
||||
} else {
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(
|
||||
groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
|
||||
return new GroupActionResult(groupRecipient, threadId);
|
||||
}
|
||||
}
|
||||
@ -127,10 +129,30 @@ public class GroupManager {
|
||||
|
||||
groupDatabase.updateProfilePicture(groupId, avatarBytes);
|
||||
|
||||
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
|
||||
long threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(
|
||||
groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
|
||||
return new GroupActionResult(groupRecipient, threadID);
|
||||
}
|
||||
|
||||
public static boolean deleteGroup(@NonNull String groupId,
|
||||
@NonNull Context context)
|
||||
{
|
||||
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
final ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false);
|
||||
|
||||
if (!groupDatabase.getGroup(groupId).isPresent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long threadId = threadDatabase.getThreadIdIfExistsFor(groupRecipient);
|
||||
if (threadId != -1L) {
|
||||
threadDatabase.deleteConversation(threadId);
|
||||
}
|
||||
|
||||
return groupDatabase.delete(groupId);
|
||||
}
|
||||
|
||||
public static GroupActionResult updateGroup(@NonNull Context context,
|
||||
@NonNull String groupId,
|
||||
@NonNull Set<Recipient> members,
|
||||
@ -154,7 +176,7 @@ public class GroupManager {
|
||||
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses);
|
||||
} else {
|
||||
Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), true);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipient);
|
||||
return new GroupActionResult(groupRecipient, threadId);
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ public class GroupMessageProcessor {
|
||||
String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender());
|
||||
if (masterDevice == null) { masterDevice = content.getSender(); }
|
||||
if (members.contains(Address.fromExternal(context, masterDevice))) {
|
||||
database.remove(id, Address.fromExternal(context, masterDevice));
|
||||
database.removeMember(id, Address.fromExternal(context, masterDevice));
|
||||
if (outgoing) database.setActive(id, false);
|
||||
|
||||
return storeMessage(context, content, group, builder.build(), outgoing);
|
||||
@ -260,7 +260,7 @@ public class GroupMessageProcessor {
|
||||
Address address = Address.fromExternal(context, GroupUtil.getEncodedId(group));
|
||||
Recipient recipient = Recipient.from(context, address, false);
|
||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, null, Collections.emptyList(), Collections.emptyList());
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null);
|
||||
|
||||
mmsDatabase.markAsSent(messageId, true);
|
||||
|
@ -498,7 +498,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipient, "", -1);
|
||||
OutgoingEndSessionMessage outgoingEndSessionMessage = new OutgoingEndSessionMessage(outgoingTextMessage);
|
||||
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
|
||||
if (!recipient.isGroupRecipient()) {
|
||||
// TODO: Handle session reset on sync messages
|
||||
@ -808,7 +808,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
if (result.getMessageId() > -1) {
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
||||
long originalThreadId = threadDatabase.getThreadIdFor(originalRecipient);
|
||||
long originalThreadId = threadDatabase.getOrCreateThreadIdFor(originalRecipient);
|
||||
lokiMessageDatabase.setOriginalThreadID(result.getMessageId(), originalThreadId);
|
||||
}
|
||||
}
|
||||
@ -822,7 +822,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
message.getTimestamp(),
|
||||
message.getMessage().getExpiresInSeconds() * 1000L);
|
||||
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
long messageId = database.insertMessageOutbox(expirationUpdateMessage, threadId, false, null);
|
||||
|
||||
database.markAsSent(messageId, true);
|
||||
@ -864,7 +864,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
handleSynchronizeSentExpirationUpdate(message);
|
||||
}
|
||||
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipients);
|
||||
|
||||
database.beginTransaction();
|
||||
|
||||
@ -995,7 +995,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
if (result.getMessageId() > -1) {
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
||||
long originalThreadId = threadDatabase.getThreadIdFor(originalRecipient);
|
||||
long originalThreadId = threadDatabase.getOrCreateThreadIdFor(originalRecipient);
|
||||
lokiMessageDatabase.setOriginalThreadID(result.getMessageId(), originalThreadId);
|
||||
}
|
||||
}
|
||||
@ -1018,7 +1018,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
handleSynchronizeSentExpirationUpdate(message);
|
||||
}
|
||||
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
boolean isGroup = recipient.getAddress().isGroup();
|
||||
|
||||
MessagingDatabase database;
|
||||
@ -1102,7 +1102,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
if (canRecoverAutomatically(e)) {
|
||||
Recipient recipient = Recipient.from(context, Address.fromSerialized(sender), false);
|
||||
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(context);
|
||||
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
long threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
threadDB.addSessionRestoreDevice(threadID, sender);
|
||||
SessionManagementProtocol.startSessionReset(context, sender);
|
||||
} else {
|
||||
@ -1249,7 +1249,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
} else {
|
||||
// See if we need to redirect the message
|
||||
author = getMessageMasterDestination(content.getSender());
|
||||
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(author);
|
||||
threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(author);
|
||||
}
|
||||
|
||||
if (threadId <= 0) {
|
||||
@ -1459,7 +1459,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
|
||||
private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull String sender, int device) {
|
||||
Recipient author = Recipient.from(context, Address.fromSerialized(sender), false);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(conversationRecipient);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(conversationRecipient);
|
||||
|
||||
if (threadId > 0) {
|
||||
Log.d(TAG, "Typing stopped on thread " + threadId + " due to an incoming message.");
|
||||
|
@ -125,7 +125,7 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderM
|
||||
ClosedGroupsProtocol.createClosedGroup(this, name.toString(), selectedMembers + setOf( userPublicKey )).successUi { groupID ->
|
||||
loader.fadeOut()
|
||||
isLoading = false
|
||||
val threadID = DatabaseFactory.getThreadDatabase(this).getThreadIdFor(Recipient.from(this, Address.fromSerialized(groupID), false))
|
||||
val threadID = DatabaseFactory.getThreadDatabase(this).getOrCreateThreadIdFor(Recipient.from(this, Address.fromSerialized(groupID), false))
|
||||
if (!isFinishing) {
|
||||
openConversationActivity(this, threadID, Recipient.from(this, Address.fromSerialized(groupID), false))
|
||||
finish()
|
||||
|
@ -26,13 +26,12 @@ import kotlinx.android.synthetic.main.activity_home.*
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId
|
||||
import org.thoughtcrime.securesms.conversation.ConversationActivity
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||
import org.thoughtcrime.securesms.groups.GroupManager
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob
|
||||
import org.thoughtcrime.securesms.loki.api.PrepareAttachmentAudioExtrasJob
|
||||
import org.thoughtcrime.securesms.loki.dialogs.ConversationOptionsBottomSheet
|
||||
import org.thoughtcrime.securesms.loki.dialogs.LightThemeFeatureIntroBottomSheet
|
||||
import org.thoughtcrime.securesms.loki.dialogs.MultiDeviceRemovalBottomSheet
|
||||
@ -343,6 +342,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
val recipient = thread.recipient
|
||||
val threadDB = DatabaseFactory.getThreadDatabase(this)
|
||||
val deleteThread = Runnable {
|
||||
//TODO Move open group related logic to OpenGroupUtilities / PublicChatManager / GroupManager
|
||||
AsyncTask.execute {
|
||||
val publicChat = DatabaseFactory.getLokiThreadDatabase(this@HomeActivity).getPublicChat(threadID)
|
||||
if (publicChat != null) {
|
||||
@ -351,6 +351,10 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
apiDB.removeLastDeletionServerID(publicChat.channel, publicChat.server)
|
||||
apiDB.clearOpenGroupProfilePictureURL(publicChat.channel, publicChat.server)
|
||||
ApplicationContext.getInstance(this@HomeActivity).publicChatAPI!!.leave(publicChat.channel, publicChat.server)
|
||||
|
||||
//FIXME Group deletion should be synchronized with the related thread deletion.
|
||||
val groupId = threadDB.getRecipientForThreadId(threadID)!!.address.serialize()
|
||||
GroupManager.deleteGroup(groupId, this@HomeActivity)
|
||||
}
|
||||
threadDB.deleteConversation(threadID)
|
||||
ApplicationContext.getInstance(this@HomeActivity).messageNotifier.updateNotification(this@HomeActivity)
|
||||
|
@ -11,8 +11,12 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.android.synthetic.main.activity_join_public_chat.*
|
||||
import kotlinx.android.synthetic.main.fragment_enter_chat_url.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import network.loki.messenger.R
|
||||
import nl.komponents.kovenant.ui.failUi
|
||||
import nl.komponents.kovenant.ui.successUi
|
||||
@ -22,6 +26,7 @@ import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment
|
||||
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate
|
||||
import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol
|
||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
||||
import java.lang.Exception
|
||||
|
||||
class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
|
||||
private val adapter = JoinPublicChatActivityAdapter(this)
|
||||
@ -67,13 +72,19 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
|
||||
}
|
||||
showLoader()
|
||||
val channel: Long = 1
|
||||
OpenGroupUtilities.addGroup(this, url, channel).success {
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
OpenGroupUtilities.addGroup(this@JoinPublicChatActivity, url, channel)
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
hideLoader()
|
||||
Toast.makeText(this@JoinPublicChatActivity, R.string.activity_join_public_chat_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
SyncMessagesProtocol.syncAllOpenGroups(this@JoinPublicChatActivity)
|
||||
}.successUi {
|
||||
finish()
|
||||
}.failUi {
|
||||
hideLoader()
|
||||
Toast.makeText(this, R.string.activity_join_public_chat_error, Toast.LENGTH_SHORT).show()
|
||||
withContext(Dispatchers.Main) { finish() }
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
@ -123,13 +134,13 @@ class EnterChatURLFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun joinPublicChatIfPossible() {
|
||||
val inputMethodManager = context!!.getSystemService(BaseActionBarActivity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
val inputMethodManager = requireContext().getSystemService(BaseActionBarActivity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
inputMethodManager.hideSoftInputFromWindow(chatURLEditText.windowToken, 0)
|
||||
var chatURL = chatURLEditText.text.trim().toString().toLowerCase().replace("http://", "https://")
|
||||
if (!chatURL.toLowerCase().startsWith("https")) {
|
||||
chatURL = "https://$chatURL"
|
||||
}
|
||||
(activity!! as JoinPublicChatActivity).joinPublicChatIfPossible(chatURL)
|
||||
(requireActivity() as JoinPublicChatActivity).joinPublicChatIfPossible(chatURL)
|
||||
}
|
||||
}
|
||||
// endregion
|
@ -25,10 +25,9 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor
|
||||
@JvmStatic
|
||||
fun scheduleInstant(context: Context) {
|
||||
val workRequest = OneTimeWorkRequestBuilder<BackgroundPollWorker>()
|
||||
.setConstraints(
|
||||
Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
.setConstraints(Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
@ -41,10 +40,9 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor
|
||||
fun schedulePeriodic(context: Context) {
|
||||
Log.v(TAG, "Scheduling periodic work.")
|
||||
val workRequest = PeriodicWorkRequestBuilder<BackgroundPollWorker>(15, TimeUnit.MINUTES)
|
||||
.setConstraints(
|
||||
Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
.setConstraints(Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
|
@ -0,0 +1,55 @@
|
||||
package org.thoughtcrime.securesms.loki.api
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.*
|
||||
import org.thoughtcrime.securesms.logging.Log
|
||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
|
||||
|
||||
/**
|
||||
* Delegates the [OpenGroupUtilities.updateGroupInfo] call to the work manager.
|
||||
*/
|
||||
class PublicChatInfoUpdateWorker(val context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||
|
||||
companion object {
|
||||
const val TAG = "PublicChatInfoUpdateWorker"
|
||||
|
||||
private const val DATA_KEY_SERVER_URL = "server_uRL"
|
||||
private const val DATA_KEY_CHANNEL = "channel"
|
||||
|
||||
@JvmStatic
|
||||
fun scheduleInstant(context: Context, serverURL: String, channel: Long) {
|
||||
val workRequest = OneTimeWorkRequestBuilder<PublicChatInfoUpdateWorker>()
|
||||
.setConstraints(Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
)
|
||||
.setInputData(workDataOf(
|
||||
DATA_KEY_SERVER_URL to serverURL,
|
||||
DATA_KEY_CHANNEL to channel
|
||||
))
|
||||
.build()
|
||||
|
||||
WorkManager
|
||||
.getInstance(context)
|
||||
.enqueue(workRequest)
|
||||
}
|
||||
}
|
||||
|
||||
override fun doWork(): Result {
|
||||
val serverUrl = inputData.getString(DATA_KEY_SERVER_URL)!!
|
||||
val channel = inputData.getLong(DATA_KEY_CHANNEL, -1)
|
||||
|
||||
val publicChatId = PublicChat.getId(channel, serverUrl)
|
||||
|
||||
return try {
|
||||
Log.v(TAG, "Updating open group info for $publicChatId.")
|
||||
OpenGroupUtilities.updateGroupInfo(context, serverUrl, channel)
|
||||
Log.v(TAG, "Open group info was successfully updated for $publicChatId.")
|
||||
Result.success()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to update open group info for $publicChatId", e)
|
||||
Result.failure()
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.database.ContentObserver
|
||||
import android.graphics.Bitmap
|
||||
import android.text.TextUtils
|
||||
import androidx.annotation.WorkerThread
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.functional.bind
|
||||
import nl.komponents.kovenant.functional.map
|
||||
@ -16,6 +17,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChatInfo
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
class PublicChatManager(private val context: Context) {
|
||||
private var chats = mutableMapOf<Long, PublicChat>()
|
||||
@ -23,7 +25,7 @@ class PublicChatManager(private val context: Context) {
|
||||
private val observers = mutableMapOf<Long, ContentObserver>()
|
||||
private var isPolling = false
|
||||
|
||||
public fun areAllCaughtUp():Boolean {
|
||||
public fun areAllCaughtUp(): Boolean {
|
||||
var areAllCaughtUp = true
|
||||
refreshChatsAndPollers()
|
||||
for ((threadID, chat) in chats) {
|
||||
@ -58,19 +60,24 @@ class PublicChatManager(private val context: Context) {
|
||||
isPolling = false
|
||||
}
|
||||
|
||||
public fun addChat(server: String, channel: Long): Promise<PublicChat, Exception> {
|
||||
//TODO Declare a specific type of checked exception instead of "Exception".
|
||||
@WorkerThread
|
||||
@Throws(java.lang.Exception::class)
|
||||
public fun addChat(server: String, channel: Long): PublicChat {
|
||||
val groupChatAPI = ApplicationContext.getInstance(context).publicChatAPI
|
||||
?: return Promise.ofFail(IllegalStateException("LokiPublicChatAPI is not set!"))
|
||||
return groupChatAPI.getAuthToken(server).bind {
|
||||
groupChatAPI.getChannelInfo(channel, server)
|
||||
}.map {
|
||||
addChat(server, channel, it)
|
||||
}
|
||||
?: throw IllegalStateException("LokiPublicChatAPI is not set!")
|
||||
|
||||
// Ensure the auth token is acquired.
|
||||
groupChatAPI.getAuthToken(server).get()
|
||||
|
||||
val channelInfo = groupChatAPI.getChannelInfo(channel, server).get()
|
||||
return addChat(server, channel, channelInfo)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public fun addChat(server: String, channel: Long, info: PublicChatInfo): PublicChat {
|
||||
val chat = PublicChat(channel, server, info.displayName, true)
|
||||
var threadID = GroupManager.getOpenGroupThreadID(chat.id, context)
|
||||
var threadID = GroupManager.getOpenGroupThreadID(chat.id, context)
|
||||
var profilePicture: Bitmap? = null
|
||||
// Create the group if we don't have one
|
||||
if (threadID < 0) {
|
||||
@ -105,7 +112,7 @@ class PublicChatManager(private val context: Context) {
|
||||
|
||||
private fun listenToThreadDeletion(threadID: Long) {
|
||||
if (threadID < 0 || observers[threadID] != null) { return }
|
||||
val observer = createDeletionObserver(threadID, Runnable {
|
||||
val observer = createDeletionObserver(threadID) {
|
||||
val chat = chats[threadID]
|
||||
|
||||
// Reset last message cache
|
||||
@ -119,7 +126,7 @@ class PublicChatManager(private val context: Context) {
|
||||
pollers.remove(threadID)?.stop()
|
||||
observers.remove(threadID)
|
||||
startPollersIfNeeded()
|
||||
})
|
||||
}
|
||||
observers[threadID] = observer
|
||||
|
||||
context.applicationContext.contentResolver.registerContentObserver(DatabaseContentProviders.Conversation.getUriForThread(threadID), true, observer)
|
||||
|
@ -193,8 +193,8 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
|
||||
val messageID = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID)
|
||||
var isDuplicate = false
|
||||
if (messageID != null) {
|
||||
isDuplicate = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageID) > 0
|
||||
|| DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageID) > 0
|
||||
isDuplicate = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageID) >= 0
|
||||
|| DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageID) >= 0
|
||||
}
|
||||
if (isDuplicate) { return }
|
||||
if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return }
|
||||
|
@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.loki.database
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.util.Log
|
||||
import org.thoughtcrime.securesms.database.Address
|
||||
import org.thoughtcrime.securesms.database.Database
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
@ -34,7 +33,7 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
||||
override fun getThreadID(hexEncodedPublicKey: String): Long {
|
||||
val address = Address.fromSerialized(hexEncodedPublicKey)
|
||||
val recipient = Recipient.from(context, address, false)
|
||||
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
|
||||
return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient)
|
||||
}
|
||||
|
||||
fun getThreadID(messageID: Long): Long {
|
||||
|
@ -25,7 +25,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.GroupType
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext
|
||||
import org.whispersystems.signalservice.loki.api.SnodeAPI
|
||||
import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupRatchet
|
||||
import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupRatchetCollectionType
|
||||
import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupSenderKey
|
||||
@ -82,7 +81,7 @@ object ClosedGroupsProtocol {
|
||||
// Add the group to the user's set of public keys to poll for
|
||||
DatabaseFactory.getSSKDatabase(context).setClosedGroupPrivateKey(groupPublicKey, groupKeyPair.hexEncodedPrivateKey)
|
||||
// Notify the user
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false))
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false))
|
||||
insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID)
|
||||
// Notify the PN server
|
||||
LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey)
|
||||
@ -166,7 +165,7 @@ object ClosedGroupsProtocol {
|
||||
if (isUserLeaving) {
|
||||
sskDatabase.removeClosedGroupPrivateKey(groupPublicKey)
|
||||
groupDB.setActive(groupID, false)
|
||||
groupDB.remove(groupID, Address.fromSerialized(userPublicKey))
|
||||
groupDB.removeMember(groupID, Address.fromSerialized(userPublicKey))
|
||||
// Notify the PN server
|
||||
LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey)
|
||||
} else {
|
||||
@ -230,7 +229,7 @@ object ClosedGroupsProtocol {
|
||||
}
|
||||
// Notify the user
|
||||
val infoType = if (isUserLeaving) GroupContext.Type.QUIT else GroupContext.Type.UPDATE
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false))
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false))
|
||||
insertOutgoingInfoMessage(context, groupID, infoType, name, members, admins, threadID)
|
||||
deferred.resolve(Unit)
|
||||
}.start()
|
||||
@ -385,7 +384,7 @@ object ClosedGroupsProtocol {
|
||||
if (wasCurrentUserRemoved) {
|
||||
sskDatabase.removeClosedGroupPrivateKey(groupPublicKey)
|
||||
groupDB.setActive(groupID, false)
|
||||
groupDB.remove(groupID, Address.fromSerialized(userPublicKey))
|
||||
groupDB.removeMember(groupID, Address.fromSerialized(userPublicKey))
|
||||
// Notify the PN server
|
||||
LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey)
|
||||
} else {
|
||||
@ -510,7 +509,7 @@ object ClosedGroupsProtocol {
|
||||
@JvmStatic
|
||||
fun leaveLegacyGroup(context: Context, recipient: Recipient): Boolean {
|
||||
if (!recipient.address.isClosedGroup) { return true }
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient)
|
||||
val message = GroupUtil.createGroupLeaveMessage(context, recipient).orNull()
|
||||
if (threadID < 0 || message == null) { return false }
|
||||
MessageSender.send(context, message, threadID, false, null)
|
||||
@ -522,7 +521,7 @@ object ClosedGroupsProtocol {
|
||||
val groupDatabase = DatabaseFactory.getGroupDatabase(context)
|
||||
val groupID = recipient.address.toGroupString()
|
||||
groupDatabase.setActive(groupID, false)
|
||||
groupDatabase.remove(groupID, Address.fromSerialized(userPublicKey))
|
||||
groupDatabase.removeMember(groupID, Address.fromSerialized(userPublicKey))
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import android.content.Context
|
||||
import android.os.AsyncTask
|
||||
import android.util.Log
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
@ -11,7 +10,6 @@ import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob
|
||||
import org.thoughtcrime.securesms.loki.utilities.recipient
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.sms.MessageSender
|
||||
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage
|
||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage
|
||||
@ -28,7 +26,7 @@ object SessionManagementProtocol {
|
||||
val recipient = recipient(context, publicKey)
|
||||
if (recipient.isGroupRecipient) { return }
|
||||
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient)
|
||||
val devices = lokiThreadDB.getSessionRestoreDevices(threadID)
|
||||
for (device in devices) {
|
||||
val endSessionMessage = OutgoingEndSessionMessage(OutgoingTextMessage(recipient, "TERMINATE", 0, -1))
|
||||
@ -106,7 +104,7 @@ object SessionManagementProtocol {
|
||||
if (TextSecurePreferences.getRestorationTime(context) > errorTimestamp) {
|
||||
return ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(publicKey)
|
||||
}
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(masterDeviceAsRecipient)
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(masterDeviceAsRecipient)
|
||||
DatabaseFactory.getLokiThreadDatabase(context).addSessionRestoreDevice(threadID, publicKey)
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ class SessionResetImplementation(private val context: Context) : SessionResetPro
|
||||
}
|
||||
val smsDB = DatabaseFactory.getSmsDatabase(context)
|
||||
val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false)
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient)
|
||||
val infoMessage = OutgoingTextMessage(recipient, "", 0, 0)
|
||||
val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null)
|
||||
if (infoMessageID > -1) {
|
||||
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.loki.protocol.shelved
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.NumberData
|
||||
@ -132,6 +133,7 @@ object SyncMessagesProtocol {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@WorkerThread
|
||||
fun handleOpenGroupSyncMessage(context: Context, content: SignalServiceContent, openGroups: List<PublicChat>) {
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||
val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey)
|
||||
|
@ -6,7 +6,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
|
||||
fun getOpenGroupDisplayName(recipient: Recipient, threadRecipient: Recipient, context: Context): String {
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(threadRecipient)
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(threadRecipient)
|
||||
val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
||||
val publicKey = recipient.address.toString()
|
||||
val displayName = if (publicChat != null) {
|
||||
|
@ -1,28 +1,42 @@
|
||||
package org.thoughtcrime.securesms.loki.utilities
|
||||
|
||||
import android.content.Context
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.then
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
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.GroupUtil
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
|
||||
import java.lang.Exception
|
||||
import java.lang.IllegalStateException
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
//TODO Refactor so methods declare specific type of checked exceptions and not generalized Exception.
|
||||
object OpenGroupUtilities {
|
||||
|
||||
@JvmStatic fun addGroup(context: Context, url: String, channel: Long): Promise<PublicChat, Exception> {
|
||||
// Check for an existing group
|
||||
val groupID = PublicChat.getId(channel, url)
|
||||
val threadID = GroupManager.getOpenGroupThreadID(groupID, context)
|
||||
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
||||
if (openGroup != null) { return Promise.of(openGroup) }
|
||||
// Add the new group
|
||||
val application = ApplicationContext.getInstance(context)
|
||||
val displayName = TextSecurePreferences.getProfileName(context)
|
||||
val lokiPublicChatAPI = application.publicChatAPI ?: throw Error("LokiPublicChatAPI is not initialized.")
|
||||
return application.publicChatManager.addChat(url, channel).then { group ->
|
||||
private const val TAG = "OpenGroupUtilities"
|
||||
|
||||
@JvmStatic
|
||||
@WorkerThread
|
||||
@Throws(Exception::class)
|
||||
fun addGroup(context: Context, url: String, channel: Long): PublicChat {
|
||||
// Check for an existing group.
|
||||
val groupID = PublicChat.getId(channel, url)
|
||||
val threadID = GroupManager.getOpenGroupThreadID(groupID, context)
|
||||
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
||||
if (openGroup != null) return openGroup
|
||||
|
||||
// Add the new group.
|
||||
val application = ApplicationContext.getInstance(context)
|
||||
val displayName = TextSecurePreferences.getProfileName(context)
|
||||
val lokiPublicChatAPI = application.publicChatAPI
|
||||
?: throw IllegalStateException("LokiPublicChatAPI is not initialized.")
|
||||
|
||||
val group = application.publicChatManager.addChat(url, channel)
|
||||
|
||||
DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url)
|
||||
DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url)
|
||||
lokiPublicChatAPI.getMessages(channel, url)
|
||||
@ -31,7 +45,34 @@ object OpenGroupUtilities {
|
||||
val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(context)
|
||||
val profileUrl: String? = TextSecurePreferences.getProfilePictureURL(context)
|
||||
lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl)
|
||||
group
|
||||
return group
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls the general public chat data from the server and updates related records.
|
||||
* Fires [GroupInfoUpdatedEvent] on [EventBus] upon success.
|
||||
*
|
||||
* Consider using [org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker] for lazy approach.
|
||||
*/
|
||||
@JvmStatic
|
||||
@WorkerThread
|
||||
@Throws(Exception::class)
|
||||
fun updateGroupInfo(context: Context, url: String, channel: Long) {
|
||||
val publicChatAPI = ApplicationContext.getInstance(context).publicChatAPI
|
||||
?: throw IllegalStateException("Public chat API is not initialized!")
|
||||
|
||||
// Check if open group has a related DB record.
|
||||
val groupId = GroupUtil.getEncodedOpenGroupId(PublicChat.getId(channel, url).toByteArray())
|
||||
if (!DatabaseFactory.getGroupDatabase(context).hasGroup(groupId)) {
|
||||
throw IllegalStateException("Attempt to update open group info for non-existent DB record: $groupId")
|
||||
}
|
||||
|
||||
val info = publicChatAPI.getChannelInfo(channel, url).get()
|
||||
|
||||
publicChatAPI.updateProfileIfNeeded(channel, url, groupId, info, false)
|
||||
|
||||
EventBus.getDefault().post(GroupInfoUpdatedEvent(url, channel))
|
||||
}
|
||||
|
||||
data class GroupInfoUpdatedEvent(val url: String, val channel: Long)
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package org.thoughtcrime.securesms.loki.views
|
||||
|
||||
import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@ -10,11 +9,9 @@ import kotlinx.android.synthetic.main.view_conversation.view.profilePictureView
|
||||
import kotlinx.android.synthetic.main.view_user.view.*
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.groups.GroupManager
|
||||
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager
|
||||
|
||||
class UserView : LinearLayout {
|
||||
var openGroupThreadID: Long = -1 // FIXME: This is a bit ugly
|
||||
@ -63,7 +60,7 @@ class UserView : LinearLayout {
|
||||
return result ?: publicKey
|
||||
}
|
||||
}
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(user)
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(user)
|
||||
MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded(threadID, context) // FIXME: This is a bad place to do this
|
||||
val address = user.address.serialize()
|
||||
profilePictureView.glide = glide
|
||||
|
@ -121,6 +121,10 @@ public class Recipient implements RecipientModifiedListener {
|
||||
if (recipient.isPresent()) consumer.accept(recipient.get());
|
||||
}
|
||||
|
||||
public static boolean removeCached(@NonNull Address address) {
|
||||
return provider.removeCached(address);
|
||||
}
|
||||
|
||||
Recipient(@NonNull Context context,
|
||||
@NonNull Address address,
|
||||
@Nullable Recipient stale,
|
||||
|
@ -79,6 +79,10 @@ class RecipientProvider {
|
||||
return Optional.fromNullable(recipientCache.get(address));
|
||||
}
|
||||
|
||||
boolean removeCached(@NonNull Address address) {
|
||||
return recipientCache.remove(address);
|
||||
}
|
||||
|
||||
private @NonNull Optional<RecipientDetails> createPrefetchedRecipientDetails(@NonNull Context context, @NonNull Address address,
|
||||
@NonNull Optional<RecipientSettings> settings,
|
||||
@NonNull Optional<GroupRecord> groupRecord)
|
||||
@ -230,6 +234,10 @@ class RecipientProvider {
|
||||
cache.put(address, recipient);
|
||||
}
|
||||
|
||||
public synchronized boolean remove(Address address) {
|
||||
return cache.remove(address) != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -69,7 +69,7 @@ public class MessageSender {
|
||||
long allocatedThreadId;
|
||||
|
||||
if (threadId == -1) {
|
||||
allocatedThreadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
allocatedThreadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
} else {
|
||||
allocatedThreadId = threadId;
|
||||
}
|
||||
@ -94,7 +94,7 @@ public class MessageSender {
|
||||
long allocatedThreadId;
|
||||
|
||||
if (threadId == -1) {
|
||||
allocatedThreadId = threadDatabase.getThreadIdFor(message.getRecipient(), message.getDistributionType());
|
||||
allocatedThreadId = threadDatabase.getOrCreateThreadIdFor(message.getRecipient(), message.getDistributionType());
|
||||
} else {
|
||||
allocatedThreadId = threadId;
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ public class CommunicationActions {
|
||||
new AsyncTask<Void, Void, Long>() {
|
||||
@Override
|
||||
protected Long doInBackground(Void... voids) {
|
||||
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -88,7 +88,7 @@ public class IdentityUtil {
|
||||
smsDatabase.insertMessageInbox(incoming);
|
||||
} else {
|
||||
Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(group.getGroupId(), false)), true);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipient);
|
||||
OutgoingTextMessage outgoing ;
|
||||
|
||||
if (verified) outgoing = new OutgoingIdentityVerifiedMessage(recipient);
|
||||
@ -112,7 +112,7 @@ public class IdentityUtil {
|
||||
if (verified) outgoing = new OutgoingIdentityVerifiedMessage(recipient);
|
||||
else outgoing = new OutgoingIdentityDefaultMessage(recipient);
|
||||
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
|
||||
Log.i(TAG, "Inserting verified outbox...");
|
||||
DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoing, false, time, null);
|
||||
|
Loading…
Reference in New Issue
Block a user