mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-12 04:57:43 +00:00
Merge branch 'dev' of https://github.com/oxen-io/session-android into client-side-nickname
This commit is contained in:
@@ -46,11 +46,12 @@ dependencies {
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
|
||||
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-viewmodel-ktx:2.2.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
|
||||
implementation 'androidx.activity:activity-ktx:1.1.0'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.3.0-beta01'
|
||||
implementation 'androidx.lifecycle:lifecycle-common-java8:2.3.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
|
||||
implementation 'androidx.activity:activity-ktx:1.2.2'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.3.2'
|
||||
implementation "androidx.core:core-ktx:1.3.2"
|
||||
implementation "androidx.work:work-runtime-ktx:2.4.0"
|
||||
|
||||
@@ -157,8 +158,8 @@ dependencies {
|
||||
testImplementation 'org.robolectric:shadows-multidex:4.2'
|
||||
}
|
||||
|
||||
def canonicalVersionCode = 151
|
||||
def canonicalVersionName = "1.9.1"
|
||||
def canonicalVersionCode = 157
|
||||
def canonicalVersionName = "1.10.0"
|
||||
|
||||
def postFixSize = 10
|
||||
def abiPostFix = ['armeabi-v7a' : 1,
|
||||
|
@@ -84,6 +84,7 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
|
||||
}
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
keyCachingService.setMasterSecret(new Object());
|
||||
keyCachingService = null;
|
||||
}
|
||||
}, Context.BIND_AUTO_CREATE);
|
||||
@@ -133,7 +134,9 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
|
||||
private void handleAuthenticated() {
|
||||
authenticated = true;
|
||||
//TODO Replace with a proper call.
|
||||
keyCachingService.setMasterSecret(new Object());
|
||||
if (keyCachingService != null) {
|
||||
keyCachingService.setMasterSecret(new Object());
|
||||
}
|
||||
|
||||
// Finish and proceed with the next intent.
|
||||
Intent nextIntent = getIntent().getParcelableExtra("next_intent");
|
||||
@@ -188,6 +191,8 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
|
||||
|
||||
if (!keyguardManager.isKeyguardSecure()) {
|
||||
Log.w(TAG ,"Keyguard not secure...");
|
||||
TextSecurePreferences.setScreenLockEnabled(getApplicationContext(), false);
|
||||
TextSecurePreferences.setScreenLockTimeout(getApplicationContext(), 0);
|
||||
handleAuthenticated();
|
||||
return;
|
||||
}
|
||||
|
@@ -137,9 +137,20 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
|
||||
return openGroupMessagingDatabase.getMessageID(serverID)
|
||||
}
|
||||
|
||||
override fun deleteMessage(messageID: Long) {
|
||||
val messagingDatabase = DatabaseFactory.getSmsDatabase(context)
|
||||
messagingDatabase.deleteMessage(messageID)
|
||||
override fun getMessageID(serverId: Long, threadId: Long): Pair<Long, Boolean>? {
|
||||
val messageDB = DatabaseFactory.getLokiMessageDatabase(context)
|
||||
return messageDB.getMessageID(serverId, threadId)
|
||||
}
|
||||
|
||||
override fun deleteMessage(messageID: Long, isSms: Boolean) {
|
||||
if (isSms) {
|
||||
val db = DatabaseFactory.getSmsDatabase(context)
|
||||
db.deleteMessage(messageID)
|
||||
} else {
|
||||
val db = DatabaseFactory.getMmsDatabase(context)
|
||||
db.delete(messageID)
|
||||
}
|
||||
DatabaseFactory.getLokiMessageDatabase(context).deleteMessage(messageID, isSms)
|
||||
}
|
||||
|
||||
override fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment? {
|
||||
|
@@ -87,22 +87,43 @@ import com.annimon.stream.Stream;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
|
||||
import org.session.libsession.messaging.mentions.MentionsManager;
|
||||
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingSecureMediaMessage;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||
import org.session.libsession.messaging.open_groups.OpenGroup;
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupV2;
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
||||
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
||||
import org.session.libsession.messaging.threads.Address;
|
||||
import org.session.libsession.messaging.threads.DistributionTypes;
|
||||
import org.session.libsession.messaging.threads.GroupRecord;
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||
import org.session.libsession.messaging.threads.recipients.RecipientFormattingException;
|
||||
import org.session.libsession.messaging.threads.recipients.RecipientModifiedListener;
|
||||
import org.session.libsession.utilities.ExpirationUtil;
|
||||
import org.session.libsession.utilities.GroupUtil;
|
||||
import org.session.libsession.utilities.MediaTypes;
|
||||
import org.session.libsession.utilities.SSKEnvironment;
|
||||
import org.session.libsession.utilities.ServiceUtil;
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.session.libsession.utilities.concurrent.AssertedSuccessListener;
|
||||
import org.session.libsession.utilities.views.Stub;
|
||||
import org.session.libsignal.libsignal.InvalidMessageException;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
import org.session.libsignal.service.loki.Mention;
|
||||
import org.session.libsignal.service.loki.utilities.HexEncodingKt;
|
||||
import org.session.libsignal.service.loki.utilities.PublicKeyValidation;
|
||||
import org.session.libsignal.utilities.concurrent.ListenableFuture;
|
||||
import org.session.libsignal.utilities.concurrent.SettableFuture;
|
||||
import org.session.libsignal.utilities.logging.Log;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.ExpirationDialog;
|
||||
import org.thoughtcrime.securesms.MediaOverviewActivity;
|
||||
@@ -126,7 +147,6 @@ import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
import org.thoughtcrime.securesms.contactshare.ContactUtil;
|
||||
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
|
||||
import org.session.libsession.messaging.threads.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.DraftDatabase;
|
||||
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
|
||||
@@ -140,7 +160,6 @@ import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel;
|
||||
import org.session.libsignal.utilities.logging.Log;
|
||||
import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity;
|
||||
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
||||
import org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker;
|
||||
@@ -163,8 +182,6 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.ImageSlide;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingSecureMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.QuoteId;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||
@@ -173,31 +190,12 @@ import org.thoughtcrime.securesms.mms.VideoSlide;
|
||||
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||
import org.session.libsession.messaging.threads.recipients.RecipientFormattingException;
|
||||
import org.session.libsession.messaging.threads.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.search.model.MessageResult;
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.PushCharacterCalculator;
|
||||
import org.session.libsession.utilities.ServiceUtil;
|
||||
import org.session.libsession.utilities.Util;
|
||||
|
||||
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
|
||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
||||
import org.session.libsession.messaging.threads.GroupRecord;
|
||||
import org.session.libsession.utilities.ExpirationUtil;
|
||||
import org.session.libsession.utilities.views.Stub;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.session.libsession.utilities.concurrent.AssertedSuccessListener;
|
||||
import org.session.libsignal.utilities.concurrent.ListenableFuture;
|
||||
import org.session.libsignal.utilities.concurrent.SettableFuture;
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -429,9 +427,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(threadId, this);
|
||||
|
||||
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||
OpenGroupV2 openGroupV2 = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadId);
|
||||
if (publicChat != null) {
|
||||
// Request open group info update and handle the successful result in #onOpenGroupInfoUpdated().
|
||||
PublicChatInfoUpdateWorker.scheduleInstant(this, publicChat.getServer(), publicChat.getChannel());
|
||||
} else if (openGroupV2 != null) {
|
||||
PublicChatInfoUpdateWorker.scheduleInstant(this, openGroupV2.getServer(), openGroupV2.getRoom());
|
||||
}
|
||||
|
||||
View rootView = findViewById(R.id.rootView);
|
||||
@@ -1459,11 +1460,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onOpenGroupInfoUpdated(OpenGroupUtilities.GroupInfoUpdatedEvent event) {
|
||||
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||
OpenGroupV2 openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadId);
|
||||
if (publicChat != null &&
|
||||
publicChat.getChannel() == event.getChannel() &&
|
||||
publicChat.getServer().equals(event.getUrl())) {
|
||||
this.updateSubtitleTextView();
|
||||
}
|
||||
if (openGroup != null &&
|
||||
openGroup.getRoom().equals(event.getRoom()) &&
|
||||
openGroup.getServer().equals(event.getUrl())) {
|
||||
this.updateSubtitleTextView();
|
||||
}
|
||||
}
|
||||
|
||||
//////// Helper Methods
|
||||
@@ -1780,7 +1787,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
boolean initiating = threadId == -1;
|
||||
boolean needsSplit = message.length() > characterCalculator.calculateCharacters(message).maxPrimaryMessageSize;
|
||||
boolean isMediaMessage = attachmentManager.isAttachmentPresent() ||
|
||||
recipient.isGroupRecipient() ||
|
||||
// recipient.isGroupRecipient() ||
|
||||
inputPanel.getQuote().isPresent() ||
|
||||
linkPreviewViewModel.hasLinkPreview() ||
|
||||
LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages
|
||||
@@ -2397,10 +2404,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
subtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault()));
|
||||
} else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) {
|
||||
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||
OpenGroupV2 openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadId);
|
||||
if (publicChat != null) {
|
||||
Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(publicChat.getChannel(), publicChat.getServer());
|
||||
if (userCount == null) { userCount = 0; }
|
||||
subtitleTextView.setText(userCount + " members");
|
||||
} else if (openGroup != null) {
|
||||
Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(openGroup.getRoom(),openGroup.getServer());
|
||||
if (userCount == null) { userCount = 0; }
|
||||
subtitleTextView.setText(userCount + " members");
|
||||
} else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) {
|
||||
subtitleTextView.setText(recipient.getAddress().toString());
|
||||
} else {
|
||||
|
@@ -57,49 +57,51 @@ import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration;
|
||||
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
||||
import org.session.libsession.messaging.messages.visible.Quote;
|
||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||
import org.session.libsession.messaging.open_groups.OpenGroup;
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupAPI;
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2;
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupV2;
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||
import org.session.libsession.messaging.threads.Address;
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.session.libsession.utilities.concurrent.SimpleTask;
|
||||
import org.session.libsession.utilities.task.ProgressDialogAsyncTask;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
import org.session.libsignal.utilities.logging.Log;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.MessageDetailsActivity;
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
|
||||
import org.thoughtcrime.securesms.ShareActivity;
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.components.ConversationTypingView;
|
||||
import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationAdapter.HeaderViewHolder;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickListener;
|
||||
import org.session.libsession.messaging.threads.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.loaders.ConversationLoader;
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.session.libsignal.utilities.logging.Log;
|
||||
import org.thoughtcrime.securesms.longmessage.LongMessageActivity;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||
import org.session.libsession.utilities.task.ProgressDialogAsyncTask;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.session.libsession.utilities.concurrent.SimpleTask;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -397,7 +399,8 @@ public class ConversationFragment extends Fragment
|
||||
|
||||
if (isGroupChat) {
|
||||
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
||||
boolean isPublicChat = (publicChat != null);
|
||||
OpenGroupV2 openGroupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getOpenGroupChat(threadId);
|
||||
boolean isPublicChat = (publicChat != null || openGroupChat != null);
|
||||
int selectedMessageCount = messageRecords.size();
|
||||
boolean areAllSentByUser = true;
|
||||
Set<String> uniqueUserSet = new HashSet<>();
|
||||
@@ -407,8 +410,12 @@ public class ConversationFragment extends Fragment
|
||||
}
|
||||
menu.findItem(R.id.menu_context_copy_public_key).setVisible(selectedMessageCount == 1 && !areAllSentByUser);
|
||||
menu.findItem(R.id.menu_context_reply).setVisible(selectedMessageCount == 1);
|
||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext());
|
||||
boolean userCanModerate = isPublicChat && OpenGroupAPI.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer());
|
||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(requireContext());
|
||||
boolean userCanModerate =
|
||||
(isPublicChat &&
|
||||
((publicChat != null && OpenGroupAPI.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer()))
|
||||
|| (openGroupChat != null && OpenGroupAPIV2.isUserModerator(userHexEncodedPublicKey, openGroupChat.getRoom(), openGroupChat.getServer())))
|
||||
);
|
||||
boolean isDeleteOptionVisible = !isPublicChat || (areAllSentByUser || userCanModerate);
|
||||
// allow banning if moderating a public chat and only one user's messages are selected
|
||||
boolean isBanOptionVisible = isPublicChat && userCanModerate && !areAllSentByUser && uniqueUserSet.size() == 1;
|
||||
@@ -509,6 +516,7 @@ public class ConversationFragment extends Fragment
|
||||
builder.setCancelable(true);
|
||||
|
||||
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
||||
OpenGroupV2 openGroupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getOpenGroupChat(threadId);
|
||||
|
||||
builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
@@ -519,14 +527,14 @@ public class ConversationFragment extends Fragment
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground(MessageRecord... messageRecords) {
|
||||
if (publicChat != null) {
|
||||
if (publicChat != null || openGroupChat != null) {
|
||||
ArrayList<Long> serverIDs = new ArrayList<>();
|
||||
ArrayList<Long> ignoredMessages = new ArrayList<>();
|
||||
ArrayList<Long> failedMessages = new ArrayList<>();
|
||||
boolean isSentByUser = true;
|
||||
for (MessageRecord messageRecord : messageRecords) {
|
||||
isSentByUser = isSentByUser && messageRecord.isOutgoing();
|
||||
Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id);
|
||||
Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id, !messageRecord.isMms());
|
||||
if (serverID != null) {
|
||||
serverIDs.add(serverID);
|
||||
} else {
|
||||
@@ -538,7 +546,7 @@ public class ConversationFragment extends Fragment
|
||||
.deleteMessages(serverIDs, publicChat.getChannel(), publicChat.getServer(), isSentByUser)
|
||||
.success(l -> {
|
||||
for (MessageRecord messageRecord : messageRecords) {
|
||||
Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id);
|
||||
Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id, !messageRecord.isMms());
|
||||
if (l.contains(serverID)) {
|
||||
if (messageRecord.isMms()) {
|
||||
DatabaseFactory.getMmsDatabase(getActivity()).delete(messageRecord.getId());
|
||||
@@ -555,7 +563,25 @@ public class ConversationFragment extends Fragment
|
||||
Log.w("Loki", "Couldn't delete message due to error: " + e.toString() + ".");
|
||||
return null;
|
||||
});
|
||||
}
|
||||
} else if (openGroupChat != null) {
|
||||
for (Long serverId : serverIDs) {
|
||||
OpenGroupAPIV2
|
||||
.deleteMessage(serverId, openGroupChat.getRoom(), openGroupChat.getServer())
|
||||
.success(l -> {
|
||||
for (MessageRecord messageRecord : messageRecords) {
|
||||
Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id, !messageRecord.isMms());
|
||||
if (serverID != null && serverID.equals(serverId)) {
|
||||
MessagingModuleConfiguration.shared.getMessageDataProvider().deleteMessage(messageRecord.id, !messageRecord.isMms());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}).fail(e->{
|
||||
Log.e("Loki", "Couldn't delete message due to error",e);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (MessageRecord messageRecord : messageRecords) {
|
||||
if (messageRecord.isMms()) {
|
||||
@@ -591,7 +617,8 @@ public class ConversationFragment extends Fragment
|
||||
builder.setTitle(R.string.ConversationFragment_ban_selected_user);
|
||||
builder.setCancelable(true);
|
||||
|
||||
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
||||
final OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
||||
final OpenGroupV2 openGroupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getOpenGroupChat(threadId);
|
||||
|
||||
builder.setPositiveButton(R.string.ban, (dialog, which) -> {
|
||||
ConversationAdapter chatAdapter = getListAdapter();
|
||||
@@ -610,9 +637,19 @@ public class ConversationFragment extends Fragment
|
||||
Log.d("Loki", "User banned");
|
||||
return Unit.INSTANCE;
|
||||
}).fail(e -> {
|
||||
Log.d("Loki", "Couldn't ban user due to error: " + e.toString() + ".");
|
||||
Log.e("Loki", "Couldn't ban user due to error",e);
|
||||
return null;
|
||||
});
|
||||
} else if (openGroupChat != null) {
|
||||
OpenGroupAPIV2
|
||||
.ban(userPublicKey, openGroupChat.getRoom(), openGroupChat.getServer())
|
||||
.success(l -> {
|
||||
Log.d("Loki", "User banned");
|
||||
return Unit.INSTANCE;
|
||||
}).fail(e -> {
|
||||
Log.e("Loki", "Failed to ban user",e);
|
||||
return null;
|
||||
});
|
||||
} else {
|
||||
Log.d("Loki", "Tried to ban user from a non-public chat");
|
||||
}
|
||||
|
@@ -56,6 +56,8 @@ import org.session.libsession.messaging.jobs.AttachmentDownloadJob;
|
||||
import org.session.libsession.messaging.jobs.JobQueue;
|
||||
import org.session.libsession.messaging.open_groups.OpenGroup;
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupAPI;
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2;
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupV2;
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress;
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
|
||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||
@@ -88,6 +90,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.Quote;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
||||
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
|
||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities;
|
||||
import org.thoughtcrime.securesms.loki.views.MessageAudioView;
|
||||
import org.thoughtcrime.securesms.loki.views.ProfilePictureView;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
@@ -724,9 +727,9 @@ public class ConversationItem extends LinearLayout
|
||||
String publicKey = recipient.getAddress().toString();
|
||||
profilePictureView.setPublicKey(publicKey);
|
||||
String displayName = recipient.getName();
|
||||
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID);
|
||||
if (displayName == null && publicChat != null) {
|
||||
displayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.getId(), publicKey);
|
||||
OpenGroup openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID);
|
||||
if (displayName == null && openGroup != null) {
|
||||
displayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(openGroup.getId(), publicKey);
|
||||
}
|
||||
profilePictureView.setDisplayName(displayName);
|
||||
profilePictureView.setAdditionalPublicKey(null);
|
||||
@@ -867,7 +870,12 @@ public class ConversationItem extends LinearLayout
|
||||
try {
|
||||
String serverId = GroupUtil.getDecodedGroupID(conversationRecipient.getAddress().serialize());
|
||||
String senderDisplayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(serverId, recipient.getAddress().serialize());
|
||||
if (senderDisplayName != null) { displayName = senderDisplayName; }
|
||||
if (senderDisplayName != null) {
|
||||
displayName = senderDisplayName;
|
||||
} else {
|
||||
// opengroupv2 format
|
||||
displayName = OpenGroupUtilities.getDisplayName(recipient);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Do nothing
|
||||
}
|
||||
@@ -912,9 +920,13 @@ public class ConversationItem extends LinearLayout
|
||||
int visibility = View.GONE;
|
||||
|
||||
OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(messageRecord.getThreadId());
|
||||
OpenGroupV2 openGroupV2 = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(messageRecord.getThreadId());
|
||||
if (publicChat != null) {
|
||||
boolean isModerator = OpenGroupAPI.isUserModerator(current.getRecipient().getAddress().toString(), publicChat.getChannel(), publicChat.getServer());
|
||||
visibility = isModerator ? View.VISIBLE : View.GONE;
|
||||
} else if (openGroupV2 != null) {
|
||||
boolean isModerator = OpenGroupAPIV2.isUserModerator(current.getRecipient().getAddress().toString(), openGroupV2.getRoom(), openGroupV2.getServer());
|
||||
visibility = isModerator ? View.VISIBLE : View.GONE;
|
||||
}
|
||||
|
||||
moderatorIconImageView.setVisibility(visibility);
|
||||
|
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import okhttp3.HttpUrl
|
||||
import org.session.libsession.messaging.StorageProtocol
|
||||
import org.session.libsession.messaging.jobs.AttachmentUploadJob
|
||||
import org.session.libsession.messaging.jobs.Job
|
||||
@@ -13,6 +14,7 @@ import org.session.libsession.messaging.messages.signal.IncomingTextMessage
|
||||
import org.session.libsession.messaging.messages.visible.Attachment
|
||||
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupV2
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage
|
||||
@@ -220,6 +222,21 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
DatabaseFactory.getLokiAPIDatabase(context).setAuthToken(server, null)
|
||||
}
|
||||
|
||||
override fun getAuthToken(room: String, server: String): String? {
|
||||
val id = "$server.$room"
|
||||
return DatabaseFactory.getLokiAPIDatabase(context).getAuthToken(id)
|
||||
}
|
||||
|
||||
override fun setAuthToken(room: String, server: String, newValue: String) {
|
||||
val id = "$server.$room"
|
||||
DatabaseFactory.getLokiAPIDatabase(context).setAuthToken(id, newValue)
|
||||
}
|
||||
|
||||
override fun removeAuthToken(room: String, server: String) {
|
||||
val id = "$server.$room"
|
||||
DatabaseFactory.getLokiAPIDatabase(context).setAuthToken(id, null)
|
||||
}
|
||||
|
||||
override fun getOpenGroup(threadID: String): OpenGroup? {
|
||||
if (threadID.toInt() < 0) { return null }
|
||||
val database = databaseHelper.readableDatabase
|
||||
@@ -229,6 +246,15 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
}
|
||||
}
|
||||
|
||||
override fun getV2OpenGroup(threadId: String): OpenGroupV2? {
|
||||
if (threadId.toInt() < 0) { return null }
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(LokiThreadDatabase.publicChatTable, "${LokiThreadDatabase.threadID} = ?", arrayOf(threadId)) { cursor ->
|
||||
val publicChatAsJson = cursor.getString(LokiThreadDatabase.publicChat)
|
||||
OpenGroupV2.fromJson(publicChatAsJson)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getThreadID(openGroupID: String): String {
|
||||
val address = Address.fromSerialized(openGroupID)
|
||||
val recipient = Recipient.from(context, address, false)
|
||||
@@ -248,11 +274,33 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(groupID, publicKey, displayName)
|
||||
}
|
||||
|
||||
override fun setOpenGroupDisplayName(publicKey: String, room: String, server: String, displayName: String) {
|
||||
val groupID = "$server.$room"
|
||||
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(groupID, publicKey, displayName)
|
||||
}
|
||||
|
||||
override fun getOpenGroupDisplayName(publicKey: String, channel: Long, server: String): String? {
|
||||
val groupID = "$server.$channel"
|
||||
return DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(groupID, publicKey)
|
||||
}
|
||||
|
||||
override fun getOpenGroupDisplayName(publicKey: String, room: String, server: String): String? {
|
||||
val groupID = "$server.$room"
|
||||
return DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(groupID, publicKey)
|
||||
}
|
||||
|
||||
override fun getLastMessageServerId(room: String, server: String): Long? {
|
||||
return DatabaseFactory.getLokiAPIDatabase(context).getLastMessageServerID(room, server)
|
||||
}
|
||||
|
||||
override fun setLastMessageServerId(room: String, server: String, newValue: Long) {
|
||||
DatabaseFactory.getLokiAPIDatabase(context).setLastMessageServerID(room, server, newValue)
|
||||
}
|
||||
|
||||
override fun removeLastMessageServerId(room: String, server: String) {
|
||||
DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(room, server)
|
||||
}
|
||||
|
||||
override fun getLastMessageServerID(group: Long, server: String): Long? {
|
||||
return DatabaseFactory.getLokiAPIDatabase(context).getLastMessageServerID(group, server)
|
||||
}
|
||||
@@ -265,6 +313,22 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(group, server)
|
||||
}
|
||||
|
||||
override fun getLastDeletionServerId(room: String, server: String): Long? {
|
||||
return DatabaseFactory.getLokiAPIDatabase(context).getLastDeletionServerID(room, server)
|
||||
}
|
||||
|
||||
override fun setLastDeletionServerId(room: String, server: String, newValue: Long) {
|
||||
DatabaseFactory.getLokiAPIDatabase(context).setLastDeletionServerID(room, server, newValue)
|
||||
}
|
||||
|
||||
override fun removeLastDeletionServerId(room: String, server: String) {
|
||||
DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(room, server)
|
||||
}
|
||||
|
||||
override fun setUserCount(room: String, server: String, newValue: Int) {
|
||||
DatabaseFactory.getLokiAPIDatabase(context).setUserCount(room, server, newValue)
|
||||
}
|
||||
|
||||
override fun getLastDeletionServerID(group: Long, server: String): Long? {
|
||||
return DatabaseFactory.getLokiAPIDatabase(context).getLastDeletionServerID(group, server)
|
||||
}
|
||||
@@ -309,9 +373,9 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
SessionMetaProtocol.addTimestamp(timestamp)
|
||||
}
|
||||
|
||||
// override fun removeReceivedMessageTimestamps(timestamps: Set<Long>) {
|
||||
// TODO("Not yet implemented")
|
||||
// }
|
||||
override fun removeReceivedMessageTimestamps(timestamps: Set<Long>) {
|
||||
SessionMetaProtocol.removeTimestamps(timestamps)
|
||||
}
|
||||
|
||||
override fun getMessageIdInDatabase(timestamp: Long, author: String): Long? {
|
||||
val database = DatabaseFactory.getMmsSmsDatabase(context)
|
||||
@@ -319,8 +383,9 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
return database.getMessageFor(timestamp, address)?.getId()
|
||||
}
|
||||
|
||||
override fun setOpenGroupServerMessageID(messageID: Long, serverID: Long) {
|
||||
DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageID, serverID)
|
||||
override fun setOpenGroupServerMessageID(messageID: Long, serverID: Long, threadID: Long, isSms: Boolean) {
|
||||
DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageID, serverID, isSms)
|
||||
DatabaseFactory.getLokiMessageDatabase(context).setOriginalThreadID(messageID, serverID, threadID)
|
||||
}
|
||||
|
||||
override fun getQuoteServerID(quoteID: Long, publicKey: String): Long? {
|
||||
@@ -469,8 +534,27 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
}
|
||||
}
|
||||
|
||||
override fun addOpenGroup(server: String, channel: Long) {
|
||||
OpenGroupUtilities.addGroup(context, server, channel)
|
||||
override fun getAllV2OpenGroups(): Map<Long, OpenGroupV2> {
|
||||
return DatabaseFactory.getLokiThreadDatabase(context).getAllV2OpenGroups()
|
||||
}
|
||||
|
||||
override fun addOpenGroup(serverUrl: String, channel: Long) {
|
||||
val httpUrl = HttpUrl.parse(serverUrl) ?: return
|
||||
if (httpUrl.queryParameterNames().contains("public_key")) {
|
||||
// open group v2
|
||||
val server = HttpUrl.Builder().scheme(httpUrl.scheme()).host(httpUrl.host()).apply {
|
||||
if (httpUrl.port() != 80 || httpUrl.port() != 443) {
|
||||
// non-standard port, add to server
|
||||
this.port(httpUrl.port())
|
||||
}
|
||||
}.build()
|
||||
val room = httpUrl.pathSegments().firstOrNull() ?: return
|
||||
val publicKey = httpUrl.queryParameter("public_key") ?: return
|
||||
|
||||
OpenGroupUtilities.addGroup(context, server.toString().removeSuffix("/"), room, publicKey)
|
||||
} else {
|
||||
OpenGroupUtilities.addGroup(context, serverUrl, channel)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAllGroups(): List<GroupRecord> {
|
||||
@@ -507,6 +591,15 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
return if (threadID < 0) null else threadID
|
||||
}
|
||||
|
||||
override fun getThreadIdForMms(mmsId: Long): Long {
|
||||
val mmsDb = DatabaseFactory.getMmsDatabase(context)
|
||||
val cursor = mmsDb.getMessage(mmsId)
|
||||
val reader = mmsDb.readerFor(cursor)
|
||||
val threadId = reader.next.threadId
|
||||
cursor.close()
|
||||
return threadId
|
||||
}
|
||||
|
||||
override fun getSessionRequestSentTimestamp(publicKey: String): Long? {
|
||||
return DatabaseFactory.getLokiAPIDatabase(context).getSessionRequestSentTimestamp(publicKey)
|
||||
}
|
||||
|
@@ -49,6 +49,7 @@ import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||
|
||||
@@ -410,6 +411,7 @@ public class ThreadDatabase extends Database {
|
||||
deleteThread(threadId);
|
||||
notifyConversationListeners(threadId);
|
||||
notifyConversationListListeners();
|
||||
SessionMetaProtocol.clearReceivedMessages();
|
||||
}
|
||||
|
||||
public boolean hasThread(long threadId) {
|
||||
|
@@ -55,9 +55,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
private static final int lokiV21 = 42;
|
||||
private static final int lokiV22 = 43;
|
||||
private static final int lokiV23 = 44;
|
||||
private static final int lokiV24 = 45;
|
||||
|
||||
// Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
|
||||
private static final int DATABASE_VERSION = lokiV23;
|
||||
private static final int DATABASE_VERSION = lokiV24;
|
||||
private static final String DATABASE_NAME = "signal.db";
|
||||
|
||||
private final Context context;
|
||||
@@ -125,6 +126,8 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand());
|
||||
db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand());
|
||||
db.execSQL(SessionJobDatabase.getCreateSessionJobTableCommand());
|
||||
db.execSQL(LokiMessageDatabase.getUpdateMessageIDTableForType());
|
||||
db.execSQL(LokiMessageDatabase.getUpdateMessageMappingTable());
|
||||
|
||||
executeStatements(db, SmsDatabase.CREATE_INDEXS);
|
||||
executeStatements(db, MmsDatabase.CREATE_INDEXS);
|
||||
@@ -275,6 +278,17 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
if (oldVersion < lokiV23) {
|
||||
db.execSQL("ALTER TABLE groups ADD COLUMN zombie_members TEXT");
|
||||
db.execSQL(LokiMessageDatabase.getUpdateMessageIDTableForType());
|
||||
db.execSQL(LokiMessageDatabase.getUpdateMessageMappingTable());
|
||||
}
|
||||
|
||||
if (oldVersion < lokiV24) {
|
||||
String swarmTable = LokiAPIDatabase.Companion.getSwarmTable();
|
||||
String snodePoolTable = LokiAPIDatabase.Companion.getSnodePoolTable();
|
||||
db.execSQL("DROP TABLE " + swarmTable);
|
||||
db.execSQL("DROP TABLE " + snodePoolTable);
|
||||
db.execSQL(LokiAPIDatabase.getCreateSnodePoolTableCommand());
|
||||
db.execSQL(LokiAPIDatabase.getCreateSwarmTableCommand());
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
|
@@ -353,6 +353,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
||||
val openGroupV2 = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadID)
|
||||
//TODO Move open group related logic to OpenGroupUtilities / PublicChatManager / GroupManager
|
||||
if (publicChat != null) {
|
||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
||||
@@ -364,6 +365,13 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
||||
|
||||
ApplicationContext.getInstance(context).publicChatManager
|
||||
.removeChat(publicChat.server, publicChat.channel)
|
||||
} else if (openGroupV2 != null) {
|
||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
||||
apiDB.removeLastMessageServerID(openGroupV2.room, openGroupV2.server)
|
||||
apiDB.removeLastDeletionServerID(openGroupV2.room, openGroupV2.server)
|
||||
|
||||
ApplicationContext.getInstance(context).publicChatManager
|
||||
.removeChat(openGroupV2.server, openGroupV2.room)
|
||||
} else {
|
||||
threadDB.deleteConversation(threadID)
|
||||
}
|
||||
|
@@ -2,31 +2,49 @@ package org.thoughtcrime.securesms.loki.activities
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Bundle
|
||||
import android.util.Patterns
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.*
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.chip.Chip
|
||||
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 okhttp3.HttpUrl
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2.DefaultGroup
|
||||
import org.session.libsession.messaging.threads.Address
|
||||
import org.session.libsession.messaging.threads.DistributionTypes
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
import org.thoughtcrime.securesms.conversation.ConversationActivity
|
||||
import org.thoughtcrime.securesms.groups.GroupManager
|
||||
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment
|
||||
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate
|
||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
|
||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
||||
import org.thoughtcrime.securesms.loki.viewmodel.DefaultGroupsViewModel
|
||||
import org.thoughtcrime.securesms.loki.viewmodel.State
|
||||
|
||||
class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
|
||||
|
||||
private val viewModel by viewModels<DefaultGroupsViewModel>()
|
||||
|
||||
private val adapter = JoinPublicChatActivityAdapter(this)
|
||||
|
||||
// region Lifecycle
|
||||
@@ -65,16 +83,43 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
|
||||
}
|
||||
|
||||
fun joinPublicChatIfPossible(url: String) {
|
||||
if (!Patterns.WEB_URL.matcher(url).matches() || !url.startsWith("https://")) {
|
||||
return Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
// add http if just an IP style / host style URL is entered but leave it if scheme is included
|
||||
val properString = if (!url.startsWith("http")) "http://$url" else url
|
||||
val httpUrl = HttpUrl.parse(properString) ?: return Toast.makeText(this,R.string.invalid_url, Toast.LENGTH_SHORT).show()
|
||||
|
||||
val room = httpUrl.pathSegments().firstOrNull()
|
||||
val publicKey = httpUrl.queryParameter("public_key")
|
||||
val isV2OpenGroup = !room.isNullOrEmpty()
|
||||
showLoader()
|
||||
val channel: Long = 1
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
OpenGroupUtilities.addGroup(this@JoinPublicChatActivity, url, channel)
|
||||
val (threadID, groupID) = if (isV2OpenGroup) {
|
||||
val server = HttpUrl.Builder().scheme(httpUrl.scheme()).host(httpUrl.host()).apply {
|
||||
if (httpUrl.port() != 80 || httpUrl.port() != 443) {
|
||||
// non-standard port, add to server
|
||||
this.port(httpUrl.port())
|
||||
}
|
||||
}.build()
|
||||
val group = OpenGroupUtilities.addGroup(this@JoinPublicChatActivity, server.toString().removeSuffix("/"), room!!, publicKey!!)
|
||||
val threadID = GroupManager.getOpenGroupThreadID(group.id, this@JoinPublicChatActivity)
|
||||
val groupID = GroupUtil.getEncodedOpenGroupID(group.id.toByteArray())
|
||||
threadID to groupID
|
||||
} else {
|
||||
val channel: Long = 1
|
||||
val group = OpenGroupUtilities.addGroup(this@JoinPublicChatActivity, properString, channel)
|
||||
val threadID = GroupManager.getOpenGroupThreadID(group.id, this@JoinPublicChatActivity)
|
||||
val groupID = GroupUtil.getEncodedOpenGroupID(group.id.toByteArray())
|
||||
threadID to groupID
|
||||
}
|
||||
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(this@JoinPublicChatActivity)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
// go to the new conversation and finish this one
|
||||
openConversationActivity(this@JoinPublicChatActivity, threadID, Recipient.from(this@JoinPublicChatActivity, Address.fromSerialized(groupID), false))
|
||||
finish()
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e("JoinPublicChatActivity", "Fialed to join open group.", e)
|
||||
withContext(Dispatchers.Main) {
|
||||
@@ -83,10 +128,19 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
withContext(Dispatchers.Main) { finish() }
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Convenience
|
||||
private fun openConversationActivity(context: Context, threadId: Long, recipient: Recipient) {
|
||||
val intent = Intent(context, ConversationActivity::class.java)
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId)
|
||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, DistributionTypes.DEFAULT)
|
||||
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.address)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
||||
// region Adapter
|
||||
@@ -109,7 +163,7 @@ private class JoinPublicChatActivityAdapter(val activity: JoinPublicChatActivity
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPageTitle(index: Int): CharSequence? {
|
||||
override fun getPageTitle(index: Int): CharSequence {
|
||||
return when (index) {
|
||||
0 -> activity.resources.getString(R.string.activity_join_public_chat_enter_group_url_tab_title)
|
||||
1 -> activity.resources.getString(R.string.activity_join_public_chat_scan_qr_code_tab_title)
|
||||
@@ -122,24 +176,63 @@ private class JoinPublicChatActivityAdapter(val activity: JoinPublicChatActivity
|
||||
// region Enter Chat URL Fragment
|
||||
class EnterChatURLFragment : Fragment() {
|
||||
|
||||
private val viewModel by activityViewModels<DefaultGroupsViewModel>()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
return inflater.inflate(R.layout.fragment_enter_chat_url, container, false)
|
||||
}
|
||||
|
||||
private fun populateDefaultGroups(groups: List<DefaultGroup>) {
|
||||
defaultRoomsGridLayout.removeAllViews()
|
||||
groups.forEach { defaultGroup ->
|
||||
val chip = layoutInflater.inflate(R.layout.default_group_chip,defaultRoomsGridLayout, false) as Chip
|
||||
val drawable = defaultGroup.image?.let { bytes ->
|
||||
val bitmap = BitmapFactory.decodeByteArray(bytes,0,bytes.size)
|
||||
RoundedBitmapDrawableFactory.create(resources,bitmap).apply {
|
||||
isCircular = true
|
||||
}
|
||||
}
|
||||
chip.chipIcon = drawable
|
||||
chip.text = defaultGroup.name
|
||||
chip.setOnClickListener {
|
||||
(requireActivity() as JoinPublicChatActivity).joinPublicChatIfPossible(defaultGroup.toJoinUrl())
|
||||
}
|
||||
defaultRoomsGridLayout.addView(chip)
|
||||
}
|
||||
if (groups.size and 1 != 0) {
|
||||
// add a filler weight 1 view
|
||||
layoutInflater.inflate(R.layout.grid_layout_filler, defaultRoomsGridLayout)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
chatURLEditText.imeOptions = chatURLEditText.imeOptions or 16777216 // Always use incognito keyboard
|
||||
joinPublicChatButton.setOnClickListener { joinPublicChatIfPossible() }
|
||||
viewModel.defaultRooms.observe(viewLifecycleOwner) { state ->
|
||||
defaultRoomsParent.isVisible = state is State.Success
|
||||
defaultRoomsLoader.isVisible = state is State.Loading
|
||||
when (state) {
|
||||
State.Loading -> {
|
||||
// show a loader here probs
|
||||
}
|
||||
is State.Error -> {
|
||||
// hide the loader and the
|
||||
}
|
||||
is State.Success -> {
|
||||
populateDefaultGroups(state.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Convenience
|
||||
private fun joinPublicChatIfPossible() {
|
||||
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"
|
||||
}
|
||||
val chatURL = chatURLEditText.text.trim().toString().toLowerCase()
|
||||
(requireActivity() as JoinPublicChatActivity).joinPublicChatIfPossible(chatURL)
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
// endregion
|
@@ -9,8 +9,10 @@ import nl.komponents.kovenant.all
|
||||
import nl.komponents.kovenant.functional.map
|
||||
import org.session.libsession.messaging.jobs.MessageReceiveJob
|
||||
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupV2
|
||||
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPoller
|
||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller
|
||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupV2Poller
|
||||
import org.session.libsession.snode.SnodeAPI
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
@@ -90,6 +92,14 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor
|
||||
promises.add(poller.pollForNewMessages())
|
||||
}
|
||||
|
||||
val openGroupsV2 = DatabaseFactory.getLokiThreadDatabase(context).getAllV2OpenGroups().values.groupBy(OpenGroupV2::server)
|
||||
|
||||
openGroupsV2.values.map { groups ->
|
||||
OpenGroupV2Poller(groups)
|
||||
}.forEach { poller ->
|
||||
promises.add(poller.compactPoll(true).map{ /*Unit*/ })
|
||||
}
|
||||
|
||||
// Wait till all the promises get resolved
|
||||
all(promises).get()
|
||||
|
||||
|
@@ -16,6 +16,25 @@ class PublicChatInfoUpdateWorker(val context: Context, params: WorkerParameters)
|
||||
|
||||
private const val DATA_KEY_SERVER_URL = "server_uRL"
|
||||
private const val DATA_KEY_CHANNEL = "channel"
|
||||
private const val DATA_KEY_ROOM = "room"
|
||||
|
||||
@JvmStatic
|
||||
fun scheduleInstant(context: Context, serverUrl: String, room :String) {
|
||||
val workRequest = OneTimeWorkRequestBuilder<PublicChatInfoUpdateWorker>()
|
||||
.setConstraints(Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
)
|
||||
.setInputData(workDataOf(
|
||||
DATA_KEY_SERVER_URL to serverUrl,
|
||||
DATA_KEY_ROOM to room
|
||||
))
|
||||
.build()
|
||||
|
||||
WorkManager
|
||||
.getInstance(context)
|
||||
.enqueue(workRequest)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun scheduleInstant(context: Context, serverURL: String, channel: Long) {
|
||||
@@ -39,17 +58,35 @@ class PublicChatInfoUpdateWorker(val context: Context, params: WorkerParameters)
|
||||
override fun doWork(): Result {
|
||||
val serverUrl = inputData.getString(DATA_KEY_SERVER_URL)!!
|
||||
val channel = inputData.getLong(DATA_KEY_CHANNEL, -1)
|
||||
val room = inputData.getString(DATA_KEY_ROOM)
|
||||
|
||||
val publicChatId = OpenGroup.getId(channel, serverUrl)
|
||||
val isOpenGroupV2 = !room.isNullOrEmpty() && channel == -1L
|
||||
|
||||
if (!isOpenGroupV2) {
|
||||
val publicChatId = OpenGroup.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()
|
||||
}
|
||||
} else {
|
||||
val openGroupId = "$serverUrl.$room"
|
||||
|
||||
return try {
|
||||
Log.v(TAG, "Updating open group info for $openGroupId.")
|
||||
OpenGroupUtilities.updateGroupInfo(context, serverUrl, room!!)
|
||||
Log.v(TAG, "Open group info was successfully updated for $openGroupId.")
|
||||
Result.success()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to update open group info for $openGroupId", e)
|
||||
Result.failure()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,10 +6,9 @@ import android.graphics.Bitmap
|
||||
import android.text.TextUtils
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupAPI
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupInfo
|
||||
import org.session.libsession.messaging.open_groups.*
|
||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller
|
||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupV2Poller
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.Util
|
||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders
|
||||
@@ -20,15 +19,17 @@ import java.util.concurrent.Executors
|
||||
|
||||
class PublicChatManager(private val context: Context) {
|
||||
private var chats = mutableMapOf<Long, OpenGroup>()
|
||||
private var v2Chats = mutableMapOf<Long, OpenGroupV2>()
|
||||
private val pollers = mutableMapOf<Long, OpenGroupPoller>()
|
||||
private val v2Pollers = mutableMapOf<String, OpenGroupV2Poller>()
|
||||
private val observers = mutableMapOf<Long, ContentObserver>()
|
||||
private var isPolling = false
|
||||
private val executorService = Executors.newScheduledThreadPool(16)
|
||||
private val executorService = Executors.newScheduledThreadPool(4)
|
||||
|
||||
public fun areAllCaughtUp(): Boolean {
|
||||
var areAllCaughtUp = true
|
||||
refreshChatsAndPollers()
|
||||
for ((threadID, chat) in chats) {
|
||||
for ((threadID, _) in chats) {
|
||||
val poller = pollers[threadID]
|
||||
areAllCaughtUp = if (poller != null) areAllCaughtUp && poller.isCaughtUp else true
|
||||
}
|
||||
@@ -52,6 +53,17 @@ class PublicChatManager(private val context: Context) {
|
||||
listenToThreadDeletion(threadId)
|
||||
if (!pollers.containsKey(threadId)) { pollers[threadId] = poller }
|
||||
}
|
||||
v2Pollers.values.forEach { it.stop() }
|
||||
v2Pollers.clear()
|
||||
v2Chats.entries.groupBy { (_, group) -> group.server }.forEach { (server, threadedRooms) ->
|
||||
val poller = OpenGroupV2Poller(threadedRooms.map { it.value }, executorService)
|
||||
poller.startIfNeeded()
|
||||
threadedRooms.forEach { (thread, _) ->
|
||||
listenToThreadDeletion(thread)
|
||||
}
|
||||
v2Pollers[server] = poller
|
||||
}
|
||||
|
||||
isPolling = true
|
||||
}
|
||||
|
||||
@@ -98,6 +110,26 @@ class PublicChatManager(private val context: Context) {
|
||||
return chat
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun addChat(server: String, room: String, info: OpenGroupAPIV2.Info, publicKey: String): OpenGroupV2 {
|
||||
val chat = OpenGroupV2(server, room, info.name, publicKey)
|
||||
var threadID = GroupManager.getOpenGroupThreadID(chat.id, context)
|
||||
val profilePicture: Bitmap?
|
||||
if (threadID < 0) {
|
||||
val profilePictureAsByteArray = try {
|
||||
OpenGroupAPIV2.downloadOpenGroupProfilePicture(info.id,server).get()
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
profilePicture = BitmapUtil.fromByteArray(profilePictureAsByteArray)
|
||||
val result = GroupManager.createOpenGroup(chat.id, context, profilePicture, info.name)
|
||||
threadID = result.threadId
|
||||
}
|
||||
DatabaseFactory.getLokiThreadDatabase(context).setOpenGroupChat(chat, threadID)
|
||||
Util.runOnMain { startPollersIfNeeded() }
|
||||
return chat
|
||||
}
|
||||
|
||||
public fun removeChat(server: String, channel: Long) {
|
||||
val threadDB = DatabaseFactory.getThreadDatabase(context)
|
||||
val groupId = OpenGroup.getId(channel, server)
|
||||
@@ -108,14 +140,26 @@ class PublicChatManager(private val context: Context) {
|
||||
Util.runOnMain { startPollersIfNeeded() }
|
||||
}
|
||||
|
||||
fun removeChat(server: String, room: String) {
|
||||
val threadDB = DatabaseFactory.getThreadDatabase(context)
|
||||
val groupId = "$server.$room"
|
||||
val threadId = GroupManager.getOpenGroupThreadID(groupId, context)
|
||||
val groupAddress = threadDB.getRecipientForThreadId(threadId)!!.address.serialize()
|
||||
GroupManager.deleteGroup(groupAddress, context)
|
||||
|
||||
Util.runOnMain { startPollersIfNeeded() }
|
||||
}
|
||||
|
||||
private fun refreshChatsAndPollers() {
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
val chatsInDB = storage.getAllOpenGroups()
|
||||
val v2ChatsInDB = storage.getAllV2OpenGroups()
|
||||
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.getOpenGroupThreadID(it.value.id, context) > -1 }.toMutableMap()
|
||||
v2Chats = v2ChatsInDB.filter { GroupManager.getOpenGroupThreadID(it.value.id, context) > -1 }.toMutableMap()
|
||||
}
|
||||
|
||||
private fun listenToThreadDeletion(threadID: Long) {
|
||||
@@ -132,6 +176,8 @@ class PublicChatManager(private val context: Context) {
|
||||
|
||||
DatabaseFactory.getLokiThreadDatabase(context).removePublicChat(threadID)
|
||||
pollers.remove(threadID)?.stop()
|
||||
v2Pollers.values.forEach { it.stop() }
|
||||
v2Pollers.clear()
|
||||
observers.remove(threadID)
|
||||
startPollersIfNeeded()
|
||||
}
|
||||
|
@@ -27,7 +27,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
private val timestamp = "timestamp"
|
||||
private val snode = "snode"
|
||||
// Snode pool
|
||||
private val snodePoolTable = "loki_snode_pool_cache"
|
||||
public val snodePoolTable = "loki_snode_pool_cache"
|
||||
private val dummyKey = "dummy_key"
|
||||
private val snodePool = "snode_pool_key"
|
||||
@JvmStatic val createSnodePoolTableCommand = "CREATE TABLE $snodePoolTable ($dummyKey TEXT PRIMARY KEY, $snodePool TEXT);"
|
||||
@@ -36,7 +36,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
private val indexPath = "index_path"
|
||||
@JvmStatic val createOnionRequestPathTableCommand = "CREATE TABLE $onionRequestPathTable ($indexPath TEXT PRIMARY KEY, $snode TEXT);"
|
||||
// Swarms
|
||||
private val swarmTable = "loki_api_swarm_cache"
|
||||
public val swarmTable = "loki_api_swarm_cache"
|
||||
private val swarmPublicKey = "hex_encoded_public_key"
|
||||
private val swarm = "swarm"
|
||||
@JvmStatic val createSwarmTableCommand = "CREATE TABLE $swarmTable ($swarmPublicKey TEXT PRIMARY KEY, $swarm TEXT);"
|
||||
@@ -286,6 +286,14 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
}?.toLong()
|
||||
}
|
||||
|
||||
override fun getLastMessageServerID(room: String, server: String): Long? {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$room"
|
||||
return database.get(lastMessageServerIDTable, "$lastMessageServerIDTableIndex = ?", wrap(index)) { cursor ->
|
||||
cursor.getInt(lastMessageServerID)
|
||||
}?.toLong()
|
||||
}
|
||||
|
||||
override fun setLastMessageServerID(group: Long, server: String, newValue: Long) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$group"
|
||||
@@ -293,12 +301,25 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
database.insertOrUpdate(lastMessageServerIDTable, row, "$lastMessageServerIDTableIndex = ?", wrap(index))
|
||||
}
|
||||
|
||||
override fun setLastMessageServerID(room: String, server: String, newValue: Long) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$room"
|
||||
val row = wrap(mapOf( lastMessageServerIDTableIndex to index, lastMessageServerID to newValue.toString() ))
|
||||
database.insertOrUpdate(lastMessageServerIDTable, row, "$lastMessageServerIDTableIndex = ?", wrap(index))
|
||||
}
|
||||
|
||||
fun removeLastMessageServerID(group: Long, server: String) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$group"
|
||||
database.delete(lastMessageServerIDTable,"$lastMessageServerIDTableIndex = ?", wrap(index))
|
||||
}
|
||||
|
||||
fun removeLastMessageServerID(room: String, server:String) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$room"
|
||||
database.delete(lastMessageServerIDTable, "$lastMessageServerIDTableIndex = ?", wrap(index))
|
||||
}
|
||||
|
||||
override fun getLastDeletionServerID(group: Long, server: String): Long? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
val index = "$server.$group"
|
||||
@@ -307,6 +328,14 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
}?.toLong()
|
||||
}
|
||||
|
||||
override fun getLastDeletionServerID(room: String, server: String): Long? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
val index = "$server.$room"
|
||||
return database.get(lastDeletionServerIDTable, "$lastDeletionServerIDTableIndex = ?", wrap(index)) { cursor ->
|
||||
cursor.getInt(lastDeletionServerID)
|
||||
}?.toLong()
|
||||
}
|
||||
|
||||
override fun setLastDeletionServerID(group: Long, server: String, newValue: Long) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$group"
|
||||
@@ -314,6 +343,19 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
database.insertOrUpdate(lastDeletionServerIDTable, row, "$lastDeletionServerIDTableIndex = ?", wrap(index))
|
||||
}
|
||||
|
||||
override fun setLastDeletionServerID(room: String, server: String, newValue: Long) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$room"
|
||||
val row = wrap(mapOf(lastDeletionServerIDTableIndex to index, lastDeletionServerID to newValue.toString()))
|
||||
database.insertOrUpdate(lastDeletionServerIDTable, row, "$lastDeletionServerIDTableIndex = ?", wrap(index))
|
||||
}
|
||||
|
||||
fun removeLastDeletionServerID(room: String, server: String) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$room"
|
||||
database.delete(lastDeletionServerIDTable, "$lastDeletionServerIDTableIndex = ?", wrap(index))
|
||||
}
|
||||
|
||||
fun removeLastDeletionServerID(group: Long, server: String) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$group"
|
||||
@@ -328,6 +370,14 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
}?.toInt()
|
||||
}
|
||||
|
||||
fun getUserCount(room: String, server: String): Int? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
val index = "$server.$room"
|
||||
return database.get(userCountTable, "$publicChatID = ?", wrap(index)) { cursor ->
|
||||
cursor.getInt(userCount)
|
||||
}?.toInt()
|
||||
}
|
||||
|
||||
override fun setUserCount(group: Long, server: String, newValue: Int) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$group"
|
||||
@@ -335,6 +385,13 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
database.insertOrUpdate(userCountTable, row, "$publicChatID = ?", wrap(index))
|
||||
}
|
||||
|
||||
override fun setUserCount(room: String, server: String, newValue: Int) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$room"
|
||||
val row = wrap(mapOf( publicChatID to index, userCount to newValue.toString() ))
|
||||
database.insertOrUpdate(userCountTable, row, "$publicChatID = ?", wrap(index))
|
||||
}
|
||||
|
||||
override fun getSessionRequestSentTimestamp(publicKey: String): Long? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(sessionRequestSentTimestampTable, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) { cursor ->
|
||||
|
@@ -5,10 +5,7 @@ import android.content.Context
|
||||
import org.thoughtcrime.securesms.database.Database
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
import org.thoughtcrime.securesms.loki.utilities.get
|
||||
import org.thoughtcrime.securesms.loki.utilities.getInt
|
||||
import org.thoughtcrime.securesms.loki.utilities.getString
|
||||
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
|
||||
import org.thoughtcrime.securesms.loki.utilities.*
|
||||
import org.session.libsignal.service.loki.LokiMessageDatabaseProtocol
|
||||
|
||||
class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol {
|
||||
@@ -22,56 +19,111 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
||||
private val friendRequestStatus = "friend_request_status"
|
||||
private val threadID = "thread_id"
|
||||
private val errorMessage = "error_message"
|
||||
@JvmStatic val createMessageIDTableCommand = "CREATE TABLE $messageIDTable ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);"
|
||||
@JvmStatic val createMessageToThreadMappingTableCommand = "CREATE TABLE IF NOT EXISTS $messageThreadMappingTable ($messageID INTEGER PRIMARY KEY, $threadID INTEGER);"
|
||||
@JvmStatic val createErrorMessageTableCommand = "CREATE TABLE IF NOT EXISTS $errorMessageTable ($messageID INTEGER PRIMARY KEY, $errorMessage STRING);"
|
||||
private val messageType = "message_type"
|
||||
@JvmStatic
|
||||
val createMessageIDTableCommand = "CREATE TABLE $messageIDTable ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);"
|
||||
@JvmStatic
|
||||
val createMessageToThreadMappingTableCommand = "CREATE TABLE IF NOT EXISTS $messageThreadMappingTable ($messageID INTEGER PRIMARY KEY, $threadID INTEGER);"
|
||||
@JvmStatic
|
||||
val createErrorMessageTableCommand = "CREATE TABLE IF NOT EXISTS $errorMessageTable ($messageID INTEGER PRIMARY KEY, $errorMessage STRING);"
|
||||
@JvmStatic
|
||||
val updateMessageIDTableForType = "ALTER TABLE $messageIDTable ADD COLUMN $messageType INTEGER DEFAULT 0; ALTER TABLE $messageIDTable ADD CONSTRAINT PK_$messageIDTable PRIMARY KEY ($messageID, $serverID);"
|
||||
@JvmStatic
|
||||
val updateMessageMappingTable = "ALTER TABLE $messageThreadMappingTable ADD COLUMN $serverID INTEGER DEFAULT 0; ALTER TABLE $messageThreadMappingTable ADD CONSTRAINT PK_$messageThreadMappingTable PRIMARY KEY ($messageID, $serverID);"
|
||||
|
||||
const val SMS_TYPE = 0
|
||||
const val MMS_TYPE = 1
|
||||
|
||||
}
|
||||
|
||||
override fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long? {
|
||||
val message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quoteID, quoteePublicKey)
|
||||
return if (message != null) getServerID(message.getId()) else null
|
||||
return if (message != null) getServerID(message.getId(), !message.isMms) else null
|
||||
}
|
||||
|
||||
fun getServerID(messageID: Long): Long? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(messageIDTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
|
||||
return database.get(messageIDTable, "${Companion.messageID} = ?", arrayOf(messageID.toString())) { cursor ->
|
||||
cursor.getInt(serverID)
|
||||
}?.toLong()
|
||||
}
|
||||
|
||||
fun getServerID(messageID: Long, isSms: Boolean): Long? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(messageIDTable, "${Companion.messageID} = ? AND $messageType = ?", arrayOf(messageID.toString(), if (isSms) SMS_TYPE.toString() else MMS_TYPE.toString())) { cursor ->
|
||||
cursor.getInt(serverID)
|
||||
}?.toLong()
|
||||
}
|
||||
|
||||
fun getMessageID(serverID: Long): Long? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(messageIDTable, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor ->
|
||||
return database.get(messageIDTable, "${Companion.serverID} = ?", arrayOf(serverID.toString())) { cursor ->
|
||||
cursor.getInt(messageID)
|
||||
}?.toLong()
|
||||
}
|
||||
|
||||
override fun setServerID(messageID: Long, serverID: Long) {
|
||||
fun deleteMessage(messageID: Long, isSms: Boolean) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
|
||||
val serverID = database.get(messageIDTable,
|
||||
"${Companion.messageID} = ? AND ${Companion.messageType} = ?",
|
||||
arrayOf(messageID.toString(), (if (isSms) SMS_TYPE else MMS_TYPE).toString())) { cursor ->
|
||||
cursor.getInt(Companion.serverID).toLong()
|
||||
} ?: return
|
||||
|
||||
database.beginTransaction()
|
||||
|
||||
database.delete(messageIDTable, "${Companion.messageID} = ? AND ${Companion.serverID} = ?", arrayOf(messageID.toString(), serverID.toString()))
|
||||
database.delete(messageThreadMappingTable, "${Companion.messageID} = ? AND ${Companion.serverID} = ?", arrayOf(messageID.toString(), serverID.toString()))
|
||||
|
||||
database.setTransactionSuccessful()
|
||||
database.endTransaction()
|
||||
}
|
||||
|
||||
fun getMessageID(serverID: Long, threadID: Long): Pair<Long, Boolean>? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
val mappingResult = database.get(messageThreadMappingTable, "${Companion.serverID} = ? AND ${Companion.threadID} = ?",
|
||||
arrayOf(serverID.toString(), threadID.toString())) { cursor ->
|
||||
cursor.getInt(messageID) to cursor.getInt(Companion.serverID)
|
||||
} ?: return null
|
||||
|
||||
val (mappedID, mappedServerID) = mappingResult
|
||||
|
||||
return database.get(messageIDTable,
|
||||
"$messageID = ? AND ${Companion.serverID} = ?",
|
||||
arrayOf(mappedID.toString(), mappedServerID.toString())) { cursor ->
|
||||
cursor.getInt(Companion.messageID).toLong() to (cursor.getInt(messageType) == SMS_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setServerID(messageID: Long, serverID: Long, isSms: Boolean) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val contentValues = ContentValues(2)
|
||||
contentValues.put(Companion.messageID, messageID)
|
||||
contentValues.put(Companion.serverID, serverID)
|
||||
database.insertOrUpdate(messageIDTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
|
||||
contentValues.put(messageType, if (isSms) SMS_TYPE else MMS_TYPE)
|
||||
database.insertOrUpdate(messageIDTable, contentValues, "${Companion.messageID} = ? AND ${Companion.serverID} = ?", arrayOf(messageID.toString(), serverID.toString()))
|
||||
}
|
||||
|
||||
fun getOriginalThreadID(messageID: Long): Long {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(messageThreadMappingTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
|
||||
return database.get(messageThreadMappingTable, "${Companion.messageID} = ?", arrayOf(messageID.toString())) { cursor ->
|
||||
cursor.getInt(threadID)
|
||||
}?.toLong() ?: -1L
|
||||
}
|
||||
|
||||
fun setOriginalThreadID(messageID: Long, threadID: Long) {
|
||||
fun setOriginalThreadID(messageID: Long, serverID: Long, threadID: Long) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val contentValues = ContentValues(2)
|
||||
contentValues.put(Companion.messageID, messageID)
|
||||
contentValues.put(Companion.serverID, serverID)
|
||||
contentValues.put(Companion.threadID, threadID)
|
||||
database.insertOrUpdate(messageThreadMappingTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
|
||||
database.insertOrUpdate(messageThreadMappingTable, contentValues, "${Companion.messageID} = ? AND ${Companion.serverID} = ?", arrayOf(messageID.toString(), serverID.toString()))
|
||||
}
|
||||
|
||||
fun getErrorMessage(messageID: Long): String? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(errorMessageTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
|
||||
return database.get(errorMessageTable, "${Companion.messageID} = ?", arrayOf(messageID.toString())) { cursor ->
|
||||
cursor.getString(errorMessage)
|
||||
}
|
||||
}
|
||||
@@ -81,6 +133,6 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
||||
val contentValues = ContentValues(2)
|
||||
contentValues.put(Companion.messageID, messageID)
|
||||
contentValues.put(Companion.errorMessage, errorMessage)
|
||||
database.insertOrUpdate(errorMessageTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
|
||||
database.insertOrUpdate(errorMessageTable, contentValues, "${Companion.messageID} = ?", arrayOf(messageID.toString()))
|
||||
}
|
||||
}
|
@@ -10,12 +10,11 @@ import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
import org.thoughtcrime.securesms.loki.utilities.*
|
||||
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupV2
|
||||
import org.session.libsession.messaging.threads.Address
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
|
||||
import org.session.libsignal.utilities.JsonUtil
|
||||
import org.session.libsignal.service.loki.utilities.PublicKeyValidation
|
||||
|
||||
class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) {
|
||||
|
||||
@@ -26,8 +25,10 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
||||
private val friendRequestStatus = "friend_request_status"
|
||||
private val sessionResetStatus = "session_reset_status"
|
||||
val publicChat = "public_chat"
|
||||
@JvmStatic val createSessionResetTableCommand = "CREATE TABLE $sessionResetTable ($threadID INTEGER PRIMARY KEY, $sessionResetStatus INTEGER DEFAULT 0);"
|
||||
@JvmStatic val createPublicChatTableCommand = "CREATE TABLE $publicChatTable ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);"
|
||||
@JvmStatic
|
||||
val createSessionResetTableCommand = "CREATE TABLE $sessionResetTable ($threadID INTEGER PRIMARY KEY, $sessionResetStatus INTEGER DEFAULT 0);"
|
||||
@JvmStatic
|
||||
val createPublicChatTableCommand = "CREATE TABLE $publicChatTable ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);"
|
||||
}
|
||||
|
||||
fun getThreadID(hexEncodedPublicKey: String): Long {
|
||||
@@ -46,11 +47,33 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
||||
val threadID = cursor.getLong(threadID)
|
||||
val string = cursor.getString(publicChat)
|
||||
val publicChat = OpenGroup.fromJSON(string)
|
||||
if (publicChat != null) { result[threadID] = publicChat }
|
||||
if (publicChat != null) {
|
||||
result[threadID] = publicChat
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Do nothing
|
||||
} finally {
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun getAllV2OpenGroups(): Map<Long, OpenGroupV2> {
|
||||
val database = databaseHelper.readableDatabase
|
||||
var cursor: Cursor? = null
|
||||
val result = mutableMapOf<Long, OpenGroupV2>()
|
||||
try {
|
||||
cursor = database.rawQuery("select * from $publicChatTable", null)
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
val threadID = cursor.getLong(threadID)
|
||||
val string = cursor.getString(publicChat)
|
||||
val openGroup = OpenGroupV2.fromJson(string)
|
||||
if (openGroup != null) result[threadID] = openGroup
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// do nothing
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
return result
|
||||
@@ -62,23 +85,48 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
||||
|
||||
fun getPublicChat(threadID: Long): OpenGroup? {
|
||||
if (threadID < 0) { return null }
|
||||
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor ->
|
||||
return database.get(publicChatTable, "${Companion.threadID} = ?", arrayOf(threadID.toString())) { cursor ->
|
||||
val publicChatAsJSON = cursor.getString(publicChat)
|
||||
OpenGroup.fromJSON(publicChatAsJSON)
|
||||
}
|
||||
}
|
||||
|
||||
fun getOpenGroupChat(threadID: Long): OpenGroupV2? {
|
||||
if (threadID < 0) {
|
||||
return null
|
||||
}
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(publicChatTable, "${Companion.threadID} = ?", arrayOf(threadID.toString())) { cursor ->
|
||||
val json = cursor.getString(publicChat)
|
||||
OpenGroupV2.fromJson(json)
|
||||
}
|
||||
}
|
||||
|
||||
fun setOpenGroupChat(openGroupV2: OpenGroupV2, threadID: Long) {
|
||||
if (threadID < 0) {
|
||||
return
|
||||
}
|
||||
val database = databaseHelper.writableDatabase
|
||||
val contentValues = ContentValues(2)
|
||||
contentValues.put(Companion.threadID, threadID)
|
||||
contentValues.put(publicChat, JsonUtil.toJson(openGroupV2.toJson()))
|
||||
database.insertOrUpdate(publicChatTable, contentValues, "${Companion.threadID} = ?", arrayOf(threadID.toString()))
|
||||
}
|
||||
|
||||
fun setPublicChat(publicChat: OpenGroup, threadID: Long) {
|
||||
if (threadID < 0) { return }
|
||||
if (threadID < 0) {
|
||||
return
|
||||
}
|
||||
val database = databaseHelper.writableDatabase
|
||||
val contentValues = ContentValues(2)
|
||||
contentValues.put(Companion.threadID, threadID)
|
||||
contentValues.put(Companion.publicChat, JsonUtil.toJson(publicChat.toJSON()))
|
||||
database.insertOrUpdate(publicChatTable, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() ))
|
||||
database.insertOrUpdate(publicChatTable, contentValues, "${Companion.threadID} = ?", arrayOf(threadID.toString()))
|
||||
}
|
||||
|
||||
fun removePublicChat(threadID: Long) {
|
||||
databaseHelper.writableDatabase.delete(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() ))
|
||||
databaseHelper.writableDatabase.delete(publicChatTable, "${Companion.threadID} = ?", arrayOf(threadID.toString()))
|
||||
}
|
||||
}
|
@@ -17,7 +17,7 @@ object MultiDeviceProtocol {
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return
|
||||
val lastSyncTime = TextSecurePreferences.getLastConfigurationSyncTime(context)
|
||||
val now = System.currentTimeMillis()
|
||||
if (now - lastSyncTime < 2 * 24 * 60 * 60 * 1000) return
|
||||
if (now - lastSyncTime < 7 * 24 * 60 * 60 * 1000) return
|
||||
val contacts = ContactUtilities.getAllContacts(context).filter { recipient ->
|
||||
!recipient.isBlocked && !recipient.name.isNullOrEmpty() && !recipient.isLocalNumber && recipient.address.serialize().isNotEmpty()
|
||||
}.map { recipient ->
|
||||
|
@@ -24,6 +24,15 @@ object SessionMetaProtocol {
|
||||
timestamps.add(timestamp)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun clearReceivedMessages() {
|
||||
timestamps.clear()
|
||||
}
|
||||
|
||||
fun removeTimestamps(timestamps: Set<Long>) {
|
||||
this.timestamps.removeAll(timestamps)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun shouldIgnoreMessage(timestamp: Long): Boolean {
|
||||
val shouldIgnoreMessage = timestamps.contains(timestamp)
|
||||
|
@@ -1,10 +1,10 @@
|
||||
package org.thoughtcrime.securesms.loki.utilities
|
||||
|
||||
import android.content.Context
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.messaging.mentions.MentionsManager
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
|
||||
object MentionManagerUtilities {
|
||||
|
||||
|
@@ -3,20 +3,47 @@ package org.thoughtcrime.securesms.loki.utilities
|
||||
import android.content.Context
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupAPI
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupV2
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.preferences.ProfileKeyUtil
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.groups.GroupManager
|
||||
import java.util.*
|
||||
|
||||
//TODO Refactor so methods declare specific type of checked exceptions and not generalized Exception.
|
||||
object OpenGroupUtilities {
|
||||
|
||||
private const val TAG = "OpenGroupUtilities"
|
||||
|
||||
@JvmStatic
|
||||
@WorkerThread
|
||||
fun addGroup(context: Context, server: String, room: String, publicKey: String): OpenGroupV2 {
|
||||
val groupId = "$server.$room"
|
||||
val threadID = GroupManager.getOpenGroupThreadID(groupId, context)
|
||||
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadID)
|
||||
if (openGroup != null) return openGroup
|
||||
|
||||
MessagingModuleConfiguration.shared.storage.setOpenGroupPublicKey(server,publicKey)
|
||||
OpenGroupAPIV2.getAuthToken(room, server).get()
|
||||
val groupInfo = OpenGroupAPIV2.getInfo(room,server).get()
|
||||
val application = ApplicationContext.getInstance(context)
|
||||
|
||||
val group = application.publicChatManager.addChat(server, room, groupInfo, publicKey)
|
||||
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
storage.removeLastDeletionServerId(room, server)
|
||||
storage.removeLastMessageServerId(room, server)
|
||||
|
||||
return group
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@WorkerThread
|
||||
@Throws(Exception::class)
|
||||
@@ -67,5 +94,30 @@ object OpenGroupUtilities {
|
||||
EventBus.getDefault().post(GroupInfoUpdatedEvent(url, channel))
|
||||
}
|
||||
|
||||
data class GroupInfoUpdatedEvent(val url: String, val channel: Long)
|
||||
@JvmStatic
|
||||
@WorkerThread
|
||||
@Throws(Exception::class)
|
||||
fun updateGroupInfo(context: Context, server: String, room: String) {
|
||||
val groupId = GroupUtil.getEncodedOpenGroupID("$server.$room".toByteArray())
|
||||
if (!DatabaseFactory.getGroupDatabase(context).hasGroup(groupId)) {
|
||||
throw IllegalStateException("Attempt to update open group info for non-existent DB record: $groupId")
|
||||
}
|
||||
|
||||
val info = OpenGroupAPIV2.getInfo(room, server).get() // store info again?
|
||||
OpenGroupAPIV2.getMemberCount(room, server).get()
|
||||
|
||||
EventBus.getDefault().post(GroupInfoUpdatedEvent(server, room = room))
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a generated name for users in the style of `$name (...$hex.takeLast(8))` for public groups
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getDisplayName(recipient: Recipient): String {
|
||||
return String.format(Locale.ROOT, PUBLIC_GROUP_STRING_FORMAT, recipient.name, recipient.address.serialize().takeLast(8))
|
||||
}
|
||||
|
||||
const val PUBLIC_GROUP_STRING_FORMAT = "%s (...%s)"
|
||||
|
||||
data class GroupInfoUpdatedEvent(val url: String, val channel: Long = -1, val room: String = "")
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package org.thoughtcrime.securesms.loki.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.asLiveData
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
||||
|
||||
typealias DefaultGroups = List<OpenGroupAPIV2.DefaultGroup>
|
||||
typealias GroupState = State<DefaultGroups>
|
||||
|
||||
class DefaultGroupsViewModel : ViewModel() {
|
||||
|
||||
init {
|
||||
OpenGroupAPIV2.getDefaultRoomsIfNeeded()
|
||||
}
|
||||
|
||||
val defaultRooms = OpenGroupAPIV2.defaultRooms.map<DefaultGroups, GroupState> {
|
||||
State.Success(it)
|
||||
}.onStart {
|
||||
emit(State.Loading)
|
||||
}.asLiveData()
|
||||
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package org.thoughtcrime.securesms.loki.viewmodel
|
||||
|
||||
sealed class State<out T> {
|
||||
object Loading : State<Nothing>()
|
||||
data class Success<T>(val value: T): State<T>()
|
||||
data class Error(val error: Exception): State<Nothing>()
|
||||
}
|
@@ -102,7 +102,8 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
||||
if (duration == 0) {
|
||||
TextSecurePreferences.setScreenLockTimeout(getContext(), 0);
|
||||
} else {
|
||||
long timeoutSeconds = Math.max(TimeUnit.MILLISECONDS.toSeconds(duration), 60);
|
||||
long timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(duration);
|
||||
// long timeoutSeconds = Math.max(TimeUnit.MILLISECONDS.toSeconds(duration), 60);
|
||||
TextSecurePreferences.setScreenLockTimeout(getContext(), timeoutSeconds);
|
||||
}
|
||||
|
||||
|
@@ -119,7 +119,6 @@ public class KeyCachingService extends Service {
|
||||
KeyCachingService.masterSecret = masterSecret;
|
||||
|
||||
foregroundService();
|
||||
startTimeoutIfAppropriate(this);
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
@@ -210,7 +209,7 @@ public class KeyCachingService extends Service {
|
||||
boolean passLockActive = timeoutEnabled && !TextSecurePreferences.isPasswordDisabled(context);
|
||||
|
||||
long screenTimeout = TextSecurePreferences.getScreenLockTimeout(context);
|
||||
boolean screenLockActive = screenTimeout >= 60 && TextSecurePreferences.isScreenLockEnabled(context);
|
||||
boolean screenLockActive = screenTimeout >= 0 && TextSecurePreferences.isScreenLockEnabled(context);
|
||||
|
||||
if (!appVisible && secretSet && (passLockActive || screenLockActive)) {
|
||||
long passphraseTimeoutMinutes = TextSecurePreferences.getPassphraseTimeoutInterval(context);
|
||||
|
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/contentView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -22,7 +21,36 @@
|
||||
android:layout_marginTop="@dimen/large_spacing"
|
||||
android:layout_marginRight="@dimen/large_spacing"
|
||||
android:inputType="textWebEmailAddress"
|
||||
android:hint="Enter an open group URL" />
|
||||
android:hint="@string/fragment_enter_chat_url_edit_text_hint" />
|
||||
|
||||
<com.github.ybq.android.spinkit.SpinKitView
|
||||
android:visibility="gone"
|
||||
android:id="@+id/defaultRoomsLoader"
|
||||
style="@style/SpinKitView.Small.WanderingCubes"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<LinearLayout
|
||||
android:visibility="gone"
|
||||
android:paddingHorizontal="24dp"
|
||||
android:id="@+id/defaultRoomsParent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:layout_marginVertical="16dp"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/activity_join_public_chat_join_rooms"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<GridLayout
|
||||
android:id="@+id/defaultRoomsGridLayout"
|
||||
android:columnCount="2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
|
14
app/src/main/res/layout/default_group_chip.xml
Normal file
14
app/src/main/res/layout/default_group_chip.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.chip.Chip xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:theme="@style/Theme.MaterialComponents.DayNight"
|
||||
style="?attr/chipStyle"
|
||||
app:chipStartPadding="6dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_marginHorizontal="2dp"
|
||||
tools:text="Main Group"
|
||||
android:ellipsize="end"
|
||||
tools:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="52dp" />
|
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/contentView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -24,6 +23,35 @@
|
||||
android:inputType="textWebEmailAddress"
|
||||
android:hint="@string/fragment_enter_chat_url_edit_text_hint" />
|
||||
|
||||
<com.github.ybq.android.spinkit.SpinKitView
|
||||
android:visibility="gone"
|
||||
android:id="@+id/defaultRoomsLoader"
|
||||
style="@style/SpinKitView.Small.WanderingCubes"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<LinearLayout
|
||||
android:visibility="gone"
|
||||
android:paddingHorizontal="24dp"
|
||||
android:id="@+id/defaultRoomsParent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:layout_marginVertical="16dp"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/activity_join_public_chat_join_rooms"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<GridLayout
|
||||
android:id="@+id/defaultRoomsGridLayout"
|
||||
android:columnCount="2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
|
5
app/src/main/res/layout/grid_layout_filler.xml
Normal file
5
app/src/main/res/layout/grid_layout_filler.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="0dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_height="0dp"/>
|
@@ -1898,5 +1898,6 @@
|
||||
<string name="activity_backup_restore_passphrase">30-digit passphrase</string>
|
||||
<!-- LinkDeviceActivity -->
|
||||
<string name="activity_link_device_skip_prompt">This is taking a while, would you like to skip?</string>
|
||||
<string name="activity_join_public_chat_join_rooms">Or join one of these...</string>
|
||||
|
||||
</resources>
|
||||
|
Reference in New Issue
Block a user