Remove the concept of friend requests

This commit is contained in:
nielsandriesse 2020-07-15 14:26:20 +10:00
parent 21554441f3
commit 455471b20e
53 changed files with 405 additions and 1332 deletions

View File

@ -44,23 +44,16 @@
</RelativeLayout> </RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_toStartOf="@+id/indicators_parent"
android:layout_alignWithParentIfMissing="true"
android:clipToPadding="false"
android:clipChildren="false">
<LinearLayout <LinearLayout
android:id="@+id/body_bubble" android:id="@+id/body_bubble"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/massive_spacing" android:layout_marginEnd="@dimen/massive_spacing"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:paddingTop="@dimen/medium_spacing" android:paddingTop="@dimen/medium_spacing"
android:paddingBottom="@dimen/medium_spacing" android:paddingBottom="@dimen/medium_spacing"
android:layout_toStartOf="@+id/indicators_parent"
android:layout_alignWithParentIfMissing="true"
android:orientation="vertical" android:orientation="vertical"
android:clipToPadding="false" android:clipToPadding="false"
android:clipChildren="false" android:clipChildren="false"
@ -208,14 +201,6 @@
</LinearLayout> </LinearLayout>
<org.thoughtcrime.securesms.loki.views.FriendRequestView
android:id="@+id/friend_request_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
</LinearLayout>
<org.thoughtcrime.securesms.components.AlertView <org.thoughtcrime.securesms.components.AlertView
android:id="@+id/indicators_parent" android:id="@+id/indicators_parent"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -22,23 +22,16 @@
android:clipToPadding="false" android:clipToPadding="false"
android:clipChildren="false"> android:clipChildren="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_toStartOf="@+id/indicators_parent"
android:layout_alignWithParentIfMissing="true"
android:clipToPadding="false"
android:clipChildren="false">
<LinearLayout <LinearLayout
android:id="@+id/body_bubble" android:id="@+id/body_bubble"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:layout_marginStart="@dimen/massive_spacing" android:layout_marginStart="@dimen/massive_spacing"
android:paddingTop="@dimen/medium_spacing" android:paddingTop="@dimen/medium_spacing"
android:paddingBottom="@dimen/medium_spacing" android:paddingBottom="@dimen/medium_spacing"
android:layout_toStartOf="@+id/indicators_parent"
android:layout_alignWithParentIfMissing="true"
android:layout_gravity="end" android:layout_gravity="end"
android:clipToPadding="false" android:clipToPadding="false"
android:clipChildren="false" android:clipChildren="false"
@ -161,14 +154,6 @@
</LinearLayout> </LinearLayout>
<org.thoughtcrime.securesms.loki.views.FriendRequestView
android:id="@+id/friend_request_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
</LinearLayout>
<org.thoughtcrime.securesms.components.AlertView <org.thoughtcrime.securesms.components.AlertView
android:id="@+id/indicators_parent" android:id="@+id/indicators_parent"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -1561,4 +1561,5 @@ Otrzymano wiadomość wymiany klucz dla niepoprawnej wersji protokołu.</string>
<string name="fragment_contact_selection_contacts_title">Łączność</string> <string name="fragment_contact_selection_contacts_title">Łączność</string>
<string name="fragment_contact_selection_closed_groups_title">Grupy zamknięte</string> <string name="fragment_contact_selection_closed_groups_title">Grupy zamknięte</string>
<string name="fragment_contact_selection_open_groups_title">Otwórz grupy</string> <string name="fragment_contact_selection_open_groups_title">Otwórz grupy</string>
</resources> </resources>

View File

@ -1358,6 +1358,7 @@
<!-- Session -->
<string name="continue_2">Продолжить</string> <string name="continue_2">Продолжить</string>
<string name="copy">Копировать</string> <string name="copy">Копировать</string>
<string name="invalid_url">Неверная ссылка</string> <string name="invalid_url">Неверная ссылка</string>

View File

@ -1655,7 +1655,6 @@
<!-- Session --> <!-- Session -->
<string name="continue_2">Continue</string> <string name="continue_2">Continue</string>
<string name="copy">Copy</string> <string name="copy">Copy</string>
<string name="invalid_url">Invalid URL</string> <string name="invalid_url">Invalid URL</string>

View File

@ -61,13 +61,13 @@ import org.thoughtcrime.securesms.logging.PersistentLogger;
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger; import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
import org.thoughtcrime.securesms.loki.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker; import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker;
import org.thoughtcrime.securesms.loki.api.LokiPublicChatManager; import org.thoughtcrime.securesms.loki.api.PublicChatManager;
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager; import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager;
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase; import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.protocol.EphemeralMessage; import org.thoughtcrime.securesms.loki.protocol.EphemeralMessage;
import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation; import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob; import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
import org.thoughtcrime.securesms.loki.utilities.Broadcaster; import org.thoughtcrime.securesms.loki.utilities.Broadcaster;
import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier; import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier;
@ -154,7 +154,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
// Loki // Loki
public MessageNotifier messageNotifier = null; public MessageNotifier messageNotifier = null;
public Poller lokiPoller = null; public Poller lokiPoller = null;
public LokiPublicChatManager lokiPublicChatManager = null; public PublicChatManager publicChatManager = null;
private PublicChatAPI publicChatAPI = null; private PublicChatAPI publicChatAPI = null;
public Broadcaster broadcaster = null; public Broadcaster broadcaster = null;
public SignalCommunicationModule communicationModule; public SignalCommunicationModule communicationModule;
@ -184,7 +184,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(this); LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(this);
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this); LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
String userPublicKey = TextSecurePreferences.getLocalNumber(this); String userPublicKey = TextSecurePreferences.getLocalNumber(this);
LokiSessionResetImplementation sessionResetImpl = new LokiSessionResetImplementation(this); SessionResetImplementation sessionResetImpl = new SessionResetImplementation(this);
if (userPublicKey != null) { if (userPublicKey != null) {
SwarmAPI.Companion.configureIfNeeded(apiDB); SwarmAPI.Companion.configureIfNeeded(apiDB);
SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster); SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster);
@ -203,7 +203,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
} }
} }
resubmitProfilePictureIfNeeded(); resubmitProfilePictureIfNeeded();
lokiPublicChatManager = new LokiPublicChatManager(this); publicChatManager = new PublicChatManager(this);
updateOpenGroupProfilePicturesIfNeeded(); updateOpenGroupProfilePicturesIfNeeded();
registerForFCMIfNeeded(false); registerForFCMIfNeeded(false);
// ======== // ========
@ -229,8 +229,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
// Loki // Loki
if (lokiPoller != null) { lokiPoller.setCaughtUp(false); } if (lokiPoller != null) { lokiPoller.setCaughtUp(false); }
startPollingIfNeeded(); startPollingIfNeeded();
lokiPublicChatManager.markAllAsNotCaughtUp(); publicChatManager.markAllAsNotCaughtUp();
lokiPublicChatManager.startPollersIfNeeded(); publicChatManager.startPollersIfNeeded();
} }
@Override @Override
@ -241,7 +241,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
messageNotifier.setVisibleThread(-1); messageNotifier.setVisibleThread(-1);
// Loki // Loki
if (lokiPoller != null) { lokiPoller.stopIfNeeded(); } if (lokiPoller != null) { lokiPoller.stopIfNeeded(); }
if (lokiPublicChatManager != null) { lokiPublicChatManager.stopPollers(); } if (publicChatManager != null) { publicChatManager.stopPollers(); }
} }
@Override @Override

View File

@ -322,7 +322,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class); Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class);
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_FRIENDS); intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_CONTACTS);
startActivityForResult(intent, PICK_CONTACT); startActivityForResult(intent, PICK_CONTACT);
} }
} }

View File

@ -53,7 +53,7 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState, boolean ready) { protected void onCreate(Bundle savedInstanceState, boolean ready) {
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_FRIENDS); getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_CONTACTS);
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true); getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true);
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false); getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);

View File

@ -84,7 +84,7 @@ public class TypingStatusSender {
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
Recipient recipient = threadDatabase.getRecipientForThreadId(threadId); Recipient recipient = threadDatabase.getRecipientForThreadId(threadId);
// Loki - Check whether we want to send a typing indicator to this user // Loki - Check whether we want to send a typing indicator to this user
if (!SessionMetaProtocol.shouldSendTypingIndicator(recipient, context)) { return; } if (recipient != null && !SessionMetaProtocol.shouldSendTypingIndicator(recipient.getAddress())) { return; }
// Loki - Take into account multi device // Loki - Take into account multi device
Set<String> linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.getAddress().serialize()); Set<String> linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.getAddress().serialize());
for (String device : linkedDevices) { for (String device : linkedDevices) {

View File

@ -68,7 +68,6 @@ import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener; import android.view.View.OnKeyListener;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
@ -81,7 +80,6 @@ import com.annimon.stream.Stream;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode; import org.greenrobot.eventbus.ThreadMode;
import org.jetbrains.annotations.NotNull;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.ExpirationDialog; import org.thoughtcrime.securesms.ExpirationDialog;
import org.thoughtcrime.securesms.GroupCreateActivity; import org.thoughtcrime.securesms.GroupCreateActivity;
@ -159,10 +157,8 @@ import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabaseDelegate; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabaseDelegate;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol;
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities; import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView; import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView;
import org.thoughtcrime.securesms.loki.views.SessionRestoreBannerView; import org.thoughtcrime.securesms.loki.views.SessionRestoreBannerView;
import org.thoughtcrime.securesms.mediasend.Media; import org.thoughtcrime.securesms.mediasend.Media;
@ -231,7 +227,6 @@ import org.whispersystems.signalservice.loki.protocol.mentions.Mention;
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager; import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol; import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol; import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus;
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation; import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation;
import java.io.IOException; import java.io.IOException;
@ -272,8 +267,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
ComposeText.CursorPositionChangedListener, ComposeText.CursorPositionChangedListener,
ConversationSearchBottomBar.EventListener, ConversationSearchBottomBar.EventListener,
StickerKeyboardProvider.StickerEventListener, StickerKeyboardProvider.StickerEventListener,
LokiThreadDatabaseDelegate, LokiThreadDatabaseDelegate
FriendRequestViewDelegate
{ {
private static final String TAG = ConversationActivity.class.getSimpleName(); private static final String TAG = ConversationActivity.class.getSimpleName();
@ -386,7 +380,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
getWindow().getDecorView().setBackgroundColor(color); getWindow().getDecorView().setBackgroundColor(color);
fragment = initFragment(R.id.fragment_content, new ConversationFragment(), dynamicLanguage.getCurrentLocale()); fragment = initFragment(R.id.fragment_content, new ConversationFragment(), dynamicLanguage.getCurrentLocale());
fragment.friendRequestViewDelegate = this;
registerMessageStatusObserver("calculatingPoW"); registerMessageStatusObserver("calculatingPoW");
registerMessageStatusObserver("contactingNetwork"); registerMessageStatusObserver("contactingNetwork");
@ -554,8 +547,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
DatabaseFactory.getLokiThreadDatabase(this).setDelegate(this); DatabaseFactory.getLokiThreadDatabase(this).setDelegate(this);
updateInputPanel();
updateSessionRestoreBanner(); updateSessionRestoreBanner();
Log.i(TAG, "onResume() Finished: " + (System.currentTimeMillis() - getIntent().getLongExtra(TIMING_EXTRA, 0))); Log.i(TAG, "onResume() Finished: " + (System.currentTimeMillis() - getIntent().getLongExtra(TIMING_EXTRA, 0)));
@ -2252,26 +2243,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
updateLinkPreviewState(); updateLinkPreviewState();
} }
@Override
public void handleThreadFriendRequestStatusChanged(long threadID) {
if (recipient.isGroupRecipient()) { return; }
boolean isUpdateNeeded = false;
if (threadID == this.threadId) {
isUpdateNeeded = true;
} else {
String thisThreadPublicKey = recipient.getAddress().serialize();
Set<String> thisThreadAssociatedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(thisThreadPublicKey);
Recipient changedThreadRecipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
String changedThreadPublicKey = changedThreadRecipient.getAddress().serialize();
for (String device : thisThreadAssociatedDevices) {
if (device.equals(changedThreadPublicKey)) { isUpdateNeeded = true; }
}
}
if (isUpdateNeeded) {
updateInputPanel();
}
}
@Override @Override
public void handleSessionRestoreDevicesChanged(long threadID) { public void handleSessionRestoreDevicesChanged(long threadID) {
if (threadID == this.threadId) { if (threadID == this.threadId) {
@ -2279,21 +2250,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
} }
private void updateInputPanel() {
boolean shouldInputPanelBeEnabled = FriendRequestProtocol.shouldInputPanelBeEnabled(this, recipient);
Util.runOnMain(() -> {
updateToggleButtonState();
String hint = shouldInputPanelBeEnabled ? "Message" : "Pending session request";
inputPanel.setHint(hint);
inputPanel.setEnabled(shouldInputPanelBeEnabled);
if (shouldInputPanelBeEnabled && inputPanel.getVisibility() == View.VISIBLE) {
inputPanel.composeText.requestFocus();
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(inputPanel.composeText, 0);
}
});
}
private void sendMessage() { private void sendMessage() {
if (inputPanel.isRecordingInLockedMode()) { if (inputPanel.isRecordingInLockedMode()) {
inputPanel.releaseRecordingLock(); inputPanel.releaseRecordingLock();
@ -2395,11 +2351,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
outgoingMessage = outgoingMessageCandidate; outgoingMessage = outgoingMessageCandidate;
} }
// Loki - Send a friend request if we're not yet friends with the user in question
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId);
outgoingMessage.isFriendRequest = !isGroupConversation() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS
&& !SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize()); // Needed for stageOutgoingMessage(...)
if (clearComposeBox) { if (clearComposeBox) {
inputPanel.clearQuote(); inputPanel.clearQuote();
attachmentManager.clear(glideRequests, false); attachmentManager.clear(glideRequests, false);
@ -2448,11 +2399,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
message = new OutgoingTextMessage(recipient, messageBody, expiresIn, subscriptionId); message = new OutgoingTextMessage(recipient, messageBody, expiresIn, subscriptionId);
} }
// Loki - Send a friend request if we're not yet friends with the user in question
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId);
message.isFriendRequest = !isGroupConversation() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS
&& !SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize()); // Needed for stageOutgoingMessage(...)
silentlySetComposeText(""); silentlySetComposeText("");
final long id = fragment.stageOutgoingMessage(message); final long id = fragment.stageOutgoingMessage(message);
@ -2482,13 +2428,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void updateToggleButtonState() { private void updateToggleButtonState() {
if (!FriendRequestProtocol.shouldAttachmentButtonBeEnabled(this, recipient)) {
buttonToggle.display(sendButton);
quickAttachmentToggle.hide();
inlineAttachmentToggle.hide();
return;
}
if (inputPanel.isRecordingInLockedMode()) { if (inputPanel.isRecordingInLockedMode()) {
buttonToggle.display(sendButton); buttonToggle.display(sendButton);
quickAttachmentToggle.show(); quickAttachmentToggle.show();
@ -3192,19 +3131,5 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
updateSubtitleTextView(); updateSubtitleTextView();
updateMessageStatusProgressBar(); updateMessageStatusProgressBar();
} }
@Override
public void acceptFriendRequest(@NotNull MessageRecord friendRequest) {
if (recipient.isGroupRecipient()) { return; }
FriendRequestProtocol.acceptFriendRequest(this, recipient);
updateInputPanel();
}
@Override
public void rejectFriendRequest(@NotNull MessageRecord friendRequest) {
if (recipient.isGroupRecipient()) { return; }
FriendRequestProtocol.rejectFriendRequest(this, recipient);
updateInputPanel();
}
// endregion // endregion
} }

View File

@ -40,7 +40,6 @@ import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
@ -108,8 +107,6 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
private MessageRecord recordToPulseHighlight; private MessageRecord recordToPulseHighlight;
private String searchQuery; private String searchQuery;
public FriendRequestViewDelegate friendRequestViewDelegate; // Loki
protected static class ViewHolder extends RecyclerView.ViewHolder { protected static class ViewHolder extends RecyclerView.ViewHolder {
public <V extends View & BindableConversationItem> ViewHolder(final @NonNull V itemView) { public <V extends View & BindableConversationItem> ViewHolder(final @NonNull V itemView) {
super(itemView); super(itemView);
@ -202,11 +199,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
MessageRecord previousRecord = adapterPosition < getItemCount() - 1 && !isFooterPosition(adapterPosition + 1) ? getRecordForPositionOrThrow(adapterPosition + 1) : null; MessageRecord previousRecord = adapterPosition < getItemCount() - 1 && !isFooterPosition(adapterPosition + 1) ? getRecordForPositionOrThrow(adapterPosition + 1) : null;
MessageRecord nextRecord = adapterPosition > 0 && !isHeaderPosition(adapterPosition - 1) ? getRecordForPositionOrThrow(adapterPosition - 1) : null; MessageRecord nextRecord = adapterPosition > 0 && !isHeaderPosition(adapterPosition - 1) ? getRecordForPositionOrThrow(adapterPosition - 1) : null;
BindableConversationItem conversationItem = viewHolder.getView(); viewHolder.getView().bind(messageRecord,
if (conversationItem instanceof ConversationItem) {
((ConversationItem)conversationItem).friendRequestViewDelegate = this.friendRequestViewDelegate;
}
conversationItem.bind(messageRecord,
Optional.fromNullable(previousRecord), Optional.fromNullable(previousRecord),
Optional.fromNullable(nextRecord), Optional.fromNullable(nextRecord),
glideRequests, glideRequests,

View File

@ -79,7 +79,6 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
import org.thoughtcrime.securesms.longmessage.LongMessageActivity; import org.thoughtcrime.securesms.longmessage.LongMessageActivity;
import org.thoughtcrime.securesms.mediasend.Media; import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideApp;
@ -151,7 +150,6 @@ public class ConversationFragment extends Fragment
private View composeDivider; private View composeDivider;
private View scrollToBottomButton; private View scrollToBottomButton;
private TextView scrollDateHeader; private TextView scrollDateHeader;
public FriendRequestViewDelegate friendRequestViewDelegate; // Loki
@Override @Override
public void onCreate(Bundle icicle) { public void onCreate(Bundle icicle) {
@ -706,7 +704,6 @@ public class ConversationFragment extends Fragment
if (adapter == null) { if (adapter == null) {
return; return;
} }
adapter.friendRequestViewDelegate = this.friendRequestViewDelegate;
if (cursor.getCount() >= PARTIAL_CONVERSATION_LIMIT && loader.hasLimit()) { if (cursor.getCount() >= PARTIAL_CONVERSATION_LIMIT && loader.hasLimit()) {
adapter.setFooterView(topLoadMoreView); adapter.setFooterView(topLoadMoreView);

View File

@ -86,10 +86,7 @@ import org.thoughtcrime.securesms.jobs.SmsSendJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities; import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
import org.thoughtcrime.securesms.loki.views.FriendRequestView;
import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
import org.thoughtcrime.securesms.loki.views.ProfilePictureView; import org.thoughtcrime.securesms.loki.views.ProfilePictureView;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.ImageSlide; import org.thoughtcrime.securesms.mms.ImageSlide;
@ -158,7 +155,6 @@ public class ConversationItem extends LinearLayout
private ViewGroup contactPhotoHolder; private ViewGroup contactPhotoHolder;
private AlertView alertView; private AlertView alertView;
private ViewGroup container; private ViewGroup container;
private FriendRequestView friendRequestView;
private @NonNull Set<MessageRecord> batchSelected = new HashSet<>(); private @NonNull Set<MessageRecord> batchSelected = new HashSet<>();
private Recipient conversationRecipient; private Recipient conversationRecipient;
@ -182,8 +178,6 @@ public class ConversationItem extends LinearLayout
private final Context context; private final Context context;
public FriendRequestViewDelegate friendRequestViewDelegate; // Loki
public ConversationItem(Context context) { public ConversationItem(Context context) {
this(context, null); this(context, null);
} }
@ -223,7 +217,6 @@ public class ConversationItem extends LinearLayout
this.groupSenderHolder = findViewById(R.id.group_sender_holder); this.groupSenderHolder = findViewById(R.id.group_sender_holder);
this.quoteView = findViewById(R.id.quote_view); this.quoteView = findViewById(R.id.quote_view);
this.container = findViewById(R.id.container); this.container = findViewById(R.id.container);
this.friendRequestView = findViewById(R.id.friend_request_view);
setOnClickListener(new ClickListener(null)); setOnClickListener(new ClickListener(null));
@ -269,7 +262,6 @@ public class ConversationItem extends LinearLayout
setQuote(messageRecord, previousMessageRecord, nextMessageRecord, groupThread); setQuote(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
setMessageSpacing(context, messageRecord, previousMessageRecord, nextMessageRecord, groupThread); setMessageSpacing(context, messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
setFooter(messageRecord, nextMessageRecord, locale, groupThread); setFooter(messageRecord, nextMessageRecord, locale, groupThread);
setFriendRequestView(messageRecord);
adjustMarginsIfNeeded(messageRecord); adjustMarginsIfNeeded(messageRecord);
} }
@ -807,8 +799,8 @@ public class ConversationItem extends LinearLayout
layoutParams.setMarginStart((groupThread && !isRSSFeed) ? groupThreadMargin : defaultMargin); layoutParams.setMarginStart((groupThread && !isRSSFeed) ? groupThreadMargin : defaultMargin);
bodyBubble.setLayoutParams(layoutParams); bodyBubble.setLayoutParams(layoutParams);
if (profilePictureView == null) return; if (profilePictureView == null) return;
profilePictureView.setHexEncodedPublicKey(recipient.getAddress().toString()); profilePictureView.setPublicKey(recipient.getAddress().toString());
profilePictureView.setAdditionalHexEncodedPublicKey(null); profilePictureView.setAdditionalPublicKey(null);
profilePictureView.setRSSFeed(false); profilePictureView.setRSSFeed(false);
profilePictureView.setGlide(glideRequests); profilePictureView.setGlide(glideRequests);
profilePictureView.update(); profilePictureView.update();
@ -920,11 +912,6 @@ public class ConversationItem extends LinearLayout
} }
} }
private void setFriendRequestView(@NonNull MessageRecord record) {
friendRequestView.setDelegate(friendRequestViewDelegate);
friendRequestView.update(record);
}
private ConversationItemFooter getActiveFooter(@NonNull MessageRecord messageRecord) { private ConversationItemFooter getActiveFooter(@NonNull MessageRecord messageRecord) {
if (hasSticker(messageRecord)) { if (hasSticker(messageRecord)) {
return stickerFooter; return stickerFooter;
@ -1067,10 +1054,8 @@ public class ConversationItem extends LinearLayout
int spacingBottom = spacingTop; int spacingBottom = spacingTop;
boolean isOutgoingStack = current.isOutgoing() && previous.orNull() != null && previous.get().isOutgoing(); boolean isOutgoingStack = current.isOutgoing() && previous.orNull() != null && previous.get().isOutgoing();
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
boolean isPreviousMessageFriendRequest = previous.orNull() != null && lokiMessageDatabase.isFriendRequest(previous.get().id);
if (isOutgoingStack && isPreviousMessageFriendRequest) { if (isOutgoingStack) {
spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_default); spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_default);
} }

View File

@ -885,7 +885,7 @@ public class SmsDatabase extends MessagingDatabase {
0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(), 0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(),
threadId, 0, new LinkedList<IdentityKeyMismatch>(), threadId, 0, new LinkedList<IdentityKeyMismatch>(),
message.getSubscriptionId(), message.getExpiresIn(), message.getSubscriptionId(), message.getExpiresIn(),
System.currentTimeMillis(), 0, false, message.isFriendRequest); System.currentTimeMillis(), 0, false);
} }
} }
@ -934,15 +934,12 @@ public class SmsDatabase extends MessagingDatabase {
List<IdentityKeyMismatch> mismatches = getMismatches(mismatchDocument); List<IdentityKeyMismatch> mismatches = getMismatches(mismatchDocument);
Recipient recipient = Recipient.from(context, address, true); Recipient recipient = Recipient.from(context, address, true);
// Loki - Check to see if this message was a friend request
boolean isFriendRequest = DatabaseFactory.getLokiMessageDatabase(context).isFriendRequest(messageId);
return new SmsMessageRecord(messageId, body, recipient, return new SmsMessageRecord(messageId, body, recipient,
recipient, recipient,
addressDeviceId, addressDeviceId,
dateSent, dateReceived, deliveryReceiptCount, type, dateSent, dateReceived, deliveryReceiptCount, type,
threadId, status, mismatches, subscriptionId, threadId, status, mismatches, subscriptionId,
expiresIn, expireStarted, readReceiptCount, unidentified, isFriendRequest); expiresIn, expireStarted, readReceiptCount, unidentified);
} }
private List<IdentityKeyMismatch> getMismatches(String document) { private List<IdentityKeyMismatch> getMismatches(String document) {

View File

@ -145,10 +145,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiAPIDatabase.getCreateSessionRequestTimestampCacheCommand()); db.execSQL(LokiAPIDatabase.getCreateSessionRequestTimestampCacheCommand());
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand()); db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand()); db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
db.execSQL(LokiMessageDatabase.getCreateMessageFriendRequestTableCommand()); db.execSQL(LokiMessageDatabase.getCreateMessageIDTableCommand());
db.execSQL(LokiMessageDatabase.getCreateMessageToThreadMappingTableCommand()); db.execSQL(LokiMessageDatabase.getCreateMessageToThreadMappingTableCommand());
db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand()); db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand());
db.execSQL(LokiThreadDatabase.getCreateFriendRequestTableCommand());
db.execSQL(LokiThreadDatabase.getCreateSessionResetTableCommand()); db.execSQL(LokiThreadDatabase.getCreateSessionResetTableCommand());
db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand()); db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand());
db.execSQL(LokiUserDatabase.getCreateDisplayNameTableCommand()); db.execSQL(LokiUserDatabase.getCreateDisplayNameTableCommand());

View File

@ -39,9 +39,6 @@ import network.loki.messenger.R;
*/ */
public class SmsMessageRecord extends MessageRecord { public class SmsMessageRecord extends MessageRecord {
// Loki
private final boolean isFriendRequest;
public SmsMessageRecord(long id, public SmsMessageRecord(long id,
String body, Recipient recipient, String body, Recipient recipient,
Recipient individualRecipient, Recipient individualRecipient,
@ -52,35 +49,17 @@ public class SmsMessageRecord extends MessageRecord {
int status, List<IdentityKeyMismatch> mismatches, int status, List<IdentityKeyMismatch> mismatches,
int subscriptionId, long expiresIn, long expireStarted, int subscriptionId, long expiresIn, long expireStarted,
int readReceiptCount, boolean unidentified) int readReceiptCount, boolean unidentified)
{
this(id, body, recipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, deliveryReceiptCount, type, threadId, status, mismatches, subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified, false);
}
public SmsMessageRecord(long id,
String body, Recipient recipient,
Recipient individualRecipient,
int recipientDeviceId,
long dateSent, long dateReceived,
int deliveryReceiptCount,
long type, long threadId,
int status, List<IdentityKeyMismatch> mismatches,
int subscriptionId, long expiresIn, long expireStarted,
int readReceiptCount, boolean unidentified, boolean isFriendRequest)
{ {
super(id, body, recipient, individualRecipient, recipientDeviceId, super(id, body, recipient, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, status, deliveryReceiptCount, type, dateSent, dateReceived, threadId, status, deliveryReceiptCount, type,
mismatches, new LinkedList<>(), subscriptionId, mismatches, new LinkedList<>(), subscriptionId,
expiresIn, expireStarted, readReceiptCount, unidentified); expiresIn, expireStarted, readReceiptCount, unidentified);
this.isFriendRequest = isFriendRequest;
} }
public long getType() { public long getType() {
return type; return type;
} }
// Loki
public boolean isFriendRequest() { return isFriendRequest; }
@Override @Override
public SpannableString getDisplayBody(@NonNull Context context) { public SpannableString getDisplayBody(@NonNull Context context) {
Recipient recipient = getRecipient(); Recipient recipient = getRecipient();

View File

@ -45,7 +45,7 @@ import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
import org.thoughtcrime.securesms.jobs.TypingSendJob; import org.thoughtcrime.securesms.jobs.TypingSendJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation; import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceOpenGroupUpdateJob; import org.thoughtcrime.securesms.loki.protocol.MultiDeviceOpenGroupUpdateJob;
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment; import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
import org.thoughtcrime.securesms.push.MessageSenderEventListener; import org.thoughtcrime.securesms.push.MessageSenderEventListener;
@ -156,7 +156,7 @@ public class SignalCommunicationModule {
DatabaseFactory.getLokiThreadDatabase(context), DatabaseFactory.getLokiThreadDatabase(context),
DatabaseFactory.getLokiMessageDatabase(context), DatabaseFactory.getLokiMessageDatabase(context),
DatabaseFactory.getLokiPreKeyBundleDatabase(context), DatabaseFactory.getLokiPreKeyBundleDatabase(context),
new LokiSessionResetImplementation(context), new SessionResetImplementation(context),
DatabaseFactory.getLokiUserDatabase(context), DatabaseFactory.getLokiUserDatabase(context),
((ApplicationContext)context.getApplicationContext()).broadcaster); ((ApplicationContext)context.getApplicationContext()).broadcaster);
} else { } else {

View File

@ -139,7 +139,6 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
Optional<IdentityDatabase.IdentityRecord> identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(address); Optional<IdentityDatabase.IdentityRecord> identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(address);
Optional<VerifiedMessage> verifiedMessage = getVerifiedMessage(recipient, identityRecord); Optional<VerifiedMessage> verifiedMessage = getVerifiedMessage(recipient, identityRecord);
// Loki - Only sync contacts we are friends with
if (SyncMessagesProtocol.shouldSyncContact(context, address)) { if (SyncMessagesProtocol.shouldSyncContact(context, address)) {
out.write(new DeviceContact(address.toPhoneString(), out.write(new DeviceContact(address.toPhoneString(),
Optional.fromNullable(recipient.getName()), Optional.fromNullable(recipient.getName()),

View File

@ -66,15 +66,11 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.loki.protocol.EphemeralMessage;
import org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol;
import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation;
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol; import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol;
import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
import org.thoughtcrime.securesms.loki.protocol.SyncMessagesProtocol; import org.thoughtcrime.securesms.loki.protocol.SyncMessagesProtocol;
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities; import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
import org.thoughtcrime.securesms.loki.utilities.PromiseUtilities; import org.thoughtcrime.securesms.loki.utilities.PromiseUtilities;
@ -130,7 +126,6 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI; import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI;
import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher; import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher;
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager; import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus;
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation; import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation;
import java.security.MessageDigest; import java.security.MessageDigest;
@ -145,8 +140,6 @@ import javax.inject.Inject;
import network.loki.messenger.R; import network.loki.messenger.R;
import static org.thoughtcrime.securesms.loki.utilities.RecipientUtilitiesKt.recipient;
public class PushDecryptJob extends BaseJob implements InjectableType { public class PushDecryptJob extends BaseJob implements InjectableType {
public static final String KEY = "PushDecryptJob"; public static final String KEY = "PushDecryptJob";
@ -225,9 +218,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} }
@Override @Override
public void onCanceled() { public void onCanceled() { }
}
public void processMessage(@NonNull SignalServiceEnvelope envelope, boolean isPushNotification) { public void processMessage(@NonNull SignalServiceEnvelope envelope, boolean isPushNotification) {
synchronized (PushReceivedJob.RECEIVE_LOCK) { synchronized (PushReceivedJob.RECEIVE_LOCK) {
@ -264,18 +255,12 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
try { try {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context); SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
SessionResetProtocol sessionResetProtocol = new LokiSessionResetImplementation(context); SessionResetProtocol sessionResetProtocol = new SessionResetImplementation(context);
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context)); SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, sessionResetProtocol, UnidentifiedAccessUtil.getCertificateValidator()); LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, sessionResetProtocol, UnidentifiedAccessUtil.getCertificateValidator());
SignalServiceContent content = cipher.decrypt(envelope); SignalServiceContent content = cipher.decrypt(envelope);
// Loki - Ignore any friend requests from before restoration
if (FriendRequestProtocol.isFriendRequestFromBeforeRestoration(context, content)) {
Log.d("Loki", "Ignoring friend request from before restoration.");
return;
}
if (shouldIgnore(content)) { if (shouldIgnore(content)) {
Log.i(TAG, "Ignoring message."); Log.i(TAG, "Ignoring message.");
return; return;
@ -300,8 +285,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
if (message.isDeviceUnlinkingRequest()) { if (message.isDeviceUnlinkingRequest()) {
MultiDeviceProtocol.handleUnlinkingRequestIfNeeded(context, content); MultiDeviceProtocol.handleUnlinkingRequestIfNeeded(context, content);
} else { } else {
// Loki - Handle friend request acceptance if needed
FriendRequestProtocol.handleFriendRequestAcceptanceIfNeeded(context, content.getSender(), content);
if (message.isEndSession()) { if (message.isEndSession()) {
handleEndSessionMessage(content, smsMessageId); handleEndSessionMessage(content, smsMessageId);
@ -311,14 +294,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
handleExpirationUpdate(content, message, smsMessageId); handleExpirationUpdate(content, message, smsMessageId);
} else if (isMediaMessage) { } else if (isMediaMessage) {
handleMediaMessage(content, message, smsMessageId, Optional.absent()); handleMediaMessage(content, message, smsMessageId, Optional.absent());
// Loki - Handle friend request message if needed
FriendRequestProtocol.handleFriendRequestMessageIfNeeded(context, content.getSender(), content);
} else if (message.getBody().isPresent()) { } else if (message.getBody().isPresent()) {
handleTextMessage(content, message, smsMessageId, Optional.absent()); handleTextMessage(content, message, smsMessageId, Optional.absent());
// Loki - Handle friend request message if needed
FriendRequestProtocol.handleFriendRequestMessageIfNeeded(context, content.getSender(), content);
} }
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get()))) { if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get()))) {
@ -364,7 +341,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} else if (content.getTypingMessage().isPresent()) { } else if (content.getTypingMessage().isPresent()) {
handleTypingMessage(content, content.getTypingMessage().get()); handleTypingMessage(content, content.getTypingMessage().get());
} else if (content.getNullMessage().isPresent()) { } else if (content.getNullMessage().isPresent()) {
// Loki - This is needed for compatibility with refactored desktop clients // Loki - This is needed for compatibility with refactored desktop clients
// ======== // ========
// if (content.isFriendRequest()) { // if (content.isFriendRequest()) {
@ -372,18 +348,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
// } else { // } else {
// Log.w(TAG, "Got unrecognized message..."); // Log.w(TAG, "Got unrecognized message...");
// } // }
Recipient recipient = recipient(context, content.getSender());
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(context);
LokiThreadFriendRequestStatus threadFriendRequestStatus = threadDB.getFriendRequestStatus(threadID);
if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.NONE || threadFriendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) {
threadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.REQUEST_RECEIVED);
} else if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT) {
threadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS);
EphemeralMessage ephemeralMessage = EphemeralMessage.create(content.getSender());
ApplicationContext.getInstance(context).getJobManager().add(new PushEphemeralMessageSendJob(ephemeralMessage));
SyncMessagesProtocol.syncContact(context, Address.fromSerialized(content.getSender()));
}
// ======== // ========
} }

View File

@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread; import android.support.annotation.WorkerThread;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
@ -31,7 +30,6 @@ import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.state.PreKeyBundle;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
@ -64,46 +62,34 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
private static final String KEY_TEMPLATE_MESSAGE_ID = "template_message_id"; private static final String KEY_TEMPLATE_MESSAGE_ID = "template_message_id";
private static final String KEY_MESSAGE_ID = "message_id"; private static final String KEY_MESSAGE_ID = "message_id";
private static final String KEY_DESTINATION = "destination"; private static final String KEY_DESTINATION = "destination";
private static final String KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE = "is_friend_request";
private static final String KEY_CUSTOM_FR_MESSAGE = "custom_friend_request_message";
@Inject SignalServiceMessageSender messageSender; @Inject SignalServiceMessageSender messageSender;
private long messageId; // The message ID private long messageId;
private long templateMessageId; // The message ID of the message to template this send job from private long templateMessageId;
private Address destination;
// Loki - Multi device
private Address destination; // Used to check whether this is another device we're sending to
private boolean isLokiPreKeyBundleMessage; // Whether this is a friend request / session request / device link message
private String customFriendRequestMessage; // If this isn't set then we use the message body
public PushMediaSendJob(long messageId, Address destination) { public PushMediaSendJob(long messageId, Address destination) {
this(messageId, messageId, destination); this(messageId, messageId, destination);
} }
public PushMediaSendJob(long templateMessageId, long messageId, Address destination) { public PushMediaSendJob(long templateMessageId, long messageId, Address destination) {
this(templateMessageId, messageId, destination, false, null); this(constructParameters(destination), templateMessageId, messageId, destination);
} }
public PushMediaSendJob(long templateMessageId, long messageId, Address destination, boolean isLokiPreKeyBundleMessage, String customFriendRequestMessage) { private PushMediaSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination) {
this(constructParameters(destination), templateMessageId, messageId, destination, isLokiPreKeyBundleMessage, customFriendRequestMessage);
}
private PushMediaSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination, boolean isLokiPreKeyBundleMessage, String customFriendRequestMessage) {
super(parameters); super(parameters);
this.templateMessageId = templateMessageId; this.templateMessageId = templateMessageId;
this.messageId = messageId; this.messageId = messageId;
this.destination = destination; this.destination = destination;
this.isLokiPreKeyBundleMessage = isLokiPreKeyBundleMessage;
this.customFriendRequestMessage = customFriendRequestMessage;
} }
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Address destination) { public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Address destination) {
enqueue(context, jobManager, messageId, messageId, destination, false, null); enqueue(context, jobManager, messageId, messageId, destination);
} }
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long templateMessageId, long messageId, @NonNull Address destination, Boolean isFriendRequest, @Nullable String customFriendRequestMessage) { public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long templateMessageId, long messageId, @NonNull Address destination) {
enqueue(context, jobManager, Collections.singletonList(new PushMediaSendJob(templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage))); enqueue(context, jobManager, Collections.singletonList(new PushMediaSendJob(templateMessageId, messageId, destination)));
} }
@WorkerThread @WorkerThread
@ -144,14 +130,10 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
@Override @Override
public @NonNull Data serialize() { public @NonNull Data serialize() {
Data.Builder builder = new Data.Builder() return new Data.Builder()
.putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId) .putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
.putLong(KEY_MESSAGE_ID, messageId) .putLong(KEY_MESSAGE_ID, messageId)
.putString(KEY_DESTINATION, destination.serialize()) .putString(KEY_DESTINATION, destination.serialize()).build();
.putBoolean(KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE, isLokiPreKeyBundleMessage);
if (customFriendRequestMessage != null) { builder.putString(KEY_CUSTOM_FR_MESSAGE, customFriendRequestMessage); }
return builder.build();
} }
@Override @Override
@ -270,17 +252,8 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
List<SharedContact> sharedContacts = getSharedContactsFor(message); List<SharedContact> sharedContacts = getSharedContactsFor(message);
List<Preview> previews = getPreviewsFor(message); List<Preview> previews = getPreviewsFor(message);
// Loki - Include a pre key bundle if needed
PreKeyBundle preKeyBundle;
if (isLokiPreKeyBundleMessage) {
preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.getNumber());
} else {
preKeyBundle = null;
}
String body = (isLokiPreKeyBundleMessage && customFriendRequestMessage != null) ? customFriendRequestMessage : message.getBody();
SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder()
.withBody(body) .withBody(message.getBody())
.withAttachments(serviceAttachments) .withAttachments(serviceAttachments)
.withTimestamp(message.getSentTimeMillis()) .withTimestamp(message.getSentTimeMillis())
.withExpiration((int)(message.getExpiresIn() / 1000)) .withExpiration((int)(message.getExpiresIn() / 1000))
@ -290,7 +263,6 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
.withSharedContacts(sharedContacts) .withSharedContacts(sharedContacts)
.withPreviews(previews) .withPreviews(previews)
.asExpirationUpdate(message.isExpirationUpdate()) .asExpirationUpdate(message.isExpirationUpdate())
.withPreKeyBundle(preKeyBundle)
.build(); .build();
if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) { if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) {
@ -326,9 +298,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID); long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID);
long messageID = data.getLong(KEY_MESSAGE_ID); long messageID = data.getLong(KEY_MESSAGE_ID);
Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION)); Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
boolean isLokiPreKeyBundleMessage = data.getBoolean(KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE); return new PushMediaSendJob(parameters, templateMessageID, messageID, destination);
String customFRMessage = data.hasString(KEY_CUSTOM_FR_MESSAGE) ? data.getString(KEY_CUSTOM_FR_MESSAGE) : null;
return new PushMediaSendJob(parameters, templateMessageID, messageID, destination, isLokiPreKeyBundleMessage, customFRMessage);
} }
} }
} }

View File

@ -21,7 +21,6 @@ import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.state.PreKeyBundle;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
@ -47,50 +46,34 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
private static final String KEY_TEMPLATE_MESSAGE_ID = "template_message_id"; private static final String KEY_TEMPLATE_MESSAGE_ID = "template_message_id";
private static final String KEY_MESSAGE_ID = "message_id"; private static final String KEY_MESSAGE_ID = "message_id";
private static final String KEY_DESTINATION = "destination"; private static final String KEY_DESTINATION = "destination";
private static final String KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE = "is_friend_request";
private static final String KEY_CUSTOM_FR_MESSAGE = "custom_friend_request_message";
@Inject SignalServiceMessageSender messageSender; @Inject SignalServiceMessageSender messageSender;
private long messageId; // The message ID private long messageId;
private long templateMessageId; // The message ID of the message to template this send job from private long templateMessageId;
private Address destination;
// Loki - Multi device
private Address destination; // Used to check whether this is another device we're sending to
private boolean isLokiPreKeyBundleMessage; // Whether this is a friend request / session request / device link message
private String customFriendRequestMessage; // If this isn't set then we use the message body
public PushTextSendJob(long messageId, Address destination) { public PushTextSendJob(long messageId, Address destination) {
this(messageId, messageId, destination); this(messageId, messageId, destination);
} }
public PushTextSendJob(long templateMessageId, long messageId, Address destination) { public PushTextSendJob(long templateMessageId, long messageId, Address destination) {
this(templateMessageId, messageId, destination, false, null); this(constructParameters(destination), templateMessageId, messageId, destination);
} }
public PushTextSendJob(long templateMessageId, long messageId, Address destination, boolean isLokiPreKeyBundleMessage, String customFriendRequestMessage) { private PushTextSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination) {
this(constructParameters(destination), templateMessageId, messageId, destination, isLokiPreKeyBundleMessage, customFriendRequestMessage);
}
private PushTextSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination, boolean isLokiPreKeyBundleMessage, String customFriendRequestMessage) {
super(parameters); super(parameters);
this.templateMessageId = templateMessageId; this.templateMessageId = templateMessageId;
this.messageId = messageId; this.messageId = messageId;
this.destination = destination; this.destination = destination;
this.isLokiPreKeyBundleMessage = isLokiPreKeyBundleMessage;
this.customFriendRequestMessage = customFriendRequestMessage;
} }
@Override @Override
public @NonNull Data serialize() { public @NonNull Data serialize() {
Data.Builder builder = new Data.Builder() return new Data.Builder()
.putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId) .putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
.putLong(KEY_MESSAGE_ID, messageId) .putLong(KEY_MESSAGE_ID, messageId)
.putString(KEY_DESTINATION, destination.serialize()) .putString(KEY_DESTINATION, destination.serialize()).build();
.putBoolean(KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE, isLokiPreKeyBundleMessage);
if (customFriendRequestMessage != null) { builder.putString(KEY_CUSTOM_FR_MESSAGE, customFriendRequestMessage); }
return builder.build();
} }
@Override @Override
@ -213,22 +196,12 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent()); log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent());
// Loki - Include a pre key bundle if needed
PreKeyBundle preKeyBundle;
if (isLokiPreKeyBundleMessage || message.isEndSession()) {
preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.getNumber());
} else {
preKeyBundle = null;
}
String body = (isLokiPreKeyBundleMessage && customFriendRequestMessage != null) ? customFriendRequestMessage : message.getBody();
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getDateSent()) .withTimestamp(message.getDateSent())
.withBody(body) .withBody(message.getBody())
.withExpiration((int)(message.getExpiresIn() / 1000)) .withExpiration((int)(message.getExpiresIn() / 1000))
.withProfileKey(profileKey.orNull()) .withProfileKey(profileKey.orNull())
.asEndSessionMessage(message.isEndSession()) .asEndSessionMessage(message.isEndSession())
.withPreKeyBundle(preKeyBundle)
.build(); .build();
if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) { if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) {
@ -261,9 +234,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID); long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID);
long messageID = data.getLong(KEY_MESSAGE_ID); long messageID = data.getLong(KEY_MESSAGE_ID);
Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION)); Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
boolean isLokiPreKeyBundleMessage = data.getBoolean(KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE); return new PushTextSendJob(parameters, templateMessageID, messageID, destination);
String customFRMessage = data.hasString(KEY_CUSTOM_FR_MESSAGE) ? data.getString(KEY_CUSTOM_FR_MESSAGE) : null;
return new PushTextSendJob(parameters, templateMessageID, messageID, destination, isLokiPreKeyBundleMessage, customFRMessage);
} }
} }
} }

View File

@ -8,9 +8,8 @@ class CreateClosedGroupLoader(context: Context) : AsyncLoader<List<String>>(cont
override fun loadInBackground(): List<String> { override fun loadInBackground(): List<String> {
val contacts = ContactUtilities.getAllContacts(context) val contacts = ContactUtilities.getAllContacts(context)
// Only show the master devices of the users we are friends with
return contacts.filter { contact -> return contacts.filter { contact ->
!contact.recipient.isGroupRecipient && contact.isFriend && !contact.isOurDevice && !contact.isSlave !contact.recipient.isGroupRecipient && !contact.isOurDevice && !contact.isSlave
}.map { }.map {
it.recipient.address.toPhoneString() it.recipient.address.toPhoneString()
} }

View File

@ -33,7 +33,7 @@ import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.loki.dialogs.PNModeBottomSheet import org.thoughtcrime.securesms.loki.dialogs.PNModeBottomSheet
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol
import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation
import org.thoughtcrime.securesms.loki.utilities.* import org.thoughtcrime.securesms.loki.utilities.*
import org.thoughtcrime.securesms.loki.views.ConversationView import org.thoughtcrime.securesms.loki.views.ConversationView
import org.thoughtcrime.securesms.loki.views.NewConversationButtonSetViewDelegate import org.thoughtcrime.securesms.loki.views.NewConversationButtonSetViewDelegate
@ -47,18 +47,16 @@ import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol
import org.whispersystems.signalservice.loki.protocol.syncmessages.SyncMessagesProtocol import org.whispersystems.signalservice.loki.protocol.syncmessages.SyncMessagesProtocol
import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequestStatus
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
import kotlin.math.abs import kotlin.math.abs
class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListener, SeedReminderViewDelegate, NewConversationButtonSetViewDelegate { class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListener, SeedReminderViewDelegate, NewConversationButtonSetViewDelegate {
private lateinit var glide: GlideRequests private lateinit var glide: GlideRequests
private val hexEncodedPublicKey: String private val publicKey: String
get() { get() {
val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this) val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this)
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this) val userPublicKey = TextSecurePreferences.getLocalNumber(this)
return masterHexEncodedPublicKey ?: userHexEncodedPublicKey return masterPublicKey ?: userPublicKey
} }
// region Lifecycle // region Lifecycle
@ -94,7 +92,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
glide = GlideApp.with(this) glide = GlideApp.with(this)
// Set up toolbar buttons // Set up toolbar buttons
profileButton.glide = glide profileButton.glide = glide
profileButton.hexEncodedPublicKey = hexEncodedPublicKey profileButton.publicKey = publicKey
profileButton.update() profileButton.update()
profileButton.setOnClickListener { openSettings() } profileButton.setOnClickListener { openSettings() }
pathStatusViewContainer.setOnClickListener { showPath() } pathStatusViewContainer.setOnClickListener { showPath() }
@ -156,45 +154,23 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
val threadDB = DatabaseFactory.getLokiThreadDatabase(this) val threadDB = DatabaseFactory.getLokiThreadDatabase(this)
val userDB = DatabaseFactory.getLokiUserDatabase(this) val userDB = DatabaseFactory.getLokiUserDatabase(this)
val userPublicKey = TextSecurePreferences.getLocalNumber(this) val userPublicKey = TextSecurePreferences.getLocalNumber(this)
val sessionResetImpl = LokiSessionResetImplementation(this) val sessionResetImpl = SessionResetImplementation(this)
if (userPublicKey != null) { if (userPublicKey != null) {
MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB) MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB)
SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey) SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey)
SyncMessagesProtocol.configureIfNeeded(apiDB, userPublicKey) SyncMessagesProtocol.configureIfNeeded(apiDB, userPublicKey)
application.lokiPublicChatManager.startPollersIfNeeded() application.publicChatManager.startPollersIfNeeded()
} }
SessionManagementProtocol.configureIfNeeded(sessionResetImpl, threadDB, application) SessionManagementProtocol.configureIfNeeded(sessionResetImpl, threadDB, application)
MultiDeviceProtocol.configureIfNeeded(apiDB) MultiDeviceProtocol.configureIfNeeded(apiDB)
IP2Country.configureIfNeeded(this) IP2Country.configureIfNeeded(this)
// Preload device links to make message sending quicker // Preload device links to make message sending quicker
val publicKeys = ContactUtilities.getAllContacts(this).filter { contact -> val publicKeys = ContactUtilities.getAllContacts(this).filter { contact ->
!contact.recipient.isGroupRecipient && contact.isFriend && !contact.isOurDevice && !contact.isSlave !contact.recipient.isGroupRecipient && !contact.isOurDevice && !contact.isSlave
}.map { }.map {
it.recipient.address.toPhoneString() it.recipient.address.toPhoneString()
}.toSet() }.toSet()
FileServerAPI.shared.getDeviceLinks(publicKeys) FileServerAPI.shared.getDeviceLinks(publicKeys)
// TODO: Temporary hack to unbork existing clients
val allContacts = DatabaseFactory.getRecipientDatabase(this).allAddresses.map {
MultiDeviceProtocol.shared.getMasterDevice(it.serialize()) ?: it.serialize()
}.toSet()
val lokiMessageDB = DatabaseFactory.getLokiMessageDatabase(this)
for (contact in allContacts) {
val slaveDeviceHasPendingFR = MultiDeviceProtocol.shared.getSlaveDevices(contact).any {
val slaveDeviceThreadID = DatabaseFactory.getThreadDatabase(this).getThreadIdFor(recipient(this, it))
DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(slaveDeviceThreadID) == LokiThreadFriendRequestStatus.REQUEST_RECEIVED
}
val masterDeviceThreadID = DatabaseFactory.getThreadDatabase(this).getThreadIdFor(recipient(this, contact))
val masterDeviceHasNoPendingFR = (DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(masterDeviceThreadID) == LokiThreadFriendRequestStatus.NONE)
if (slaveDeviceHasPendingFR && masterDeviceHasNoPendingFR) {
val lastMessageID = org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol.getLastMessageID(this, masterDeviceThreadID)
if (lastMessageID != null) {
val lastMessageFRStatus = lokiMessageDB.getFriendRequestStatus(lastMessageID)
if (lastMessageFRStatus != LokiMessageFriendRequestStatus.REQUEST_PENDING) {
lokiMessageDB.setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_PENDING)
}
}
}
}
} }
override fun onResume() { override fun onResume() {

View File

@ -15,7 +15,7 @@ import org.thoughtcrime.securesms.database.IdentityDatabase
import org.thoughtcrime.securesms.logging.Log import org.thoughtcrime.securesms.logging.Log
import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialog import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialog
import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialogDelegate import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialogDelegate
import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
import org.thoughtcrime.securesms.loki.utilities.push import org.thoughtcrime.securesms.loki.utilities.push
import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo
@ -109,7 +109,7 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
val threadDB = DatabaseFactory.getLokiThreadDatabase(this) val threadDB = DatabaseFactory.getLokiThreadDatabase(this)
val userDB = DatabaseFactory.getLokiUserDatabase(this) val userDB = DatabaseFactory.getLokiUserDatabase(this)
val userPublicKey = TextSecurePreferences.getLocalNumber(this) val userPublicKey = TextSecurePreferences.getLocalNumber(this)
val sessionResetImpl = LokiSessionResetImplementation(this) val sessionResetImpl = SessionResetImplementation(this)
MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB) MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB)
SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey) SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey)
org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.configureIfNeeded(apiDB) org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.configureIfNeeded(apiDB)

View File

@ -73,7 +73,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
showQRCodeButton.setOnClickListener { showQRCode() } showQRCodeButton.setOnClickListener { showQRCode() }
glide = GlideApp.with(this) glide = GlideApp.with(this)
profilePictureView.glide = glide profilePictureView.glide = glide
profilePictureView.hexEncodedPublicKey = hexEncodedPublicKey profilePictureView.publicKey = hexEncodedPublicKey
profilePictureView.isLarge = true profilePictureView.isLarge = true
profilePictureView.update() profilePictureView.update()
profilePictureView.setOnClickListener { showEditProfilePictureUI() } profilePictureView.setOnClickListener { showEditProfilePictureUI() }

View File

@ -47,7 +47,7 @@ class BackgroundPollWorker : PersistentAlarmManagerListener() {
} }
val openGroups = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().map { it.value } val openGroups = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().map { it.value }
for (openGroup in openGroups) { for (openGroup in openGroups) {
val poller = LokiPublicChatPoller(context, openGroup) val poller = PublicChatPoller(context, openGroup)
poller.stop() poller.stop()
poller.pollForNewMessages() poller.pollForNewMessages()
} }

View File

@ -14,9 +14,9 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
class LokiPublicChatManager(private val context: Context) { class PublicChatManager(private val context: Context) {
private var chats = mutableMapOf<Long, PublicChat>() private var chats = mutableMapOf<Long, PublicChat>()
private val pollers = mutableMapOf<Long, LokiPublicChatPoller>() private val pollers = mutableMapOf<Long, PublicChatPoller>()
private val observers = mutableMapOf<Long, ContentObserver>() private val observers = mutableMapOf<Long, ContentObserver>()
private var isPolling = false private var isPolling = false
@ -24,7 +24,7 @@ class LokiPublicChatManager(private val context: Context) {
var areAllCaughtUp = true var areAllCaughtUp = true
refreshChatsAndPollers() refreshChatsAndPollers()
for ((threadID, chat) in chats) { for ((threadID, chat) in chats) {
val poller = pollers[threadID] ?: LokiPublicChatPoller(context, chat) val poller = pollers[threadID] ?: PublicChatPoller(context, chat)
areAllCaughtUp = areAllCaughtUp && poller.isCaughtUp areAllCaughtUp = areAllCaughtUp && poller.isCaughtUp
} }
return areAllCaughtUp return areAllCaughtUp
@ -33,7 +33,7 @@ class LokiPublicChatManager(private val context: Context) {
public fun markAllAsNotCaughtUp() { public fun markAllAsNotCaughtUp() {
refreshChatsAndPollers() refreshChatsAndPollers()
for ((threadID, chat) in chats) { for ((threadID, chat) in chats) {
val poller = pollers[threadID] ?: LokiPublicChatPoller(context, chat) val poller = pollers[threadID] ?: PublicChatPoller(context, chat)
poller.isCaughtUp = false poller.isCaughtUp = false
} }
} }
@ -42,7 +42,7 @@ class LokiPublicChatManager(private val context: Context) {
refreshChatsAndPollers() refreshChatsAndPollers()
for ((threadId, chat) in chats) { for ((threadId, chat) in chats) {
val poller = pollers[threadId] ?: LokiPublicChatPoller(context, chat) val poller = pollers[threadId] ?: PublicChatPoller(context, chat)
poller.startIfNeeded() poller.startIfNeeded()
listenToThreadDeletion(threadId) listenToThreadDeletion(threadId)
if (!pollers.containsKey(threadId)) { pollers[threadId] = poller } if (!pollers.containsKey(threadId)) { pollers[threadId] = poller }

View File

@ -27,11 +27,10 @@ import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI
import org.whispersystems.signalservice.loki.api.opengroups.PublicChatMessage import org.whispersystems.signalservice.loki.api.opengroups.PublicChatMessage
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
import java.security.MessageDigest import java.security.MessageDigest
import java.util.* import java.util.*
class LokiPublicChatPoller(private val context: Context, private val group: PublicChat) { class PublicChatPoller(private val context: Context, private val group: PublicChat) {
private val handler = Handler() private val handler = Handler()
private var hasStarted = false private var hasStarted = false
public var isCaughtUp = false public var isCaughtUp = false
@ -182,13 +181,6 @@ class LokiPublicChatPoller(private val context: Context, private val group: Publ
database.setProfileKey(senderAsRecipient, profileKey) database.setProfileKey(senderAsRecipient, profileKey)
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderAsRecipient, url)) ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderAsRecipient, url))
} }
} else if (senderAsRecipient.profileAvatar.orEmpty().isNotEmpty()) {
// Clear the profile picture if we had a profile picture before and we're not friends with the person
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(senderAsRecipient)
val friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID)
if (friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) {
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderAsRecipient, ""))
}
} }
} }
fun processOutgoingMessage(message: PublicChatMessage) { fun processOutgoingMessage(message: PublicChatMessage) {

View File

@ -11,12 +11,11 @@ import org.thoughtcrime.securesms.loki.utilities.getInt
import org.thoughtcrime.securesms.loki.utilities.getString import org.thoughtcrime.securesms.loki.utilities.getString
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
import org.whispersystems.signalservice.loki.database.LokiMessageDatabaseProtocol import org.whispersystems.signalservice.loki.database.LokiMessageDatabaseProtocol
import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequestStatus
class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol { class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol {
companion object { companion object {
private val messageFriendRequestTable = "loki_message_friend_request_database" private val messageIDTable = "loki_message_friend_request_database"
private val messageThreadMappingTable = "loki_message_thread_mapping_database" private val messageThreadMappingTable = "loki_message_thread_mapping_database"
private val errorMessageTable = "loki_error_message_database" private val errorMessageTable = "loki_error_message_database"
private val messageID = "message_id" private val messageID = "message_id"
@ -24,7 +23,7 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
private val friendRequestStatus = "friend_request_status" private val friendRequestStatus = "friend_request_status"
private val threadID = "thread_id" private val threadID = "thread_id"
private val errorMessage = "error_message" private val errorMessage = "error_message"
@JvmStatic val createMessageFriendRequestTableCommand = "CREATE TABLE $messageFriendRequestTable ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);" @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 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 createErrorMessageTableCommand = "CREATE TABLE IF NOT EXISTS $errorMessageTable ($messageID INTEGER PRIMARY KEY, $errorMessage STRING);"
} }
@ -36,14 +35,14 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
fun getServerID(messageID: Long): Long? { fun getServerID(messageID: Long): Long? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(messageFriendRequestTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor -> return database.get(messageIDTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
cursor.getInt(serverID) cursor.getInt(serverID)
}?.toLong() }?.toLong()
} }
fun getMessageID(serverID: Long): Long? { fun getMessageID(serverID: Long): Long? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(messageFriendRequestTable, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor -> return database.get(messageIDTable, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor ->
cursor.getInt(messageID) cursor.getInt(messageID)
}?.toLong() }?.toLong()
} }
@ -53,7 +52,7 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
val contentValues = ContentValues(2) val contentValues = ContentValues(2)
contentValues.put(Companion.messageID, messageID) contentValues.put(Companion.messageID, messageID)
contentValues.put(Companion.serverID, serverID) contentValues.put(Companion.serverID, serverID)
database.insertOrUpdate(messageFriendRequestTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) database.insertOrUpdate(messageIDTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
} }
fun getOriginalThreadID(messageID: Long): Long { fun getOriginalThreadID(messageID: Long): Long {
@ -71,32 +70,6 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
database.insertOrUpdate(messageThreadMappingTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) database.insertOrUpdate(messageThreadMappingTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
} }
fun getFriendRequestStatus(messageID: Long): LokiMessageFriendRequestStatus {
val database = databaseHelper.readableDatabase
val result = database.get(messageFriendRequestTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
cursor.getInt(friendRequestStatus)
}
return if (result != null) {
LokiMessageFriendRequestStatus.values().first { it.rawValue == result }
} else {
LokiMessageFriendRequestStatus.NONE
}
}
fun setFriendRequestStatus(messageID: Long, friendRequestStatus: LokiMessageFriendRequestStatus) {
val database = databaseHelper.writableDatabase
val contentValues = ContentValues(2)
contentValues.put(Companion.messageID, messageID)
contentValues.put(Companion.friendRequestStatus, friendRequestStatus.rawValue)
database.insertOrUpdate(messageFriendRequestTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
val threadID = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageID)
notifyConversationListeners(threadID)
}
fun isFriendRequest(messageID: Long): Boolean {
return getFriendRequestStatus(messageID) != LokiMessageFriendRequestStatus.NONE
}
fun getErrorMessage(messageID: Long): String? { fun getErrorMessage(messageID: Long): String? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(errorMessageTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor -> return database.get(errorMessageTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->

View File

@ -15,21 +15,18 @@ import org.whispersystems.libsignal.loki.SessionResetStatus
import org.whispersystems.signalservice.internal.util.JsonUtil import org.whispersystems.signalservice.internal.util.JsonUtil
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
import org.whispersystems.signalservice.loki.database.LokiThreadDatabaseProtocol import org.whispersystems.signalservice.loki.database.LokiThreadDatabaseProtocol
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation
class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiThreadDatabaseProtocol { class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiThreadDatabaseProtocol {
var delegate: LokiThreadDatabaseDelegate? = null var delegate: LokiThreadDatabaseDelegate? = null
companion object { companion object {
private val friendRequestTable = "loki_thread_friend_request_database"
private val sessionResetTable = "loki_thread_session_reset_database" private val sessionResetTable = "loki_thread_session_reset_database"
val publicChatTable = "loki_public_chat_database" val publicChatTable = "loki_public_chat_database"
val threadID = "thread_id" val threadID = "thread_id"
private val friendRequestStatus = "friend_request_status" private val friendRequestStatus = "friend_request_status"
private val sessionResetStatus = "session_reset_status" private val sessionResetStatus = "session_reset_status"
val publicChat = "public_chat" val publicChat = "public_chat"
@JvmStatic val createFriendRequestTableCommand = "CREATE TABLE $friendRequestTable ($threadID INTEGER PRIMARY KEY, $friendRequestStatus INTEGER DEFAULT 0);"
@JvmStatic val createSessionResetTableCommand = "CREATE TABLE $sessionResetTable ($threadID INTEGER PRIMARY KEY, $sessionResetStatus INTEGER DEFAULT 0);" @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 createPublicChatTableCommand = "CREATE TABLE $publicChatTable ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);"
} }
@ -44,34 +41,6 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
return DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageID) return DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageID)
} }
fun getFriendRequestStatus(threadID: Long): LokiThreadFriendRequestStatus {
if (threadID < 0) { return LokiThreadFriendRequestStatus.NONE }
val recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadID)
if (recipient != null && recipient.isGroupRecipient) { return LokiThreadFriendRequestStatus.FRIENDS; }
val database = databaseHelper.readableDatabase
val result = database.get(friendRequestTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor ->
cursor.getInt(friendRequestStatus)
}
return if (result != null) {
LokiThreadFriendRequestStatus.values().first { it.rawValue == result }
} else {
LokiThreadFriendRequestStatus.NONE
}
}
fun setFriendRequestStatus(threadID: Long, friendRequestStatus: LokiThreadFriendRequestStatus) {
if (threadID < 0) { return }
Log.d("Loki", "Setting FR status for thread with ID $threadID to $friendRequestStatus.")
val database = databaseHelper.writableDatabase
val contentValues = ContentValues(2)
contentValues.put(Companion.threadID, threadID)
contentValues.put(Companion.friendRequestStatus, friendRequestStatus.rawValue)
database.insertOrUpdate(friendRequestTable, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() ))
notifyConversationListListeners()
notifyConversationListeners(threadID)
delegate?.handleThreadFriendRequestStatusChanged(threadID)
}
fun getSessionResetStatus(hexEncodedPublicKey: String): SessionResetStatus { fun getSessionResetStatus(hexEncodedPublicKey: String): SessionResetStatus {
val threadID = getThreadID(hexEncodedPublicKey) val threadID = getThreadID(hexEncodedPublicKey)
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase

View File

@ -2,6 +2,5 @@ package org.thoughtcrime.securesms.loki.database
interface LokiThreadDatabaseDelegate { interface LokiThreadDatabaseDelegate {
fun handleThreadFriendRequestStatusChanged(threadID: Long)
fun handleSessionRestoreDevicesChanged(threadID: Long) fun handleSessionRestoreDevicesChanged(threadID: Long)
} }

View File

@ -15,10 +15,10 @@ sealed class ContactSelectionListItem {
class ContactSelectionListLoader(context: Context, val mode: Int, val filter: String?) : AsyncLoader<List<ContactSelectionListItem>>(context) { class ContactSelectionListLoader(context: Context, val mode: Int, val filter: String?) : AsyncLoader<List<ContactSelectionListItem>>(context) {
object DisplayMode { object DisplayMode {
const val FLAG_FRIENDS = 1 const val FLAG_CONTACTS = 1
const val FLAG_CLOSED_GROUPS = 1 shl 1 const val FLAG_CLOSED_GROUPS = 1 shl 1
const val FLAG_OPEN_GROUPS = 1 shl 2 const val FLAG_OPEN_GROUPS = 1 shl 2
const val FLAG_ALL = FLAG_FRIENDS or FLAG_CLOSED_GROUPS or FLAG_OPEN_GROUPS const val FLAG_ALL = FLAG_CONTACTS or FLAG_CLOSED_GROUPS or FLAG_OPEN_GROUPS
} }
private fun isFlagSet(flag: Int): Boolean { private fun isFlagSet(flag: Int): Boolean {
@ -39,15 +39,15 @@ class ContactSelectionListLoader(context: Context, val mode: Int, val filter: St
if (isFlagSet(DisplayMode.FLAG_OPEN_GROUPS)) { if (isFlagSet(DisplayMode.FLAG_OPEN_GROUPS)) {
list.addAll(getOpenGroups(contacts)) list.addAll(getOpenGroups(contacts))
} }
if (isFlagSet(DisplayMode.FLAG_FRIENDS)) { if (isFlagSet(DisplayMode.FLAG_CONTACTS)) {
list.addAll(getFriends(contacts)) list.addAll(getContacts(contacts))
} }
return list return list
} }
private fun getFriends(contacts: List<Contact>): List<ContactSelectionListItem> { private fun getContacts(contacts: List<Contact>): List<ContactSelectionListItem> {
return getItems(contacts, context.getString(R.string.fragment_contact_selection_contacts_title)) { return getItems(contacts, context.getString(R.string.fragment_contact_selection_contacts_title)) {
!it.recipient.isGroupRecipient && it.isFriend && !it.isOurDevice && !it.isSlave !it.recipient.isGroupRecipient && !it.isOurDevice && !it.isSlave
} }
} }

View File

@ -10,10 +10,7 @@ class EphemeralMessage private constructor(val data: Map<*, *>) {
fun create(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey )) fun create(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey ))
@JvmStatic @JvmStatic
fun createUnlinkingRequest(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey, "unpairingRequest" to true )) fun createDeviceUnlinkingRequest(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey, "unpairingRequest" to true ))
@JvmStatic
fun createSessionRestorationRequest(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey, "friendRequest" to true, "sessionRestore" to true ))
@JvmStatic @JvmStatic
fun createSessionRequest(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey, "friendRequest" to true, "sessionRequest" to true )) fun createSessionRequest(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey, "friendRequest" to true, "sessionRequest" to true ))

View File

@ -1,316 +0,0 @@
package org.thoughtcrime.securesms.loki.protocol
import android.content.Context
import android.util.Log
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.utilities.recipient
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage
import org.thoughtcrime.securesms.sms.OutgoingTextMessage
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.messages.SignalServiceContent
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequestStatus
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
object FriendRequestProtocol {
@JvmStatic
fun acceptFriendRequest(context: Context, recipient: Recipient) {
if (recipient.isGroupRecipient) { return; }
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey)
// Accept all outstanding friend requests associated with this user and try to establish sessions with the
// subset of their devices that haven't sent a friend request.
val allContactDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.address.serialize())
val threadDB = DatabaseFactory.getThreadDatabase(context)
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
for (device in allContactDevices) {
val deviceAsRecipient = recipient(context, device)
val deviceThreadID = threadDB.getThreadIdFor(deviceAsRecipient)
val deviceFRStatus = lokiThreadDB.getFriendRequestStatus(deviceThreadID)
if (deviceFRStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
lokiThreadDB.setFriendRequestStatus(deviceThreadID, LokiThreadFriendRequestStatus.FRIENDS)
val lastMessageID = getLastMessageID(context, deviceThreadID)
if (lastMessageID != null) {
DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED)
}
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient(context, device), true)
val ephemeralMessage = EphemeralMessage.create(device)
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))
// Sync contact if needed
if (allUserDevices.contains(device)) { return }
val deviceToSync = MultiDeviceProtocol.shared.getMasterDevice(device) ?: device
SyncMessagesProtocol.syncContact(context, Address.fromSerialized(deviceToSync))
} else if (deviceFRStatus == LokiThreadFriendRequestStatus.REQUEST_SENT) {
// Do nothing
} else if (!allUserDevices.contains(device)
&& (deviceFRStatus == LokiThreadFriendRequestStatus.NONE || deviceFRStatus == LokiThreadFriendRequestStatus.REQUEST_EXPIRED)) {
sendAutoGeneratedFriendRequest(context, device)
}
}
}
@JvmStatic
fun rejectFriendRequest(context: Context, recipient: Recipient) {
if (recipient.isGroupRecipient) { return; }
val linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.address.serialize())
val threadDB = DatabaseFactory.getThreadDatabase(context)
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
for (device in linkedDevices) {
val deviceAsRecipient = recipient(context, device)
val deviceThreadID = threadDB.getThreadIdFor(deviceAsRecipient)
val deviceFRStatus = lokiThreadDB.getFriendRequestStatus(deviceThreadID)
// We only want to decline incoming requests
if (deviceFRStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
// Delete the pre key bundle for the given contact. This ensures that if we send a
// new message after this, it restarts the friend request process from scratch.
DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(device)
lokiThreadDB.setFriendRequestStatus(deviceThreadID, LokiThreadFriendRequestStatus.NONE)
val lastMessageID = getLastMessageID(context, deviceThreadID)
if (lastMessageID != null) {
DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_REJECTED)
}
}
}
}
@JvmStatic
fun shouldInputPanelBeEnabled(context: Context, recipient: Recipient): Boolean {
// Friend requests have nothing to do with groups, so if this is a group thread the input panel should be enabled
if (recipient.isGroupRecipient) { return true }
// If this is a note to self the input panel should be enabled
if (SessionMetaProtocol.shared.isNoteToSelf(recipient.address.serialize())) { return true }
// Gather friend request statuses
val linkedDeviceFRStatuses = mutableSetOf<LokiThreadFriendRequestStatus>()
val linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.address.serialize())
val threadDB = DatabaseFactory.getThreadDatabase(context)
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
for (device in linkedDevices) {
val deviceAsRecipient = recipient(context, device)
val deviceThreadID = threadDB.getThreadIdFor(deviceAsRecipient)
val deviceFRStatus = lokiThreadDB.getFriendRequestStatus(deviceThreadID)
linkedDeviceFRStatuses.add(deviceFRStatus)
}
// If the user is friends with any of the other user's devices the input panel should be enabled
if (linkedDeviceFRStatuses.contains(LokiThreadFriendRequestStatus.FRIENDS)) { return true }
// If no friend request has been sent the input panel should be enabled
if (linkedDeviceFRStatuses.all { it == LokiThreadFriendRequestStatus.NONE || it == LokiThreadFriendRequestStatus.REQUEST_EXPIRED }) { return true }
// There must be a pending friend request
return false
}
@JvmStatic
fun shouldAttachmentButtonBeEnabled(context: Context, recipient: Recipient): Boolean {
// Friend requests have nothing to do with groups, so if this is a group thread the attachment button should be enabled
if (recipient.isGroupRecipient) { return true }
// If this is a note to self the attachment button should be enabled
if (SessionMetaProtocol.shared.isNoteToSelf(recipient.address.serialize())) { return true }
// Gather friend request statuses
val linkedDeviceFRStatuses = mutableSetOf<LokiThreadFriendRequestStatus>()
val linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.address.serialize())
val threadDB = DatabaseFactory.getThreadDatabase(context)
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
for (device in linkedDevices) {
val deviceAsRecipient = recipient(context, device)
val deviceThreadID = threadDB.getThreadIdFor(deviceAsRecipient)
val deviceFRStatus = lokiThreadDB.getFriendRequestStatus(deviceThreadID)
linkedDeviceFRStatuses.add(deviceFRStatus)
}
// If the user is friends with any of the other user's devices the attachment button should be enabled
if (linkedDeviceFRStatuses.contains(LokiThreadFriendRequestStatus.FRIENDS)) { return true }
// Otherwise don't allow attachments
return false
}
@JvmStatic
fun getLastMessageID(context: Context, threadID: Long): Long? {
val db = DatabaseFactory.getSmsDatabase(context)
val messageCount = db.getMessageCountForThread(threadID)
if (messageCount == 0) { return null }
return db.getIDForMessageAtIndex(threadID, messageCount - 1)
}
@JvmStatic
fun handleFriendRequestAcceptanceIfNeeded(context: Context, publicKey: String, content: SignalServiceContent) {
// If we get an envelope that isn't a friend request, then we can infer that we had to use
// Signal cipher decryption and thus that we have a session with the other person.
val recipient = recipient(context, publicKey)
// Friend requests don't apply to groups
if (recipient.isGroupRecipient) { return }
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
val threadFRStatus = lokiThreadDB.getFriendRequestStatus(threadID)
// Guard against invalid state transitions
if (threadFRStatus != LokiThreadFriendRequestStatus.REQUEST_SENDING && threadFRStatus != LokiThreadFriendRequestStatus.REQUEST_SENT
&& threadFRStatus != LokiThreadFriendRequestStatus.REQUEST_RECEIVED) { return }
Log.d("Loki", "Received a friend request accepted message from $publicKey.")
lokiThreadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS)
val lastMessageID = getLastMessageID(context, threadID)
if (lastMessageID != null) {
DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED)
}
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true)
// Send a contact sync message if needed
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey)
if (allUserDevices.contains(publicKey)) { return }
val deviceToSync = MultiDeviceProtocol.shared.getMasterDevice(publicKey) ?: publicKey
SyncMessagesProtocol.syncContact(context, Address.fromSerialized(deviceToSync))
}
private fun canFriendRequestBeAutoAccepted(context: Context, publicKey: String): Boolean {
val recipient = recipient(context, publicKey)
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
val threadFRStatus = lokiThreadDB.getFriendRequestStatus(threadID)
if (threadFRStatus == LokiThreadFriendRequestStatus.REQUEST_SENT) {
// This can happen if Alice sent Bob a friend request, Bob declined, but then Bob changed his
// mind and sent a friend request to Alice. In this case we want Alice to auto-accept the request
// and send a friend request accepted message back to Bob. We don't check that sending the
// friend request accepted message succeeds. Even if it doesn't, the thread's current friend
// request status will be set to FRIENDS for Alice making it possible for Alice to send messages
// to Bob. When Bob receives a message, his thread's friend request status will then be set to
// FRIENDS. If we do check for a successful send before updating Alice's thread's friend request
// status to FRIENDS, we can end up in a deadlock where both users' threads' friend request statuses
// are SENT.
return true
}
// Auto-accept any friend requests from the user's own linked devices
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey)
if (allUserDevices.contains(publicKey)) { return true }
// Auto-accept if the user is friends with any of the sender's linked devices.
val allSenderDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(publicKey)
if (allSenderDevices.any { device ->
val deviceAsRecipient = recipient(context, publicKey)
val deviceThreadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(deviceAsRecipient)
lokiThreadDB.getFriendRequestStatus(deviceThreadID) == LokiThreadFriendRequestStatus.FRIENDS
}) {
return true
}
return false
}
@JvmStatic
fun handleFriendRequestMessageIfNeeded(context: Context, publicKey: String, content: SignalServiceContent) {
val recipient = recipient(context, publicKey)
// Friend requests don't apply to groups
if (recipient.isGroupRecipient) { return }
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
val threadFRStatus = lokiThreadDB.getFriendRequestStatus(threadID)
if (canFriendRequestBeAutoAccepted(context, publicKey)) {
Log.d("Loki", "Auto-accepting friend request from $publicKey.")
lokiThreadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS)
val lastMessageID = getLastMessageID(context, threadID)
if (lastMessageID != null) {
DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED)
}
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true)
val ephemeralMessage = EphemeralMessage.create(publicKey)
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))
} else if (threadFRStatus != LokiThreadFriendRequestStatus.FRIENDS) {
Log.d("Loki", "Handling friend request from $publicKey.")
// Checking that the sender of the message isn't already a friend is necessary because otherwise
// the following situation can occur: Alice and Bob are friends. Bob loses his database and his
// friend request status is reset to NONE. Bob now sends Alice a friend request. Alice's thread's
// friend request status is reset to RECEIVED
lokiThreadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.REQUEST_RECEIVED)
val masterPublicKey = MultiDeviceProtocol.shared.getMasterDevice(publicKey) ?: publicKey
val masterThreadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient(context, masterPublicKey))
val masterThreadLastMessageID = getLastMessageID(context, masterThreadID) // Messages get routed into the master thread
if (masterThreadLastMessageID != null) {
DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(masterThreadLastMessageID, LokiMessageFriendRequestStatus.REQUEST_PENDING)
} else {
// Device link fetching could fail, in which case the message could get routed into the slave thread
val slaveThreadLastMessageID = getLastMessageID(context, threadID)
if (slaveThreadLastMessageID != null) {
DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(slaveThreadLastMessageID, LokiMessageFriendRequestStatus.REQUEST_PENDING)
}
}
}
}
@JvmStatic
fun isFriendRequestFromBeforeRestoration(context: Context, content: SignalServiceContent): Boolean {
return content.timestamp < TextSecurePreferences.getRestorationTime(context)
}
@JvmStatic
fun shouldUpdateFriendRequestStatusFromOutgoingTextMessage(context: Context, message: OutgoingTextMessage): Boolean {
// The order of these checks matters
if (message.recipient.isGroupRecipient) { return false }
if (message.recipient.address.serialize() == TextSecurePreferences.getLocalNumber(context)) { return false }
// TODO: Return true if the message is a device linking request
// TODO: Return false if the message is a session request
return message.isFriendRequest
}
@JvmStatic
fun shouldUpdateFriendRequestStatusFromOutgoingMediaMessage(context: Context, message: OutgoingMediaMessage): Boolean {
// The order of these checks matters
if (message.recipient.isGroupRecipient) { return false }
if (message.recipient.address.serialize() == TextSecurePreferences.getLocalNumber(context)) { return false }
// TODO: Return true if the message is a device linking request
// TODO: Return false if the message is a session request
return message.isFriendRequest
}
@JvmStatic
fun setFriendRequestStatusToSendingIfNeeded(context: Context, messageID: Long, threadID: Long) {
val messageDB = DatabaseFactory.getLokiMessageDatabase(context)
val messageFRStatus = messageDB.getFriendRequestStatus(messageID)
if (messageFRStatus == LokiMessageFriendRequestStatus.NONE || messageFRStatus == LokiMessageFriendRequestStatus.REQUEST_EXPIRED) {
messageDB.setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_SENDING)
}
val threadDB = DatabaseFactory.getLokiThreadDatabase(context)
val threadFRStatus = threadDB.getFriendRequestStatus(threadID)
if (threadFRStatus == LokiThreadFriendRequestStatus.NONE || threadFRStatus == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) {
threadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.REQUEST_SENDING)
}
}
@JvmStatic
fun setFriendRequestStatusToSentIfNeeded(context: Context, messageID: Long, threadID: Long) {
val messageDB = DatabaseFactory.getLokiMessageDatabase(context)
val messageFRStatus = messageDB.getFriendRequestStatus(messageID)
if (messageFRStatus == LokiMessageFriendRequestStatus.NONE || messageFRStatus == LokiMessageFriendRequestStatus.REQUEST_EXPIRED
|| messageFRStatus == LokiMessageFriendRequestStatus.REQUEST_SENDING) {
messageDB.setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_PENDING)
}
val threadDB = DatabaseFactory.getLokiThreadDatabase(context)
val threadFRStatus = threadDB.getFriendRequestStatus(threadID)
if (threadFRStatus == LokiThreadFriendRequestStatus.NONE || threadFRStatus == LokiThreadFriendRequestStatus.REQUEST_EXPIRED
|| threadFRStatus == LokiThreadFriendRequestStatus.REQUEST_SENDING) {
threadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.REQUEST_SENT)
}
}
@JvmStatic
fun setFriendRequestStatusToFailedIfNeeded(context: Context, messageID: Long, threadID: Long) {
val messageDB = DatabaseFactory.getLokiMessageDatabase(context)
val messageFRStatus = messageDB.getFriendRequestStatus(messageID)
if (messageFRStatus == LokiMessageFriendRequestStatus.REQUEST_SENDING) {
messageDB.setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_FAILED)
}
val threadDB = DatabaseFactory.getLokiThreadDatabase(context)
val threadFRStatus = threadDB.getFriendRequestStatus(threadID)
if (threadFRStatus == LokiThreadFriendRequestStatus.REQUEST_SENDING) {
threadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.NONE)
}
}
fun sendAutoGeneratedFriendRequest(context: Context, publicKey: String) {
val recipient = recipient(context, publicKey)
val message = OutgoingEncryptedMessage(recipient, "Please accept to enable messages to be synced across devices", 0)
message.isFriendRequest = true
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
MessageSender.send(context, message, threadID, false, null)
}
}

View File

@ -23,7 +23,6 @@ import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol
import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink
import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLinkingSession import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLinkingSession
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
import org.whispersystems.signalservice.loki.utilities.retryIfNeeded import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
object MultiDeviceProtocol { object MultiDeviceProtocol {
@ -40,36 +39,6 @@ object MultiDeviceProtocol {
sendMessagePush(context, recipient, messageID, MessageType.Media, false) sendMessagePush(context, recipient, messageID, MessageType.Media, false)
} }
private fun sendMessagePushToDevice(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType, isEndSession: Boolean): PushSendJob {
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
val threadFRStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID)
val isNoteToSelf = SessionMetaProtocol.shared.isNoteToSelf(recipient.address.serialize())
val isContactFriend = (threadFRStatus == LokiThreadFriendRequestStatus.FRIENDS || isNoteToSelf) // In the note to self case the device linking request was the FR
val isFRMessage = !isContactFriend
val hasVisibleContent = when (messageType) {
MessageType.Text -> DatabaseFactory.getSmsDatabase(context).getMessage(messageID).body.isNotBlank()
MessageType.Media -> {
val outgoingMediaMessage = DatabaseFactory.getMmsDatabase(context).getOutgoingMessage(messageID)
outgoingMediaMessage.body.isNotBlank() || outgoingMediaMessage.attachments.isNotEmpty()
}
}
val shouldSendAutoGeneratedFR = !isContactFriend && !isFRMessage
&& !isNoteToSelf && !recipient.address.isGroup // Group threads work through session requests
&& hasVisibleContent && !isEndSession
if (!shouldSendAutoGeneratedFR) {
when (messageType) {
MessageType.Text -> return PushTextSendJob(messageID, messageID, recipient.address, isFRMessage, null)
MessageType.Media -> return PushMediaSendJob(messageID, messageID, recipient.address, isFRMessage, null)
}
} else {
val autoGeneratedFRMessage = "Please accept to enable messages to be synced across devices"
when (messageType) {
MessageType.Text -> return PushTextSendJob(messageID, messageID, recipient.address, true, autoGeneratedFRMessage)
MessageType.Media -> return PushMediaSendJob(messageID, messageID, recipient.address, true, autoGeneratedFRMessage)
}
}
}
private fun sendMessagePush(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType, isEndSession: Boolean) { private fun sendMessagePush(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType, isEndSession: Boolean) {
val jobManager = ApplicationContext.getInstance(context).jobManager val jobManager = ApplicationContext.getInstance(context).jobManager
val isMultiDeviceRequired = !recipient.address.isOpenGroup val isMultiDeviceRequired = !recipient.address.isOpenGroup
@ -82,7 +51,12 @@ object MultiDeviceProtocol {
val publicKey = recipient.address.serialize() val publicKey = recipient.address.serialize()
FileServerAPI.shared.getDeviceLinks(publicKey).success { FileServerAPI.shared.getDeviceLinks(publicKey).success {
val devices = MultiDeviceProtocol.shared.getAllLinkedDevices(publicKey) val devices = MultiDeviceProtocol.shared.getAllLinkedDevices(publicKey)
val jobs = devices.map { sendMessagePushToDevice(context, recipient(context, it), messageID, messageType, isEndSession) } val jobs = devices.map {
when (messageType) {
MessageType.Text -> PushTextSendJob(messageID, messageID, recipient(context, it).address) as PushSendJob
MessageType.Media -> PushMediaSendJob(messageID, messageID, recipient(context, it).address) as PushSendJob
}
}
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
when (messageType) { when (messageType) {
MessageType.Text -> jobManager.startChain(jobs).enqueue() MessageType.Text -> jobManager.startChain(jobs).enqueue()

View File

@ -27,8 +27,8 @@ object SessionManagementProtocol {
val smsDB = DatabaseFactory.getSmsDatabase(context) val smsDB = DatabaseFactory.getSmsDatabase(context)
val devices = lokiThreadDB.getSessionRestoreDevices(threadID) val devices = lokiThreadDB.getSessionRestoreDevices(threadID)
for (device in devices) { for (device in devices) {
val sessionRestorationRequest = EphemeralMessage.createSessionRestorationRequest(recipient.address.serialize()) val sessionRequest = EphemeralMessage.createSessionRequest(recipient.address.serialize())
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(sessionRestorationRequest)) ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(sessionRequest))
} }
val infoMessage = OutgoingTextMessage(recipient, "", 0, 0) val infoMessage = OutgoingTextMessage(recipient, "", 0, 0)
val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null) val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null)

View File

@ -11,7 +11,6 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.messages.SignalServiceContent import org.whispersystems.signalservice.api.messages.SignalServiceContent
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
import java.security.MessageDigest import java.security.MessageDigest
object SessionMetaProtocol { object SessionMetaProtocol {
@ -75,31 +74,23 @@ object SessionMetaProtocol {
* Should be invoked for the recipient's master device. * Should be invoked for the recipient's master device.
*/ */
@JvmStatic @JvmStatic
fun canUserReplyToNotification(recipient: Recipient, context: Context): Boolean { fun canUserReplyToNotification(recipient: Recipient): Boolean {
val isGroup = recipient.isGroupRecipient return !recipient.address.isRSSFeed
if (isGroup) { return !recipient.address.isRSSFeed }
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
return DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS
} }
/** /**
* Should be invoked for the recipient's master device. * Should be invoked for the recipient's master device.
*/ */
@JvmStatic @JvmStatic
fun shouldSendReadReceipt(address: Address, context: Context): Boolean { fun shouldSendReadReceipt(address: Address): Boolean {
if (address.isGroup) { return false } return !address.isGroup
val recipient = Recipient.from(context, address,false)
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
return DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS
} }
/** /**
* Should be invoked for the recipient's master device. * Should be invoked for the recipient's master device.
*/ */
@JvmStatic @JvmStatic
fun shouldSendTypingIndicator(recipient: Recipient?, context: Context): Boolean { fun shouldSendTypingIndicator(address: Address): Boolean {
if (recipient == null || recipient.isGroupRecipient) { return false } return !address.isGroup
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
return DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS
} }
} }

View File

@ -7,7 +7,7 @@ import org.whispersystems.libsignal.loki.SessionResetProtocol
import org.whispersystems.libsignal.loki.SessionResetStatus import org.whispersystems.libsignal.loki.SessionResetStatus
import org.whispersystems.libsignal.protocol.PreKeySignalMessage import org.whispersystems.libsignal.protocol.PreKeySignalMessage
class LokiSessionResetImplementation(private val context: Context) : SessionResetProtocol { class SessionResetImplementation(private val context: Context) : SessionResetProtocol {
override fun getSessionResetStatus(publicKey: String): SessionResetStatus { override fun getSessionResetStatus(publicKey: String): SessionResetStatus {
return DatabaseFactory.getLokiThreadDatabase(context).getSessionResetStatus(publicKey) return DatabaseFactory.getLokiThreadDatabase(context).getSessionResetStatus(publicKey)

View File

@ -24,8 +24,6 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsI
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequestStatus
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation
import java.util.* import java.util.*
@ -67,9 +65,7 @@ object SyncMessagesProtocol {
if (!PublicKeyValidation.isValid(address.serialize())) { return false } if (!PublicKeyValidation.isValid(address.serialize())) { return false }
if (address.serialize() == TextSecurePreferences.getMasterHexEncodedPublicKey(context)) { return false } if (address.serialize() == TextSecurePreferences.getMasterHexEncodedPublicKey(context)) { return false }
if (address.serialize() == TextSecurePreferences.getLocalNumber(context)) { return false } if (address.serialize() == TextSecurePreferences.getLocalNumber(context)) { return false }
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, address, false)) return true
val isFriend = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS
return isFriend
} }
@JvmStatic @JvmStatic
@ -99,29 +95,9 @@ object SyncMessagesProtocol {
for (contactPublicKey in contactPublicKeys) { for (contactPublicKey in contactPublicKeys) {
if (contactPublicKey == userPublicKey || !PublicKeyValidation.isValid(contactPublicKey)) { return } if (contactPublicKey == userPublicKey || !PublicKeyValidation.isValid(contactPublicKey)) { return }
val recipient = recipient(context, contactPublicKey) val recipient = recipient(context, contactPublicKey)
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient) val applicationContext = context.applicationContext as ApplicationContext
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context) applicationContext.sendSessionRequestIfNeeded(contactPublicKey)
val threadFRStatus = lokiThreadDB.getFriendRequestStatus(threadID) // TODO: Make the thread visible
when (threadFRStatus) {
LokiThreadFriendRequestStatus.NONE, LokiThreadFriendRequestStatus.REQUEST_EXPIRED -> {
val contactLinkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(contactPublicKey)
for (device in contactLinkedDevices) {
FriendRequestProtocol.sendAutoGeneratedFriendRequest(context, device)
}
}
LokiThreadFriendRequestStatus.REQUEST_RECEIVED -> {
FriendRequestProtocol.acceptFriendRequest(context, recipient(context, contactPublicKey)) // Takes into account multi device internally
lokiThreadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS)
val lastMessageID = FriendRequestProtocol.getLastMessageID(context, threadID)
if (lastMessageID != null) {
DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED)
}
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient(context, contactPublicKey), true)
}
else -> {
// Do nothing
}
}
} }
} }

View File

@ -5,11 +5,9 @@ import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
data class Contact( data class Contact(
val recipient: Recipient, val recipient: Recipient,
val isFriend: Boolean,
val isSlave: Boolean, val isSlave: Boolean,
val isOurDevice: Boolean val isOurDevice: Boolean
) { ) {
@ -31,10 +29,9 @@ object ContactUtilities {
@JvmStatic @JvmStatic
fun getAllContacts(context: Context): Set<Contact> { fun getAllContacts(context: Context): Set<Contact> {
val threadDatabase = DatabaseFactory.getThreadDatabase(context) val threadDatabase = DatabaseFactory.getThreadDatabase(context)
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context) val userPublicKey = TextSecurePreferences.getLocalNumber(context)
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context) val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context)
val userDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userHexEncodedPublicKey) val userDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey)
val cursor = threadDatabase.conversationList val cursor = threadDatabase.conversationList
val result = mutableSetOf<Contact>() val result = mutableSetOf<Contact>()
threadDatabase.readerFor(cursor).use { reader -> threadDatabase.readerFor(cursor).use { reader ->
@ -43,13 +40,12 @@ object ContactUtilities {
val recipient = thread.recipient val recipient = thread.recipient
val publicKey = recipient.address.serialize() val publicKey = recipient.address.serialize()
val isUserDevice = userDevices.contains(publicKey) val isUserDevice = userDevices.contains(publicKey)
val isFriend = lokiThreadDatabase.getFriendRequestStatus(thread.threadId) == LokiThreadFriendRequestStatus.FRIENDS
var isSlave = false var isSlave = false
if (!recipient.isGroupRecipient) { if (!recipient.isGroupRecipient) {
val deviceLinks = lokiAPIDatabase.getDeviceLinks(publicKey) val deviceLinks = lokiAPIDatabase.getDeviceLinks(publicKey)
isSlave = deviceLinks.find { it.slavePublicKey == publicKey } != null isSlave = deviceLinks.find { it.slavePublicKey == publicKey } != null
} }
result.add(Contact(recipient, isFriend, isSlave, isUserDevice)) result.add(Contact(recipient, isSlave, isUserDevice))
} }
} }
return result return result

View File

@ -8,11 +8,11 @@ import org.thoughtcrime.securesms.recipients.Recipient
fun getOpenGroupDisplayName(recipient: Recipient, threadRecipient: Recipient, context: Context): String { fun getOpenGroupDisplayName(recipient: Recipient, threadRecipient: Recipient, context: Context): String {
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(threadRecipient) val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(threadRecipient)
val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
val hexEncodedPublicKey = recipient.address.toString() val publicKey = recipient.address.toString()
val displayName = if (publicChat != null) { val displayName = if (publicChat != null) {
DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, hexEncodedPublicKey) DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, publicKey)
} else { } else {
DatabaseFactory.getLokiUserDatabase(context).getDisplayName(hexEncodedPublicKey) DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey)
} }
return displayName ?: hexEncodedPublicKey return displayName ?: publicKey
} }

View File

@ -22,7 +22,7 @@ object OpenGroupUtilities {
val application = ApplicationContext.getInstance(context) val application = ApplicationContext.getInstance(context)
val displayName = TextSecurePreferences.getProfileName(context) val displayName = TextSecurePreferences.getProfileName(context)
val lokiPublicChatAPI = application.publicChatAPI ?: throw Error("LokiPublicChatAPI is not initialized.") val lokiPublicChatAPI = application.publicChatAPI ?: throw Error("LokiPublicChatAPI is not initialized.")
return application.lokiPublicChatManager.addChat(url, channel).then { group -> return application.publicChatManager.addChat(url, channel).then { group ->
DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url) DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url)
DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url) DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url)
lokiPublicChatAPI.getMessages(channel, url) lokiPublicChatAPI.getMessages(channel, url)

View File

@ -50,18 +50,18 @@ class ConversationView : LinearLayout {
unreadMessagesIndicatorView.visibility = if (thread.unreadCount > 0) View.VISIBLE else View.INVISIBLE unreadMessagesIndicatorView.visibility = if (thread.unreadCount > 0) View.VISIBLE else View.INVISIBLE
if (thread.recipient.isGroupRecipient) { if (thread.recipient.isGroupRecipient) {
if ("Session Public Chat" == thread.recipient.name) { if ("Session Public Chat" == thread.recipient.name) {
profilePictureView.hexEncodedPublicKey = "" profilePictureView.publicKey = ""
profilePictureView.isRSSFeed = true profilePictureView.isRSSFeed = true
} else { } else {
val users = MentionsManager.shared.userPublicKeyCache[thread.threadId]?.toList() ?: listOf() val users = MentionsManager.shared.userPublicKeyCache[thread.threadId]?.toList() ?: listOf()
val randomUsers = users.sorted() // Sort to provide a level of stability val randomUsers = users.sorted() // Sort to provide a level of stability
profilePictureView.hexEncodedPublicKey = randomUsers.getOrNull(0) ?: "" profilePictureView.publicKey = randomUsers.getOrNull(0) ?: ""
profilePictureView.additionalHexEncodedPublicKey = randomUsers.getOrNull(1) ?: "" profilePictureView.additionalPublicKey = randomUsers.getOrNull(1) ?: ""
profilePictureView.isRSSFeed = thread.recipient.name == "Loki News" || thread.recipient.name == "Session Updates" profilePictureView.isRSSFeed = thread.recipient.name == "Loki News" || thread.recipient.name == "Session Updates"
} }
} else { } else {
profilePictureView.hexEncodedPublicKey = thread.recipient.address.toString() profilePictureView.publicKey = thread.recipient.address.toString()
profilePictureView.additionalHexEncodedPublicKey = null profilePictureView.additionalPublicKey = null
profilePictureView.isRSSFeed = false profilePictureView.isRSSFeed = false
} }
profilePictureView.glide = glide profilePictureView.glide = glide

View File

@ -1,190 +0,0 @@
package org.thoughtcrime.securesms.loki.views
import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.widget.Button
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.TextView
import com.github.ybq.android.spinkit.style.DoubleBounce
import network.loki.messenger.R
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
import org.thoughtcrime.securesms.loki.utilities.toPx
import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequestStatus
class FriendRequestView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) {
private var isUISetUp = false
private var message: MessageRecord? = null
var delegate: FriendRequestViewDelegate? = null
// region Components
private val topSpacer by lazy {
val result = View(context)
result.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, toPx(12, resources))
result
}
private val label by lazy {
val result = TextView(context)
result.setTextColor(resources.getColorWithID(R.color.text, context.theme))
result.textAlignment = TextView.TEXT_ALIGNMENT_CENTER
result.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.small_font_size))
result
}
private val buttonLinearLayout by lazy {
val result = LinearLayout(context)
result.orientation = HORIZONTAL
result.setPadding(0, resources.getDimension(R.dimen.medium_spacing).toInt(), 0, 0)
result
}
private val loaderContainer by lazy {
val result = LinearLayout(context)
val layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, toPx(50, resources))
result.layoutParams = layoutParams
result.gravity = Gravity.CENTER
result
}
// endregion
// region Initialization
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context) : this(context, null)
// endregion
// region Updating
fun update(message: MessageRecord) {
this.message = message
setUpUIIfNeeded()
updateUI()
}
private fun setUpUIIfNeeded() {
if (isUISetUp) { return }
isUISetUp = true
orientation = VERTICAL
setPadding(toPx(48, resources), 0, toPx(48, resources), 0)
addView(topSpacer)
addView(label)
if (!message!!.isOutgoing) {
val loader = ProgressBar(context)
loader.isIndeterminate = true
loader.indeterminateDrawable = DoubleBounce()
val loaderLayoutParams = LayoutParams(LayoutParams.MATCH_PARENT, toPx(24, resources))
loader.layoutParams = loaderLayoutParams
loaderContainer.addView(loader)
addView(loaderContainer)
fun button(): Button {
val result = Button(context)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
result.elevation = 0f
result.stateListAnimator = null
}
result.setTextColor(resources.getColorWithID(R.color.text, context.theme))
result.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.small_font_size))
result.isAllCaps = false
result.setPadding(0, 0, 0, 0)
val buttonLayoutParams = LayoutParams(0, resources.getDimension(R.dimen.small_button_height).toInt())
buttonLayoutParams.weight = 1f
result.layoutParams = buttonLayoutParams
return result
}
val rejectButton = button()
rejectButton.text = resources.getString(R.string.view_friend_request_reject_button_title)
rejectButton.setBackgroundResource(R.drawable.unimportant_dialog_button_background)
rejectButton.setOnClickListener { reject() }
buttonLinearLayout.addView(rejectButton)
val acceptButton = button()
acceptButton.text = resources.getString(R.string.view_friend_request_accept_button_title)
acceptButton.setBackgroundResource(R.drawable.prominent_dialog_button_background)
val acceptButtonLayoutParams = acceptButton.layoutParams as LayoutParams
acceptButtonLayoutParams.setMargins(resources.getDimension(R.dimen.medium_spacing).toInt(), 0, 0, 0)
acceptButton.layoutParams = acceptButtonLayoutParams
acceptButton.setOnClickListener { accept() }
buttonLinearLayout.addView(acceptButton)
buttonLinearLayout.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, toPx(50, resources))
addView(buttonLinearLayout)
}
}
private fun updateUI() {
val message = message
val lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context)
val contactID = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(message!!.threadId)!!.address.toString()
val contactDisplayName = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(contactID) ?: contactID
val friendRequestStatus = lokiMessageDatabase.getFriendRequestStatus(message.id)
if (message is MediaMmsMessageRecord) {
visibility = View.GONE
return
}
if (!message.isOutgoing) {
visibility = if (friendRequestStatus == LokiMessageFriendRequestStatus.NONE) View.GONE else View.VISIBLE
buttonLinearLayout.visibility = if (friendRequestStatus != LokiMessageFriendRequestStatus.REQUEST_PENDING) View.GONE else View.VISIBLE
loaderContainer.visibility = if (friendRequestStatus == LokiMessageFriendRequestStatus.REQUEST_SENDING) View.VISIBLE else View.GONE
val formatID = when (friendRequestStatus) {
LokiMessageFriendRequestStatus.NONE, LokiMessageFriendRequestStatus.REQUEST_SENDING, LokiMessageFriendRequestStatus.REQUEST_FAILED -> return
LokiMessageFriendRequestStatus.REQUEST_PENDING -> R.string.view_friend_request_incoming_pending_message
LokiMessageFriendRequestStatus.REQUEST_ACCEPTED -> R.string.view_friend_request_incoming_accepted_message
LokiMessageFriendRequestStatus.REQUEST_REJECTED -> R.string.view_friend_request_incoming_declined_message
LokiMessageFriendRequestStatus.REQUEST_EXPIRED -> R.string.view_friend_request_incoming_expired_message
}
label.text = resources.getString(formatID, contactDisplayName)
} else {
visibility = if (friendRequestStatus == LokiMessageFriendRequestStatus.NONE) View.GONE else View.VISIBLE
buttonLinearLayout.visibility = View.GONE
loaderContainer.visibility = View.GONE
val formatID = when (friendRequestStatus) {
LokiMessageFriendRequestStatus.NONE -> return
LokiMessageFriendRequestStatus.REQUEST_SENDING, LokiMessageFriendRequestStatus.REQUEST_FAILED -> null
LokiMessageFriendRequestStatus.REQUEST_PENDING, LokiMessageFriendRequestStatus.REQUEST_REJECTED -> R.string.view_friend_request_outgoing_pending_message
LokiMessageFriendRequestStatus.REQUEST_ACCEPTED -> R.string.view_friend_request_outgoing_accepted_message
LokiMessageFriendRequestStatus.REQUEST_EXPIRED -> R.string.view_friend_request_outgoing_expired_message
}
if (formatID != null) {
label.text = resources.getString(formatID, contactDisplayName)
}
label.visibility = if (formatID != null) View.VISIBLE else View.GONE
topSpacer.visibility = label.visibility
}
}
// endregion
// region Interaction
private fun accept() {
val lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context)
lokiMessageDatabase.setFriendRequestStatus(message!!.id, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED)
updateUI()
delegate?.acceptFriendRequest(message!!)
}
private fun reject() {
val lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context)
lokiMessageDatabase.setFriendRequestStatus(message!!.id, LokiMessageFriendRequestStatus.REQUEST_REJECTED)
updateUI()
delegate?.rejectFriendRequest(message!!)
}
// endregion
}
// region Delegate
interface FriendRequestViewDelegate {
/**
* Implementations of this method should update the thread's friend request status
* and send a friend request accepted message.
*/
fun acceptFriendRequest(friendRequest: MessageRecord)
/**
* Implementations of this method should update the thread's friend request status
* and remove the pre keys associated with the contact.
*/
fun rejectFriendRequest(friendRequest: MessageRecord)
}
// endregion

View File

@ -31,8 +31,8 @@ class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr:
private fun update() { private fun update() {
displayNameTextView.text = mentionCandidate.displayName displayNameTextView.text = mentionCandidate.displayName
profilePictureView.hexEncodedPublicKey = mentionCandidate.publicKey profilePictureView.publicKey = mentionCandidate.publicKey
profilePictureView.additionalHexEncodedPublicKey = null profilePictureView.additionalPublicKey = null
profilePictureView.isRSSFeed = false profilePictureView.isRSSFeed = false
profilePictureView.glide = glide!! profilePictureView.glide = glide!!
profilePictureView.update() profilePictureView.update()

View File

@ -21,8 +21,8 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences
class ProfilePictureView : RelativeLayout { class ProfilePictureView : RelativeLayout {
lateinit var glide: GlideRequests lateinit var glide: GlideRequests
var hexEncodedPublicKey: String? = null var publicKey: String? = null
var additionalHexEncodedPublicKey: String? = null var additionalPublicKey: String? = null
var isRSSFeed = false var isRSSFeed = false
var isLarge = false var isLarge = false
@ -52,11 +52,11 @@ class ProfilePictureView : RelativeLayout {
// region Updating // region Updating
fun update() { fun update() {
val hexEncodedPublicKey = hexEncodedPublicKey ?: return val publicKey = publicKey ?: return
val additionalHexEncodedPublicKey = additionalHexEncodedPublicKey val additionalPublicKey = additionalPublicKey
doubleModeImageViewContainer.visibility = if (additionalHexEncodedPublicKey != null && !isRSSFeed) View.VISIBLE else View.INVISIBLE doubleModeImageViewContainer.visibility = if (additionalPublicKey != null && !isRSSFeed) View.VISIBLE else View.INVISIBLE
singleModeImageViewContainer.visibility = if (additionalHexEncodedPublicKey == null && !isRSSFeed && !isLarge) View.VISIBLE else View.INVISIBLE singleModeImageViewContainer.visibility = if (additionalPublicKey == null && !isRSSFeed && !isLarge) View.VISIBLE else View.INVISIBLE
largeSingleModeImageViewContainer.visibility = if (additionalHexEncodedPublicKey == null && !isRSSFeed && isLarge) View.VISIBLE else View.INVISIBLE largeSingleModeImageViewContainer.visibility = if (additionalPublicKey == null && !isRSSFeed && isLarge) View.VISIBLE else View.INVISIBLE
rssImageView.visibility = if (isRSSFeed) View.VISIBLE else View.INVISIBLE rssImageView.visibility = if (isRSSFeed) View.VISIBLE else View.INVISIBLE
fun setProfilePictureIfNeeded(imageView: ImageView, hexEncodedPublicKey: String, @DimenRes sizeID: Int) { fun setProfilePictureIfNeeded(imageView: ImageView, hexEncodedPublicKey: String, @DimenRes sizeID: Int) {
glide.clear(imageView) glide.clear(imageView)
@ -76,10 +76,10 @@ class ProfilePictureView : RelativeLayout {
imageView.setImageDrawable(null) imageView.setImageDrawable(null)
} }
} }
setProfilePictureIfNeeded(doubleModeImageView1, hexEncodedPublicKey, R.dimen.small_profile_picture_size) setProfilePictureIfNeeded(doubleModeImageView1, publicKey, R.dimen.small_profile_picture_size)
setProfilePictureIfNeeded(doubleModeImageView2, additionalHexEncodedPublicKey ?: "", R.dimen.small_profile_picture_size) setProfilePictureIfNeeded(doubleModeImageView2, additionalPublicKey ?: "", R.dimen.small_profile_picture_size)
setProfilePictureIfNeeded(singleModeImageView, hexEncodedPublicKey, R.dimen.medium_profile_picture_size) setProfilePictureIfNeeded(singleModeImageView, publicKey, R.dimen.medium_profile_picture_size)
setProfilePictureIfNeeded(largeSingleModeImageView, hexEncodedPublicKey, R.dimen.large_profile_picture_size) setProfilePictureIfNeeded(largeSingleModeImageView, publicKey, R.dimen.large_profile_picture_size)
} }
// endregion // endregion
} }

View File

@ -48,20 +48,20 @@ class UserView : LinearLayout {
val address = user.address.serialize() val address = user.address.serialize()
if (user.isGroupRecipient) { if (user.isGroupRecipient) {
if ("Session Public Chat" == user.name || user.address.isRSSFeed) { if ("Session Public Chat" == user.name || user.address.isRSSFeed) {
profilePictureView.hexEncodedPublicKey = "" profilePictureView.publicKey = ""
profilePictureView.additionalHexEncodedPublicKey = null profilePictureView.additionalPublicKey = null
profilePictureView.isRSSFeed = true profilePictureView.isRSSFeed = true
} else { } else {
val threadID = GroupManager.getThreadIDFromGroupID(address, context) val threadID = GroupManager.getThreadIDFromGroupID(address, context)
val users = MentionsManager.shared.userPublicKeyCache[threadID]?.toList() ?: listOf() val users = MentionsManager.shared.userPublicKeyCache[threadID]?.toList() ?: listOf()
val randomUsers = users.sorted() // Sort to provide a level of stability val randomUsers = users.sorted() // Sort to provide a level of stability
profilePictureView.hexEncodedPublicKey = randomUsers.getOrNull(0) ?: "" profilePictureView.publicKey = randomUsers.getOrNull(0) ?: ""
profilePictureView.additionalHexEncodedPublicKey = randomUsers.getOrNull(1) ?: "" profilePictureView.additionalPublicKey = randomUsers.getOrNull(1) ?: ""
profilePictureView.isRSSFeed = false profilePictureView.isRSSFeed = false
} }
} else { } else {
profilePictureView.hexEncodedPublicKey = address profilePictureView.publicKey = address
profilePictureView.additionalHexEncodedPublicKey = null profilePictureView.additionalPublicKey = null
profilePictureView.isRSSFeed = false profilePictureView.isRSSFeed = false
} }
profilePictureView.glide = glide profilePictureView.glide = glide

View File

@ -23,7 +23,6 @@ public class OutgoingMediaMessage {
private final int distributionType; private final int distributionType;
private final int subscriptionId; private final int subscriptionId;
private final long expiresIn; private final long expiresIn;
public boolean isFriendRequest = false;
private final QuoteModel outgoingQuote; private final QuoteModel outgoingQuote;
private final List<NetworkFailure> networkFailures = new LinkedList<>(); private final List<NetworkFailure> networkFailures = new LinkedList<>();

View File

@ -325,7 +325,7 @@ public class DefaultMessageNotifier implements MessageNotifier {
ReplyMethod replyMethod = ReplyMethod.forRecipient(context, recipient); ReplyMethod replyMethod = ReplyMethod.forRecipient(context, recipient);
boolean canReply = SessionMetaProtocol.canUserReplyToNotification(recipient, context); boolean canReply = SessionMetaProtocol.canUserReplyToNotification(recipient);
PendingIntent quickReplyIntent = canReply ? notificationState.getQuickReplyIntent(context, recipient) : null; PendingIntent quickReplyIntent = canReply ? notificationState.getQuickReplyIntent(context, recipient) : null;
PendingIntent remoteReplyIntent = canReply ? notificationState.getRemoteReplyIntent(context, recipient, replyMethod) : null; PendingIntent remoteReplyIntent = canReply ? notificationState.getRemoteReplyIntent(context, recipient, replyMethod) : null;

View File

@ -93,7 +93,7 @@ public class MarkReadReceiver extends BroadcastReceiver {
for (Address address : addressMap.keySet()) { for (Address address : addressMap.keySet()) {
List<Long> timestamps = Stream.of(addressMap.get(address)).map(SyncMessageId::getTimetamp).toList(); List<Long> timestamps = Stream.of(addressMap.get(address)).map(SyncMessageId::getTimetamp).toList();
// Loki - Check whether we want to send a read receipt to this user // Loki - Check whether we want to send a read receipt to this user
if (!SessionMetaProtocol.shouldSendReadReceipt(address, context)) { continue; } if (!SessionMetaProtocol.shouldSendReadReceipt(address)) { continue; }
// Loki - Take into account multi device // Loki - Take into account multi device
Set<String> linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(address.serialize()); Set<String> linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(address.serialize());
for (String device : linkedDevices) { for (String device : linkedDevices) {

View File

@ -7,7 +7,7 @@ import androidx.annotation.MainThread;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.loki.api.LokiPublicChatManager; import org.thoughtcrime.securesms.loki.api.PublicChatManager;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Debouncer; import org.thoughtcrime.securesms.util.Debouncer;
import org.whispersystems.signalservice.loki.api.Poller; import org.whispersystems.signalservice.loki.api.Poller;
@ -41,14 +41,14 @@ public class OptimizedMessageNotifier implements MessageNotifier {
@Override @Override
public void updateNotification(@NonNull Context context) { public void updateNotification(@NonNull Context context) {
Poller lokiPoller = ApplicationContext.getInstance(context).lokiPoller; Poller lokiPoller = ApplicationContext.getInstance(context).lokiPoller;
LokiPublicChatManager lokiPublicChatManager = ApplicationContext.getInstance(context).lokiPublicChatManager; PublicChatManager publicChatManager = ApplicationContext.getInstance(context).publicChatManager;
boolean isCaughtUp = true; boolean isCaughtUp = true;
if (lokiPoller != null) { if (lokiPoller != null) {
isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp(); isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp();
} }
if (lokiPublicChatManager != null) { if (publicChatManager != null) {
isCaughtUp = isCaughtUp && lokiPublicChatManager.areAllCaughtUp(); isCaughtUp = isCaughtUp && publicChatManager.areAllCaughtUp();
} }
if (isCaughtUp) { if (isCaughtUp) {
@ -61,14 +61,14 @@ public class OptimizedMessageNotifier implements MessageNotifier {
@Override @Override
public void updateNotification(@NonNull Context context, long threadId) { public void updateNotification(@NonNull Context context, long threadId) {
Poller lokiPoller = ApplicationContext.getInstance(context).lokiPoller; Poller lokiPoller = ApplicationContext.getInstance(context).lokiPoller;
LokiPublicChatManager lokiPublicChatManager = ApplicationContext.getInstance(context).lokiPublicChatManager; PublicChatManager publicChatManager = ApplicationContext.getInstance(context).publicChatManager;
boolean isCaughtUp = true; boolean isCaughtUp = true;
if (lokiPoller != null) { if (lokiPoller != null) {
isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp(); isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp();
} }
if (lokiPublicChatManager != null) { if (publicChatManager != null) {
isCaughtUp = isCaughtUp && lokiPublicChatManager.areAllCaughtUp(); isCaughtUp = isCaughtUp && publicChatManager.areAllCaughtUp();
} }
if (isCaughtUp) { if (isCaughtUp) {
@ -81,14 +81,14 @@ public class OptimizedMessageNotifier implements MessageNotifier {
@Override @Override
public void updateNotification(@NonNull Context context, long threadId, boolean signal) { public void updateNotification(@NonNull Context context, long threadId, boolean signal) {
Poller lokiPoller = ApplicationContext.getInstance(context).lokiPoller; Poller lokiPoller = ApplicationContext.getInstance(context).lokiPoller;
LokiPublicChatManager lokiPublicChatManager = ApplicationContext.getInstance(context).lokiPublicChatManager; PublicChatManager publicChatManager = ApplicationContext.getInstance(context).publicChatManager;
boolean isCaughtUp = true; boolean isCaughtUp = true;
if (lokiPoller != null) { if (lokiPoller != null) {
isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp(); isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp();
} }
if (lokiPublicChatManager != null) { if (publicChatManager != null) {
isCaughtUp = isCaughtUp && lokiPublicChatManager.areAllCaughtUp(); isCaughtUp = isCaughtUp && publicChatManager.areAllCaughtUp();
} }
if (isCaughtUp) { if (isCaughtUp) {
@ -101,14 +101,14 @@ public class OptimizedMessageNotifier implements MessageNotifier {
@Override @Override
public void updateNotification(@android.support.annotation.NonNull Context context, boolean signal, int reminderCount) { public void updateNotification(@android.support.annotation.NonNull Context context, boolean signal, int reminderCount) {
Poller lokiPoller = ApplicationContext.getInstance(context).lokiPoller; Poller lokiPoller = ApplicationContext.getInstance(context).lokiPoller;
LokiPublicChatManager lokiPublicChatManager = ApplicationContext.getInstance(context).lokiPublicChatManager; PublicChatManager publicChatManager = ApplicationContext.getInstance(context).publicChatManager;
boolean isCaughtUp = true; boolean isCaughtUp = true;
if (lokiPoller != null) { if (lokiPoller != null) {
isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp(); isCaughtUp = isCaughtUp && lokiPoller.isCaughtUp();
} }
if (lokiPublicChatManager != null) { if (publicChatManager != null) {
isCaughtUp = isCaughtUp && lokiPublicChatManager.areAllCaughtUp(); isCaughtUp = isCaughtUp && publicChatManager.areAllCaughtUp();
} }
if (isCaughtUp) { if (isCaughtUp) {

View File

@ -38,7 +38,6 @@ import org.thoughtcrime.securesms.jobs.MmsSendJob;
import org.thoughtcrime.securesms.jobs.PushGroupSendJob; import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
import org.thoughtcrime.securesms.jobs.SmsSendJob; import org.thoughtcrime.securesms.jobs.SmsSendJob;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol;
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol; import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
@ -76,11 +75,6 @@ public class MessageSender {
long messageId = database.insertMessageOutbox(allocatedThreadId, message, forceSms, System.currentTimeMillis(), insertListener); long messageId = database.insertMessageOutbox(allocatedThreadId, message, forceSms, System.currentTimeMillis(), insertListener);
// Loki - Set the message's friend request status as soon as it hits the database
if (FriendRequestProtocol.shouldUpdateFriendRequestStatusFromOutgoingTextMessage(context, message)) {
FriendRequestProtocol.setFriendRequestStatusToSendingIfNeeded(context, messageId, allocatedThreadId);
}
sendTextMessage(context, recipient, forceSms, keyExchange, messageId, message.isEndSession()); sendTextMessage(context, recipient, forceSms, keyExchange, messageId, message.isEndSession());
return allocatedThreadId; return allocatedThreadId;
@ -107,11 +101,6 @@ public class MessageSender {
Recipient recipient = message.getRecipient(); Recipient recipient = message.getRecipient();
long messageId = database.insertMessageOutbox(message, allocatedThreadId, forceSms, insertListener); long messageId = database.insertMessageOutbox(message, allocatedThreadId, forceSms, insertListener);
// Loki - Set the message's friend request status as soon as it hits the database
if (FriendRequestProtocol.shouldUpdateFriendRequestStatusFromOutgoingMediaMessage(context, message)) {
FriendRequestProtocol.setFriendRequestStatusToSendingIfNeeded(context, messageId, allocatedThreadId);
}
sendMediaMessage(context, recipient, forceSms, messageId, message.getExpiresIn()); sendMediaMessage(context, recipient, forceSms, messageId, message.getExpiresIn());
return allocatedThreadId; return allocatedThreadId;
} catch (MmsException e) { } catch (MmsException e) {

View File

@ -9,7 +9,6 @@ public class OutgoingTextMessage {
private final String message; private final String message;
private final int subscriptionId; private final int subscriptionId;
private final long expiresIn; private final long expiresIn;
public boolean isFriendRequest = false;
public OutgoingTextMessage(Recipient recipient, String message, int subscriptionId) { public OutgoingTextMessage(Recipient recipient, String message, int subscriptionId) {
this(recipient, message, 0, subscriptionId); this(recipient, message, 0, subscriptionId);